diff options
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/Kconfig | 28 | ||||
-rw-r--r-- | drivers/platform/x86/dell-wmi.c | 18 | ||||
-rw-r--r-- | drivers/platform/x86/hp-wmi.c | 9 | ||||
-rw-r--r-- | drivers/platform/x86/msi-wmi.c | 9 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 59 | ||||
-rw-r--r-- | drivers/platform/x86/wmi.c | 36 |
6 files changed, 131 insertions, 28 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index ec4faffe6b05..db32c25e3605 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -231,8 +231,36 @@ config THINKPAD_ACPI | |||
231 | 231 | ||
232 | This driver was formerly known as ibm-acpi. | 232 | This driver was formerly known as ibm-acpi. |
233 | 233 | ||
234 | Extra functionality will be available if the rfkill (CONFIG_RFKILL) | ||
235 | and/or ALSA (CONFIG_SND) subsystems are available in the kernel. | ||
236 | Note that if you want ThinkPad-ACPI to be built-in instead of | ||
237 | modular, ALSA and rfkill will also have to be built-in. | ||
238 | |||
234 | If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. | 239 | If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. |
235 | 240 | ||
241 | config THINKPAD_ACPI_ALSA_SUPPORT | ||
242 | bool "Console audio control ALSA interface" | ||
243 | depends on THINKPAD_ACPI | ||
244 | depends on SND | ||
245 | depends on SND = y || THINKPAD_ACPI = SND | ||
246 | default y | ||
247 | ---help--- | ||
248 | Enables monitoring of the built-in console audio output control | ||
249 | (headphone and speakers), which is operated by the mute and (in | ||
250 | some ThinkPad models) volume hotkeys. | ||
251 | |||
252 | If this option is enabled, ThinkPad-ACPI will export an ALSA card | ||
253 | with a single read-only mixer control, which should be used for | ||
254 | on-screen-display feedback purposes by the Desktop Environment. | ||
255 | |||
256 | Optionally, the driver will also allow software control (the | ||
257 | ALSA mixer will be made read-write). Please refer to the driver | ||
258 | documentation for details. | ||
259 | |||
260 | All IBM models have both volume and mute control. Newer Lenovo | ||
261 | models only have mute control (the volume hotkeys are just normal | ||
262 | keys and volume control is done through the main HDA mixer). | ||
263 | |||
236 | config THINKPAD_ACPI_DEBUGFACILITIES | 264 | config THINKPAD_ACPI_DEBUGFACILITIES |
237 | bool "Maintainer debug facilities" | 265 | bool "Maintainer debug facilities" |
238 | depends on THINKPAD_ACPI | 266 | depends on THINKPAD_ACPI |
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 916ccb2b316c..1b1dddbd5744 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c | |||
@@ -202,8 +202,13 @@ static void dell_wmi_notify(u32 value, void *context) | |||
202 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | 202 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; |
203 | static struct key_entry *key; | 203 | static struct key_entry *key; |
204 | union acpi_object *obj; | 204 | union acpi_object *obj; |
205 | acpi_status status; | ||
205 | 206 | ||
206 | wmi_get_event_data(value, &response); | 207 | status = wmi_get_event_data(value, &response); |
208 | if (status != AE_OK) { | ||
209 | printk(KERN_INFO "dell-wmi: bad event status 0x%x\n", status); | ||
210 | return; | ||
211 | } | ||
207 | 212 | ||
208 | obj = (union acpi_object *)response.pointer; | 213 | obj = (union acpi_object *)response.pointer; |
209 | 214 | ||
@@ -323,8 +328,9 @@ static int __init dell_wmi_input_setup(void) | |||
323 | static int __init dell_wmi_init(void) | 328 | static int __init dell_wmi_init(void) |
324 | { | 329 | { |
325 | int err; | 330 | int err; |
331 | acpi_status status; | ||
326 | 332 | ||
327 | if (wmi_has_guid(DELL_EVENT_GUID)) { | 333 | if (!wmi_has_guid(DELL_EVENT_GUID)) { |
328 | printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n"); | 334 | printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n"); |
329 | return -ENODEV; | 335 | return -ENODEV; |
330 | } | 336 | } |
@@ -336,14 +342,14 @@ static int __init dell_wmi_init(void) | |||
336 | if (err) | 342 | if (err) |
337 | return err; | 343 | return err; |
338 | 344 | ||
339 | err = wmi_install_notify_handler(DELL_EVENT_GUID, | 345 | status = wmi_install_notify_handler(DELL_EVENT_GUID, |
340 | dell_wmi_notify, NULL); | 346 | dell_wmi_notify, NULL); |
341 | if (err) { | 347 | if (ACPI_FAILURE(status)) { |
342 | input_unregister_device(dell_wmi_input_dev); | 348 | input_unregister_device(dell_wmi_input_dev); |
343 | printk(KERN_ERR | 349 | printk(KERN_ERR |
344 | "dell-wmi: Unable to register notify handler - %d\n", | 350 | "dell-wmi: Unable to register notify handler - %d\n", |
345 | err); | 351 | status); |
346 | return err; | 352 | return -ENODEV; |
347 | } | 353 | } |
348 | 354 | ||
349 | return 0; | 355 | return 0; |
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 8781d8fa7a57..5b648f0c6075 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c | |||
@@ -338,8 +338,13 @@ static void hp_wmi_notify(u32 value, void *context) | |||
338 | static struct key_entry *key; | 338 | static struct key_entry *key; |
339 | union acpi_object *obj; | 339 | union acpi_object *obj; |
340 | int eventcode; | 340 | int eventcode; |
341 | acpi_status status; | ||
341 | 342 | ||
342 | wmi_get_event_data(value, &response); | 343 | status = wmi_get_event_data(value, &response); |
344 | if (status != AE_OK) { | ||
345 | printk(KERN_INFO "hp-wmi: bad event status 0x%x\n", status); | ||
346 | return; | ||
347 | } | ||
343 | 348 | ||
344 | obj = (union acpi_object *)response.pointer; | 349 | obj = (union acpi_object *)response.pointer; |
345 | 350 | ||
@@ -581,7 +586,7 @@ static int __init hp_wmi_init(void) | |||
581 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | 586 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { |
582 | err = wmi_install_notify_handler(HPWMI_EVENT_GUID, | 587 | err = wmi_install_notify_handler(HPWMI_EVENT_GUID, |
583 | hp_wmi_notify, NULL); | 588 | hp_wmi_notify, NULL); |
584 | if (!err) | 589 | if (ACPI_SUCCESS(err)) |
585 | hp_wmi_input_setup(); | 590 | hp_wmi_input_setup(); |
586 | } | 591 | } |
587 | 592 | ||
diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index 7f77f908bb01..f5f70d4c6913 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c | |||
@@ -149,8 +149,13 @@ static void msi_wmi_notify(u32 value, void *context) | |||
149 | static struct key_entry *key; | 149 | static struct key_entry *key; |
150 | union acpi_object *obj; | 150 | union acpi_object *obj; |
151 | ktime_t cur; | 151 | ktime_t cur; |
152 | acpi_status status; | ||
152 | 153 | ||
153 | wmi_get_event_data(value, &response); | 154 | status = wmi_get_event_data(value, &response); |
155 | if (status != AE_OK) { | ||
156 | printk(KERN_INFO DRV_PFX "bad event status 0x%x\n", status); | ||
157 | return; | ||
158 | } | ||
154 | 159 | ||
155 | obj = (union acpi_object *)response.pointer; | 160 | obj = (union acpi_object *)response.pointer; |
156 | 161 | ||
@@ -236,7 +241,7 @@ static int __init msi_wmi_init(void) | |||
236 | } | 241 | } |
237 | err = wmi_install_notify_handler(MSIWMI_EVENT_GUID, | 242 | err = wmi_install_notify_handler(MSIWMI_EVENT_GUID, |
238 | msi_wmi_notify, NULL); | 243 | msi_wmi_notify, NULL); |
239 | if (err) | 244 | if (ACPI_FAILURE(err)) |
240 | return -EINVAL; | 245 | return -EINVAL; |
241 | 246 | ||
242 | err = msi_wmi_input_setup(); | 247 | err = msi_wmi_input_setup(); |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 448c8aeb166b..e67e4feb35cb 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -6384,11 +6384,13 @@ static struct ibm_struct brightness_driver_data = { | |||
6384 | * and we leave them unchanged. | 6384 | * and we leave them unchanged. |
6385 | */ | 6385 | */ |
6386 | 6386 | ||
6387 | #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT | ||
6388 | |||
6387 | #define TPACPI_ALSA_DRVNAME "ThinkPad EC" | 6389 | #define TPACPI_ALSA_DRVNAME "ThinkPad EC" |
6388 | #define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control" | 6390 | #define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control" |
6389 | #define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME | 6391 | #define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME |
6390 | 6392 | ||
6391 | static int alsa_index = SNDRV_DEFAULT_IDX1; | 6393 | static int alsa_index = ~((1 << (SNDRV_CARDS - 3)) - 1); /* last three slots */ |
6392 | static char *alsa_id = "ThinkPadEC"; | 6394 | static char *alsa_id = "ThinkPadEC"; |
6393 | static int alsa_enable = SNDRV_DEFAULT_ENABLE1; | 6395 | static int alsa_enable = SNDRV_DEFAULT_ENABLE1; |
6394 | 6396 | ||
@@ -6705,10 +6707,11 @@ static int __init volume_create_alsa_mixer(void) | |||
6705 | 6707 | ||
6706 | rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE, | 6708 | rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE, |
6707 | sizeof(struct tpacpi_alsa_data), &card); | 6709 | sizeof(struct tpacpi_alsa_data), &card); |
6708 | if (rc < 0) | 6710 | if (rc < 0 || !card) { |
6709 | return rc; | 6711 | printk(TPACPI_ERR |
6710 | if (!card) | 6712 | "Failed to create ALSA card structures: %d\n", rc); |
6711 | return -ENOMEM; | 6713 | return 1; |
6714 | } | ||
6712 | 6715 | ||
6713 | BUG_ON(!card->private_data); | 6716 | BUG_ON(!card->private_data); |
6714 | data = card->private_data; | 6717 | data = card->private_data; |
@@ -6741,8 +6744,9 @@ static int __init volume_create_alsa_mixer(void) | |||
6741 | rc = snd_ctl_add(card, ctl_vol); | 6744 | rc = snd_ctl_add(card, ctl_vol); |
6742 | if (rc < 0) { | 6745 | if (rc < 0) { |
6743 | printk(TPACPI_ERR | 6746 | printk(TPACPI_ERR |
6744 | "Failed to create ALSA volume control\n"); | 6747 | "Failed to create ALSA volume control: %d\n", |
6745 | goto err_out; | 6748 | rc); |
6749 | goto err_exit; | ||
6746 | } | 6750 | } |
6747 | data->ctl_vol_id = &ctl_vol->id; | 6751 | data->ctl_vol_id = &ctl_vol->id; |
6748 | } | 6752 | } |
@@ -6750,22 +6754,25 @@ static int __init volume_create_alsa_mixer(void) | |||
6750 | ctl_mute = snd_ctl_new1(&volume_alsa_control_mute, NULL); | 6754 | ctl_mute = snd_ctl_new1(&volume_alsa_control_mute, NULL); |
6751 | rc = snd_ctl_add(card, ctl_mute); | 6755 | rc = snd_ctl_add(card, ctl_mute); |
6752 | if (rc < 0) { | 6756 | if (rc < 0) { |
6753 | printk(TPACPI_ERR "Failed to create ALSA mute control\n"); | 6757 | printk(TPACPI_ERR "Failed to create ALSA mute control: %d\n", |
6754 | goto err_out; | 6758 | rc); |
6759 | goto err_exit; | ||
6755 | } | 6760 | } |
6756 | data->ctl_mute_id = &ctl_mute->id; | 6761 | data->ctl_mute_id = &ctl_mute->id; |
6757 | 6762 | ||
6758 | snd_card_set_dev(card, &tpacpi_pdev->dev); | 6763 | snd_card_set_dev(card, &tpacpi_pdev->dev); |
6759 | rc = snd_card_register(card); | 6764 | rc = snd_card_register(card); |
6760 | |||
6761 | err_out: | ||
6762 | if (rc < 0) { | 6765 | if (rc < 0) { |
6763 | snd_card_free(card); | 6766 | printk(TPACPI_ERR "Failed to register ALSA card: %d\n", rc); |
6764 | card = NULL; | 6767 | goto err_exit; |
6765 | } | 6768 | } |
6766 | 6769 | ||
6767 | alsa_card = card; | 6770 | alsa_card = card; |
6768 | return rc; | 6771 | return 0; |
6772 | |||
6773 | err_exit: | ||
6774 | snd_card_free(card); | ||
6775 | return 1; | ||
6769 | } | 6776 | } |
6770 | 6777 | ||
6771 | #define TPACPI_VOL_Q_MUTEONLY 0x0001 /* Mute-only control available */ | 6778 | #define TPACPI_VOL_Q_MUTEONLY 0x0001 /* Mute-only control available */ |
@@ -7016,6 +7023,28 @@ static struct ibm_struct volume_driver_data = { | |||
7016 | .shutdown = volume_shutdown, | 7023 | .shutdown = volume_shutdown, |
7017 | }; | 7024 | }; |
7018 | 7025 | ||
7026 | #else /* !CONFIG_THINKPAD_ACPI_ALSA_SUPPORT */ | ||
7027 | |||
7028 | #define alsa_card NULL | ||
7029 | |||
7030 | static void inline volume_alsa_notify_change(void) | ||
7031 | { | ||
7032 | } | ||
7033 | |||
7034 | static int __init volume_init(struct ibm_init_struct *iibm) | ||
7035 | { | ||
7036 | printk(TPACPI_INFO | ||
7037 | "volume: disabled as there is no ALSA support in this kernel\n"); | ||
7038 | |||
7039 | return 1; | ||
7040 | } | ||
7041 | |||
7042 | static struct ibm_struct volume_driver_data = { | ||
7043 | .name = "volume", | ||
7044 | }; | ||
7045 | |||
7046 | #endif /* CONFIG_THINKPAD_ACPI_ALSA_SUPPORT */ | ||
7047 | |||
7019 | /************************************************************************* | 7048 | /************************************************************************* |
7020 | * Fan subdriver | 7049 | * Fan subdriver |
7021 | */ | 7050 | */ |
@@ -8738,6 +8767,7 @@ MODULE_PARM_DESC(hotkey_report_mode, | |||
8738 | "used for backwards compatibility with userspace, " | 8767 | "used for backwards compatibility with userspace, " |
8739 | "see documentation"); | 8768 | "see documentation"); |
8740 | 8769 | ||
8770 | #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT | ||
8741 | module_param_named(volume_mode, volume_mode, uint, 0444); | 8771 | module_param_named(volume_mode, volume_mode, uint, 0444); |
8742 | MODULE_PARM_DESC(volume_mode, | 8772 | MODULE_PARM_DESC(volume_mode, |
8743 | "Selects volume control strategy: " | 8773 | "Selects volume control strategy: " |
@@ -8760,6 +8790,7 @@ module_param_named(id, alsa_id, charp, 0444); | |||
8760 | MODULE_PARM_DESC(id, "ALSA id for the ACPI EC Mixer"); | 8790 | MODULE_PARM_DESC(id, "ALSA id for the ACPI EC Mixer"); |
8761 | module_param_named(enable, alsa_enable, bool, 0444); | 8791 | module_param_named(enable, alsa_enable, bool, 0444); |
8762 | MODULE_PARM_DESC(enable, "Enable the ALSA interface for the ACPI EC Mixer"); | 8792 | MODULE_PARM_DESC(enable, "Enable the ALSA interface for the ACPI EC Mixer"); |
8793 | #endif /* CONFIG_THINKPAD_ACPI_ALSA_SUPPORT */ | ||
8763 | 8794 | ||
8764 | #define TPACPI_PARAM(feature) \ | 8795 | #define TPACPI_PARAM(feature) \ |
8765 | module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ | 8796 | module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ |
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 9f93d6c0f510..b104302fea0a 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
@@ -492,8 +492,7 @@ wmi_notify_handler handler, void *data) | |||
492 | if (!guid || !handler) | 492 | if (!guid || !handler) |
493 | return AE_BAD_PARAMETER; | 493 | return AE_BAD_PARAMETER; |
494 | 494 | ||
495 | find_guid(guid, &block); | 495 | if (!find_guid(guid, &block)) |
496 | if (!block) | ||
497 | return AE_NOT_EXIST; | 496 | return AE_NOT_EXIST; |
498 | 497 | ||
499 | if (block->handler) | 498 | if (block->handler) |
@@ -521,8 +520,7 @@ acpi_status wmi_remove_notify_handler(const char *guid) | |||
521 | if (!guid) | 520 | if (!guid) |
522 | return AE_BAD_PARAMETER; | 521 | return AE_BAD_PARAMETER; |
523 | 522 | ||
524 | find_guid(guid, &block); | 523 | if (!find_guid(guid, &block)) |
525 | if (!block) | ||
526 | return AE_NOT_EXIST; | 524 | return AE_NOT_EXIST; |
527 | 525 | ||
528 | if (!block->handler) | 526 | if (!block->handler) |
@@ -716,6 +714,22 @@ static int wmi_class_init(void) | |||
716 | return ret; | 714 | return ret; |
717 | } | 715 | } |
718 | 716 | ||
717 | static bool guid_already_parsed(const char *guid_string) | ||
718 | { | ||
719 | struct guid_block *gblock; | ||
720 | struct wmi_block *wblock; | ||
721 | struct list_head *p; | ||
722 | |||
723 | list_for_each(p, &wmi_blocks.list) { | ||
724 | wblock = list_entry(p, struct wmi_block, list); | ||
725 | gblock = &wblock->gblock; | ||
726 | |||
727 | if (strncmp(gblock->guid, guid_string, 16) == 0) | ||
728 | return true; | ||
729 | } | ||
730 | return false; | ||
731 | } | ||
732 | |||
719 | /* | 733 | /* |
720 | * Parse the _WDG method for the GUID data blocks | 734 | * Parse the _WDG method for the GUID data blocks |
721 | */ | 735 | */ |
@@ -725,6 +739,7 @@ static __init acpi_status parse_wdg(acpi_handle handle) | |||
725 | union acpi_object *obj; | 739 | union acpi_object *obj; |
726 | struct guid_block *gblock; | 740 | struct guid_block *gblock; |
727 | struct wmi_block *wblock; | 741 | struct wmi_block *wblock; |
742 | char guid_string[37]; | ||
728 | acpi_status status; | 743 | acpi_status status; |
729 | u32 i, total; | 744 | u32 i, total; |
730 | 745 | ||
@@ -747,6 +762,19 @@ static __init acpi_status parse_wdg(acpi_handle handle) | |||
747 | memcpy(gblock, obj->buffer.pointer, obj->buffer.length); | 762 | memcpy(gblock, obj->buffer.pointer, obj->buffer.length); |
748 | 763 | ||
749 | for (i = 0; i < total; i++) { | 764 | for (i = 0; i < total; i++) { |
765 | /* | ||
766 | Some WMI devices, like those for nVidia hooks, have a | ||
767 | duplicate GUID. It's not clear what we should do in this | ||
768 | case yet, so for now, we'll just ignore the duplicate. | ||
769 | Anyone who wants to add support for that device can come | ||
770 | up with a better workaround for the mess then. | ||
771 | */ | ||
772 | if (guid_already_parsed(gblock[i].guid) == true) { | ||
773 | wmi_gtoa(gblock[i].guid, guid_string); | ||
774 | printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n", | ||
775 | guid_string); | ||
776 | continue; | ||
777 | } | ||
750 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); | 778 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); |
751 | if (!wblock) | 779 | if (!wblock) |
752 | return AE_NO_MEMORY; | 780 | return AE_NO_MEMORY; |