diff options
-rw-r--r-- | Documentation/laptops/thinkpad-acpi.txt | 19 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 159 |
2 files changed, 149 insertions, 29 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 96687d0106a9..bd87682103c1 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt | |||
@@ -1098,18 +1098,31 @@ Volume control | |||
1098 | procfs: /proc/acpi/ibm/volume | 1098 | procfs: /proc/acpi/ibm/volume |
1099 | 1099 | ||
1100 | This feature allows volume control on ThinkPad models with a digital | 1100 | This feature allows volume control on ThinkPad models with a digital |
1101 | volume knob, as well as mute/unmute control. The available commands are: | 1101 | volume knob (when available, not all models have it), as well as |
1102 | mute/unmute control. The available commands are: | ||
1102 | 1103 | ||
1103 | echo up >/proc/acpi/ibm/volume | 1104 | echo up >/proc/acpi/ibm/volume |
1104 | echo down >/proc/acpi/ibm/volume | 1105 | echo down >/proc/acpi/ibm/volume |
1105 | echo mute >/proc/acpi/ibm/volume | 1106 | echo mute >/proc/acpi/ibm/volume |
1107 | echo unmute >/proc/acpi/ibm/volume | ||
1106 | echo 'level <level>' >/proc/acpi/ibm/volume | 1108 | echo 'level <level>' >/proc/acpi/ibm/volume |
1107 | 1109 | ||
1108 | The <level> number range is 0 to 14 although not all of them may be | 1110 | The <level> number range is 0 to 14 although not all of them may be |
1109 | distinct. The unmute the volume after the mute command, use either the | 1111 | distinct. The unmute the volume after the mute command, use either the |
1110 | up or down command (the level command will not unmute the volume). | 1112 | up or down command (the level command will not unmute the volume), or |
1113 | the unmute command. | ||
1114 | |||
1111 | The current volume level and mute state is shown in the file. | 1115 | The current volume level and mute state is shown in the file. |
1112 | 1116 | ||
1117 | You can use the volume_capabilities parameter to tell the driver | ||
1118 | whether your thinkpad has volume control or mute-only control: | ||
1119 | volume_capabilities=1 for mixers with mute and volume control, | ||
1120 | volume_capabilities=2 for mixers with only mute control. | ||
1121 | |||
1122 | If the driver misdetects the capabilities for your ThinkPad model, | ||
1123 | please report this to ibm-acpi-devel@lists.sourceforge.net, so that we | ||
1124 | can update the driver. | ||
1125 | |||
1113 | There are two strategies for volume control. To select which one | 1126 | There are two strategies for volume control. To select which one |
1114 | should be used, use the volume_mode module parameter: volume_mode=1 | 1127 | should be used, use the volume_mode module parameter: volume_mode=1 |
1115 | selects EC mode, and volume_mode=3 selects EC mode with NVRAM backing | 1128 | selects EC mode, and volume_mode=3 selects EC mode with NVRAM backing |
@@ -1450,3 +1463,5 @@ Sysfs interface changelog: | |||
1450 | is deprecated and marked for removal. | 1463 | is deprecated and marked for removal. |
1451 | 1464 | ||
1452 | 0x020600: Marker for backlight change event support. | 1465 | 0x020600: Marker for backlight change event support. |
1466 | |||
1467 | 0x020700: Support for mute-only mixers. | ||
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index a2f5312c6a4e..4d909d5a0340 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -22,7 +22,7 @@ | |||
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define TPACPI_VERSION "0.23" | 24 | #define TPACPI_VERSION "0.23" |
25 | #define TPACPI_SYSFS_VERSION 0x020600 | 25 | #define TPACPI_SYSFS_VERSION 0x020700 |
26 | 26 | ||
27 | /* | 27 | /* |
28 | * Changelog: | 28 | * Changelog: |
@@ -299,6 +299,7 @@ static struct { | |||
299 | u32 fan_ctrl_status_undef:1; | 299 | u32 fan_ctrl_status_undef:1; |
300 | u32 second_fan:1; | 300 | u32 second_fan:1; |
301 | u32 beep_needs_two_args:1; | 301 | u32 beep_needs_two_args:1; |
302 | u32 mixer_no_level_control:1; | ||
302 | u32 input_device_registered:1; | 303 | u32 input_device_registered:1; |
303 | u32 platform_drv_registered:1; | 304 | u32 platform_drv_registered:1; |
304 | u32 platform_drv_attrs_registered:1; | 305 | u32 platform_drv_attrs_registered:1; |
@@ -426,6 +427,12 @@ static void tpacpi_log_usertask(const char * const what) | |||
426 | .ec = TPACPI_MATCH_ANY, \ | 427 | .ec = TPACPI_MATCH_ANY, \ |
427 | .quirks = (__quirk) } | 428 | .quirks = (__quirk) } |
428 | 429 | ||
430 | #define TPACPI_QEC_LNV(__id1, __id2, __quirk) \ | ||
431 | { .vendor = PCI_VENDOR_ID_LENOVO, \ | ||
432 | .bios = TPACPI_MATCH_ANY, \ | ||
433 | .ec = TPID(__id1, __id2), \ | ||
434 | .quirks = (__quirk) } | ||
435 | |||
429 | struct tpacpi_quirk { | 436 | struct tpacpi_quirk { |
430 | unsigned int vendor; | 437 | unsigned int vendor; |
431 | u16 bios; | 438 | u16 bios; |
@@ -6416,9 +6423,17 @@ enum tpacpi_volume_access_mode { | |||
6416 | TPACPI_VOL_MODE_MAX | 6423 | TPACPI_VOL_MODE_MAX |
6417 | }; | 6424 | }; |
6418 | 6425 | ||
6426 | enum tpacpi_volume_capabilities { | ||
6427 | TPACPI_VOL_CAP_AUTO = 0, /* Use white/blacklist */ | ||
6428 | TPACPI_VOL_CAP_VOLMUTE, /* Output vol and mute */ | ||
6429 | TPACPI_VOL_CAP_MUTEONLY, /* Output mute only */ | ||
6430 | TPACPI_VOL_CAP_MAX | ||
6431 | }; | ||
6432 | |||
6419 | static enum tpacpi_volume_access_mode volume_mode = | 6433 | static enum tpacpi_volume_access_mode volume_mode = |
6420 | TPACPI_VOL_MODE_MAX; | 6434 | TPACPI_VOL_MODE_MAX; |
6421 | 6435 | ||
6436 | static enum tpacpi_volume_capabilities volume_capabilities; | ||
6422 | 6437 | ||
6423 | /* | 6438 | /* |
6424 | * Used to syncronize writers to TP_EC_AUDIO and | 6439 | * Used to syncronize writers to TP_EC_AUDIO and |
@@ -6430,7 +6445,7 @@ static void tpacpi_volume_checkpoint_nvram(void) | |||
6430 | { | 6445 | { |
6431 | u8 lec = 0; | 6446 | u8 lec = 0; |
6432 | u8 b_nvram; | 6447 | u8 b_nvram; |
6433 | const u8 ec_mask = TP_EC_AUDIO_LVL_MSK | TP_EC_AUDIO_MUTESW_MSK; | 6448 | u8 ec_mask; |
6434 | 6449 | ||
6435 | if (volume_mode != TPACPI_VOL_MODE_ECNVRAM) | 6450 | if (volume_mode != TPACPI_VOL_MODE_ECNVRAM) |
6436 | return; | 6451 | return; |
@@ -6438,6 +6453,11 @@ static void tpacpi_volume_checkpoint_nvram(void) | |||
6438 | vdbg_printk(TPACPI_DBG_MIXER, | 6453 | vdbg_printk(TPACPI_DBG_MIXER, |
6439 | "trying to checkpoint mixer state to NVRAM...\n"); | 6454 | "trying to checkpoint mixer state to NVRAM...\n"); |
6440 | 6455 | ||
6456 | if (tp_features.mixer_no_level_control) | ||
6457 | ec_mask = TP_EC_AUDIO_MUTESW_MSK; | ||
6458 | else | ||
6459 | ec_mask = TP_EC_AUDIO_MUTESW_MSK | TP_EC_AUDIO_LVL_MSK; | ||
6460 | |||
6441 | if (mutex_lock_killable(&volume_mutex) < 0) | 6461 | if (mutex_lock_killable(&volume_mutex) < 0) |
6442 | return; | 6462 | return; |
6443 | 6463 | ||
@@ -6575,8 +6595,36 @@ static void volume_exit(void) | |||
6575 | tpacpi_volume_checkpoint_nvram(); | 6595 | tpacpi_volume_checkpoint_nvram(); |
6576 | } | 6596 | } |
6577 | 6597 | ||
6598 | #define TPACPI_VOL_Q_MUTEONLY 0x0001 /* Mute-only control available */ | ||
6599 | #define TPACPI_VOL_Q_LEVEL 0x0002 /* Volume control available */ | ||
6600 | |||
6601 | static const struct tpacpi_quirk volume_quirk_table[] __initconst = { | ||
6602 | /* Whitelist volume level on all IBM by default */ | ||
6603 | { .vendor = PCI_VENDOR_ID_IBM, | ||
6604 | .bios = TPACPI_MATCH_ANY, | ||
6605 | .ec = TPACPI_MATCH_ANY, | ||
6606 | .quirks = TPACPI_VOL_Q_LEVEL }, | ||
6607 | |||
6608 | /* Lenovo models with volume control (needs confirmation) */ | ||
6609 | TPACPI_QEC_LNV('7', 'C', TPACPI_VOL_Q_LEVEL), /* R60/i */ | ||
6610 | TPACPI_QEC_LNV('7', 'E', TPACPI_VOL_Q_LEVEL), /* R60e/i */ | ||
6611 | TPACPI_QEC_LNV('7', '9', TPACPI_VOL_Q_LEVEL), /* T60/p */ | ||
6612 | TPACPI_QEC_LNV('7', 'B', TPACPI_VOL_Q_LEVEL), /* X60/s */ | ||
6613 | TPACPI_QEC_LNV('7', 'J', TPACPI_VOL_Q_LEVEL), /* X60t */ | ||
6614 | TPACPI_QEC_LNV('7', '7', TPACPI_VOL_Q_LEVEL), /* Z60 */ | ||
6615 | TPACPI_QEC_LNV('7', 'F', TPACPI_VOL_Q_LEVEL), /* Z61 */ | ||
6616 | |||
6617 | /* Whitelist mute-only on all Lenovo by default */ | ||
6618 | { .vendor = PCI_VENDOR_ID_LENOVO, | ||
6619 | .bios = TPACPI_MATCH_ANY, | ||
6620 | .ec = TPACPI_MATCH_ANY, | ||
6621 | .quirks = TPACPI_VOL_Q_MUTEONLY } | ||
6622 | }; | ||
6623 | |||
6578 | static int __init volume_init(struct ibm_init_struct *iibm) | 6624 | static int __init volume_init(struct ibm_init_struct *iibm) |
6579 | { | 6625 | { |
6626 | unsigned long quirks; | ||
6627 | |||
6580 | vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n"); | 6628 | vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n"); |
6581 | 6629 | ||
6582 | mutex_init(&volume_mutex); | 6630 | mutex_init(&volume_mutex); |
@@ -6596,6 +6644,36 @@ static int __init volume_init(struct ibm_init_struct *iibm) | |||
6596 | return 1; | 6644 | return 1; |
6597 | } | 6645 | } |
6598 | 6646 | ||
6647 | if (volume_capabilities >= TPACPI_VOL_CAP_MAX) | ||
6648 | return -EINVAL; | ||
6649 | |||
6650 | quirks = tpacpi_check_quirks(volume_quirk_table, | ||
6651 | ARRAY_SIZE(volume_quirk_table)); | ||
6652 | |||
6653 | switch (volume_capabilities) { | ||
6654 | case TPACPI_VOL_CAP_AUTO: | ||
6655 | if (quirks & TPACPI_VOL_Q_MUTEONLY) | ||
6656 | tp_features.mixer_no_level_control = 1; | ||
6657 | else if (quirks & TPACPI_VOL_Q_LEVEL) | ||
6658 | tp_features.mixer_no_level_control = 0; | ||
6659 | else | ||
6660 | return 1; /* no mixer */ | ||
6661 | break; | ||
6662 | case TPACPI_VOL_CAP_VOLMUTE: | ||
6663 | tp_features.mixer_no_level_control = 0; | ||
6664 | break; | ||
6665 | case TPACPI_VOL_CAP_MUTEONLY: | ||
6666 | tp_features.mixer_no_level_control = 1; | ||
6667 | break; | ||
6668 | default: | ||
6669 | return 1; | ||
6670 | } | ||
6671 | |||
6672 | if (volume_capabilities != TPACPI_VOL_CAP_AUTO) | ||
6673 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6674 | "using user-supplied volume_capabilities=%d\n", | ||
6675 | volume_capabilities); | ||
6676 | |||
6599 | if (volume_mode == TPACPI_VOL_MODE_AUTO || | 6677 | if (volume_mode == TPACPI_VOL_MODE_AUTO || |
6600 | volume_mode == TPACPI_VOL_MODE_MAX) { | 6678 | volume_mode == TPACPI_VOL_MODE_MAX) { |
6601 | volume_mode = TPACPI_VOL_MODE_ECNVRAM; | 6679 | volume_mode = TPACPI_VOL_MODE_ECNVRAM; |
@@ -6610,7 +6688,8 @@ static int __init volume_init(struct ibm_init_struct *iibm) | |||
6610 | } | 6688 | } |
6611 | 6689 | ||
6612 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | 6690 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, |
6613 | "volume is supported\n"); | 6691 | "mute is supported, volume control is %s\n", |
6692 | str_supported(!tp_features.mixer_no_level_control)); | ||
6614 | 6693 | ||
6615 | return 0; | 6694 | return 0; |
6616 | } | 6695 | } |
@@ -6623,13 +6702,21 @@ static int volume_read(char *p) | |||
6623 | if (volume_get_status(&status) < 0) { | 6702 | if (volume_get_status(&status) < 0) { |
6624 | len += sprintf(p + len, "level:\t\tunreadable\n"); | 6703 | len += sprintf(p + len, "level:\t\tunreadable\n"); |
6625 | } else { | 6704 | } else { |
6626 | len += sprintf(p + len, "level:\t\t%d\n", | 6705 | if (tp_features.mixer_no_level_control) |
6627 | status & TP_EC_AUDIO_LVL_MSK); | 6706 | len += sprintf(p + len, "level:\t\tunsupported\n"); |
6707 | else | ||
6708 | len += sprintf(p + len, "level:\t\t%d\n", | ||
6709 | status & TP_EC_AUDIO_LVL_MSK); | ||
6710 | |||
6628 | len += sprintf(p + len, "mute:\t\t%s\n", | 6711 | len += sprintf(p + len, "mute:\t\t%s\n", |
6629 | onoff(status, TP_EC_AUDIO_MUTESW)); | 6712 | onoff(status, TP_EC_AUDIO_MUTESW)); |
6630 | len += sprintf(p + len, "commands:\tup, down, mute\n"); | 6713 | |
6631 | len += sprintf(p + len, "commands:\tlevel <level>" | 6714 | len += sprintf(p + len, "commands:\tunmute, mute\n"); |
6715 | if (!tp_features.mixer_no_level_control) { | ||
6716 | len += sprintf(p + len, "commands:\tup, down\n"); | ||
6717 | len += sprintf(p + len, "commands:\tlevel <level>" | ||
6632 | " (<level> is 0-%d)\n", TP_EC_VOLUME_MAX); | 6718 | " (<level> is 0-%d)\n", TP_EC_VOLUME_MAX); |
6719 | } | ||
6633 | } | 6720 | } |
6634 | 6721 | ||
6635 | return len; | 6722 | return len; |
@@ -6651,30 +6738,43 @@ static int volume_write(char *buf) | |||
6651 | new_mute = s & TP_EC_AUDIO_MUTESW_MSK; | 6738 | new_mute = s & TP_EC_AUDIO_MUTESW_MSK; |
6652 | 6739 | ||
6653 | while ((cmd = next_cmd(&buf))) { | 6740 | while ((cmd = next_cmd(&buf))) { |
6654 | if (strlencmp(cmd, "up") == 0) { | 6741 | if (!tp_features.mixer_no_level_control) { |
6655 | if (new_mute) | 6742 | if (strlencmp(cmd, "up") == 0) { |
6656 | new_mute = 0; | 6743 | if (new_mute) |
6657 | else if (new_level < TP_EC_VOLUME_MAX) | 6744 | new_mute = 0; |
6658 | new_level++; | 6745 | else if (new_level < TP_EC_VOLUME_MAX) |
6659 | } else if (strlencmp(cmd, "down") == 0) { | 6746 | new_level++; |
6660 | if (new_mute) | 6747 | continue; |
6661 | new_mute = 0; | 6748 | } else if (strlencmp(cmd, "down") == 0) { |
6662 | else if (new_level > 0) | 6749 | if (new_mute) |
6663 | new_level--; | 6750 | new_mute = 0; |
6664 | } else if (sscanf(cmd, "level %u", &l) == 1 && | 6751 | else if (new_level > 0) |
6665 | l >= 0 && l <= TP_EC_VOLUME_MAX) { | 6752 | new_level--; |
6666 | new_level = l; | 6753 | continue; |
6667 | } else if (strlencmp(cmd, "mute") == 0) { | 6754 | } else if (sscanf(cmd, "level %u", &l) == 1 && |
6755 | l >= 0 && l <= TP_EC_VOLUME_MAX) { | ||
6756 | new_level = l; | ||
6757 | continue; | ||
6758 | } | ||
6759 | } | ||
6760 | if (strlencmp(cmd, "mute") == 0) | ||
6668 | new_mute = TP_EC_AUDIO_MUTESW_MSK; | 6761 | new_mute = TP_EC_AUDIO_MUTESW_MSK; |
6669 | } else | 6762 | else if (strlencmp(cmd, "unmute") == 0) |
6763 | new_mute = 0; | ||
6764 | else | ||
6670 | return -EINVAL; | 6765 | return -EINVAL; |
6671 | } | 6766 | } |
6672 | 6767 | ||
6673 | tpacpi_disclose_usertask("procfs volume", | 6768 | if (tp_features.mixer_no_level_control) { |
6674 | "%smute and set level to %d\n", | 6769 | tpacpi_disclose_usertask("procfs volume", "%smute\n", |
6675 | new_mute ? "" : "un", new_level); | 6770 | new_mute ? "" : "un"); |
6676 | 6771 | rc = volume_set_mute(!!new_mute); | |
6677 | rc = volume_set_status(new_mute | new_level); | 6772 | } else { |
6773 | tpacpi_disclose_usertask("procfs volume", | ||
6774 | "%smute and set level to %d\n", | ||
6775 | new_mute ? "" : "un", new_level); | ||
6776 | rc = volume_set_status(new_mute | new_level); | ||
6777 | } | ||
6678 | 6778 | ||
6679 | return (rc == -EINTR) ? -ERESTARTSYS : rc; | 6779 | return (rc == -EINTR) ? -ERESTARTSYS : rc; |
6680 | } | 6780 | } |
@@ -8410,6 +8510,11 @@ MODULE_PARM_DESC(volume_mode, | |||
8410 | "Selects volume control strategy: " | 8510 | "Selects volume control strategy: " |
8411 | "0=auto, 1=EC, 2=N/A, 3=EC+NVRAM"); | 8511 | "0=auto, 1=EC, 2=N/A, 3=EC+NVRAM"); |
8412 | 8512 | ||
8513 | module_param_named(volume_capabilities, volume_capabilities, uint, 0444); | ||
8514 | MODULE_PARM_DESC(volume_capabilities, | ||
8515 | "Selects the mixer capabilites: " | ||
8516 | "0=auto, 1=volume and mute, 2=mute only"); | ||
8517 | |||
8413 | #define TPACPI_PARAM(feature) \ | 8518 | #define TPACPI_PARAM(feature) \ |
8414 | module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ | 8519 | module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ |
8415 | MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ | 8520 | MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ |