aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/laptops/thinkpad-acpi.txt7
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c237
2 files changed, 239 insertions, 5 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
index 6a5814330e51..b4ed30cb1375 100644
--- a/Documentation/laptops/thinkpad-acpi.txt
+++ b/Documentation/laptops/thinkpad-acpi.txt
@@ -1096,6 +1096,7 @@ Volume control
1096-------------- 1096--------------
1097 1097
1098procfs: /proc/acpi/ibm/volume 1098procfs: /proc/acpi/ibm/volume
1099ALSA: "ThinkPad Console Audio Control", default ID: "ThinkPadEC"
1099 1100
1100NOTE: by default, the volume control interface operates in read-only 1101NOTE: by default, the volume control interface operates in read-only
1101mode, as it is supposed to be used for on-screen-display purposes. 1102mode, as it is supposed to be used for on-screen-display purposes.
@@ -1144,9 +1145,8 @@ The driver will operate in volume_mode=3 by default. If that does not
1144work well on your ThinkPad model, please report this to 1145work well on your ThinkPad model, please report this to
1145ibm-acpi-devel@lists.sourceforge.net. 1146ibm-acpi-devel@lists.sourceforge.net.
1146 1147
1147The ALSA mixer interface to this feature is still missing, but patches 1148The driver supports the standard ALSA module parameters. If the ALSA
1148to add it exist. That problem should be addressed in the not so 1149mixer is disabled, the driver will disable all volume functionality.
1149distant future.
1150 1150
1151 1151
1152Fan control and monitoring: fan speed, fan enable/disable 1152Fan control and monitoring: fan speed, fan enable/disable
@@ -1478,3 +1478,4 @@ Sysfs interface changelog:
1478 1478
14790x020700: Support for mute-only mixers. 14790x020700: Support for mute-only mixers.
1480 Volume control in read-only mode by default. 1480 Volume control in read-only mode by default.
1481 Marker for ALSA mixer support.
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 2d74926913d2..e0fbe73b8dff 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -76,6 +76,10 @@
76#include <linux/jiffies.h> 76#include <linux/jiffies.h>
77#include <linux/workqueue.h> 77#include <linux/workqueue.h>
78 78
79#include <sound/core.h>
80#include <sound/control.h>
81#include <sound/initval.h>
82
79#include <acpi/acpi_drivers.h> 83#include <acpi/acpi_drivers.h>
80 84
81#include <linux/pci_ids.h> 85#include <linux/pci_ids.h>
@@ -6402,6 +6406,22 @@ static struct ibm_struct brightness_driver_data = {
6402 * and we leave them unchanged. 6406 * and we leave them unchanged.
6403 */ 6407 */
6404 6408
6409#define TPACPI_ALSA_DRVNAME "ThinkPad EC"
6410#define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control"
6411#define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME
6412
6413static int alsa_index = SNDRV_DEFAULT_IDX1;
6414static char *alsa_id = "ThinkPadEC";
6415static int alsa_enable = SNDRV_DEFAULT_ENABLE1;
6416
6417struct tpacpi_alsa_data {
6418 struct snd_card *card;
6419 struct snd_ctl_elem_id *ctl_mute_id;
6420 struct snd_ctl_elem_id *ctl_vol_id;
6421};
6422
6423static struct snd_card *alsa_card;
6424
6405enum { 6425enum {
6406 TP_EC_AUDIO = 0x30, 6426 TP_EC_AUDIO = 0x30,
6407 6427
@@ -6584,11 +6604,104 @@ static int volume_set_volume(const u8 vol)
6584 return volume_set_volume_ec(vol); 6604 return volume_set_volume_ec(vol);
6585} 6605}
6586 6606
6607static void volume_alsa_notify_change(void)
6608{
6609 struct tpacpi_alsa_data *d;
6610
6611 if (alsa_card && alsa_card->private_data) {
6612 d = alsa_card->private_data;
6613 if (d->ctl_mute_id)
6614 snd_ctl_notify(alsa_card,
6615 SNDRV_CTL_EVENT_MASK_VALUE,
6616 d->ctl_mute_id);
6617 if (d->ctl_vol_id)
6618 snd_ctl_notify(alsa_card,
6619 SNDRV_CTL_EVENT_MASK_VALUE,
6620 d->ctl_vol_id);
6621 }
6622}
6623
6624static int volume_alsa_vol_info(struct snd_kcontrol *kcontrol,
6625 struct snd_ctl_elem_info *uinfo)
6626{
6627 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
6628 uinfo->count = 1;
6629 uinfo->value.integer.min = 0;
6630 uinfo->value.integer.max = TP_EC_VOLUME_MAX;
6631 return 0;
6632}
6633
6634static int volume_alsa_vol_get(struct snd_kcontrol *kcontrol,
6635 struct snd_ctl_elem_value *ucontrol)
6636{
6637 u8 s;
6638 int rc;
6639
6640 rc = volume_get_status(&s);
6641 if (rc < 0)
6642 return rc;
6643
6644 ucontrol->value.integer.value[0] = s & TP_EC_AUDIO_LVL_MSK;
6645 return 0;
6646}
6647
6648static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol,
6649 struct snd_ctl_elem_value *ucontrol)
6650{
6651 return volume_set_volume(ucontrol->value.integer.value[0]);
6652}
6653
6654#define volume_alsa_mute_info snd_ctl_boolean_mono_info
6655
6656static int volume_alsa_mute_get(struct snd_kcontrol *kcontrol,
6657 struct snd_ctl_elem_value *ucontrol)
6658{
6659 u8 s;
6660 int rc;
6661
6662 rc = volume_get_status(&s);
6663 if (rc < 0)
6664 return rc;
6665
6666 ucontrol->value.integer.value[0] =
6667 (s & TP_EC_AUDIO_MUTESW_MSK) ? 0 : 1;
6668 return 0;
6669}
6670
6671static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol,
6672 struct snd_ctl_elem_value *ucontrol)
6673{
6674 return volume_set_mute(!ucontrol->value.integer.value[0]);
6675}
6676
6677static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = {
6678 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
6679 .name = "Console Playback Volume",
6680 .index = 0,
6681 .access = SNDRV_CTL_ELEM_ACCESS_READ,
6682 .info = volume_alsa_vol_info,
6683 .get = volume_alsa_vol_get,
6684};
6685
6686static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = {
6687 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
6688 .name = "Console Playback Switch",
6689 .index = 0,
6690 .access = SNDRV_CTL_ELEM_ACCESS_READ,
6691 .info = volume_alsa_mute_info,
6692 .get = volume_alsa_mute_get,
6693};
6694
6587static void volume_suspend(pm_message_t state) 6695static void volume_suspend(pm_message_t state)
6588{ 6696{
6589 tpacpi_volume_checkpoint_nvram(); 6697 tpacpi_volume_checkpoint_nvram();
6590} 6698}
6591 6699
6700static void volume_resume(void)
6701{
6702 volume_alsa_notify_change();
6703}
6704
6592static void volume_shutdown(void) 6705static void volume_shutdown(void)
6593{ 6706{
6594 tpacpi_volume_checkpoint_nvram(); 6707 tpacpi_volume_checkpoint_nvram();
@@ -6596,9 +6709,87 @@ static void volume_shutdown(void)
6596 6709
6597static void volume_exit(void) 6710static void volume_exit(void)
6598{ 6711{
6712 if (alsa_card) {
6713 snd_card_free(alsa_card);
6714 alsa_card = NULL;
6715 }
6716
6599 tpacpi_volume_checkpoint_nvram(); 6717 tpacpi_volume_checkpoint_nvram();
6600} 6718}
6601 6719
6720static int __init volume_create_alsa_mixer(void)
6721{
6722 struct snd_card *card;
6723 struct tpacpi_alsa_data *data;
6724 struct snd_kcontrol *ctl_vol;
6725 struct snd_kcontrol *ctl_mute;
6726 int rc;
6727
6728 rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE,
6729 sizeof(struct tpacpi_alsa_data), &card);
6730 if (rc < 0)
6731 return rc;
6732 if (!card)
6733 return -ENOMEM;
6734
6735 BUG_ON(!card->private_data);
6736 data = card->private_data;
6737 data->card = card;
6738
6739 strlcpy(card->driver, TPACPI_ALSA_DRVNAME,
6740 sizeof(card->driver));
6741 strlcpy(card->shortname, TPACPI_ALSA_SHRTNAME,
6742 sizeof(card->shortname));
6743 snprintf(card->mixername, sizeof(card->mixername), "ThinkPad EC %s",
6744 (thinkpad_id.ec_version_str) ?
6745 thinkpad_id.ec_version_str : "(unknown)");
6746 snprintf(card->longname, sizeof(card->longname),
6747 "%s at EC reg 0x%02x, fw %s", card->shortname, TP_EC_AUDIO,
6748 (thinkpad_id.ec_version_str) ?
6749 thinkpad_id.ec_version_str : "unknown");
6750
6751 if (volume_control_allowed) {
6752 volume_alsa_control_vol.put = volume_alsa_vol_put;
6753 volume_alsa_control_vol.access =
6754 SNDRV_CTL_ELEM_ACCESS_READWRITE;
6755
6756 volume_alsa_control_mute.put = volume_alsa_mute_put;
6757 volume_alsa_control_mute.access =
6758 SNDRV_CTL_ELEM_ACCESS_READWRITE;
6759 }
6760
6761 if (!tp_features.mixer_no_level_control) {
6762 ctl_vol = snd_ctl_new1(&volume_alsa_control_vol, NULL);
6763 rc = snd_ctl_add(card, ctl_vol);
6764 if (rc < 0) {
6765 printk(TPACPI_ERR
6766 "Failed to create ALSA volume control\n");
6767 goto err_out;
6768 }
6769 data->ctl_vol_id = &ctl_vol->id;
6770 }
6771
6772 ctl_mute = snd_ctl_new1(&volume_alsa_control_mute, NULL);
6773 rc = snd_ctl_add(card, ctl_mute);
6774 if (rc < 0) {
6775 printk(TPACPI_ERR "Failed to create ALSA mute control\n");
6776 goto err_out;
6777 }
6778 data->ctl_mute_id = &ctl_mute->id;
6779
6780 snd_card_set_dev(card, &tpacpi_pdev->dev);
6781 rc = snd_card_register(card);
6782
6783err_out:
6784 if (rc < 0) {
6785 snd_card_free(card);
6786 card = NULL;
6787 }
6788
6789 alsa_card = card;
6790 return rc;
6791}
6792
6602#define TPACPI_VOL_Q_MUTEONLY 0x0001 /* Mute-only control available */ 6793#define TPACPI_VOL_Q_MUTEONLY 0x0001 /* Mute-only control available */
6603#define TPACPI_VOL_Q_LEVEL 0x0002 /* Volume control available */ 6794#define TPACPI_VOL_Q_LEVEL 0x0002 /* Volume control available */
6604 6795
@@ -6628,6 +6819,7 @@ static const struct tpacpi_quirk volume_quirk_table[] __initconst = {
6628static int __init volume_init(struct ibm_init_struct *iibm) 6819static int __init volume_init(struct ibm_init_struct *iibm)
6629{ 6820{
6630 unsigned long quirks; 6821 unsigned long quirks;
6822 int rc;
6631 6823
6632 vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n"); 6824 vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n");
6633 6825
@@ -6651,6 +6843,17 @@ static int __init volume_init(struct ibm_init_struct *iibm)
6651 if (volume_capabilities >= TPACPI_VOL_CAP_MAX) 6843 if (volume_capabilities >= TPACPI_VOL_CAP_MAX)
6652 return -EINVAL; 6844 return -EINVAL;
6653 6845
6846 /*
6847 * The ALSA mixer is our primary interface.
6848 * When disabled, don't install the subdriver at all
6849 */
6850 if (!alsa_enable) {
6851 vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
6852 "ALSA mixer disabled by parameter, "
6853 "not loading volume subdriver...\n");
6854 return 1;
6855 }
6856
6654 quirks = tpacpi_check_quirks(volume_quirk_table, 6857 quirks = tpacpi_check_quirks(volume_quirk_table,
6655 ARRAY_SIZE(volume_quirk_table)); 6858 ARRAY_SIZE(volume_quirk_table));
6656 6859
@@ -6695,12 +6898,26 @@ static int __init volume_init(struct ibm_init_struct *iibm)
6695 "mute is supported, volume control is %s\n", 6898 "mute is supported, volume control is %s\n",
6696 str_supported(!tp_features.mixer_no_level_control)); 6899 str_supported(!tp_features.mixer_no_level_control));
6697 6900
6901 rc = volume_create_alsa_mixer();
6902 if (rc) {
6903 printk(TPACPI_ERR
6904 "Could not create the ALSA mixer interface\n");
6905 return rc;
6906 }
6907
6698 printk(TPACPI_INFO 6908 printk(TPACPI_INFO
6699 "Console audio control enabled, mode: %s\n", 6909 "Console audio control enabled, mode: %s\n",
6700 (volume_control_allowed) ? 6910 (volume_control_allowed) ?
6701 "override (read/write)" : 6911 "override (read/write)" :
6702 "monitor (read only)"); 6912 "monitor (read only)");
6703 6913
6914 vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
6915 "registering volume hotkeys as change notification\n");
6916 tpacpi_hotkey_driver_mask_set(hotkey_driver_mask
6917 | TP_ACPI_HKEY_VOLUP_MASK
6918 | TP_ACPI_HKEY_VOLDWN_MASK
6919 | TP_ACPI_HKEY_MUTE_MASK);
6920
6704 return 0; 6921 return 0;
6705} 6922}
6706 6923
@@ -6807,6 +7024,7 @@ static int volume_write(char *buf)
6807 new_mute ? "" : "un", new_level); 7024 new_mute ? "" : "un", new_level);
6808 rc = volume_set_status(new_mute | new_level); 7025 rc = volume_set_status(new_mute | new_level);
6809 } 7026 }
7027 volume_alsa_notify_change();
6810 7028
6811 return (rc == -EINTR) ? -ERESTARTSYS : rc; 7029 return (rc == -EINTR) ? -ERESTARTSYS : rc;
6812} 7030}
@@ -6817,6 +7035,7 @@ static struct ibm_struct volume_driver_data = {
6817 .write = volume_write, 7035 .write = volume_write,
6818 .exit = volume_exit, 7036 .exit = volume_exit,
6819 .suspend = volume_suspend, 7037 .suspend = volume_suspend,
7038 .resume = volume_resume,
6820 .shutdown = volume_shutdown, 7039 .shutdown = volume_shutdown,
6821}; 7040};
6822 7041
@@ -8115,10 +8334,16 @@ static void tpacpi_driver_event(const unsigned int hkey_event)
8115 tpacpi_brightness_notify_change(); 8334 tpacpi_brightness_notify_change();
8116 } 8335 }
8117 } 8336 }
8337 if (alsa_card) {
8338 switch (hkey_event) {
8339 case TP_HKEY_EV_VOL_UP:
8340 case TP_HKEY_EV_VOL_DOWN:
8341 case TP_HKEY_EV_VOL_MUTE:
8342 volume_alsa_notify_change();
8343 }
8344 }
8118} 8345}
8119 8346
8120
8121
8122static void hotkey_driver_event(const unsigned int scancode) 8347static void hotkey_driver_event(const unsigned int scancode)
8123{ 8348{
8124 tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode); 8349 tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode);
@@ -8552,6 +8777,14 @@ MODULE_PARM_DESC(volume_control,
8552 "Enables software override for the console audio " 8777 "Enables software override for the console audio "
8553 "control when true"); 8778 "control when true");
8554 8779
8780/* ALSA module API parameters */
8781module_param_named(index, alsa_index, int, 0444);
8782MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer");
8783module_param_named(id, alsa_id, charp, 0444);
8784MODULE_PARM_DESC(id, "ALSA id for the ACPI EC Mixer");
8785module_param_named(enable, alsa_enable, bool, 0444);
8786MODULE_PARM_DESC(enable, "Enable the ALSA interface for the ACPI EC Mixer");
8787
8555#define TPACPI_PARAM(feature) \ 8788#define TPACPI_PARAM(feature) \
8556 module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ 8789 module_param_call(feature, set_ibm_param, NULL, NULL, 0); \
8557 MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ 8790 MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \