diff options
-rw-r--r-- | Documentation/thinkpad-acpi.txt | 116 | ||||
-rw-r--r-- | drivers/acpi/asus_acpi.c | 55 | ||||
-rw-r--r-- | drivers/misc/Kconfig | 19 | ||||
-rw-r--r-- | drivers/misc/asus-laptop.c | 26 | ||||
-rw-r--r-- | drivers/misc/sony-laptop.c | 445 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 3256 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.h | 606 | ||||
-rw-r--r-- | include/linux/sonypi.h | 2 |
8 files changed, 2641 insertions, 1884 deletions
diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index 10c041ca13c..6c2477754a2 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -1,7 +1,7 @@ ThinkPad ACPI Extras Driver - Version 0.17 - October 04th, 2007 + Version 0.19 + January 06th, 2008 Borislav Deianov <borislav@users.sf.net> Henrique de Moraes Holschuh <hmh@hmh.eng.br> @@ -215,6 +215,11 @@ The following commands can be written to the /proc/acpi/ibm/hotkey file: ... any other 8-hex-digit mask ... echo reset > /proc/acpi/ibm/hotkey -- restore the original mask +The procfs interface does not support NVRAM polling control. So as to +maintain maximum bug-to-bug compatibility, it does not report any masks, +nor does it allow one to manipulate the hot key mask when the firmware +does not support masks at all, even if NVRAM polling is in use. + sysfs notes: hotkey_bios_enabled: @@ -231,17 +236,26 @@ sysfs notes: to this value. hotkey_enable: - Enables/disables the hot keys feature, and reports - current status of the hot keys feature. + Enables/disables the hot keys feature in the ACPI + firmware, and reports current status of the hot keys + feature. Has no effect on the NVRAM hot key polling + functionality. 0: disables the hot keys feature / feature disabled 1: enables the hot keys feature / feature enabled hotkey_mask: - bit mask to enable driver-handling and ACPI event - generation for each hot key (see above). Returns the - current status of the hot keys mask, and allows one to - modify it. + bit mask to enable driver-handling (and depending on + the firmware, ACPI event generation) for each hot key + (see above). Returns the current status of the hot keys + mask, and allows one to modify it. + + Note: when NVRAM polling is active, the firmware mask + will be different from the value returned by + hotkey_mask. The driver will retain enabled bits for + hotkeys that are under NVRAM polling even if the + firmware refuses them, and will not set these bits on + the firmware hot key mask. hotkey_all_mask: bit mask that should enable event reporting for all @@ -257,12 +271,48 @@ sysfs notes: handled by the firmware anyway. Echo it to hotkey_mask above, to use. + hotkey_source_mask: + bit mask that selects which hot keys will the driver + poll the NVRAM for. This is auto-detected by the driver + based on the capabilities reported by the ACPI firmware, + but it can be overridden at runtime. + + Hot keys whose bits are set in both hotkey_source_mask + and also on hotkey_mask are polled for in NVRAM. Only a + few hot keys are available through CMOS NVRAM polling. + + Warning: when in NVRAM mode, the volume up/down/mute + keys are synthesized according to changes in the mixer, + so you have to use volume up or volume down to unmute, + as per the ThinkPad volume mixer user interface. When + in ACPI event mode, volume up/down/mute are reported as + separate events, but this behaviour may be corrected in + future releases of this driver, in which case the + ThinkPad volume mixer user interface semanthics will be + enforced. + + hotkey_poll_freq: + frequency in Hz for hot key polling. It must be between + 0 and 25 Hz. Polling is only carried out when strictly + needed. + + Setting hotkey_poll_freq to zero disables polling, and + will cause hot key presses that require NVRAM polling + to never be reported. + + Setting hotkey_poll_freq too low will cause repeated + pressings of the same hot key to be misreported as a + single key press, or to not even be detected at all. + The recommended polling frequency is 10Hz. + hotkey_radio_sw: if the ThinkPad has a hardware radio switch, this attribute will read 0 if the switch is in the "radios disabled" postition, and 1 if the switch is in the "radios enabled" position. + This attribute has poll()/select() support. + hotkey_report_mode: Returns the state of the procfs ACPI event report mode filter for hot keys. If it is set to 1 (the default), @@ -277,6 +327,25 @@ sysfs notes: May return -EPERM (write access locked out by module parameter) or -EACCES (read-only). + wakeup_reason: + Set to 1 if the system is waking up because the user + requested a bay ejection. Set to 2 if the system is + waking up because the user requested the system to + undock. Set to zero for normal wake-ups or wake-ups + due to unknown reasons. + + This attribute has poll()/select() support. + + wakeup_hotunplug_complete: + Set to 1 if the system was waken up because of an + undock or bay ejection request, and that request + was sucessfully completed. At this point, it might + be useful to send the system back to sleep, at the + user's choice. Refer to HKEY events 0x4003 and + 0x3003, below. + + This attribute has poll()/select() support. + input layer notes: A Hot key is mapped to a single input layer EV_KEY event, possibly @@ -427,6 +496,23 @@ Non hot-key ACPI HKEY event map: The above events are not propagated by the driver, except for legacy compatibility purposes when hotkey_report_mode is set to 1. +0x2304 System is waking up from suspend to undock +0x2305 System is waking up from suspend to eject bay +0x2404 System is waking up from hibernation to undock +0x2405 System is waking up from hibernation to eject bay + +The above events are never propagated by the driver. + +0x3003 Bay ejection (see 0x2x05) complete, can sleep again +0x4003 Undocked (see 0x2x04), can sleep again +0x5009 Tablet swivel: switched to tablet mode +0x500A Tablet swivel: switched to normal mode +0x500B Tablet pen insterted into its storage bay +0x500C Tablet pen removed from its storage bay +0x5010 Brightness level changed (newer Lenovo BIOSes) + +The above events are propagated by the driver. + Compatibility notes: ibm-acpi and thinkpad-acpi 0.15 (mainline kernels before 2.6.23) never @@ -1263,3 +1349,17 @@ Sysfs interface changelog: and the hwmon class for libsensors4 (lm-sensors 3) compatibility. Moved all hwmon attributes to this new platform device. + +0x020100: Marker for thinkpad-acpi with hot key NVRAM polling + support. If you must, use it to know you should not + start an userspace NVRAM poller (allows to detect when + NVRAM is compiled out by the user because it is + unneeded/undesired in the first place). +0x020101: Marker for thinkpad-acpi with hot key NVRAM polling + and proper hotkey_mask semanthics (version 8 of the + NVRAM polling patch). Some development snapshots of + 0.18 had an earlier version that did strange things + to hotkey_mask. + +0x020200: Add poll()/select() support to the following attributes: + hotkey_radio_sw, wakeup_hotunplug_complete, wakeup_reason diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c index d915fec9bf6..d25ef961415 100644 --- a/drivers/acpi/asus_acpi.c +++ b/drivers/acpi/asus_acpi.c @@ -142,6 +142,7 @@ struct asus_hotk { xxN, //M2400N, M3700N, M5200N, M6800N, S1300N, S5200N A4S, //Z81sp //(Centrino) + F3Sa, END_MODEL } model; //Models currently supported u16 event_count[128]; //count for each event TODO make this better @@ -405,7 +406,20 @@ static struct model_data model_conf[END_MODEL] = { .brightness_get = "GPLV", .mt_bt_switch = "BLED", .mt_wled = "WLED" - } + }, + + { + .name = "F3Sa", + .mt_bt_switch = "BLED", + .mt_wled = "WLED", + .mt_mled = "MLED", + .brightness_get = "GPLV", + .brightness_set = "SPLV", + .mt_lcd_switch = "\\_SB.PCI0.SBRG.EC0._Q10", + .lcd_status = "\\_SB.PCI0.SBRG.EC0.RPIN", + .display_get = "\\ADVG", + .display_set = "SDSP", + }, }; @@ -710,15 +724,8 @@ static int get_lcd_state(void) { int lcd = 0; - if (hotk->model != L3H) { - /* We don't have to check anything if we are here */ - if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd)) - printk(KERN_WARNING - "Asus ACPI: Error reading LCD status\n"); - - if (hotk->model == L2D) - lcd = ~lcd; - } else { /* L3H and the like have to be handled differently */ + if (hotk->model == L3H) { + /* L3H and the like have to be handled differently */ acpi_status status = 0; struct acpi_object_list input; union acpi_object mt_params[2]; @@ -745,6 +752,32 @@ static int get_lcd_state(void) if (out_obj.type == ACPI_TYPE_INTEGER) /* That's what the AML code does */ lcd = out_obj.integer.value >> 8; + } else if (hotk->model == F3Sa) { + unsigned long tmp; + union acpi_object param; + struct acpi_object_list input; + acpi_status status; + + /* Read pin 11 */ + param.type = ACPI_TYPE_INTEGER; + param.integer.value = 0x11; + input.count = 1; + input.pointer = ¶m; + + status = acpi_evaluate_integer(NULL, hotk->methods->lcd_status, + &input, &tmp); + if (status != AE_OK) + return -1; + + lcd = tmp; + } else { + /* We don't have to check anything if we are here */ + if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd)) + printk(KERN_WARNING + "Asus ACPI: Error reading LCD status\n"); + + if (hotk->model == L2D) + lcd = ~lcd; } return (lcd & 1); @@ -1134,6 +1167,8 @@ static int asus_model_match(char *model) return W5A; else if (strncmp(model, "A4S", 3) == 0) return A4S; + else if (strncmp(model, "F3Sa", 4) == 0) + return F3Sa; else return END_MODEL; } diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b5e67c0ff43..b1f9a405c82 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -219,6 +219,25 @@ config THINKPAD_ACPI_BAY If you are not sure, say Y here. +config THINKPAD_ACPI_HOTKEY_POLL + bool "Suport NVRAM polling for hot keys" + depends on THINKPAD_ACPI + default y + ---help--- + Some thinkpad models benefit from NVRAM polling to detect a few of + the hot key press events. If you know your ThinkPad model does not + need to do NVRAM polling to support any of the hot keys you use, + unselecting this option will save about 1kB of memory. + + ThinkPads T40 and newer, R52 and newer, and X31 and newer are + unlikely to need NVRAM polling in their latest BIOS versions. + + NVRAM polling can detect at most the following keys: ThinkPad/Access + IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute, + Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12). + + If you are not sure, say Y here. The driver enables polling only if + it is strictly necessary to do so. config ATMEL_SSC tristate "Device driver for Atmel SSC peripheral" diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index 0846c33296b..3bd883166e3 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c @@ -254,7 +254,7 @@ ASUS_LED(gled, "gaming"); * method is searched within the scope of the handle, can be NULL. The output * of the method is written is output, which can also be NULL * - * returns 1 if write is successful, 0 else. + * returns 0 if write is successful, -1 else. */ static int write_acpi_int(acpi_handle handle, const char *method, int val, struct acpi_buffer *output) @@ -263,13 +263,19 @@ static int write_acpi_int(acpi_handle handle, const char *method, int val, union acpi_object in_obj; //the only param we use acpi_status status; + if (!handle) + return 0; + params.count = 1; params.pointer = &in_obj; in_obj.type = ACPI_TYPE_INTEGER; in_obj.integer.value = val; status = acpi_evaluate_object(handle, (char *)method, ¶ms, output); - return (status == AE_OK); + if (status == AE_OK) + return 0; + else + return -1; } static int read_wireless_status(int mask) @@ -321,7 +327,7 @@ static void write_status(acpi_handle handle, int out, int mask) switch (mask) { case MLED_ON: - out = !out & 0x1; + out = !(out & 0x1); break; case GLED_ON: out = (out & 0x1) + 1; @@ -335,7 +341,7 @@ static void write_status(acpi_handle handle, int out, int mask) break; } - if (handle && !write_acpi_int(handle, NULL, out, NULL)) + if (write_acpi_int(handle, NULL, out, NULL)) printk(ASUS_WARNING " write failed %x\n", mask); } @@ -415,7 +421,7 @@ static int set_brightness(struct backlight_device *bd, int value) value = (0 < value) ? ((15 < value) ? 15 : value) : 0; /* 0 <= value <= 15 */ - if (!write_acpi_int(brightness_set_handle, NULL, value, NULL)) { + if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) { printk(ASUS_WARNING "Error changing brightness\n"); ret = -EIO; } @@ -545,7 +551,7 @@ static ssize_t store_ledd(struct device *dev, struct device_attribute *attr, rv = parse_arg(buf, count, &value); if (rv > 0) { - if (!write_acpi_int(ledd_set_handle, NULL, value, NULL)) + if (write_acpi_int(ledd_set_handle, NULL, value, NULL)) printk(ASUS_WARNING "LED display write failed\n"); else hotk->ledd_status = (u32) value; @@ -590,7 +596,7 @@ static ssize_t store_bluetooth(struct device *dev, static void set_display(int value) { /* no sanity check needed for now */ - if (!write_acpi_int(display_set_handle, NULL, value, NULL)) + if (write_acpi_int(display_set_handle, NULL, value, NULL)) printk(ASUS_WARNING "Error setting display\n"); return; } @@ -647,7 +653,7 @@ static ssize_t store_disp(struct device *dev, struct device_attribute *attr, */ static void set_light_sens_switch(int value) { - if (!write_acpi_int(ls_switch_handle, NULL, value, NULL)) + if (write_acpi_int(ls_switch_handle, NULL, value, NULL)) printk(ASUS_WARNING "Error setting light sensor switch\n"); hotk->light_switch = value; } @@ -672,7 +678,7 @@ static ssize_t store_lssw(struct device *dev, struct device_attribute *attr, static void set_light_sens_level(int value) { - if (!write_acpi_int(ls_level_handle, NULL, value, NULL)) + if (write_acpi_int(ls_level_handle, NULL, value, NULL)) printk(ASUS_WARNING "Error setting light sensor level\n"); hotk->light_level = value; } @@ -860,7 +866,7 @@ static int asus_hotk_get_info(void) printk(ASUS_WARNING "Couldn't get the DSDT table header\n"); /* We have to write 0 on init this far for all ASUS models */ - if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) { + if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) { printk(ASUS_ERR "Hotkey initialization failed\n"); return -ENODEV; } diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index b0f68031b49..899e3f75f28 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c @@ -73,7 +73,7 @@ if (debug) printk(KERN_WARNING DRV_PFX msg); \ } while (0) -#define SONY_LAPTOP_DRIVER_VERSION "0.5" +#define SONY_LAPTOP_DRIVER_VERSION "0.6" #define SONY_NC_CLASS "sony-nc" #define SONY_NC_HID "SNY5001" @@ -146,68 +146,70 @@ struct sony_laptop_keypress { * and input layer indexes in the keymap */ static int sony_laptop_input_index[] = { - -1, /* no event */ - -1, /* SONYPI_EVENT_JOGDIAL_DOWN */ - -1, /* SONYPI_EVENT_JOGDIAL_UP */ - -1, /* SONYPI_EVENT_JOGDIAL_DOWN_PRESSED */ - -1, /* SONYPI_EVENT_JOGDIAL_UP_PRESSED */ - -1, /* SONYPI_EVENT_JOGDIAL_PRESSED */ - -1, /* SONYPI_EVENT_JOGDIAL_RELEASED */ - 0, /* SONYPI_EVENT_CAPTURE_PRESSED */ - 1, /* SONYPI_EVENT_CAPTURE_RELEASED */ - 2, /* SONYPI_EVENT_CAPTURE_PARTIALPRESSED */ - 3, /* SONYPI_EVENT_CAPTURE_PARTIALRELEASED */ - 4, /* SONYPI_EVENT_FNKEY_ESC */ - 5, /* SONYPI_EVENT_FNKEY_F1 */ - 6, /* SONYPI_EVENT_FNKEY_F2 */ - 7, /* SONYPI_EVENT_FNKEY_F3 */ - 8, /* SONYPI_EVENT_FNKEY_F4 */ - 9, /* SONYPI_EVENT_FNKEY_F5 */ - 10, /* SONYPI_EVENT_FNKEY_F6 */ - 11, /* SONYPI_EVENT_FNKEY_F7 */ - 12, /* SONYPI_EVENT_FNKEY_F8 */ - 13, /* SONYPI_EVENT_FNKEY_F9 */ - 14, /* SONYPI_EVENT_FNKEY_F10 */ - 15, /* SONYPI_EVENT_FNKEY_F11 */ - 16, /* SONYPI_EVENT_FNKEY_F12 */ - 17, /* SONYPI_EVENT_FNKEY_1 */ - 18, /* SONYPI_EVENT_FNKEY_2 */ - 19, /* SONYPI_EVENT_FNKEY_D */ - 20, /* SONYPI_EVENT_FNKEY_E */ - 21, /* SONYPI_EVENT_FNKEY_F */ - 22, /* SONYPI_EVENT_FNKEY_S */ - 23, /* SONYPI_EVENT_FNKEY_B */ - 24, /* SONYPI_EVENT_BLUETOOTH_PRESSED */ - 25, /* SONYPI_EVENT_PKEY_P1 */ - 26, /* SONYPI_EVENT_PKEY_P2 */ - 27, /* SONYPI_EVENT_PKEY_P3 */ - 28, /* SONYPI_EVENT_BACK_PRESSED */ - -1, /* SONYPI_EVENT_LID_CLOSED */ - -1, /* SONYPI_EVENT_LID_OPENED */ - 29, /* SONYPI_EVENT_BLUETOOTH_ON */ - 30, /* SONYPI_EVENT_BLUETOOTH_OFF */ - 31, /* SONYPI_EVENT_HELP_PRESSED */ - 32, /* SONYPI_EVENT_FNKEY_ONLY */ - 33, /* SONYPI_EVENT_JOGDIAL_FAST_DOWN */ - 34, /* SONYPI_EVENT_JOGDIAL_FAST_UP */ - 35, /* SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */ - 36, /* SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */ - 37, /* SONYPI_EVENT_JOGDIAL_VFAST_DOWN */ - 38, /* SONYPI_EVENT_JOGDIAL_VFAST_UP */ - 39, /* SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */ - 40, /* SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */ - 41, /* SONYPI_EVENT_ZOOM_PRESSED */ - 42, /* SONYPI_EVENT_THUMBPHRASE_PRESSED */ - 43, /* SONYPI_EVENT_MEYE_FACE */ - 44, /* SONYPI_EVENT_MEYE_OPPOSITE */ - 45, /* SONYPI_EVENT_MEMORYSTICK_INSERT */ - 46, /* SONYPI_EVENT_MEMORYSTICK_EJECT */ - -1, /* SONYPI_EVENT_ANYBUTTON_RELEASED */ - -1, /* SONYPI_EVENT_BATTERY_INSERT */ - -1, /* SONYPI_EVENT_BATTERY_REMOVE */ - -1, /* SONYPI_EVENT_FNKEY_RELEASED */ - 47, /* SONYPI_EVENT_WIRELESS_ON */ - 48, /* SONYPI_EVENT_WIRELESS_OFF */ + -1, /* 0 no event */ + -1, /* 1 SONYPI_EVENT_JOGDIAL_DOWN */ + -1, /* 2 SONYPI_EVENT_JOGDIAL_UP */ + -1, /* 3 SONYPI_EVENT_JOGDIAL_DOWN_PRESSED */ + -1, /* 4 SONYPI_EVENT_JOGDIAL_UP_PRESSED */ + -1, /* 5 SONYPI_EVENT_JOGDIAL_PRESSED */ + -1, /* 6 SONYPI_EVENT_JOGDIAL_RELEASED */ + 0, /* 7 SONYPI_EVENT_CAPTURE_PRESSED */ + 1, /* 8 SONYPI_EVENT_CAPTURE_RELEASED */ + 2, /* 9 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */ + 3, /* 10 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */ + 4, /* 11 SONYPI_EVENT_FNKEY_ESC */ + 5, /* 12 SONYPI_EVENT_FNKEY_F1 */ + 6, /* 13 SONYPI_EVENT_FNKEY_F2 */ + 7, /* 14 SONYPI_EVENT_FNKEY_F3 */ + 8, /* 15 SONYPI_EVENT_FNKEY_F4 */ + 9, /* 16 SONYPI_EVENT_FNKEY_F5 */ + 10, /* 17 SONYPI_EVENT_FNKEY_F6 */ + 11, /* 18 SONYPI_EVENT_FNKEY_F7 */ + 12, /* 19 SONYPI_EVENT_FNKEY_F8 */ + 13, /* 20 SONYPI_EVENT_FNKEY_F9 */ + 14, /* 21 SONYPI_EVENT_FNKEY_F10 */ + 15, /* 22 SONYPI_EVENT_FNKEY_F11 */ + 16, /* 23 SONYPI_EVENT_FNKEY_F12 */ + 17, /* 24 SONYPI_EVENT_FNKEY_1 */ + 18, /* 25 SONYPI_EVENT_FNKEY_2 */ + 19, /* 26 SONYPI_EVENT_FNKEY_D */ + 20, /* 27 SONYPI_EVENT_FNKEY_E */ + 21, /* 28 SONYPI_EVENT_FNKEY_F */ + 22, /* 29 SONYPI_EVENT_FNKEY_S */ + 23, /* 30 SONYPI_EVENT_FNKEY_B */ + 24, /* 31 SONYPI_EVENT_BLUETOOTH_PRESSED */ + 25, /* 32 SONYPI_EVENT_PKEY_P1 */ + 26, /* 33 SONYPI_EVENT_PKEY_P2 */ + 27, /* 34 SONYPI_EVENT_PKEY_P3 */ + 28, /* 35 SONYPI_EVENT_BACK_PRESSED */ + -1, /* 36 SONYPI_EVENT_LID_CLOSED */ + -1, /* 37 SONYPI_EVENT_LID_OPENED */ + 29, /* 38 SONYPI_EVENT_BLUETOOTH_ON */ + 30, /* 39 SONYPI_EVENT_BLUETOOTH_OFF */ + 31, /* 40 SONYPI_EVENT_HELP_PRESSED */ + 32, /* 41 SONYPI_EVENT_FNKEY_ONLY */ + 33, /* 42 SONYPI_EVENT_JOGDIAL_FAST_DOWN */ + 34, /* 43 SONYPI_EVENT_JOGDIAL_FAST_UP */ + 35, /* 44 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */ + 36, /* 45 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */ + 37, /* 46 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */ + 38, /* 47 SONYPI_EVENT_JOGDIAL_VFAST_UP */ + 39, /* 48 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */ + 40, /* 49 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */ + 41, /* 50 SONYPI_EVENT_ZOOM_PRESSED */ + 42, /* 51 SONYPI_EVENT_THUMBPHRASE_PRESSED */ + 43, /* 52 SONYPI_EVENT_MEYE_FACE */ + 44, /* 53 SONYPI_EVENT_MEYE_OPPOSITE */ + 45, /* 54 SONYPI_EVENT_MEMORYSTICK_INSERT */ + 46, /* 55 SONYPI_EVENT_MEMORYSTICK_EJECT */ + -1, /* 56 SONYPI_EVENT_ANYBUTTON_RELEASED */ + -1, /* 57 SONYPI_EVENT_BATTERY_INSERT */ + -1, /* 58 SONYPI_EVENT_BATTERY_REMOVE */ + -1, /* 59 SONYPI_EVENT_FNKEY_RELEASED */ + 47, /* 60 SONYPI_EVENT_WIRELESS_ON */ + 48, /* 61 SONYPI_EVENT_WIRELESS_OFF */ + 49, /* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */ + 50, /* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */ }; static int sony_laptop_input_keycode_map[] = { @@ -260,6 +262,8 @@ static int sony_laptop_input_keycode_map[] = { KEY_RESERVED, /* 46 SONYPI_EVENT_MEMORYSTICK_EJECT */ KEY_WLAN, /* 47 SONYPI_EVENT_WIRELESS_ON */ KEY_WLAN, /* 48 SONYPI_EVENT_WIRELESS_OFF */ + KEY_ZOOMIN, /* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */ + KEY_ZOOMOUT /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */ }; /* release buttons after a short delay if pressed */ @@ -311,7 +315,7 @@ static void sony_laptop_report_input_event(u8 event) break; default: - if (event > ARRAY_SIZE (sony_laptop_input_keycode_map)) { + if (event > ARRAY_SIZE(sony_laptop_input_index)) { dprintk("sony_laptop_report_input_event, event not known: %d\n", event); break; } @@ -875,6 +879,15 @@ static const struct dmi_system_id sony_nc_ids[] = { DMI_MATCH(DMI_PRODUCT_NAME, "VGN-C"), }, }, + { + .ident = "Sony Vaio N Series", + .callback = sony_nc_C_enable, + .driver_data = sony_C_events, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-N"), + }, + }, { } }; @@ -1169,10 +1182,12 @@ static struct acpi_driver sony_nc_driver = { #define SONYPI_DEVICE_TYPE1 0x00000001 #define SONYPI_DEVICE_TYPE2 0x00000002 #define SONYPI_DEVICE_TYPE3 0x00000004 +#define SONYPI_DEVICE_TYPE4 0x00000008 #define SONYPI_TYPE1_OFFSET 0x04 #define SONYPI_TYPE2_OFFSET 0x12 #define SONYPI_TYPE3_OFFSET 0x12 +#define SONYPI_TYPE4_OFFSET 0x12 struct sony_pic_ioport { struct acpi_resource_io io1; @@ -1185,18 +1200,33 @@ struct sony_pic_irq { struct list_head list; }; +struct sonypi_eventtypes { + u8 data; + unsigned long mask; + struct sonypi_event *events; +}; + +struct device_ctrl { + int model; + int (*handle_irq)(const u8, const u8); + u16 evport_offset; + u8 has_camera; + u8 has_bluetooth; + u8 has_wwan; + struct sonypi_eventtypes *event_types; +}; + struct sony_pic_dev { - int model; - u16 evport_offset; - u8 camera_power; - u8 bluetooth_power; - u8 wwan_power; + struct device_ctrl *control; struct acpi_device *acpi_dev; struct sony_pic_irq *cur_irq; struct sony_pic_ioport *cur_ioport; struct list_head interrupts; struct list_head ioports; struct mutex lock; + u8 camera_power; + u8 bluetooth_power; + u8 wwan_power; }; static struct sony_pic_dev spic_dev = { @@ -1253,6 +1283,7 @@ static struct sonypi_event sonypi_joggerev[] = { static struct sonypi_event sonypi_captureev[] = { { 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED }, { 0x07, SONYPI_EVENT_CAPTURE_PRESSED }, + { 0x40, SONYPI_EVENT_CAPTURE_PRESSED }, { 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED }, { 0, 0 } }; @@ -1289,7 +1320,6 @@ static struct sonypi_event sonypi_pkeyev[] = { { 0x01, SONYPI_EVENT_PKEY_P1 }, { 0x02, SONYPI_EVENT_PKEY_P2 }, { 0x04, SONYPI_EVENT_PKEY_P3 }, - { 0x5c, SONYPI_EVENT_PKEY_P1 }, { 0, 0 } }; @@ -1331,6 +1361,8 @@ static struct sonypi_event sonypi_lidev[] = { /* The set of possible zoom events */ static struct sonypi_event sonypi_zoomev[] = { { 0x39, SONYPI_EVENT_ZOOM_PRESSED }, + { 0x10, SONYPI_EVENT_ZOOM_IN_PRESSED }, + { 0x20, SONYPI_EVENT_ZOOM_OUT_PRESSED }, { 0, 0 } }; @@ -1361,76 +1393,58 @@ static struct sonypi_event sonypi_batteryev[] = { { 0, 0 } }; -static struct sonypi_eventtypes { - int model; - u8 data; - unsigned long mask; - struct sonypi_event * events; -} sony_pic_eventtypes[] = { - { SONYPI_DEVICE_TYPE1, 0, 0xffffffff, sonypi_releaseev }, - { SONYPI_DEVICE_TYPE1, 0x70, SONYPI_MEYE_MASK, sonypi_meyeev }, - { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_LID_MASK, sonypi_lidev }, - { SONYPI_DEVICE_TYPE1, 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev }, - { SONYPI_DEVICE_TYPE1, 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev }, - { SONYPI_DEVICE_TYPE1, 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, - { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev }, - { SONYPI_DEVICE_TYPE1, 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev }, - { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, - { SONYPI_DEVICE_TYPE1, 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev }, - - { SONYPI_DEVICE_TYPE2, 0, 0xffffffff, sonypi_releaseev }, - { SONYPI_DEVICE_TYPE2, 0x38, SONYPI_LID_MASK, sonypi_lidev }, - { SONYPI_DEVICE_TYPE2, 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev }, - { SONYPI_DEVICE_TYPE2, 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev }, - { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, - { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev }, - { SONYPI_DEVICE_TYPE2, 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev }, - { SONYPI_DEVICE_TYPE2, 0x11, SONYPI_BACK_MASK, sonypi_backev }, - { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_HELP_MASK, sonypi_helpev }, - { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev }, - { SONYPI_DEVICE_TYPE2, 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev }, - { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, - { SONYPI_DEVICE_TYPE2, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev }, - { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev }, - - { SONYPI_DEVICE_TYPE3, 0, 0xffffffff, sonypi_releaseev }, - { SONYPI_DEVICE_TYPE3, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, - { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev }, - { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, - { SONYPI_DEVICE_TYPE3, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev }, - { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev }, - { 0 } +static struct sonypi_eventtypes type1_events[] = { + { 0, 0xffffffff, sonypi_releaseev }, + { 0x70, SONYPI_MEYE_MASK, sonypi_meyeev }, + { 0x30, SONYPI_LID_MASK, sonypi_lidev }, + { 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev }, + { 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev }, + { 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, + { 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev }, + { 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev }, + { 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, + { 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev }, + { 0 }, +}; +static struct sonypi_eventtypes type2_events[] = { + { 0, 0xffffffff, sonypi_releaseev }, + { 0x38, SONYPI_LID_MASK, sonypi_lidev }, + { 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev }, + { 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev }, + { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, + { 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev }, + { 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev }, + { 0x11, SONYPI_BACK_MASK, sonypi_backev }, + { 0x21, SONYPI_HELP_MASK, sonypi_helpev }, + { 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev }, + { 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev }, + { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, + { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev }, + { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev }, + { 0 }, +}; +static struct sonypi_eventtypes type3_events[] = { + { 0, 0xffffffff, sonypi_releaseev }, + { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, + { 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev }, + { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, + { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev }, + { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev }, + { 0 }, +}; +static struct sonypi_eventtypes type4_events[] = { + { 0, 0xffffffff, sonypi_releaseev }, + { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, + { 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev }, + { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, + { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev }, + { 0x05, SONYPI_PKEY_MASK, sonypi_pkeyev }, + { 0x05, SONYPI_ZOOM_MASK, sonypi_zoomev }, + { 0x05, SONYPI_CAPTURE_MASK, sonypi_captureev }, + { 0 }, }; -static int sony_pic_detect_device_type(void) -{ - struct pci_dev *pcidev; - int model = 0; - - if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82371AB_3, NULL))) - model = SONYPI_DEVICE_TYPE1; - - else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_ICH6_1, NULL))) - model = SONYPI_DEVICE_TYPE3; - - else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_ICH7_1, NULL))) - model = SONYPI_DEVICE_TYPE3; - - else - model = SONYPI_DEVICE_TYPE2; - - if (pcidev) - pci_dev_put(pcidev); - - printk(KERN_INFO DRV_PFX "detected Type%d model\n", - model == SONYPI_DEVICE_TYPE1 ? 1 : - model == SONYPI_DEVICE_TYPE2 ? 2 : 3); - return model; -} - +/* low level spic calls */ #define ITERATIONS_LONG 10000 #define ITERATIONS_SHORT 10 #define wait_on_command(command, iterations) { \ @@ -1451,7 +1465,7 @@ static u8 sony_pic_call1(u8 dev) outb(dev, spic_dev.cur_ioport->io1.minimum + 4); v1 = inb_p(spic_dev.cur_ioport->io1.minimum + 4); v2 = inb_p(spic_dev.cur_ioport->io1.minimum); - dprintk("sony_pic_call1: 0x%.4x\n", (v2 << 8) | v1); + dprintk("sony_pic_call1(0x%.2x): 0x%.4x\n", dev, (v2 << 8) | v1); return v2; } @@ -1466,7 +1480,7 @@ static u8 sony_pic_call2(u8 dev, u8 fn) ITERATIONS_LONG); outb(fn, spic_dev.cur_ioport->io1.minimum); v1 = inb_p(spic_dev.cur_ioport->io1.minimum); - dprintk("sony_pic_call2: 0x%.4x\n", v1); + dprintk("sony_pic_call2(0x%.2x - 0x%.2x): 0x%.4x\n", dev, fn, v1); return v1; } @@ -1481,10 +1495,105 @@ static u8 sony_pic_call3(u8 dev, u8 fn, u8 v) wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); outb(v, spic_dev.cur_ioport->io1.minimum); v1 = inb_p(spic_dev.cur_ioport->io1.minimum); - dprintk("sony_pic_call3: 0x%.4x\n", v1); + dprintk("sony_pic_call3(0x%.2x - 0x%.2x - 0x%.2x): 0x%.4x\n", + dev, fn, v, v1); return v1; } +/* + * minidrivers for SPIC models + */ +static int type4_handle_irq(const u8 data_mask, const u8 ev) +{ + /* + * 0x31 could mean we have to take some extra action and wait for + * the next irq for some Type4 models, it will generate a new + * irq and we can read new data from the device: + * - 0x5c and 0x5f requires 0xA0 + * - 0x61 requires 0xB3 + */ + if (data_mask == 0x31) { + if (ev == 0x5c || ev == 0x5f) + sony_pic_call1(0xA0); + else if (ev == 0x61) + sony_pic_call1(0xB3); + return 0; + } + return 1; +} + +static struct device_ctrl spic_types[] = { + { + .model = SONYPI_DEVICE_TYPE1, + .handle_irq = NULL, + .evport_offset = SONYPI_TYPE1_OFFSET, + .event_types = type1_events, + }, + { + .model = SONYPI_DEVICE_TYPE2, + .handle_irq = NULL, + .evport_offset = SONYPI_TYPE2_OFFSET, + .event_types = type2_events, + }, + { + .model = SONYPI_DEVICE_TYPE3, + .handle_irq = NULL, + .evport_offset = SONYPI_TYPE3_OFFSET, + .event_types = type3_events, + }, + { + .model = SONYPI_DEVICE_TYPE4, + .handle_irq = type4_handle_irq, + .evport_offset = SONYPI_TYPE4_OFFSET, + .event_types = type4_events, + }, +}; + +static void sony_pic_detect_device_type(struct sony_pic_dev *dev) +{ + struct pci_dev *pcidev; + + pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_3, NULL); + if (pcidev) { + dev->control = &spic_types[0]; + goto out; + } + + pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_ICH6_1, NULL); + if (pcidev) { + dev->control = &spic_types[2]; + goto out; + } + + pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_ICH7_1, NULL); + if (pcidev) { + dev->control = &spic_types[3]; + goto out; + } + + pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_ICH8_4, NULL); + if (pcidev) { + dev->control = &spic_types[3]; + goto out; + } + + /* default */ + dev->control = &spic_types[1]; + +out: + if (pcidev) + pci_dev_put(pcidev); + + printk(KERN_INFO DRV_PFX "detected Type%d model\n", + dev->control->model == SONYPI_DEVICE_TYPE1 ? 1 : + dev->control->model == SONYPI_DEVICE_TYPE2 ? 2 : + dev->control->model == SONYPI_DEVICE_TYPE3 ? 3 : 4); +} + /* camera tests and poweron/poweroff */ #define SONYPI_CAMERA_PICTURE 5 #define SONYPI_CAMERA_CONTROL 0x10 @@ -2253,7 +2362,7 @@ static int sony_pic_enable(struct acpi_device *device, buffer.pointer = resource; /* setup Type 1 resources */ - if (spic_dev.model == SONYPI_DEVICE_TYPE1) { + if (spic_dev.control->model == SONYPI_DEVICE_TYPE1) { /* setup io resources */ resource->res1.type = ACPI_RESOURCE_TYPE_IO; @@ -2335,39 +2444,49 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id) if (dev->cur_ioport->io2.minimum) data_mask = inb_p(dev->cur_ioport->io2.minimum); else - data_mask = inb_p(dev->cur_ioport->io1.minimum + dev->evport_offset); + data_mask = inb_p(dev->cur_ioport->io1.minimum + + dev->control->evport_offset); dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n", - ev, data_mask, dev->cur_ioport->io1.minimum, dev->evport_offset); + ev, data_mask, dev->cur_ioport->io1.minimum, + dev->control->evport_offset); if (ev == 0x00 || ev == 0xff) return IRQ_HANDLED; - for (i = 0; sony_pic_eventtypes[i].model; i++) { - - if (spic_dev.model != sony_pic_eventtypes[i].model) - continue; + for (i = 0; dev->control->event_types[i].mask; i++) { - if ((data_mask & sony_pic_eventtypes[i].data) != - sony_pic_eventtypes[i].data) + if ((data_mask & dev->control->event_types[i].data) != + dev->control->event_types[i].data) continue; - if (!(mask & sony_pic_eventtypes[i].mask)) + if (!(mask & dev->control->event_types[i].mask)) continue; - for (j = 0; sony_pic_eventtypes[i].events[j].event; j++) { - if (ev == sony_pic_eventtypes[i].events[j].data) { + for (j = 0; dev->control->event_types[i].events[j].event; j++) { + if (ev == dev->control->event_types[i].events[j].data) { device_event = - sony_pic_eventtypes[i].events[j].event; + dev->control-> + event_types[i].events[j].event; goto found; } } } + /* Still not able to decode the event try to pass + * it over to the minidriver + */ + if (dev->control->handle_irq && + dev->control->handle_irq(data_mask, ev) == 0) + return IRQ_HANDLED; + + dprintk("unknown event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n", + ev, data_mask, dev->cur_ioport->io1.minimum, + dev->control->evport_offset); return IRQ_HANDLED; found: sony_laptop_report_input_event(device_event); - acpi_b |