Commit 48445159 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:
 "Some significant improvements for the Sony driver on newer machines,
  but other than that mostly just minor fixes and a patch to remove the
  broken rfkill code from the Dell driver."

* 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86: (35 commits)
  apple-gmux: Fix up the suspend/resume patch
  dell-laptop: Remove rfkill code
  toshiba_acpi: Fix mis-merge
  dell-laptop: Add touchpad led support for Dell V3450
  acer-wmi: add 3 laptops to video backlight vendor mode quirk table
  sony-laptop: add touchpad enable/disable function
  sony-laptop: add missing Fn key combos for 0x100 handlers
  sony-laptop: add support for more WWAN modems
  sony-laptop: new keyboard backlight handle
  sony-laptop: add high speed battery charging function
  sony-laptop: support automatic resume on lid open
  sony-laptop: adjust error handling in finding SNC handles
  sony-laptop: add thermal profiles support
  sony-laptop: support battery care functions
  sony-laptop: additional debug statements
  sony-laptop: improve SNC initialization and acpi notify callback code
  sony-laptop: use kstrtoul to parse sysfs values
  sony-laptop: generalise ACPI calls into SNC functions
  sony-laptop: fix return path when no ACPI buffer is allocated
  sony-laptop: use soft rfkill status stored in hw
  ...
parents af4f8ba3 a2f01a89
...@@ -523,6 +523,30 @@ static const struct dmi_system_id video_vendor_dmi_table[] = { ...@@ -523,6 +523,30 @@ static const struct dmi_system_id video_vendor_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4750"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4750"),
}, },
}, },
{
.callback = video_set_backlight_video_vendor,
.ident = "Acer Extensa 5235",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Extensa 5235"),
},
},
{
.callback = video_set_backlight_video_vendor,
.ident = "Acer TravelMate 5760",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5760"),
},
},
{
.callback = video_set_backlight_video_vendor,
.ident = "Acer Aspire 5750",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5750"),
},
},
{} {}
}; };
......
...@@ -87,6 +87,9 @@ static int gmux_update_status(struct backlight_device *bd) ...@@ -87,6 +87,9 @@ static int gmux_update_status(struct backlight_device *bd)
struct apple_gmux_data *gmux_data = bl_get_data(bd); struct apple_gmux_data *gmux_data = bl_get_data(bd);
u32 brightness = bd->props.brightness; u32 brightness = bd->props.brightness;
if (bd->props.state & BL_CORE_SUSPENDED)
return 0;
/* /*
* Older gmux versions require writing out lower bytes first then * Older gmux versions require writing out lower bytes first then
* setting the upper byte to 0 to flush the values. Newer versions * setting the upper byte to 0 to flush the values. Newer versions
...@@ -102,6 +105,7 @@ static int gmux_update_status(struct backlight_device *bd) ...@@ -102,6 +105,7 @@ static int gmux_update_status(struct backlight_device *bd)
} }
static const struct backlight_ops gmux_bl_ops = { static const struct backlight_ops gmux_bl_ops = {
.options = BL_CORE_SUSPENDRESUME,
.get_brightness = gmux_get_brightness, .get_brightness = gmux_get_brightness,
.update_status = gmux_update_status, .update_status = gmux_update_status,
}; };
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/rfkill.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/mm.h> #include <linux/mm.h>
...@@ -90,11 +89,8 @@ static struct platform_driver platform_driver = { ...@@ -90,11 +89,8 @@ static struct platform_driver platform_driver = {
static struct platform_device *platform_device; static struct platform_device *platform_device;
static struct backlight_device *dell_backlight_device; static struct backlight_device *dell_backlight_device;
static struct rfkill *wifi_rfkill;
static struct rfkill *bluetooth_rfkill;
static struct rfkill *wwan_rfkill;
static const struct dmi_system_id __initdata dell_device_table[] = { static const struct dmi_system_id dell_device_table[] __initconst = {
{ {
.ident = "Dell laptop", .ident = "Dell laptop",
.matches = { .matches = {
...@@ -119,96 +115,94 @@ static const struct dmi_system_id __initdata dell_device_table[] = { ...@@ -119,96 +115,94 @@ static const struct dmi_system_id __initdata dell_device_table[] = {
}; };
MODULE_DEVICE_TABLE(dmi, dell_device_table); MODULE_DEVICE_TABLE(dmi, dell_device_table);
static struct dmi_system_id __devinitdata dell_blacklist[] = { static struct dmi_system_id __devinitdata dell_quirks[] = {
/* Supported by compal-laptop */
{
.ident = "Dell Mini 9",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
},
},
{ {
.ident = "Dell Mini 10", .callback = dmi_matched,
.ident = "Dell Vostro V130",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"),
}, },
.driver_data = &quirk_dell_vostro_v130,
}, },
{ {
.ident = "Dell Mini 10v", .callback = dmi_matched,
.ident = "Dell Vostro V131",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
}, },
.driver_data = &quirk_dell_vostro_v130,
}, },
{ {
.ident = "Dell Mini 1012", .callback = dmi_matched,
.ident = "Dell Vostro 3350",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3350"),
}, },
.driver_data = &quirk_dell_vostro_v130,
}, },
{ {
.ident = "Dell Inspiron 11z", .callback = dmi_matched,
.ident = "Dell Vostro 3555",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"),
}, },
.driver_data = &quirk_dell_vostro_v130,
}, },
{ {
.ident = "Dell Mini 12", .callback = dmi_matched,
.ident = "Dell Inspiron N311z",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"),
}, },
.driver_data = &quirk_dell_vostro_v130,
}, },
{}
};
static struct dmi_system_id __devinitdata dell_quirks[] = {
{ {
.callback = dmi_matched, .callback = dmi_matched,
.ident = "Dell Vostro V130", .ident = "Dell Inspiron M5110",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"), DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"),
}, },
.driver_data = &quirk_dell_vostro_v130, .driver_data = &quirk_dell_vostro_v130,
}, },
{ {
.callback = dmi_matched, .callback = dmi_matched,
.ident = "Dell Vostro V131", .ident = "Dell Vostro 3360",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"),
}, },
.driver_data = &quirk_dell_vostro_v130, .driver_data = &quirk_dell_vostro_v130,
}, },
{ {
.callback = dmi_matched, .callback = dmi_matched,
.ident = "Dell Vostro 3555", .ident = "Dell Vostro 3460",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3460"),
}, },
.driver_data = &quirk_dell_vostro_v130, .driver_data = &quirk_dell_vostro_v130,
}, },
{ {
.callback = dmi_matched, .callback = dmi_matched,
.ident = "Dell Inspiron N311z", .ident = "Dell Vostro 3560",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3560"),
}, },
.driver_data = &quirk_dell_vostro_v130, .driver_data = &quirk_dell_vostro_v130,
}, },
{ {
.callback = dmi_matched, .callback = dmi_matched,
.ident = "Dell Inspiron M5110", .ident = "Dell Vostro 3450",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"), DMI_MATCH(DMI_PRODUCT_NAME, "Dell System Vostro 3450"),
}, },
.driver_data = &quirk_dell_vostro_v130, .driver_data = &quirk_dell_vostro_v130,
}, },
...@@ -305,94 +299,6 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, ...@@ -305,94 +299,6 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
return buffer; return buffer;
} }
/* Derived from information in DellWirelessCtl.cpp:
Class 17, select 11 is radio control. It returns an array of 32-bit values.
Input byte 0 = 0: Wireless information
result[0]: return code
result[1]:
Bit 0: Hardware switch supported
Bit 1: Wifi locator supported
Bit 2: Wifi is supported
Bit 3: Bluetooth is supported
Bit 4: WWAN is supported
Bit 5: Wireless keyboard supported
Bits 6-7: Reserved
Bit 8: Wifi is installed
Bit 9: Bluetooth is installed
Bit 10: WWAN is installed
Bits 11-15: Reserved
Bit 16: Hardware switch is on
Bit 17: Wifi is blocked
Bit 18: Bluetooth is blocked
Bit 19: WWAN is blocked
Bits 20-31: Reserved
result[2]: NVRAM size in bytes
result[3]: NVRAM format version number
Input byte 0 = 2: Wireless switch configuration
result[0]: return code
result[1]:
Bit 0: Wifi controlled by switch
Bit 1: Bluetooth controlled by switch
Bit 2: WWAN controlled by switch
Bits 3-6: Reserved
Bit 7: Wireless switch config locked
Bit 8: Wifi locator enabled
Bits 9-14: Reserved
Bit 15: Wifi locator setting locked
Bits 16-31: Reserved
*/
static int dell_rfkill_set(void *data, bool blocked)
{
int disable = blocked ? 1 : 0;
unsigned long radio = (unsigned long)data;
int hwswitch_bit = (unsigned long)data - 1;
int ret = 0;
get_buffer();
dell_send_request(buffer, 17, 11);
/* If the hardware switch controls this radio, and the hardware
switch is disabled, don't allow changing the software state */
if ((hwswitch_state & BIT(hwswitch_bit)) &&
!(buffer->output[1] & BIT(16))) {
ret = -EINVAL;
goto out;
}
buffer->input[0] = (1 | (radio<<8) | (disable << 16));
dell_send_request(buffer, 17, 11);
out:
release_buffer();
return ret;
}
static void dell_rfkill_query(struct rfkill *rfkill, void *data)
{
int status;
int bit = (unsigned long)data + 16;
int hwswitch_bit = (unsigned long)data - 1;
get_buffer();
dell_send_request(buffer, 17, 11);
status = buffer->output[1];
release_buffer();
rfkill_set_sw_state(rfkill, !!(status & BIT(bit)));
if (hwswitch_state & (BIT(hwswitch_bit)))
rfkill_set_hw_state(rfkill, !(status & BIT(16)));
}
static const struct rfkill_ops dell_rfkill_ops = {
.set_block = dell_rfkill_set,
.query = dell_rfkill_query,
};
static struct dentry *dell_laptop_dir; static struct dentry *dell_laptop_dir;
static int dell_debugfs_show(struct seq_file *s, void *data) static int dell_debugfs_show(struct seq_file *s, void *data)
...@@ -462,108 +368,6 @@ static const struct file_operations dell_debugfs_fops = { ...@@ -462,108 +368,6 @@ static const struct file_operations dell_debugfs_fops = {
.release = single_release, .release = single_release,
}; };
static void dell_update_rfkill(struct work_struct *ignored)
{
if (wifi_rfkill)
dell_rfkill_query(wifi_rfkill, (void *)1);
if (bluetooth_rfkill)
dell_rfkill_query(bluetooth_rfkill, (void *)2);
if (wwan_rfkill)
dell_rfkill_query(wwan_rfkill, (void *)3);
}
static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
static int __init dell_setup_rfkill(void)
{
int status;
int ret;
if (dmi_check_system(dell_blacklist)) {
pr_info("Blacklisted hardware detected - not enabling rfkill\n");
return 0;
}
get_buffer();
dell_send_request(buffer, 17, 11);
status = buffer->output[1];
buffer->input[0] = 0x2;
dell_send_request(buffer, 17, 11);
hwswitch_state = buffer->output[1];
release_buffer();
if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
RFKILL_TYPE_WLAN,
&dell_rfkill_ops, (void *) 1);
if (!wifi_rfkill) {
ret = -ENOMEM;
goto err_wifi;
}
ret = rfkill_register(wifi_rfkill);
if (ret)
goto err_wifi;
}
if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
&platform_device->dev,
RFKILL_TYPE_BLUETOOTH,
&dell_rfkill_ops, (void *) 2);
if (!bluetooth_rfkill) {
ret = -ENOMEM;
goto err_bluetooth;
}
ret = rfkill_register(bluetooth_rfkill);
if (ret)
goto err_bluetooth;
}
if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
wwan_rfkill = rfkill_alloc("dell-wwan",
&platform_device->dev,
RFKILL_TYPE_WWAN,
&dell_rfkill_ops, (void *) 3);
if (!wwan_rfkill) {
ret = -ENOMEM;
goto err_wwan;
}
ret = rfkill_register(wwan_rfkill);
if (ret)
goto err_wwan;
}
return 0;
err_wwan:
rfkill_destroy(wwan_rfkill);
if (bluetooth_rfkill)
rfkill_unregister(bluetooth_rfkill);
err_bluetooth:
rfkill_destroy(bluetooth_rfkill);
if (wifi_rfkill)
rfkill_unregister(wifi_rfkill);
err_wifi:
rfkill_destroy(wifi_rfkill);
return ret;
}
static void dell_cleanup_rfkill(void)
{
if (wifi_rfkill) {
rfkill_unregister(wifi_rfkill);
rfkill_destroy(wifi_rfkill);
}
if (bluetooth_rfkill) {
rfkill_unregister(bluetooth_rfkill);
rfkill_destroy(bluetooth_rfkill);
}
if (wwan_rfkill) {
rfkill_unregister(wwan_rfkill);
rfkill_destroy(wwan_rfkill);
}
}
static int dell_send_intensity(struct backlight_device *bd) static int dell_send_intensity(struct backlight_device *bd)
{ {
int ret = 0; int ret = 0;
...@@ -655,30 +459,6 @@ static void touchpad_led_exit(void) ...@@ -655,30 +459,6 @@ static void touchpad_led_exit(void)
led_classdev_unregister(&touchpad_led); led_classdev_unregister(&touchpad_led);
} }
static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
{
static bool extended;
if (str & 0x20)
return false;
if (unlikely(data == 0xe0)) {
extended = true;
return false;
} else if (unlikely(extended)) {
switch (data) {
case 0x8:
schedule_delayed_work(&dell_rfkill_work,
round_jiffies_relative(HZ));
break;
}
extended = false;
}
return false;
}
static int __init dell_init(void) static int __init dell_init(void)
{ {
int max_intensity = 0; int max_intensity = 0;
...@@ -720,26 +500,10 @@ static int __init dell_init(void) ...@@ -720,26 +500,10 @@ static int __init dell_init(void)
goto fail_buffer; goto fail_buffer;
buffer = page_address(bufferpage); buffer = page_address(bufferpage);
ret = dell_setup_rfkill();
if (ret) {
pr_warn("Unable to setup rfkill\n");
goto fail_rfkill;
}
ret = i8042_install_filter(dell_laptop_i8042_filter);
if (ret) {
pr_warn("Unable to install key filter\n");
goto fail_filter;
}
if (quirks && quirks->touchpad_led) if (quirks && quirks->touchpad_led)
touchpad_led_init(&platform_device->dev); touchpad_led_init(&platform_device->dev);
dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
if (dell_laptop_dir != NULL)
debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
&dell_debugfs_fops);
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
/* In the event of an ACPI backlight being available, don't /* In the event of an ACPI backlight being available, don't
...@@ -782,11 +546,6 @@ static int __init dell_init(void) ...@@ -782,11 +546,6 @@ static int __init dell_init(void)
return 0; return 0;
fail_backlight: fail_backlight:
i8042_remove_filter(dell_laptop_i8042_filter);
cancel_delayed_work_sync(&dell_rfkill_work);
fail_filter:
dell_cleanup_rfkill();
fail_rfkill:
free_page((unsigned long)bufferpage); free_page((unsigned long)bufferpage);
fail_buffer: fail_buffer:
platform_device_del(platform_device); platform_device_del(platform_device);
...@@ -804,10 +563,7 @@ static void __exit dell_exit(void) ...@@ -804,10 +563,7 @@ static void __exit dell_exit(void)
debugfs_remove_recursive(dell_laptop_dir); debugfs_remove_recursive(dell_laptop_dir);
if (quirks && quirks->touchpad_led) if (quirks && quirks->touchpad_led)
touchpad_led_exit(); touchpad_led_exit();
i8042_remove_filter(dell_laptop_i8042_filter);
cancel_delayed_work_sync(&dell_rfkill_work);
backlight_device_unregister(dell_backlight_device); backlight_device_unregister(dell_backlight_device);
dell_cleanup_rfkill();
if (platform_device) { if (platform_device) {
platform_device_unregister(platform_device); platform_device_unregister(platform_device);
platform_driver_unregister(&platform_driver); platform_driver_unregister(&platform_driver);
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
* 59 Temple Place Suite 330, Boston, MA 02111-1307, USA. * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -34,7 +36,8 @@ ...@@ -34,7 +36,8 @@
#define ACPI_FUJITSU_CLASS "fujitsu" #define ACPI_FUJITSU_CLASS "fujitsu"
#define INVERT_TABLET_MODE_BIT 0x01 #define INVERT_TABLET_MODE_BIT 0x01
#define FORCE_TABLET_MODE_IF_UNDOCK 0x02 #define INVERT_DOCK_STATE_BIT 0x02
#define FORCE_TABLET_MODE_IF_UNDOCK 0x04
#define KEYMAP_LEN 16 #define KEYMAP_LEN 16
...@@ -161,6 +164,8 @@ static void fujitsu_send_state(void) ...@@ -161,6 +164,8 @@ static void fujitsu_send_state(void)
state = fujitsu_read_register(0xdd); state = fujitsu_read_register(0xdd);
dock = state & 0x02; dock = state & 0x02;
if (fujitsu.config.quirks & INVERT_DOCK_STATE_BIT)
dock = !dock;
if ((fujitsu.config.quirks & FORCE_TABLET_MODE_IF_UNDOCK) && (!dock)) { if ((fujitsu.config.quirks & FORCE_TABLET_MODE_IF_UNDOCK) && (!dock)) {
tablet_mode = 1; tablet_mode = 1;
...@@ -221,9 +226,6 @@ static int __devinit input_fujitsu_setup(struct device *parent, ...@@ -221,9 +226,6 @@ static int __devinit input_fujitsu_setup(struct device *parent,
input_set_capability(idev, EV_SW, SW_DOCK); input_set_capability(idev, EV_SW, SW_DOCK);
input_set_capability(idev, EV_SW, SW_TABLET_MODE); input_set_capability(idev, EV_SW, SW_TABLET_MODE);
input_set_capability(idev, EV_SW, SW_DOCK);
input_set_capability(idev, EV_SW, SW_TABLET_MODE);
error = input_register_device(idev); error = input_register_device(idev);
if (error) { if (error) {
input_free_device(idev); input_free_device(idev);
...@@ -275,25 +277,31 @@ static irqreturn_t fujitsu_interrupt(int irq, void *dev_id) ...@@ -275,25 +277,31 @@ static irqreturn_t fujitsu_interrupt(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int __devinit fujitsu_dmi_default(const struct dmi_system_id *dmi) static void __devinit fujitsu_dmi_common(const struct dmi_system_id *dmi)
{ {
printk(KERN_INFO MODULENAME ": %s\n", dmi->ident); pr_info("%s\n", dmi->ident);
memcpy(fujitsu.config.keymap, dmi->driver_data, memcpy(fujitsu.config.keymap, dmi->driver_data,
sizeof(fujitsu.config.keymap)); sizeof(fujitsu.config.keymap));
}
static int __devinit fujitsu_dmi_lifebook(const struct dmi_system_id *dmi)
{
fujitsu_dmi_common(dmi);
fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT;
return 1; return 1;
} }
static int __devinit fujitsu_dmi_stylistic(const struct dmi_system_id *dmi) static int __devinit fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)
{ {
fujitsu_dmi_default(dmi); fujitsu_dmi_common(dmi);
fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK; fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK;
fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT; fujitsu.config.quirks |= INVERT_DOCK_STATE_BIT;
return 1; return 1;
} }
static struct dmi_system_id dmi_ids[] __initconst = { static struct dmi_system_id dmi_ids[] __initconst = {
{ {
.callback = fujitsu_dmi_default, .callback = fujitsu_dmi_lifebook,
.ident = "Fujitsu Siemens P/T Series", .ident = "Fujitsu Siemens P/T Series",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
...@@ -302,7 +310,7 @@ static struct dmi_system_id dmi_ids[] __initconst = { ...@@ -302,7 +310,7 @@ static struct dmi_system_id dmi_ids[] __initconst = {
.driver_data = keymap_Lifebook_Tseries .driver_data = keymap_Lifebook_Tseries
}, },
{ {
.callback = fujitsu_dmi_default, .callback = fujitsu_dmi_lifebook,
.ident = "Fujitsu Lifebook T Series", .ident = "Fujitsu Lifebook T Series",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
...@@ -320,7 +328,7 @@ static struct dmi_system_id dmi_ids[] __initconst = { ...@@ -320,7 +328,7 @@ static struct dmi_system_id dmi_ids[] __initconst = {
.driver_data = keymap_Stylistic_Tseries .driver_data = keymap_Stylistic_Tseries
}, },
{ {
.callback = fujitsu_dmi_default, .callback = fujitsu_dmi_lifebook,
.ident = "Fujitsu LifeBook U810", .ident = "Fujitsu LifeBook U810",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
...@@ -347,7 +355,7 @@ static struct dmi_system_id dmi_ids[] __initconst = { ...@@ -347,7 +355,7 @@ static struct dmi_system_id dmi_ids[] __initconst = {
.driver_data = keymap_Stylistic_ST5xxx .driver_data = keymap_Stylistic_ST5xxx
}, },
{ {
.callback = fujitsu_dmi_default, .callback = fujitsu_dmi_lifebook,
.ident = "Unknown (using defaults)", .ident = "Unknown (using defaults)",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, ""), DMI_MATCH(DMI_SYS_VENDOR, ""),
...@@ -473,6 +481,6 @@ module_exit(fujitsu_module_exit); ...@@ -473,6 +481,6 @@ module_exit(fujitsu_module_exit);
MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>"); MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>");
MODULE_DESCRIPTION("Fujitsu tablet pc extras driver"); MODULE_DESCRIPTION("Fujitsu tablet pc extras driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_VERSION("2.4"); MODULE_VERSION("2.5");
MODULE_DEVICE_TABLE(acpi, fujitsu_ids); MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* hdaps.c - driver for IBM's Hard Drive Active Protection System * hdaps.c - driver for IBM's Hard Drive Active Protection System
* *
* Copyright (C) 2005 Robert Love <rml@novell.com> * Copyright (C) 2005 Robert Love <rml@novell.com>
* Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> * Copyright (C) 2005 Jesper Juhl <jj@chaosbits.net>
* *
* The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads
* starting with the R40, T41, and X40. It provides a basic two-axis * starting with the R40, T41, and X40. It provides a basic two-axis
......
...@@ -634,6 +634,8 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) ...@@ -634,6 +634,8 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)
RFKILL_TYPE_WLAN, RFKILL_TYPE_WLAN,
&hp_wmi_rfkill_ops, &hp_wmi_rfkill_ops,
(void *) HPWMI_WIFI); (void *) HPWMI_WIFI);
if (!wifi_rfkill)
return -ENOMEM;
rfkill_init_sw_state(wifi_rfkill, rfkill_init_sw_state(wifi_rfkill,
hp_wmi_get_sw_state(HPWMI_WIFI)); hp_wmi_get_sw_state(HPWMI_WIFI));
rfkill_set_hw_state(wifi_rfkill, rfkill_set_hw_state(wifi_rfkill,
...@@ -648,6 +650,10 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) ...@@ -648,6 +650,10 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)
RFKILL_TYPE_BLUETOOTH, RFKILL_TYPE_BLUETOOTH,
&hp_wmi_rfkill_ops, &hp_wmi_rfkill_ops,
(void *) HPWMI_BLUETOOTH); (void *) HPWMI_BLUETOOTH);
if (!bluetooth_rfkill) {
err = -ENOMEM;
goto register_wifi_error;
}
rfkill_init_sw_state(bluetooth_rfkill, rfkill_init_sw_state(bluetooth_rfkill,
hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); hp_wmi_get_sw_state(HPWMI_BLUETOOTH));
rfkill_set_hw_state(bluetooth_rfkill, rfkill_set_hw_state(bluetooth_rfkill,
...@@ -662,6 +668,10 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) ...@@ -662,6 +668,10 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)
RFKILL_TYPE_WWAN, RFKILL_TYPE_WWAN,
&hp_wmi_rfkill_ops, &hp_wmi_rfkill_ops,
(void *) HPWMI_WWAN); (void *) HPWMI_WWAN);
if (!wwan_rfkill) {
err = -ENOMEM;
goto register_bluetooth_error;
}
rfkill_init_sw_state(wwan_rfkill, rfkill_init_sw_state(wwan_rfkill,
hp_wmi_get_sw_state(HPWMI_WWAN)); hp_wmi_get_sw_state(HPWMI_WWAN));
rfkill_set_hw_state(wwan_rfkill, rfkill_set_hw_state(wwan_rfkill,
......
...@@ -194,7 +194,6 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) ...@@ -194,7 +194,6 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
/* /*
* debugfs * debugfs
*/ */
#define DEBUGFS_EVENT_LEN (4096)
static int debugfs_status_show(struct seq_file *s, void *data) static int debugfs_status_show(struct seq_file *s, void *data)
{ {
unsigned long value; unsigned long value;
...@@ -315,7 +314,7 @@ static int __devinit ideapad_debugfs_init(struct ideapad_private *priv) ...@@ -315,7 +314,7 @@ static int __devinit ideapad_debugfs_init(struct ideapad_private *priv)
node = debugfs_create_file("status", S_IRUGO, priv->debug, NULL, node = debugfs_create_file("status", S_IRUGO, priv->debug, NULL,
&debugfs_status_fops); &debugfs_status_fops);
if (!node) { if (!node) {
pr_err("failed to create event in debugfs"); pr_err("failed to create status in debugfs");
goto errout; goto errout;
} }
...@@ -785,6 +784,10 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) ...@@ -785,6 +784,10 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
case 9: case 9:
ideapad_sync_rfk_state(priv); ideapad_sync_rfk_state(priv);
break; break;
case 13:
case 6:
ideapad_input_report(priv, vpc_bit);
break;
case 4: case 4:
ideapad_backlight_notify_brightness(priv); ideapad_backlight_notify_brightness(priv);
break; break;
...@@ -795,7 +798,7 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) ...@@ -795,7 +798,7 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
ideapad_backlight_notify_power(priv); ideapad_backlight_notify_power(priv);
break; break;
default: default:
ideapad_input_report(priv, vpc_bit); pr_info("Unknown event: %lu\n", vpc_bit);
} }
} }
} }
......
...@@ -141,6 +141,27 @@ MODULE_PARM_DESC(kbd_backlight_timeout, ...@@ -141,6 +141,27 @@ MODULE_PARM_DESC(kbd_backlight_timeout,
"(default: 0)"); "(default: 0)");
static void sony_nc_kbd_backlight_resume(void); static void sony_nc_kbd_backlight_resume(void);
static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
unsigned int handle);
static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd);
static int sony_nc_battery_care_setup(struct platform_device *pd,
unsigned int handle);
static void sony_nc_battery_care_cleanup(struct platform_device *pd);
static int sony_nc_thermal_setup(struct platform_device *pd);
static void sony_nc_thermal_cleanup(struct platform_device *pd);
static void sony_nc_thermal_resume(void);
static int sony_nc_lid_resume_setup(struct platform_device *pd);
static void sony_nc_lid_resume_cleanup(struct platform_device *pd);
static int sony_nc_highspeed_charging_setup(struct platform_device *pd);
static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd);
static int sony_nc_touchpad_setup(struct platform_device *pd,
unsigned int handle);
static void sony_nc_touchpad_cleanup(struct platform_device *pd);
enum sony_nc_rfkill { enum sony_nc_rfkill {
SONY_WIFI, SONY_WIFI,
...@@ -153,6 +174,9 @@ enum sony_nc_rfkill { ...@@ -153,6 +174,9 @@ enum sony_nc_rfkill {
static int sony_rfkill_handle; static int sony_rfkill_handle;
static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL]; static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL];
static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900}; static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900};
static int sony_nc_rfkill_setup(struct acpi_device *device,
unsigned int handle);
static void sony_nc_rfkill_cleanup(void);
static void sony_nc_rfkill_update(void); static void sony_nc_rfkill_update(void);
/*********** Input Devices ***********/ /*********** Input Devices ***********/
...@@ -691,59 +715,97 @@ static struct acpi_device *sony_nc_acpi_device = NULL; ...@@ -691,59 +715,97 @@ static struct acpi_device *sony_nc_acpi_device = NULL;
/* /*
* acpi_evaluate_object wrappers * acpi_evaluate_object wrappers
* all useful calls into SNC methods take one or zero parameters and return
* integers or arrays.
*/ */
static int acpi_callgetfunc(acpi_handle handle, char *name, int *result) static union acpi_object *__call_snc_method(acpi_handle handle, char *method,
u64 *value)
{ {
struct acpi_buffer output; union acpi_object *result = NULL;
union acpi_object out_obj; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_status status; acpi_status status;
output.length = sizeof(out_obj); if (value) {
output.pointer = &out_obj; struct acpi_object_list params;
union acpi_object in;
in.type = ACPI_TYPE_INTEGER;
in.integer.value = *value;
params.count = 1;
params.pointer = &in;
status = acpi_evaluate_object(handle, method, &params, &output);
dprintk("__call_snc_method: [%s:0x%.8x%.8x]\n", method,
(unsigned int)(*value >> 32),
(unsigned int)*value & 0xffffffff);
} else {
status = acpi_evaluate_object(handle, method, NULL, &output);
dprintk("__call_snc_method: [%s]\n", method);
}
status = acpi_evaluate_object(handle, name, NULL, &output); if (ACPI_FAILURE(status)) {
if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) { pr_err("Failed to evaluate [%s]\n", method);
*result = out_obj.integer.value; return NULL;
return 0;
} }
pr_warn("acpi_callreadfunc failed\n"); result = (union acpi_object *) output.pointer;
if (!result)
dprintk("No return object [%s]\n", method);
return -1; return result;
} }
static int acpi_callsetfunc(acpi_handle handle, char *name, int value, static int sony_nc_int_call(acpi_handle handle, char *name, int *value,
int *result) int *result)
{ {
struct acpi_object_list params; union acpi_object *object = NULL;
union acpi_object in_obj; if (value) {
struct acpi_buffer output; u64 v = *value;
union acpi_object out_obj; object = __call_snc_method(handle, name, &v);
acpi_status status; } else
object = __call_snc_method(handle, name, NULL);
params.count = 1;
params.pointer = &in_obj;
in_obj.type = ACPI_TYPE_INTEGER;
in_obj.integer.value = value;
output.length = sizeof(out_obj); if (!object)
output.pointer = &out_obj; return -EINVAL;
status = acpi_evaluate_object(handle, name, &params, &output); if (object->type != ACPI_TYPE_INTEGER) {
if (status == AE_OK) { pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n",
if (result != NULL) { ACPI_TYPE_INTEGER, object->type);
if (out_obj.type != ACPI_TYPE_INTEGER) { kfree(object);
pr_warn("acpi_evaluate_object bad return type\n"); return -EINVAL;
return -1;
}
*result = out_obj.integer.value;
}
return 0;
} }
pr_warn("acpi_evaluate_object failed\n"); if (result)
*result = object->integer.value;
kfree(object);
return 0;
}
#define MIN(a, b) (a > b ? b : a)
static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
void *buffer, size_t buflen)
{
size_t len = len;
union acpi_object *object = __call_snc_method(handle, name, value);
if (!object)
return -EINVAL;
if (object->type == ACPI_TYPE_BUFFER)
len = MIN(buflen, object->buffer.length);
else if (object->type == ACPI_TYPE_INTEGER)
len = MIN(buflen, sizeof(object->integer.value));
else {
pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n",
ACPI_TYPE_BUFFER, object->type);
kfree(object);
return -EINVAL;
}
return -1; memcpy(buffer, object->buffer.pointer, len);
kfree(object);
return 0;
} }
struct sony_nc_handles { struct sony_nc_handles {
...@@ -770,16 +832,17 @@ static ssize_t sony_nc_handles_show(struct device *dev, ...@@ -770,16 +832,17 @@ static ssize_t sony_nc_handles_show(struct device *dev,
static int sony_nc_handles_setup(struct platform_device *pd) static int sony_nc_handles_setup(struct platform_device *pd)
{ {
int i; int i, r, result, arg;
int result;
handles = kzalloc(sizeof(*handles), GFP_KERNEL); handles = kzalloc(sizeof(*handles), GFP_KERNEL);
if (!handles) if (!handles)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
if (!acpi_callsetfunc(sony_nc_acpi_handle, arg = i + 0x20;
"SN00", i + 0x20, &result)) { r = sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg,
&result);
if (!r) {
dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n", dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n",
result, i); result, i);
handles->cap[i] = result; handles->cap[i] = result;
...@@ -819,8 +882,8 @@ static int sony_find_snc_handle(int handle) ...@@ -819,8 +882,8 @@ static int sony_find_snc_handle(int handle)
int i; int i;
/* not initialized yet, return early */ /* not initialized yet, return early */
if (!handles) if (!handles || !handle)
return -1; return -EINVAL;
for (i = 0; i < 0x10; i++) { for (i = 0; i < 0x10; i++) {
if (handles->cap[i] == handle) { if (handles->cap[i] == handle) {
...@@ -830,21 +893,20 @@ static int sony_find_snc_handle(int handle) ...@@ -830,21 +893,20 @@ static int sony_find_snc_handle(int handle)
} }
} }
dprintk("handle 0x%.4x not found\n", handle); dprintk("handle 0x%.4x not found\n", handle);
return -1; return -EINVAL;
} }
static int sony_call_snc_handle(int handle, int argument, int *result) static int sony_call_snc_handle(int handle, int argument, int *result)
{ {
int ret = 0; int arg, ret = 0;
int offset = sony_find_snc_handle(handle); int offset = sony_find_snc_handle(handle);
if (offset < 0) if (offset < 0)
return -1; return offset;
ret = acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument, arg = offset | argument;
result); ret = sony_nc_int_call(sony_nc_acpi_handle, "SN07", &arg, result);
dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", offset | argument, dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", arg, *result);
*result);
return ret; return ret;
} }
...@@ -889,14 +951,16 @@ static int boolean_validate(const int direction, const int value) ...@@ -889,14 +951,16 @@ static int boolean_validate(const int direction, const int value)
static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr, static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr,
char *buffer) char *buffer)
{ {
int value; int value, ret = 0;
struct sony_nc_value *item = struct sony_nc_value *item =
container_of(attr, struct sony_nc_value, devattr); container_of(attr, struct sony_nc_value, devattr);
if (!*item->acpiget) if (!*item->acpiget)
return -EIO; return -EIO;
if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0) ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiget, NULL,
&value);
if (ret < 0)
return -EIO; return -EIO;
if (item->validate) if (item->validate)
...@@ -909,7 +973,8 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, ...@@ -909,7 +973,8 @@ static ssize_t sony_nc_sysfs_store(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buffer, size_t count) const char *buffer, size_t count)
{ {
int value; unsigned long value = 0;
int ret = 0;
struct sony_nc_value *item = struct sony_nc_value *item =
container_of(attr, struct sony_nc_value, devattr); container_of(attr, struct sony_nc_value, devattr);
...@@ -919,7 +984,8 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, ...@@ -919,7 +984,8 @@ static ssize_t sony_nc_sysfs_store(struct device *dev,
if (count > 31) if (count > 31)
return -EINVAL; return -EINVAL;
value = simple_strtoul(buffer, NULL, 10); if (kstrtoul(buffer, 10, &value))
return -EINVAL;
if (item->validate) if (item->validate)
value = item->validate(SNC_VALIDATE_IN, value); value = item->validate(SNC_VALIDATE_IN, value);
...@@ -927,8 +993,11 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, ...@@ -927,8 +993,11 @@ static ssize_t sony_nc_sysfs_store(struct device *dev,
if (value < 0) if (value < 0)
return value; return value;
if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0) ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset,
(int *)&value, NULL);
if (ret < 0)
return -EIO; return -EIO;
item->value = value; item->value = value;
item->valid = 1; item->valid = 1;
return count; return count;
...@@ -948,15 +1017,15 @@ struct sony_backlight_props sony_bl_props; ...@@ -948,15 +1017,15 @@ struct sony_backlight_props sony_bl_props;
static int sony_backlight_update_status(struct backlight_device *bd) static int sony_backlight_update_status(struct backlight_device *bd)
{ {
return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", int arg = bd->props.brightness + 1;
bd->props.brightness + 1, NULL); return sony_nc_int_call(sony_nc_acpi_handle, "SBRT", &arg, NULL);
} }
static int sony_backlight_get_brightness(struct backlight_device *bd) static int sony_backlight_get_brightness(struct backlight_device *bd)
{ {
int value; int value;
if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) if (sony_nc_int_call(sony_nc_acpi_handle, "GBRT", NULL, &value))
return 0; return 0;
/* brightness levels are 1-based, while backlight ones are 0-based */ /* brightness levels are 1-based, while backlight ones are 0-based */
return value - 1; return value - 1;
...@@ -1024,10 +1093,14 @@ static struct sony_nc_event sony_100_events[] = { ...@@ -1024,10 +1093,14 @@ static struct sony_nc_event sony_100_events[] = {
{ 0x06, SONYPI_EVENT_FNKEY_RELEASED }, { 0x06, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x87, SONYPI_EVENT_FNKEY_F7 }, { 0x87, SONYPI_EVENT_FNKEY_F7 },
{ 0x07, SONYPI_EVENT_FNKEY_RELEASED }, { 0x07, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x88, SONYPI_EVENT_FNKEY_F8 },
{ 0x08, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x89, SONYPI_EVENT_FNKEY_F9 }, { 0x89, SONYPI_EVENT_FNKEY_F9 },
{ 0x09, SONYPI_EVENT_FNKEY_RELEASED }, { 0x09, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x8A, SONYPI_EVENT_FNKEY_F10 }, { 0x8A, SONYPI_EVENT_FNKEY_F10 },
{ 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, { 0x0A, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x8B, SONYPI_EVENT_FNKEY_F11 },
{ 0x0B, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x8C, SONYPI_EVENT_FNKEY_F12 }, { 0x8C, SONYPI_EVENT_FNKEY_F12 },
{ 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, { 0x0C, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x9d, SONYPI_EVENT_ZOOM_PRESSED }, { 0x9d, SONYPI_EVENT_ZOOM_PRESSED },
...@@ -1063,63 +1136,116 @@ static struct sony_nc_event sony_127_events[] = { ...@@ -1063,63 +1136,116 @@ static struct sony_nc_event sony_127_events[] = {
{ 0, 0 }, { 0, 0 },
}; };
static int sony_nc_hotkeys_decode(u32 event, unsigned int handle)
{
int ret = -EINVAL;
unsigned int result = 0;
struct sony_nc_event *key_event;
if (sony_call_snc_handle(handle, 0x200, &result)) {
dprintk("Unable to decode event 0x%.2x 0x%.2x\n", handle,
event);
return -EINVAL;
}
result &= 0xFF;
if (handle == 0x0100)
key_event = sony_100_events;
else
key_event = sony_127_events;
for (; key_event->data; key_event++) {
if (key_event->data == result) {
ret = key_event->event;
break;
}
}
if (!key_event->data)
pr_info("Unknown hotkey 0x%.2x/0x%.2x (handle 0x%.2x)\n",
event, result, handle);
return ret;
}
/* /*
* ACPI callbacks * ACPI callbacks
*/ */
static void sony_nc_notify(struct acpi_device *device, u32 event) static void sony_nc_notify(struct acpi_device *device, u32 event)
{ {
u32 ev = event; u32 real_ev = event;
u8 ev_type = 0;
dprintk("sony_nc_notify, event: 0x%.2x\n", event);
if (event >= 0x90) {
unsigned int result = 0;
unsigned int arg = 0;
unsigned int handle = 0;
unsigned int offset = event - 0x90;
if (offset >= ARRAY_SIZE(handles->cap)) {
pr_err("Event 0x%x outside of capabilities list\n",
event);
return;
}
handle = handles->cap[offset];
/* list of handles known for generating events */
switch (handle) {
/* hotkey event */
case 0x0100:
case 0x0127:
ev_type = 1;
real_ev = sony_nc_hotkeys_decode(event, handle);
if (real_ev > 0)
sony_laptop_report_input_event(real_ev);
else
/* restore the original event for reporting */
real_ev = event;
if (ev >= 0x90) { break;
/* 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_nc_notify, unable to decode"
" event 0x%.2x 0x%.2x\n", key_handle,
ev);
/* restore the original event */
ev = event;
} else {
ev = result & 0xFF;
if (key_handle == 0x100)
key_event = sony_100_events;
else
key_event = sony_127_events;
for (; key_event->data; key_event++) {
if (key_event->data == ev) {
ev = key_event->event;
break;
}
}
if (!key_event->data) /* wlan switch */
pr_info("Unknown event: 0x%x 0x%x\n", case 0x0124:
key_handle, ev); case 0x0135:
else /* events on this handle are reported when the
sony_laptop_report_input_event(ev); * switch changes position or for battery
} * events. We'll notify both of them but only
} else if (sony_find_snc_handle(sony_rfkill_handle) == ev) { * update the rfkill device status when the
sony_nc_rfkill_update(); * switch is moved.
return; */
ev_type = 2;
sony_call_snc_handle(handle, 0x0100, &result);
real_ev = result & 0x03;
/* hw switch event */
if (real_ev == 1)
sony_nc_rfkill_update();
break;
default:
dprintk("Unknown event 0x%x for handle 0x%x\n",
event, handle);
break;
} }
} else
sony_laptop_report_input_event(ev);
dprintk("sony_nc_notify, event: 0x%.2x\n", ev); /* clear the event (and the event reason when present) */
acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev); arg = 1 << offset;
sony_nc_int_call(sony_nc_acpi_handle, "SN05", &arg, &result);
} else {
/* old style event */
ev_type = 1;
sony_laptop_report_input_event(real_ev);
}
acpi_bus_generate_proc_event(sony_nc_acpi_device, ev_type, real_ev);
acpi_bus_generate_netlink_event(sony_nc_acpi_device->pnp.device_class,
dev_name(&sony_nc_acpi_device->dev), ev_type, real_ev);
} }
static acpi_status sony_walk_callback(acpi_handle handle, u32 level, static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
...@@ -1140,20 +1266,190 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, ...@@ -1140,20 +1266,190 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
/* /*
* ACPI device * ACPI device
*/ */
static int sony_nc_function_setup(struct acpi_device *device) static void sony_nc_function_setup(struct acpi_device *device,
struct platform_device *pf_device)
{ {
int result; unsigned int i, result, bitmask, arg;
if (!handles)
return;
/* setup found handles here */
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
unsigned int handle = handles->cap[i];
if (!handle)
continue;
dprintk("setting up handle 0x%.4x\n", handle);
switch (handle) {
case 0x0100:
case 0x0101:
case 0x0127:
/* setup hotkeys */
sony_call_snc_handle(handle, 0, &result);
break;
case 0x0102:
/* setup hotkeys */
sony_call_snc_handle(handle, 0x100, &result);
break;
case 0x0105:
case 0x0148:
/* touchpad enable/disable */
result = sony_nc_touchpad_setup(pf_device, handle);
if (result)
pr_err("couldn't set up touchpad control function (%d)\n",
result);
break;
case 0x0115:
case 0x0136:
case 0x013f:
result = sony_nc_battery_care_setup(pf_device, handle);
if (result)
pr_err("couldn't set up battery care function (%d)\n",
result);
break;
case 0x0119:
result = sony_nc_lid_resume_setup(pf_device);
if (result)
pr_err("couldn't set up lid resume function (%d)\n",
result);
break;
case 0x0122:
result = sony_nc_thermal_setup(pf_device);
if (result)
pr_err("couldn't set up thermal profile function (%d)\n",
result);
break;
case 0x0131:
result = sony_nc_highspeed_charging_setup(pf_device);
if (result)
pr_err("couldn't set up high speed charging function (%d)\n",
result);
break;
case 0x0124:
case 0x0135:
result = sony_nc_rfkill_setup(device, handle);
if (result)
pr_err("couldn't set up rfkill support (%d)\n",
result);
break;
case 0x0137:
case 0x0143:
result = sony_nc_kbd_backlight_setup(pf_device, handle);
if (result)
pr_err("couldn't set up keyboard backlight function (%d)\n",
result);
break;
default:
continue;
}
}
/* Enable all events */ /* Enable all events */
acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0xffff, &result); arg = 0x10;
if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask))
sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask,
&result);
}
/* Setup hotkeys */ static void sony_nc_function_cleanup(struct platform_device *pd)
sony_call_snc_handle(0x0100, 0, &result); {
sony_call_snc_handle(0x0101, 0, &result); unsigned int i, result, bitmask, handle;
sony_call_snc_handle(0x0102, 0x100, &result);
sony_call_snc_handle(0x0127, 0, &result);
return 0; /* get enabled events and disable them */
sony_nc_int_call(sony_nc_acpi_handle, "SN01", NULL, &bitmask);
sony_nc_int_call(sony_nc_acpi_handle, "SN03", &bitmask, &result);
/* cleanup handles here */
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
handle = handles->cap[i];
if (!handle)
continue;
switch (handle) {
case 0x0105:
case 0x0148:
sony_nc_touchpad_cleanup(pd);
break;
case 0x0115:
case 0x0136:
case 0x013f:
sony_nc_battery_care_cleanup(pd);
break;
case 0x0119:
sony_nc_lid_resume_cleanup(pd);
break;
case 0x0122:
sony_nc_thermal_cleanup(pd);
break;
case 0x0131:
sony_nc_highspeed_charging_cleanup(pd);
break;
case 0x0124:
case 0x0135:
sony_nc_rfkill_cleanup();
break;
case 0x0137:
case 0x0143:
sony_nc_kbd_backlight_cleanup(pd);
break;
default:
continue;
}
}
/* finally cleanup the handles list */
sony_nc_handles_cleanup(pd);
}
static void sony_nc_function_resume(void)
{
unsigned int i, result, bitmask, arg;
dprintk("Resuming SNC device\n");
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
unsigned int handle = handles->cap[i];
if (!handle)
continue;
switch (handle) {
case 0x0100:
case 0x0101:
case 0x0127:
/* re-enable hotkeys */
sony_call_snc_handle(handle, 0, &result);
break;
case 0x0102:
/* re-enable hotkeys */
sony_call_snc_handle(handle, 0x100, &result);
break;
case 0x0122:
sony_nc_thermal_resume();
break;
case 0x0124:
case 0x0135:
sony_nc_rfkill_update();
break;
case 0x0137:
case 0x0143:
sony_nc_kbd_backlight_resume();
break;
default:
continue;
}
}
/* Enable all events */
arg = 0x10;
if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask))
sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask,
&result);
} }
static int sony_nc_resume(struct acpi_device *device) static int sony_nc_resume(struct acpi_device *device)
...@@ -1166,8 +1462,8 @@ static int sony_nc_resume(struct acpi_device *device) ...@@ -1166,8 +1462,8 @@ static int sony_nc_resume(struct acpi_device *device)
if (!item->valid) if (!item->valid)
continue; continue;
ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset,
item->value, NULL); &item->value, NULL);
if (ret < 0) { if (ret < 0) {
pr_err("%s: %d\n", __func__, ret); pr_err("%s: %d\n", __func__, ret);
break; break;
...@@ -1176,21 +1472,14 @@ static int sony_nc_resume(struct acpi_device *device) ...@@ -1176,21 +1472,14 @@ static int sony_nc_resume(struct acpi_device *device)
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON",
&handle))) { &handle))) {
if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) int arg = 1;
if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL))
dprintk("ECON Method failed\n"); dprintk("ECON Method failed\n");
} }
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00",
&handle))) { &handle)))
dprintk("Doing SNC setup\n"); sony_nc_function_resume();
sony_nc_function_setup(device);
}
/* re-read rfkill state */
sony_nc_rfkill_update();
/* restore kbd backlight states */
sony_nc_kbd_backlight_resume();
return 0; return 0;
} }
...@@ -1213,7 +1502,7 @@ static int sony_nc_rfkill_set(void *data, bool blocked) ...@@ -1213,7 +1502,7 @@ static int sony_nc_rfkill_set(void *data, bool blocked)
int argument = sony_rfkill_address[(long) data] + 0x100; int argument = sony_rfkill_address[(long) data] + 0x100;
if (!blocked) if (!blocked)
argument |= 0xff0000; argument |= 0x030000;
return sony_call_snc_handle(sony_rfkill_handle, argument, &result); return sony_call_snc_handle(sony_rfkill_handle, argument, &result);
} }
...@@ -1230,7 +1519,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, ...@@ -1230,7 +1519,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device,
enum rfkill_type type; enum rfkill_type type;
const char *name; const char *name;
int result; int result;
bool hwblock; bool hwblock, swblock;
switch (nc_type) { switch (nc_type) {
case SONY_WIFI: case SONY_WIFI:
...@@ -1258,8 +1547,21 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, ...@@ -1258,8 +1547,21 @@ static int sony_nc_setup_rfkill(struct acpi_device *device,
if (!rfk) if (!rfk)
return -ENOMEM; return -ENOMEM;
sony_call_snc_handle(sony_rfkill_handle, 0x200, &result); if (sony_call_snc_handle(sony_rfkill_handle, 0x200, &result) < 0) {
rfkill_destroy(rfk);
return -1;
}
hwblock = !(result & 0x1); hwblock = !(result & 0x1);
if (sony_call_snc_handle(sony_rfkill_handle,
sony_rfkill_address[nc_type],
&result) < 0) {
rfkill_destroy(rfk);
return -1;
}
swblock = !(result & 0x2);
rfkill_init_sw_state(rfk, swblock);
rfkill_set_hw_state(rfk, hwblock); rfkill_set_hw_state(rfk, hwblock);
err = rfkill_register(rfk); err = rfkill_register(rfk);
...@@ -1295,101 +1597,79 @@ static void sony_nc_rfkill_update(void) ...@@ -1295,101 +1597,79 @@ static void sony_nc_rfkill_update(void)
sony_call_snc_handle(sony_rfkill_handle, argument, &result); sony_call_snc_handle(sony_rfkill_handle, argument, &result);
rfkill_set_states(sony_rfkill_devices[i], rfkill_set_states(sony_rfkill_devices[i],
!(result & 0xf), false); !(result & 0x2), false);
} }
} }
static void sony_nc_rfkill_setup(struct acpi_device *device) static int sony_nc_rfkill_setup(struct acpi_device *device,
unsigned int handle)
{ {
int offset; u64 offset;
u8 dev_code, i; int i;
acpi_status status; unsigned char buffer[32] = { 0 };
struct acpi_object_list params;
union acpi_object in_obj;
union acpi_object *device_enum;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
offset = sony_find_snc_handle(0x124);
if (offset == -1) {
offset = sony_find_snc_handle(0x135);
if (offset == -1)
return;
else
sony_rfkill_handle = 0x135;
} else
sony_rfkill_handle = 0x124;
dprintk("Found rkfill handle: 0x%.4x\n", sony_rfkill_handle);
/* need to read the whole buffer returned by the acpi call to SN06
* here otherwise we may miss some features
*/
params.count = 1;
params.pointer = &in_obj;
in_obj.type = ACPI_TYPE_INTEGER;
in_obj.integer.value = offset;
status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", &params,
&buffer);
if (ACPI_FAILURE(status)) {
dprintk("Radio device enumeration failed\n");
return;
}
device_enum = (union acpi_object *) buffer.pointer;
if (!device_enum) {
pr_err("No SN06 return object\n");
goto out_no_enum;
}
if (device_enum->type != ACPI_TYPE_BUFFER) {
pr_err("Invalid SN06 return object 0x%.2x\n",
device_enum->type);
goto out_no_enum;
}
/* the buffer is filled with magic numbers describing the devices offset = sony_find_snc_handle(handle);
* available, 0xff terminates the enumeration sony_rfkill_handle = handle;
i = sony_nc_buffer_call(sony_nc_acpi_handle, "SN06", &offset, buffer,
32);
if (i < 0)
return i;
/* The buffer is filled with magic numbers describing the devices
* available, 0xff terminates the enumeration.
* Known codes:
* 0x00 WLAN
* 0x10 BLUETOOTH
* 0x20 WWAN GPRS-EDGE
* 0x21 WWAN HSDPA
* 0x22 WWAN EV-DO
* 0x23 WWAN GPS
* 0x25 Gobi WWAN no GPS
* 0x26 Gobi WWAN + GPS
* 0x28 Gobi WWAN no GPS
* 0x29 Gobi WWAN + GPS
* 0x30 WIMAX
* 0x50 Gobi WWAN no GPS
* 0x51 Gobi WWAN + GPS
* 0x70 no SIM card slot
* 0x71 SIM card slot
*/ */
for (i = 0; i < device_enum->buffer.length; i++) { for (i = 0; i < ARRAY_SIZE(buffer); i++) {
dev_code = *(device_enum->buffer.pointer + i); if (buffer[i] == 0xff)
if (dev_code == 0xff)
break; break;
dprintk("Radio devices, looking at 0x%.2x\n", dev_code); dprintk("Radio devices, found 0x%.2x\n", buffer[i]);
if (dev_code == 0 && !sony_rfkill_devices[SONY_WIFI]) if (buffer[i] == 0 && !sony_rfkill_devices[SONY_WIFI])
sony_nc_setup_rfkill(device, SONY_WIFI); sony_nc_setup_rfkill(device, SONY_WIFI);
if (dev_code == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH]) if (buffer[i] == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH])
sony_nc_setup_rfkill(device, SONY_BLUETOOTH); sony_nc_setup_rfkill(device, SONY_BLUETOOTH);
if ((0xf0 & dev_code) == 0x20 && if (((0xf0 & buffer[i]) == 0x20 ||
(0xf0 & buffer[i]) == 0x50) &&
!sony_rfkill_devices[SONY_WWAN]) !sony_rfkill_devices[SONY_WWAN])
sony_nc_setup_rfkill(device, SONY_WWAN); sony_nc_setup_rfkill(device, SONY_WWAN);
if (dev_code == 0x30 && !sony_rfkill_devices[SONY_WIMAX]) if (buffer[i] == 0x30 && !sony_rfkill_devices[SONY_WIMAX])
sony_nc_setup_rfkill(device, SONY_WIMAX); sony_nc_setup_rfkill(device, SONY_WIMAX);
} }
return 0;
out_no_enum:
kfree(buffer.pointer);
return;
} }
/* Keyboard backlight feature */ /* Keyboard backlight feature */
#define KBDBL_HANDLER 0x137
#define KBDBL_PRESENT 0xB00
#define SET_MODE 0xC00
#define SET_STATE 0xD00
#define SET_TIMEOUT 0xE00
struct kbd_backlight { struct kbd_backlight {
int mode; unsigned int handle;
int timeout; unsigned int base;
unsigned int mode;
unsigned int timeout;
struct device_attribute mode_attr; struct device_attribute mode_attr;
struct device_attribute timeout_attr; struct device_attribute timeout_attr;
}; };
static struct kbd_backlight *kbdbl_handle; static struct kbd_backlight *kbdbl_ctl;
static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)
{ {
...@@ -1398,15 +1678,15 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) ...@@ -1398,15 +1678,15 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)
if (value > 1) if (value > 1)
return -EINVAL; return -EINVAL;
if (sony_call_snc_handle(KBDBL_HANDLER, if (sony_call_snc_handle(kbdbl_ctl->handle,
(value << 0x10) | SET_MODE, &result)) (value << 0x10) | (kbdbl_ctl->base), &result))
return -EIO; return -EIO;
/* Try to turn the light on/off immediately */ /* Try to turn the light on/off immediately */
sony_call_snc_handle(KBDBL_HANDLER, (value << 0x10) | SET_STATE, sony_call_snc_handle(kbdbl_ctl->handle,
&result); (value << 0x10) | (kbdbl_ctl->base + 0x100), &result);
kbdbl_handle->mode = value; kbdbl_ctl->mode = value;
return 0; return 0;
} }
...@@ -1421,7 +1701,7 @@ static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev, ...@@ -1421,7 +1701,7 @@ static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev,
if (count > 31) if (count > 31)
return -EINVAL; return -EINVAL;
if (strict_strtoul(buffer, 10, &value)) if (kstrtoul(buffer, 10, &value))
return -EINVAL; return -EINVAL;
ret = __sony_nc_kbd_backlight_mode_set(value); ret = __sony_nc_kbd_backlight_mode_set(value);
...@@ -1435,7 +1715,7 @@ static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev, ...@@ -1435,7 +1715,7 @@ static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev,
struct device_attribute *attr, char *buffer) struct device_attribute *attr, char *buffer)
{ {
ssize_t count = 0; ssize_t count = 0;
count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->mode); count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->mode);
return count; return count;
} }
...@@ -1446,11 +1726,11 @@ static int __sony_nc_kbd_backlight_timeout_set(u8 value) ...@@ -1446,11 +1726,11 @@ static int __sony_nc_kbd_backlight_timeout_set(u8 value)
if (value > 3) if (value > 3)
return -EINVAL; return -EINVAL;
if (sony_call_snc_handle(KBDBL_HANDLER, if (sony_call_snc_handle(kbdbl_ctl->handle, (value << 0x10) |
(value << 0x10) | SET_TIMEOUT, &result)) (kbdbl_ctl->base + 0x200), &result))
return -EIO; return -EIO;
kbdbl_handle->timeout = value; kbdbl_ctl->timeout = value;
return 0; return 0;
} }
...@@ -1465,7 +1745,7 @@ static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev, ...@@ -1465,7 +1745,7 @@ static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev,
if (count > 31) if (count > 31)
return -EINVAL; return -EINVAL;
if (strict_strtoul(buffer, 10, &value)) if (kstrtoul(buffer, 10, &value))
return -EINVAL; return -EINVAL;
ret = __sony_nc_kbd_backlight_timeout_set(value); ret = __sony_nc_kbd_backlight_timeout_set(value);
...@@ -1479,39 +1759,58 @@ static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev, ...@@ -1479,39 +1759,58 @@ static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev,
struct device_attribute *attr, char *buffer) struct device_attribute *attr, char *buffer)
{ {
ssize_t count = 0; ssize_t count = 0;
count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->timeout); count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->timeout);
return count; return count;
} }
static int sony_nc_kbd_backlight_setup(struct platform_device *pd) static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
unsigned int handle)
{ {
int result; int result;
int ret = 0;
if (sony_call_snc_handle(KBDBL_HANDLER, KBDBL_PRESENT, &result)) /* verify the kbd backlight presence, these handles are not used for
return 0; * keyboard backlight only
if (!(result & 0x02)) */
ret = sony_call_snc_handle(handle, handle == 0x0137 ? 0x0B00 : 0x0100,
&result);
if (ret)
return ret;
if ((handle == 0x0137 && !(result & 0x02)) ||
!(result & 0x01)) {
dprintk("no backlight keyboard found\n");
return 0; return 0;
}
kbdbl_handle = kzalloc(sizeof(*kbdbl_handle), GFP_KERNEL); kbdbl_ctl = kzalloc(sizeof(*kbdbl_ctl), GFP_KERNEL);
if (!kbdbl_handle) if (!kbdbl_ctl)
return -ENOMEM; return -ENOMEM;
sysfs_attr_init(&kbdbl_handle->mode_attr.attr); kbdbl_ctl->handle = handle;
kbdbl_handle->mode_attr.attr.name = "kbd_backlight"; if (handle == 0x0137)
kbdbl_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR; kbdbl_ctl->base = 0x0C00;
kbdbl_handle->mode_attr.show = sony_nc_kbd_backlight_mode_show; else
kbdbl_handle->mode_attr.store = sony_nc_kbd_backlight_mode_store; kbdbl_ctl->base = 0x4000;
sysfs_attr_init(&kbdbl_ctl->mode_attr.attr);
kbdbl_ctl->mode_attr.attr.name = "kbd_backlight";
kbdbl_ctl->mode_attr.attr.mode = S_IRUGO | S_IWUSR;
kbdbl_ctl->mode_attr.show = sony_nc_kbd_backlight_mode_show;
kbdbl_ctl->mode_attr.store = sony_nc_kbd_backlight_mode_store;
sysfs_attr_init(&kbdbl_handle->timeout_attr.attr); sysfs_attr_init(&kbdbl_ctl->timeout_attr.attr);
kbdbl_handle->timeout_attr.attr.name = "kbd_backlight_timeout"; kbdbl_ctl->timeout_attr.attr.name = "kbd_backlight_timeout";
kbdbl_handle->timeout_attr.attr.mode = S_IRUGO | S_IWUSR; kbdbl_ctl->timeout_attr.attr.mode = S_IRUGO | S_IWUSR;
kbdbl_handle->timeout_attr.show = sony_nc_kbd_backlight_timeout_show; kbdbl_ctl->timeout_attr.show = sony_nc_kbd_backlight_timeout_show;
kbdbl_handle->timeout_attr.store = sony_nc_kbd_backlight_timeout_store; kbdbl_ctl->timeout_attr.store = sony_nc_kbd_backlight_timeout_store;
if (device_create_file(&pd->dev, &kbdbl_handle->mode_attr)) ret = device_create_file(&pd->dev, &kbdbl_ctl->mode_attr);
if (ret)
goto outkzalloc; goto outkzalloc;
if (device_create_file(&pd->dev, &kbdbl_handle->timeout_attr)) ret = device_create_file(&pd->dev, &kbdbl_ctl->timeout_attr);
if (ret)
goto outmode; goto outmode;
__sony_nc_kbd_backlight_mode_set(kbd_backlight); __sony_nc_kbd_backlight_mode_set(kbd_backlight);
...@@ -1520,131 +1819,716 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd) ...@@ -1520,131 +1819,716 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd)
return 0; return 0;
outmode: outmode:
device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr);
outkzalloc: outkzalloc:
kfree(kbdbl_handle); kfree(kbdbl_ctl);
kbdbl_handle = NULL; kbdbl_ctl = NULL;
return -1; return ret;
} }
static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd) static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd)
{ {
if (kbdbl_handle) { if (kbdbl_ctl) {
int result; int result;
device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr);
device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr); device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr);
/* restore the default hw behaviour */ /* restore the default hw behaviour */
sony_call_snc_handle(KBDBL_HANDLER, 0x1000 | SET_MODE, &result); sony_call_snc_handle(kbdbl_ctl->handle,
sony_call_snc_handle(KBDBL_HANDLER, SET_TIMEOUT, &result); kbdbl_ctl->base | 0x10000, &result);
sony_call_snc_handle(kbdbl_ctl->handle,
kbdbl_ctl->base + 0x200, &result);
kfree(kbdbl_handle); kfree(kbdbl_ctl);
kbdbl_ctl = NULL;
} }
return 0;
} }
static void sony_nc_kbd_backlight_resume(void) static void sony_nc_kbd_backlight_resume(void)
{ {
int ignore = 0; int ignore = 0;
if (!kbdbl_handle) if (!kbdbl_ctl)
return; return;
if (kbdbl_handle->mode == 0) if (kbdbl_ctl->mode == 0)
sony_call_snc_handle(KBDBL_HANDLER, SET_MODE, &ignore); sony_call_snc_handle(kbdbl_ctl->handle, kbdbl_ctl->base,
if (kbdbl_handle->timeout != 0)
sony_call_snc_handle(KBDBL_HANDLER,
(kbdbl_handle->timeout << 0x10) | SET_TIMEOUT,
&ignore); &ignore);
if (kbdbl_ctl->timeout != 0)
sony_call_snc_handle(kbdbl_ctl->handle,
(kbdbl_ctl->base + 0x200) |
(kbdbl_ctl->timeout << 0x10), &ignore);
} }
static void sony_nc_backlight_ng_read_limits(int handle, struct battery_care_control {
struct sony_backlight_props *props) struct device_attribute attrs[2];
unsigned int handle;
};
static struct battery_care_control *bcare_ctl;
static ssize_t sony_nc_battery_care_limit_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{ {
int offset; unsigned int result, cmd;
acpi_status status; unsigned long value;
u8 brlvl, i;
u8 min = 0xff, max = 0x00;
struct acpi_object_list params;
union acpi_object in_obj;
union acpi_object *lvl_enum;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
props->handle = handle; if (count > 31)
props->offset = 0; return -EINVAL;
props->maxlvl = 0xff;
offset = sony_find_snc_handle(handle); if (kstrtoul(buffer, 10, &value))
if (offset < 0) return -EINVAL;
return;
/* try to read the boundaries from ACPI tables, if we fail the above /* limit values (2 bits):
* defaults should be reasonable * 00 - none
* 01 - 80%
* 10 - 50%
* 11 - 100%
*
* bit 0: 0 disable BCL, 1 enable BCL
* bit 1: 1 tell to store the battery limit (see bits 6,7) too
* bits 2,3: reserved
* bits 4,5: store the limit into the EC
* bits 6,7: store the limit into the battery
*/ */
params.count = 1;
params.pointer = &in_obj;
in_obj.type = ACPI_TYPE_INTEGER;
in_obj.integer.value = offset;
status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", &params,
&buffer);
if (ACPI_FAILURE(status))
return;
lvl_enum = (union acpi_object *) buffer.pointer;
if (!lvl_enum) {
pr_err("No SN06 return object.");
return;
}
if (lvl_enum->type != ACPI_TYPE_BUFFER) {
pr_err("Invalid SN06 return object 0x%.2x\n",
lvl_enum->type);
goto out_invalid;
}
/* the buffer lists brightness levels available, brightness levels are /*
* from 0 to 8 in the array, other values are used by ALS control. * handle 0x0115 should allow storing on battery too;
* handle 0x0136 same as 0x0115 + health status;
* handle 0x013f, same as 0x0136 but no storing on the battery
*
* Store only inside the EC for now, regardless the handle number
*/ */
for (i = 0; i < 9 && i < lvl_enum->buffer.length; i++) { if (value == 0)
/* disable limits */
cmd = 0x0;
brlvl = *(lvl_enum->buffer.pointer + i); else if (value <= 50)
dprintk("Brightness level: %d\n", brlvl); cmd = 0x21;
if (!brlvl) else if (value <= 80)
break; cmd = 0x11;
if (brlvl > max) else if (value <= 100)
max = brlvl; cmd = 0x31;
if (brlvl < min)
min = brlvl; else
} return -EINVAL;
props->offset = min;
props->maxlvl = max; if (sony_call_snc_handle(bcare_ctl->handle, (cmd << 0x10) | 0x0100,
dprintk("Brightness levels: min=%d max=%d\n", props->offset, &result))
props->maxlvl); return -EIO;
out_invalid: return count;
kfree(buffer.pointer);
return;
} }
static void sony_nc_backlight_setup(void) static ssize_t sony_nc_battery_care_limit_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{ {
acpi_handle unused; unsigned int result, status;
int max_brightness = 0;
const struct backlight_ops *ops = NULL;
struct backlight_properties props;
if (sony_find_snc_handle(0x12f) != -1) { if (sony_call_snc_handle(bcare_ctl->handle, 0x0000, &result))
ops = &sony_backlight_ng_ops; return -EIO;
sony_nc_backlight_ng_read_limits(0x12f, &sony_bl_props);
max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
} else if (sony_find_snc_handle(0x137) != -1) { status = (result & 0x01) ? ((result & 0x30) >> 0x04) : 0;
ops = &sony_backlight_ng_ops; switch (status) {
sony_nc_backlight_ng_read_limits(0x137, &sony_bl_props); case 1:
max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; status = 80;
break;
case 2:
status = 50;
break;
case 3:
status = 100;
break;
default:
status = 0;
break;
}
return snprintf(buffer, PAGE_SIZE, "%d\n", status);
}
static ssize_t sony_nc_battery_care_health_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
ssize_t count = 0;
unsigned int health;
if (sony_call_snc_handle(bcare_ctl->handle, 0x0200, &health))
return -EIO;
count = snprintf(buffer, PAGE_SIZE, "%d\n", health & 0xff);
return count;
}
static int sony_nc_battery_care_setup(struct platform_device *pd,
unsigned int handle)
{
int ret = 0;
bcare_ctl = kzalloc(sizeof(struct battery_care_control), GFP_KERNEL);
if (!bcare_ctl)
return -ENOMEM;
bcare_ctl->handle = handle;
sysfs_attr_init(&bcare_ctl->attrs[0].attr);
bcare_ctl->attrs[0].attr.name = "battery_care_limiter";
bcare_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR;
bcare_ctl->attrs[0].show = sony_nc_battery_care_limit_show;
bcare_ctl->attrs[0].store = sony_nc_battery_care_limit_store;
ret = device_create_file(&pd->dev, &bcare_ctl->attrs[0]);
if (ret)
goto outkzalloc;
/* 0x0115 is for models with no health reporting capability */
if (handle == 0x0115)
return 0;
sysfs_attr_init(&bcare_ctl->attrs[1].attr);
bcare_ctl->attrs[1].attr.name = "battery_care_health";
bcare_ctl->attrs[1].attr.mode = S_IRUGO;
bcare_ctl->attrs[1].show = sony_nc_battery_care_health_show;
ret = device_create_file(&pd->dev, &bcare_ctl->attrs[1]);
if (ret)
goto outlimiter;
return 0;
outlimiter:
device_remove_file(&pd->dev, &bcare_ctl->attrs[0]);
outkzalloc:
kfree(bcare_ctl);
bcare_ctl = NULL;
return ret;
}
static void sony_nc_battery_care_cleanup(struct platform_device *pd)
{
if (bcare_ctl) {
device_remove_file(&pd->dev, &bcare_ctl->attrs[0]);
if (bcare_ctl->handle != 0x0115)
device_remove_file(&pd->dev, &bcare_ctl->attrs[1]);
kfree(bcare_ctl);
bcare_ctl = NULL;
}
}
struct snc_thermal_ctrl {
unsigned int mode;
unsigned int profiles;
struct device_attribute mode_attr;
struct device_attribute profiles_attr;
};
static struct snc_thermal_ctrl *th_handle;
#define THM_PROFILE_MAX 3
static const char * const snc_thermal_profiles[] = {
"balanced",
"silent",
"performance"
};
static int sony_nc_thermal_mode_set(unsigned short mode)
{
unsigned int result;
/* the thermal profile seems to be a two bit bitmask:
* lsb -> silent
* msb -> performance
* no bit set is the normal operation and is always valid
* Some vaio models only have "balanced" and "performance"
*/
if ((mode && !(th_handle->profiles & mode)) || mode >= THM_PROFILE_MAX)
return -EINVAL;
if (sony_call_snc_handle(0x0122, mode << 0x10 | 0x0200, &result))
return -EIO;
th_handle->mode = mode;
return 0;
}
static int sony_nc_thermal_mode_get(void)
{
unsigned int result;
if (sony_call_snc_handle(0x0122, 0x0100, &result))
return -EIO;
return result & 0xff;
}
static ssize_t sony_nc_thermal_profiles_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
short cnt;
size_t idx = 0;
for (cnt = 0; cnt < THM_PROFILE_MAX; cnt++) {
if (!cnt || (th_handle->profiles & cnt))
idx += snprintf(buffer + idx, PAGE_SIZE - idx, "%s ",
snc_thermal_profiles[cnt]);
}
idx += snprintf(buffer + idx, PAGE_SIZE - idx, "\n");
return idx;
}
static ssize_t sony_nc_thermal_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
unsigned short cmd;
size_t len = count;
if (count == 0)
return -EINVAL;
/* skip the newline if present */
if (buffer[len - 1] == '\n')
len--;
for (cmd = 0; cmd < THM_PROFILE_MAX; cmd++)
if (strncmp(buffer, snc_thermal_profiles[cmd], len) == 0)
break;
if (sony_nc_thermal_mode_set(cmd))
return -EIO;
return count;
}
static ssize_t sony_nc_thermal_mode_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
ssize_t count = 0;
unsigned int mode = sony_nc_thermal_mode_get();
if (mode < 0)
return mode;
count = snprintf(buffer, PAGE_SIZE, "%s\n", snc_thermal_profiles[mode]);
return count;
}
static int sony_nc_thermal_setup(struct platform_device *pd)
{
int ret = 0;
th_handle = kzalloc(sizeof(struct snc_thermal_ctrl), GFP_KERNEL);
if (!th_handle)
return -ENOMEM;
ret = sony_call_snc_handle(0x0122, 0x0000, &th_handle->profiles);
if (ret) {
pr_warn("couldn't to read the thermal profiles\n");
goto outkzalloc;
}
ret = sony_nc_thermal_mode_get();
if (ret < 0) {
pr_warn("couldn't to read the current thermal profile");
goto outkzalloc;
}
th_handle->mode = ret;
sysfs_attr_init(&th_handle->profiles_attr.attr);
th_handle->profiles_attr.attr.name = "thermal_profiles";
th_handle->profiles_attr.attr.mode = S_IRUGO;
th_handle->profiles_attr.show = sony_nc_thermal_profiles_show;
sysfs_attr_init(&th_handle->mode_attr.attr);
th_handle->mode_attr.attr.name = "thermal_control";
th_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR;
th_handle->mode_attr.show = sony_nc_thermal_mode_show;
th_handle->mode_attr.store = sony_nc_thermal_mode_store;
ret = device_create_file(&pd->dev, &th_handle->profiles_attr);
if (ret)
goto outkzalloc;
ret = device_create_file(&pd->dev, &th_handle->mode_attr);
if (ret)
goto outprofiles;
return 0;
outprofiles:
device_remove_file(&pd->dev, &th_handle->profiles_attr);
outkzalloc:
kfree(th_handle);
th_handle = NULL;
return ret;
}
static void sony_nc_thermal_cleanup(struct platform_device *pd)
{
if (th_handle) {
device_remove_file(&pd->dev, &th_handle->profiles_attr);
device_remove_file(&pd->dev, &th_handle->mode_attr);
kfree(th_handle);
th_handle = NULL;
}
}
static void sony_nc_thermal_resume(void)
{
unsigned int status = sony_nc_thermal_mode_get();
if (status != th_handle->mode)
sony_nc_thermal_mode_set(th_handle->mode);
}
/* resume on LID open */
struct snc_lid_resume_control {
struct device_attribute attrs[3];
unsigned int status;
};
static struct snc_lid_resume_control *lid_ctl;
static ssize_t sony_nc_lid_resume_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
unsigned int result, pos;
unsigned long value;
if (count > 31)
return -EINVAL;
if (kstrtoul(buffer, 10, &value) || value > 1)
return -EINVAL;
/* the value we have to write to SNC is a bitmask:
* +--------------+
* | S3 | S4 | S5 |
* +--------------+
* 2 1 0
*/
if (strcmp(attr->attr.name, "lid_resume_S3") == 0)
pos = 2;
else if (strcmp(attr->attr.name, "lid_resume_S4") == 0)
pos = 1;
else if (strcmp(attr->attr.name, "lid_resume_S5") == 0)
pos = 0;
else
return -EINVAL;
if (value)
value = lid_ctl->status | (1 << pos);
else
value = lid_ctl->status & ~(1 << pos);
if (sony_call_snc_handle(0x0119, value << 0x10 | 0x0100, &result))
return -EIO;
lid_ctl->status = value;
return count;
}
static ssize_t sony_nc_lid_resume_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
unsigned int pos;
if (strcmp(attr->attr.name, "lid_resume_S3") == 0)
pos = 2;
else if (strcmp(attr->attr.name, "lid_resume_S4") == 0)
pos = 1;
else if (strcmp(attr->attr.name, "lid_resume_S5") == 0)
pos = 0;
else
return -EINVAL;
return snprintf(buffer, PAGE_SIZE, "%d\n",
(lid_ctl->status >> pos) & 0x01);
}
static int sony_nc_lid_resume_setup(struct platform_device *pd)
{
unsigned int result;
int i;
if (sony_call_snc_handle(0x0119, 0x0000, &result))
return -EIO;
lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL);
if (!lid_ctl)
return -ENOMEM;
lid_ctl->status = result & 0x7;
sysfs_attr_init(&lid_ctl->attrs[0].attr);
lid_ctl->attrs[0].attr.name = "lid_resume_S3";
lid_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR;
lid_ctl->attrs[0].show = sony_nc_lid_resume_show;
lid_ctl->attrs[0].store = sony_nc_lid_resume_store;
sysfs_attr_init(&lid_ctl->attrs[1].attr);
lid_ctl->attrs[1].attr.name = "lid_resume_S4";
lid_ctl->attrs[1].attr.mode = S_IRUGO | S_IWUSR;
lid_ctl->attrs[1].show = sony_nc_lid_resume_show;
lid_ctl->attrs[1].store = sony_nc_lid_resume_store;
sysfs_attr_init(&lid_ctl->attrs[2].attr);
lid_ctl->attrs[2].attr.name = "lid_resume_S5";
lid_ctl->attrs[2].attr.mode = S_IRUGO | S_IWUSR;
lid_ctl->attrs[2].show = sony_nc_lid_resume_show;
lid_ctl->attrs[2].store = sony_nc_lid_resume_store;
for (i = 0; i < 3; i++) {
result = device_create_file(&pd->dev, &lid_ctl->attrs[i]);
if (result)
goto liderror;
}
return 0;
liderror:
for (; i > 0; i--)
device_remove_file(&pd->dev, &lid_ctl->attrs[i]);
kfree(lid_ctl);
lid_ctl = NULL;
return result;
}
static void sony_nc_lid_resume_cleanup(struct platform_device *pd)
{
int i;
if (lid_ctl) {
for (i = 0; i < 3; i++)
device_remove_file(&pd->dev, &lid_ctl->attrs[i]);
kfree(lid_ctl);
lid_ctl = NULL;
}
}
/* High speed charging function */
static struct device_attribute *hsc_handle;
static ssize_t sony_nc_highspeed_charging_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
unsigned int result;
unsigned long value;
if (count > 31)
return -EINVAL;
if (kstrtoul(buffer, 10, &value) || value > 1)
return -EINVAL;
if (sony_call_snc_handle(0x0131, value << 0x10 | 0x0200, &result))
return -EIO;
return count;
}
static ssize_t sony_nc_highspeed_charging_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
unsigned int result;
if (sony_call_snc_handle(0x0131, 0x0100, &result))
return -EIO;
return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01);
}
static int sony_nc_highspeed_charging_setup(struct platform_device *pd)
{
unsigned int result;
if (sony_call_snc_handle(0x0131, 0x0000, &result) || !(result & 0x01)) {
/* some models advertise the handle but have no implementation
* for it
*/
pr_info("No High Speed Charging capability found\n");
return 0;
}
hsc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
if (!hsc_handle)
return -ENOMEM;
sysfs_attr_init(&hsc_handle->attr);
hsc_handle->attr.name = "battery_highspeed_charging";
hsc_handle->attr.mode = S_IRUGO | S_IWUSR;
hsc_handle->show = sony_nc_highspeed_charging_show;
hsc_handle->store = sony_nc_highspeed_charging_store;
result = device_create_file(&pd->dev, hsc_handle);
if (result) {
kfree(hsc_handle);
hsc_handle = NULL;
return result;
}
return 0;
}
static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd)
{
if (hsc_handle) {
device_remove_file(&pd->dev, hsc_handle);
kfree(hsc_handle);
hsc_handle = NULL;
}
}
/* Touchpad enable/disable */
struct touchpad_control {
struct device_attribute attr;
int handle;
};
static struct touchpad_control *tp_ctl;
static ssize_t sony_nc_touchpad_store(struct device *dev,
struct device_attribute *attr, const char *buffer, size_t count)
{
unsigned int result;
unsigned long value;
if (count > 31)
return -EINVAL;
if (kstrtoul(buffer, 10, &value) || value > 1)
return -EINVAL;
/* sysfs: 0 disabled, 1 enabled
* EC: 0 enabled, 1 disabled
*/
if (sony_call_snc_handle(tp_ctl->handle,
(!value << 0x10) | 0x100, &result))
return -EIO;
return count;
}
static ssize_t sony_nc_touchpad_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
unsigned int result;
if (sony_call_snc_handle(tp_ctl->handle, 0x000, &result))
return -EINVAL;
return snprintf(buffer, PAGE_SIZE, "%d\n", !(result & 0x01));
}
static int sony_nc_touchpad_setup(struct platform_device *pd,
unsigned int handle)
{
int ret = 0;
tp_ctl = kzalloc(sizeof(struct touchpad_control), GFP_KERNEL);
if (!tp_ctl)
return -ENOMEM;
tp_ctl->handle = handle;
sysfs_attr_init(&tp_ctl->attr.attr);
tp_ctl->attr.attr.name = "touchpad";
tp_ctl->attr.attr.mode = S_IRUGO | S_IWUSR;
tp_ctl->attr.show = sony_nc_touchpad_show;
tp_ctl->attr.store = sony_nc_touchpad_store;
ret = device_create_file(&pd->dev, &tp_ctl->attr);
if (ret) {
kfree(tp_ctl);
tp_ctl = NULL;
}
return ret;
}
static void sony_nc_touchpad_cleanup(struct platform_device *pd)
{
if (tp_ctl) {
device_remove_file(&pd->dev, &tp_ctl->attr);
kfree(tp_ctl);
tp_ctl = NULL;
}
}
static void sony_nc_backlight_ng_read_limits(int handle,
struct sony_backlight_props *props)
{
u64 offset;
int i;
u8 min = 0xff, max = 0x00;
unsigned char buffer[32] = { 0 };
props->handle = handle;
props->offset = 0;
props->maxlvl = 0xff;
offset = sony_find_snc_handle(handle);
if (offset < 0)
return;
/* try to read the boundaries from ACPI tables, if we fail the above
* defaults should be reasonable
*/
i = sony_nc_buffer_call(sony_nc_acpi_handle, "SN06", &offset, buffer,
32);
if (i < 0)
return;
/* the buffer lists brightness levels available, brightness levels are
* from position 0 to 8 in the array, other values are used by ALS
* control.
*/
for (i = 0; i < 9 && i < ARRAY_SIZE(buffer); i++) {
dprintk("Brightness level: %d\n", buffer[i]);
if (!buffer[i])
break;
if (buffer[i] > max)
max = buffer[i];
if (buffer[i] < min)
min = buffer[i];
}
props->offset = min;
props->maxlvl = max;
dprintk("Brightness levels: min=%d max=%d\n", props->offset,
props->maxlvl);
}
static void sony_nc_backlight_setup(void)
{
acpi_handle unused;
int max_brightness = 0;
const struct backlight_ops *ops = NULL;
struct backlight_properties props;
if (sony_find_snc_handle(0x12f) != -1) {
ops = &sony_backlight_ng_ops;
sony_nc_backlight_ng_read_limits(0x12f, &sony_bl_props);
max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
} else if (sony_find_snc_handle(0x137) != -1) {
ops = &sony_backlight_ng_ops;
sony_nc_backlight_ng_read_limits(0x137, &sony_bl_props);
max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
} else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
&unused))) { &unused))) {
...@@ -1715,28 +2599,25 @@ static int sony_nc_add(struct acpi_device *device) ...@@ -1715,28 +2599,25 @@ static int sony_nc_add(struct acpi_device *device)
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON",
&handle))) { &handle))) {
if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) int arg = 1;
if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL))
dprintk("ECON Method failed\n"); dprintk("ECON Method failed\n");
} }
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00",
&handle))) { &handle))) {
dprintk("Doing SNC setup\n"); dprintk("Doing SNC setup\n");
/* retrieve the available handles */
result = sony_nc_handles_setup(sony_pf_device); result = sony_nc_handles_setup(sony_pf_device);
if (result) if (!result)
goto outpresent; sony_nc_function_setup(device, sony_pf_device);
result = sony_nc_kbd_backlight_setup(sony_pf_device);
if (result)
goto outsnc;
sony_nc_function_setup(device);
sony_nc_rfkill_setup(device);
} }
/* setup input devices and helper fifo */ /* setup input devices and helper fifo */
result = sony_laptop_setup_input(device); result = sony_laptop_setup_input(device);
if (result) { if (result) {
pr_err("Unable to create input devices\n"); pr_err("Unable to create input devices\n");
goto outkbdbacklight; goto outsnc;
} }
if (acpi_video_backlight_support()) { if (acpi_video_backlight_support()) {
...@@ -1794,10 +2675,8 @@ static int sony_nc_add(struct acpi_device *device) ...@@ -1794,10 +2675,8 @@ static int sony_nc_add(struct acpi_device *device)
sony_laptop_remove_input(); sony_laptop_remove_input();
outkbdbacklight:
sony_nc_kbd_backlight_cleanup(sony_pf_device);
outsnc: outsnc:
sony_nc_function_cleanup(sony_pf_device);
sony_nc_handles_cleanup(sony_pf_device); sony_nc_handles_cleanup(sony_pf_device);
outpresent: outpresent:
...@@ -1820,11 +2699,10 @@ static int sony_nc_remove(struct acpi_device *device, int type) ...@@ -1820,11 +2699,10 @@ static int sony_nc_remove(struct acpi_device *device, int type)
device_remove_file(&sony_pf_device->dev, &item->devattr); device_remove_file(&sony_pf_device->dev, &item->devattr);
} }
sony_nc_kbd_backlight_cleanup(sony_pf_device); sony_nc_function_cleanup(sony_pf_device);
sony_nc_handles_cleanup(sony_pf_device); sony_nc_handles_cleanup(sony_pf_device);
sony_pf_remove(); sony_pf_remove();
sony_laptop_remove_input(); sony_laptop_remove_input();
sony_nc_rfkill_cleanup();
dprintk(SONY_NC_DRIVER_NAME " removed.\n"); dprintk(SONY_NC_DRIVER_NAME " removed.\n");
return 0; return 0;
...@@ -2437,7 +3315,9 @@ static ssize_t sony_pic_wwanpower_store(struct device *dev, ...@@ -2437,7 +3315,9 @@ static ssize_t sony_pic_wwanpower_store(struct device *dev,
if (count > 31) if (count > 31)
return -EINVAL; return -EINVAL;
value = simple_strtoul(buffer, NULL, 10); if (kstrtoul(buffer, 10, &value))
return -EINVAL;
mutex_lock(&spic_dev.lock); mutex_lock(&spic_dev.lock);
__sony_pic_set_wwanpower(value); __sony_pic_set_wwanpower(value);
mutex_unlock(&spic_dev.lock); mutex_unlock(&spic_dev.lock);
...@@ -2474,7 +3354,9 @@ static ssize_t sony_pic_bluetoothpower_store(struct device *dev, ...@@ -2474,7 +3354,9 @@ static ssize_t sony_pic_bluetoothpower_store(struct device *dev,
if (count > 31) if (count > 31)
return -EINVAL; return -EINVAL;
value = simple_strtoul(buffer, NULL, 10); if (kstrtoul(buffer, 10, &value))
return -EINVAL;
mutex_lock(&spic_dev.lock); mutex_lock(&spic_dev.lock);
__sony_pic_set_bluetoothpower(value); __sony_pic_set_bluetoothpower(value);
mutex_unlock(&spic_dev.lock); mutex_unlock(&spic_dev.lock);
...@@ -2513,7 +3395,9 @@ static ssize_t sony_pic_fanspeed_store(struct device *dev, ...@@ -2513,7 +3395,9 @@ static ssize_t sony_pic_fanspeed_store(struct device *dev,
if (count > 31) if (count > 31)
return -EINVAL; return -EINVAL;
value = simple_strtoul(buffer, NULL, 10); if (kstrtoul(buffer, 10, &value))
return -EINVAL;
if (sony_pic_set_fanspeed(value)) if (sony_pic_set_fanspeed(value))
return -EIO; return -EIO;
...@@ -2671,7 +3555,8 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, ...@@ -2671,7 +3555,8 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd,
ret = -EIO; ret = -EIO;
break; break;
} }
if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) { if (sony_nc_int_call(sony_nc_acpi_handle, "GBRT", NULL,
&value)) {
ret = -EIO; ret = -EIO;
break; break;
} }
...@@ -2688,8 +3573,9 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, ...@@ -2688,8 +3573,9 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd,
ret = -EFAULT; ret = -EFAULT;
break; break;
} }
if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", value = (val8 >> 5) + 1;
(val8 >> 5) + 1, NULL)) { if (sony_nc_int_call(sony_nc_acpi_handle, "SBRT", &value,
NULL)) {
ret = -EIO; ret = -EIO;
break; break;
} }
......
...@@ -3402,7 +3402,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -3402,7 +3402,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
/* Do not issue duplicate brightness change events to /* Do not issue duplicate brightness change events to
* userspace. tpacpi_detect_brightness_capabilities() must have * userspace. tpacpi_detect_brightness_capabilities() must have
* been called before this point */ * been called before this point */
if (tp_features.bright_acpimode && acpi_video_backlight_support()) { if (acpi_video_backlight_support()) {
pr_info("This ThinkPad has standard ACPI backlight " pr_info("This ThinkPad has standard ACPI backlight "
"brightness control, supported by the ACPI " "brightness control, supported by the ACPI "
"video driver\n"); "video driver\n");
......
...@@ -95,6 +95,7 @@ MODULE_LICENSE("GPL"); ...@@ -95,6 +95,7 @@ MODULE_LICENSE("GPL");
/* registers */ /* registers */
#define HCI_FAN 0x0004 #define HCI_FAN 0x0004
#define HCI_TR_BACKLIGHT 0x0005
#define HCI_SYSTEM_EVENT 0x0016 #define HCI_SYSTEM_EVENT 0x0016
#define HCI_VIDEO_OUT 0x001c #define HCI_VIDEO_OUT 0x001c
#define HCI_HOTKEY_EVENT 0x001e #define HCI_HOTKEY_EVENT 0x001e
...@@ -134,6 +135,7 @@ struct toshiba_acpi_dev { ...@@ -134,6 +135,7 @@ struct toshiba_acpi_dev {
unsigned int system_event_supported:1; unsigned int system_event_supported:1;
unsigned int ntfy_supported:1; unsigned int ntfy_supported:1;
unsigned int info_supported:1; unsigned int info_supported:1;
unsigned int tr_backlight_supported:1;
struct mutex mutex; struct mutex mutex;
}; };
...@@ -478,34 +480,70 @@ static const struct rfkill_ops toshiba_rfk_ops = { ...@@ -478,34 +480,70 @@ static const struct rfkill_ops toshiba_rfk_ops = {
.poll = bt_rfkill_poll, .poll = bt_rfkill_poll,
}; };
static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, bool *enabled)
{
u32 hci_result;
u32 status;
hci_read1(dev, HCI_TR_BACKLIGHT, &status, &hci_result);
*enabled = !status;
return hci_result == HCI_SUCCESS ? 0 : -EIO;
}
static int set_tr_backlight_status(struct toshiba_acpi_dev *dev, bool enable)
{
u32 hci_result;
u32 value = !enable;
hci_write1(dev, HCI_TR_BACKLIGHT, value, &hci_result);
return hci_result == HCI_SUCCESS ? 0 : -EIO;
}
static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
static int get_lcd(struct backlight_device *bd) static int __get_lcd_brightness(struct toshiba_acpi_dev *dev)
{ {
struct toshiba_acpi_dev *dev = bl_get_data(bd);
u32 hci_result; u32 hci_result;
u32 value; u32 value;
int brightness = 0;
if (dev->tr_backlight_supported) {
bool enabled;
int ret = get_tr_backlight_status(dev, &enabled);
if (ret)
return ret;
if (enabled)
return 0;
brightness++;
}
hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result); hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result);
if (hci_result == HCI_SUCCESS) if (hci_result == HCI_SUCCESS)
return (value >> HCI_LCD_BRIGHTNESS_SHIFT); return brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT);
return -EIO; return -EIO;
} }
static int get_lcd_brightness(struct backlight_device *bd)
{
struct toshiba_acpi_dev *dev = bl_get_data(bd);
return __get_lcd_brightness(dev);
}
static int lcd_proc_show(struct seq_file *m, void *v) static int lcd_proc_show(struct seq_file *m, void *v)
{ {
struct toshiba_acpi_dev *dev = m->private; struct toshiba_acpi_dev *dev = m->private;
int value; int value;
int levels;
if (!dev->backlight_dev) if (!dev->backlight_dev)
return -ENODEV; return -ENODEV;
value = get_lcd(dev->backlight_dev); levels = dev->backlight_dev->props.max_brightness + 1;
value = get_lcd_brightness(dev->backlight_dev);
if (value >= 0) { if (value >= 0) {
seq_printf(m, "brightness: %d\n", value); seq_printf(m, "brightness: %d\n", value);
seq_printf(m, "brightness_levels: %d\n", seq_printf(m, "brightness_levels: %d\n", levels);
HCI_LCD_BRIGHTNESS_LEVELS);
return 0; return 0;
} }
...@@ -518,10 +556,19 @@ static int lcd_proc_open(struct inode *inode, struct file *file) ...@@ -518,10 +556,19 @@ static int lcd_proc_open(struct inode *inode, struct file *file)
return single_open(file, lcd_proc_show, PDE(inode)->data); return single_open(file, lcd_proc_show, PDE(inode)->data);
} }
static int set_lcd(struct toshiba_acpi_dev *dev, int value) static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
{ {
u32 hci_result; u32 hci_result;
if (dev->tr_backlight_supported) {
bool enable = !value;
int ret = set_tr_backlight_status(dev, enable);
if (ret)
return ret;
if (value)
value--;
}
value = value << HCI_LCD_BRIGHTNESS_SHIFT; value = value << HCI_LCD_BRIGHTNESS_SHIFT;
hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result); hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result);
return hci_result == HCI_SUCCESS ? 0 : -EIO; return hci_result == HCI_SUCCESS ? 0 : -EIO;
...@@ -530,7 +577,7 @@ static int set_lcd(struct toshiba_acpi_dev *dev, int value) ...@@ -530,7 +577,7 @@ static int set_lcd(struct toshiba_acpi_dev *dev, int value)
static int set_lcd_status(struct backlight_device *bd) static int set_lcd_status(struct backlight_device *bd)
{ {
struct toshiba_acpi_dev *dev = bl_get_data(bd); struct toshiba_acpi_dev *dev = bl_get_data(bd);
return set_lcd(dev, bd->props.brightness); return set_lcd_brightness(dev, bd->props.brightness);
} }
static ssize_t lcd_proc_write(struct file *file, const char __user *buf, static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
...@@ -541,6 +588,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf, ...@@ -541,6 +588,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
size_t len; size_t len;
int value; int value;
int ret; int ret;
int levels = dev->backlight_dev->props.max_brightness + 1;
len = min(count, sizeof(cmd) - 1); len = min(count, sizeof(cmd) - 1);
if (copy_from_user(cmd, buf, len)) if (copy_from_user(cmd, buf, len))
...@@ -548,8 +596,8 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf, ...@@ -548,8 +596,8 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
cmd[len] = '\0'; cmd[len] = '\0';
if (sscanf(cmd, " brightness : %i", &value) == 1 && if (sscanf(cmd, " brightness : %i", &value) == 1 &&
value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { value >= 0 && value < levels) {
ret = set_lcd(dev, value); ret = set_lcd_brightness(dev, value);
if (ret == 0) if (ret == 0)
ret = count; ret = count;
} else { } else {
...@@ -860,8 +908,9 @@ static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev) ...@@ -860,8 +908,9 @@ static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev)
} }
static const struct backlight_ops toshiba_backlight_data = { static const struct backlight_ops toshiba_backlight_data = {
.get_brightness = get_lcd, .options = BL_CORE_SUSPENDRESUME,
.update_status = set_lcd_status, .get_brightness = get_lcd_brightness,
.update_status = set_lcd_status,
}; };
static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
...@@ -1020,6 +1069,56 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) ...@@ -1020,6 +1069,56 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
return error; return error;
} }
static int __devinit toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
{
struct backlight_properties props;
int brightness;
int ret;
bool enabled;
/*
* Some machines don't support the backlight methods at all, and
* others support it read-only. Either of these is pretty useless,
* so only register the backlight device if the backlight method
* supports both reads and writes.
*/
brightness = __get_lcd_brightness(dev);
if (brightness < 0)
return 0;
ret = set_lcd_brightness(dev, brightness);
if (ret) {
pr_debug("Backlight method is read-only, disabling backlight support\n");
return 0;
}
/* Determine whether or not BIOS supports transflective backlight */
ret = get_tr_backlight_status(dev, &enabled);
dev->tr_backlight_supported = !ret;
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
/* adding an extra level and having 0 change to transflective mode */
if (dev->tr_backlight_supported)
props.max_brightness++;
dev->backlight_dev = backlight_device_register("toshiba",
&dev->acpi_dev->dev,
dev,
&toshiba_backlight_data,
&props);
if (IS_ERR(dev->backlight_dev)) {
ret = PTR_ERR(dev->backlight_dev);
pr_err("Could not register toshiba backlight device\n");
dev->backlight_dev = NULL;
return ret;
}
dev->backlight_dev->props.brightness = brightness;
return 0;
}
static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)
{ {
struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
...@@ -1078,7 +1177,6 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) ...@@ -1078,7 +1177,6 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
u32 dummy; u32 dummy;
bool bt_present; bool bt_present;
int ret = 0; int ret = 0;
struct backlight_properties props;
if (toshiba_acpi) if (toshiba_acpi)
return -EBUSY; return -EBUSY;
...@@ -1104,22 +1202,9 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) ...@@ -1104,22 +1202,9 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
mutex_init(&dev->mutex); mutex_init(&dev->mutex);
memset(&props, 0, sizeof(props)); ret = toshiba_acpi_setup_backlight(dev);
props.type = BACKLIGHT_PLATFORM; if (ret)
props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
dev->backlight_dev = backlight_device_register("toshiba",
&acpi_dev->dev,
dev,
&toshiba_backlight_data,
&props);
if (IS_ERR(dev->backlight_dev)) {
ret = PTR_ERR(dev->backlight_dev);
pr_err("Could not register toshiba backlight device\n");
dev->backlight_dev = NULL;
goto error; goto error;
}
dev->backlight_dev->props.brightness = get_lcd(dev->backlight_dev);
/* Register rfkill switch for Bluetooth */ /* Register rfkill switch for Bluetooth */
if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) { if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) {
......
...@@ -15,15 +15,26 @@ ...@@ -15,15 +15,26 @@
#include <asm/olpc.h> #include <asm/olpc.h>
static bool card_blocked;
static int rfkill_set_block(void *data, bool blocked) static int rfkill_set_block(void *data, bool blocked)
{ {
unsigned char cmd; unsigned char cmd;
int r;
if (blocked == card_blocked)
return 0;
if (blocked) if (blocked)
cmd = EC_WLAN_ENTER_RESET; cmd = EC_WLAN_ENTER_RESET;
else else
cmd = EC_WLAN_LEAVE_RESET; cmd = EC_WLAN_LEAVE_RESET;
return olpc_ec_cmd(cmd, NULL, 0, NULL, 0); r = olpc_ec_cmd(cmd, NULL, 0, NULL, 0);
if (r == 0)
card_blocked = blocked;
return r;
} }
static const struct rfkill_ops rfkill_ops = { static const struct rfkill_ops rfkill_ops = {
......
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