aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorHenrique de Moraes Holschuh <hmh@hmh.eng.br>2009-12-15 18:51:11 -0500
committerLen Brown <len.brown@intel.com>2009-12-15 23:57:44 -0500
commit0d204c34e85d1d63e5fdd3e3192747daf0ee7ec1 (patch)
treecdf93247a1e8cb673f0a3fcae5c193e1d55d3fc3 /drivers/platform
parentc7ac6291ea7ebc568a1fce16fed87d102898f264 (diff)
thinkpad-acpi: basic ALSA mixer support (v2)
Add the basic ALSA mixer functionality. The mixer is event-driven, and will work fine on IBM ThinkPads. I expect Lenovo ThinkPads will cause some trouble with the event interface. Heavily based on work by Lorne Applebaum <lorne.applebaum@gmail.com> and ideas from Matthew Garrett <mjg@redhat.com>. Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Cc: Lorne Applebaum <lorne.applebaum@gmail.com> Cc: Matthew Garrett <mjg@redhat.com> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c237
1 files changed, 235 insertions, 2 deletions
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 " \