aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/laptops/thinkpad-acpi.txt22
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c340
2 files changed, 299 insertions, 63 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
index f5056c7fb5be..96687d0106a9 100644
--- a/Documentation/laptops/thinkpad-acpi.txt
+++ b/Documentation/laptops/thinkpad-acpi.txt
@@ -1092,22 +1092,33 @@ WARNING:
1092 its level up and down at every change. 1092 its level up and down at every change.
1093 1093
1094 1094
1095Volume control -- /proc/acpi/ibm/volume 1095Volume control
1096--------------------------------------- 1096--------------
1097
1098procfs: /proc/acpi/ibm/volume
1097 1099
1098This feature allows volume control on ThinkPad models which don't have 1100This feature allows volume control on ThinkPad models with a digital
1099a hardware volume knob. The available commands are: 1101volume knob, as well as mute/unmute control. The available commands are:
1100 1102
1101 echo up >/proc/acpi/ibm/volume 1103 echo up >/proc/acpi/ibm/volume
1102 echo down >/proc/acpi/ibm/volume 1104 echo down >/proc/acpi/ibm/volume
1103 echo mute >/proc/acpi/ibm/volume 1105 echo mute >/proc/acpi/ibm/volume
1104 echo 'level <level>' >/proc/acpi/ibm/volume 1106 echo 'level <level>' >/proc/acpi/ibm/volume
1105 1107
1106The <level> number range is 0 to 15 although not all of them may be 1108The <level> number range is 0 to 14 although not all of them may be
1107distinct. The unmute the volume after the mute command, use either the 1109distinct. The unmute the volume after the mute command, use either the
1108up or down command (the level command will not unmute the volume). 1110up or down command (the level command will not unmute the volume).
1109The current volume level and mute state is shown in the file. 1111The current volume level and mute state is shown in the file.
1110 1112
1113There are two strategies for volume control. To select which one
1114should be used, use the volume_mode module parameter: volume_mode=1
1115selects EC mode, and volume_mode=3 selects EC mode with NVRAM backing
1116(so that volume/mute changes are remembered across shutdown/reboot).
1117
1118The driver will operate in volume_mode=3 by default. If that does not
1119work well on your ThinkPad model, please report this to
1120ibm-acpi-devel@lists.sourceforge.net.
1121
1111The ALSA mixer interface to this feature is still missing, but patches 1122The ALSA mixer interface to this feature is still missing, but patches
1112to add it exist. That problem should be addressed in the not so 1123to add it exist. That problem should be addressed in the not so
1113distant future. 1124distant future.
@@ -1376,6 +1387,7 @@ to enable more than one output class, just add their values.
1376 0x0008 HKEY event interface, hotkeys 1387 0x0008 HKEY event interface, hotkeys
1377 0x0010 Fan control 1388 0x0010 Fan control
1378 0x0020 Backlight brightness 1389 0x0020 Backlight brightness
1390 0x0040 Audio mixer/volume control
1379 1391
1380There is also a kernel build option to enable more debugging 1392There is also a kernel build option to enable more debugging
1381information, which may be necessary to debug driver problems. 1393information, which may be necessary to debug driver problems.
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 05714abf5a87..a2f5312c6a4e 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -231,6 +231,7 @@ enum tpacpi_hkey_event_t {
231#define TPACPI_DBG_HKEY 0x0008 231#define TPACPI_DBG_HKEY 0x0008
232#define TPACPI_DBG_FAN 0x0010 232#define TPACPI_DBG_FAN 0x0010
233#define TPACPI_DBG_BRGHT 0x0020 233#define TPACPI_DBG_BRGHT 0x0020
234#define TPACPI_DBG_MIXER 0x0040
234 235
235#define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") 236#define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off")
236#define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") 237#define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
@@ -6375,21 +6376,260 @@ static struct ibm_struct brightness_driver_data = {
6375 * Volume subdriver 6376 * Volume subdriver
6376 */ 6377 */
6377 6378
6378static int volume_offset = 0x30; 6379/*
6380 * IBM ThinkPads have a simple volume controller with MUTE gating.
6381 * Very early Lenovo ThinkPads follow the IBM ThinkPad spec.
6382 *
6383 * Since the *61 series (and probably also the later *60 series), Lenovo
6384 * ThinkPads only implement the MUTE gate.
6385 *
6386 * EC register 0x30
6387 * Bit 6: MUTE (1 mutes sound)
6388 * Bit 3-0: Volume
6389 * Other bits should be zero as far as we know.
6390 *
6391 * This is also stored in CMOS NVRAM, byte 0x60, bit 6 (MUTE), and
6392 * bits 3-0 (volume). Other bits in NVRAM may have other functions,
6393 * such as bit 7 which is used to detect repeated presses of MUTE,
6394 * and we leave them unchanged.
6395 */
6396
6397enum {
6398 TP_EC_AUDIO = 0x30,
6399
6400 /* TP_EC_AUDIO bits */
6401 TP_EC_AUDIO_MUTESW = 6,
6402
6403 /* TP_EC_AUDIO bitmasks */
6404 TP_EC_AUDIO_LVL_MSK = 0x0F,
6405 TP_EC_AUDIO_MUTESW_MSK = (1 << TP_EC_AUDIO_MUTESW),
6406
6407 /* Maximum volume */
6408 TP_EC_VOLUME_MAX = 14,
6409};
6410
6411enum tpacpi_volume_access_mode {
6412 TPACPI_VOL_MODE_AUTO = 0, /* Not implemented yet */
6413 TPACPI_VOL_MODE_EC, /* Pure EC control */
6414 TPACPI_VOL_MODE_UCMS_STEP, /* UCMS step-based control: N/A */
6415 TPACPI_VOL_MODE_ECNVRAM, /* EC control w/ NVRAM store */
6416 TPACPI_VOL_MODE_MAX
6417};
6418
6419static enum tpacpi_volume_access_mode volume_mode =
6420 TPACPI_VOL_MODE_MAX;
6421
6422
6423/*
6424 * Used to syncronize writers to TP_EC_AUDIO and
6425 * TP_NVRAM_ADDR_MIXER, as we need to do read-modify-write
6426 */
6427static struct mutex volume_mutex;
6428
6429static void tpacpi_volume_checkpoint_nvram(void)
6430{
6431 u8 lec = 0;
6432 u8 b_nvram;
6433 const u8 ec_mask = TP_EC_AUDIO_LVL_MSK | TP_EC_AUDIO_MUTESW_MSK;
6434
6435 if (volume_mode != TPACPI_VOL_MODE_ECNVRAM)
6436 return;
6437
6438 vdbg_printk(TPACPI_DBG_MIXER,
6439 "trying to checkpoint mixer state to NVRAM...\n");
6440
6441 if (mutex_lock_killable(&volume_mutex) < 0)
6442 return;
6443
6444 if (unlikely(!acpi_ec_read(TP_EC_AUDIO, &lec)))
6445 goto unlock;
6446 lec &= ec_mask;
6447 b_nvram = nvram_read_byte(TP_NVRAM_ADDR_MIXER);
6448
6449 if (lec != (b_nvram & ec_mask)) {
6450 /* NVRAM needs update */
6451 b_nvram &= ~ec_mask;
6452 b_nvram |= lec;
6453 nvram_write_byte(b_nvram, TP_NVRAM_ADDR_MIXER);
6454 dbg_printk(TPACPI_DBG_MIXER,
6455 "updated NVRAM mixer status to 0x%02x (0x%02x)\n",
6456 (unsigned int) lec, (unsigned int) b_nvram);
6457 } else {
6458 vdbg_printk(TPACPI_DBG_MIXER,
6459 "NVRAM mixer status already is 0x%02x (0x%02x)\n",
6460 (unsigned int) lec, (unsigned int) b_nvram);
6461 }
6462
6463unlock:
6464 mutex_unlock(&volume_mutex);
6465}
6466
6467static int volume_get_status_ec(u8 *status)
6468{
6469 u8 s;
6470
6471 if (!acpi_ec_read(TP_EC_AUDIO, &s))
6472 return -EIO;
6473
6474 *status = s;
6475
6476 dbg_printk(TPACPI_DBG_MIXER, "status 0x%02x\n", s);
6477
6478 return 0;
6479}
6480
6481static int volume_get_status(u8 *status)
6482{
6483 return volume_get_status_ec(status);
6484}
6485
6486static int volume_set_status_ec(const u8 status)
6487{
6488 if (!acpi_ec_write(TP_EC_AUDIO, status))
6489 return -EIO;
6490
6491 dbg_printk(TPACPI_DBG_MIXER, "set EC mixer to 0x%02x\n", status);
6492
6493 return 0;
6494}
6495
6496static int volume_set_status(const u8 status)
6497{
6498 return volume_set_status_ec(status);
6499}
6500
6501static int volume_set_mute_ec(const bool mute)
6502{
6503 int rc;
6504 u8 s, n;
6505
6506 if (mutex_lock_killable(&volume_mutex) < 0)
6507 return -EINTR;
6508
6509 rc = volume_get_status_ec(&s);
6510 if (rc)
6511 goto unlock;
6512
6513 n = (mute) ? s | TP_EC_AUDIO_MUTESW_MSK :
6514 s & ~TP_EC_AUDIO_MUTESW_MSK;
6515
6516 if (n != s)
6517 rc = volume_set_status_ec(n);
6518
6519unlock:
6520 mutex_unlock(&volume_mutex);
6521 return rc;
6522}
6523
6524static int volume_set_mute(const bool mute)
6525{
6526 dbg_printk(TPACPI_DBG_MIXER, "trying to %smute\n",
6527 (mute) ? "" : "un");
6528 return volume_set_mute_ec(mute);
6529}
6530
6531static int volume_set_volume_ec(const u8 vol)
6532{
6533 int rc;
6534 u8 s, n;
6535
6536 if (vol > TP_EC_VOLUME_MAX)
6537 return -EINVAL;
6538
6539 if (mutex_lock_killable(&volume_mutex) < 0)
6540 return -EINTR;
6541
6542 rc = volume_get_status_ec(&s);
6543 if (rc)
6544 goto unlock;
6545
6546 n = (s & ~TP_EC_AUDIO_LVL_MSK) | vol;
6547
6548 if (n != s)
6549 rc = volume_set_status_ec(n);
6550
6551unlock:
6552 mutex_unlock(&volume_mutex);
6553 return rc;
6554}
6555
6556static int volume_set_volume(const u8 vol)
6557{
6558 dbg_printk(TPACPI_DBG_MIXER,
6559 "trying to set volume level to %hu\n", vol);
6560 return volume_set_volume_ec(vol);
6561}
6562
6563static void volume_suspend(pm_message_t state)
6564{
6565 tpacpi_volume_checkpoint_nvram();
6566}
6567
6568static void volume_shutdown(void)
6569{
6570 tpacpi_volume_checkpoint_nvram();
6571}
6572
6573static void volume_exit(void)
6574{
6575 tpacpi_volume_checkpoint_nvram();
6576}
6577
6578static int __init volume_init(struct ibm_init_struct *iibm)
6579{
6580 vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n");
6581
6582 mutex_init(&volume_mutex);
6583
6584 /*
6585 * Check for module parameter bogosity, note that we
6586 * init volume_mode to TPACPI_VOL_MODE_MAX in order to be
6587 * able to detect "unspecified"
6588 */
6589 if (volume_mode > TPACPI_VOL_MODE_MAX)
6590 return -EINVAL;
6591
6592 if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) {
6593 printk(TPACPI_ERR
6594 "UCMS step volume mode not implemented, "
6595 "please contact %s\n", TPACPI_MAIL);
6596 return 1;
6597 }
6598
6599 if (volume_mode == TPACPI_VOL_MODE_AUTO ||
6600 volume_mode == TPACPI_VOL_MODE_MAX) {
6601 volume_mode = TPACPI_VOL_MODE_ECNVRAM;
6602
6603 dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
6604 "driver auto-selected volume_mode=%d\n",
6605 volume_mode);
6606 } else {
6607 dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
6608 "using user-supplied volume_mode=%d\n",
6609 volume_mode);
6610 }
6611
6612 vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
6613 "volume is supported\n");
6614
6615 return 0;
6616}
6379 6617
6380static int volume_read(char *p) 6618static int volume_read(char *p)
6381{ 6619{
6382 int len = 0; 6620 int len = 0;
6383 u8 level; 6621 u8 status;
6384 6622
6385 if (!acpi_ec_read(volume_offset, &level)) { 6623 if (volume_get_status(&status) < 0) {
6386 len += sprintf(p + len, "level:\t\tunreadable\n"); 6624 len += sprintf(p + len, "level:\t\tunreadable\n");
6387 } else { 6625 } else {
6388 len += sprintf(p + len, "level:\t\t%d\n", level & 0xf); 6626 len += sprintf(p + len, "level:\t\t%d\n",
6389 len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6)); 6627 status & TP_EC_AUDIO_LVL_MSK);
6628 len += sprintf(p + len, "mute:\t\t%s\n",
6629 onoff(status, TP_EC_AUDIO_MUTESW));
6390 len += sprintf(p + len, "commands:\tup, down, mute\n"); 6630 len += sprintf(p + len, "commands:\tup, down, mute\n");
6391 len += sprintf(p + len, "commands:\tlevel <level>" 6631 len += sprintf(p + len, "commands:\tlevel <level>"
6392 " (<level> is 0-15)\n"); 6632 " (<level> is 0-%d)\n", TP_EC_VOLUME_MAX);
6393 } 6633 }
6394 6634
6395 return len; 6635 return len;
@@ -6397,77 +6637,55 @@ static int volume_read(char *p)
6397 6637
6398static int volume_write(char *buf) 6638static int volume_write(char *buf)
6399{ 6639{
6400 int cmos_cmd, inc, i; 6640 u8 s;
6401 u8 level, mute; 6641 u8 new_level, new_mute;
6402 int new_level, new_mute; 6642 int l;
6403 char *cmd; 6643 char *cmd;
6644 int rc;
6404 6645
6405 while ((cmd = next_cmd(&buf))) { 6646 rc = volume_get_status(&s);
6406 if (!acpi_ec_read(volume_offset, &level)) 6647 if (rc < 0)
6407 return -EIO; 6648 return rc;
6408 new_mute = mute = level & 0x40;
6409 new_level = level = level & 0xf;
6410 6649
6650 new_level = s & TP_EC_AUDIO_LVL_MSK;
6651 new_mute = s & TP_EC_AUDIO_MUTESW_MSK;
6652
6653 while ((cmd = next_cmd(&buf))) {
6411 if (strlencmp(cmd, "up") == 0) { 6654 if (strlencmp(cmd, "up") == 0) {
6412 if (mute) 6655 if (new_mute)
6413 new_mute = 0; 6656 new_mute = 0;
6414 else 6657 else if (new_level < TP_EC_VOLUME_MAX)
6415 new_level = level == 15 ? 15 : level + 1; 6658 new_level++;
6416 } else if (strlencmp(cmd, "down") == 0) { 6659 } else if (strlencmp(cmd, "down") == 0) {
6417 if (mute) 6660 if (new_mute)
6418 new_mute = 0; 6661 new_mute = 0;
6419 else 6662 else if (new_level > 0)
6420 new_level = level == 0 ? 0 : level - 1; 6663 new_level--;
6421 } else if (sscanf(cmd, "level %d", &new_level) == 1 && 6664 } else if (sscanf(cmd, "level %u", &l) == 1 &&
6422 new_level >= 0 && new_level <= 15) { 6665 l >= 0 && l <= TP_EC_VOLUME_MAX) {
6423 /* new_level set */ 6666 new_level = l;
6424 } else if (strlencmp(cmd, "mute") == 0) { 6667 } else if (strlencmp(cmd, "mute") == 0) {
6425 new_mute = 0x40; 6668 new_mute = TP_EC_AUDIO_MUTESW_MSK;
6426 } else 6669 } else
6427 return -EINVAL; 6670 return -EINVAL;
6671 }
6428 6672
6429 if (new_level != level) { 6673 tpacpi_disclose_usertask("procfs volume",
6430 /* mute doesn't change */ 6674 "%smute and set level to %d\n",
6431 6675 new_mute ? "" : "un", new_level);
6432 cmos_cmd = (new_level > level) ?
6433 TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN;
6434 inc = new_level > level ? 1 : -1;
6435
6436 if (mute && (issue_thinkpad_cmos_command(cmos_cmd) ||
6437 !acpi_ec_write(volume_offset, level)))
6438 return -EIO;
6439
6440 for (i = level; i != new_level; i += inc)
6441 if (issue_thinkpad_cmos_command(cmos_cmd) ||
6442 !acpi_ec_write(volume_offset, i + inc))
6443 return -EIO;
6444
6445 if (mute &&
6446 (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) ||
6447 !acpi_ec_write(volume_offset, new_level + mute))) {
6448 return -EIO;
6449 }
6450 }
6451
6452 if (new_mute != mute) {
6453 /* level doesn't change */
6454 6676
6455 cmos_cmd = (new_mute) ? 6677 rc = volume_set_status(new_mute | new_level);
6456 TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP;
6457 6678
6458 if (issue_thinkpad_cmos_command(cmos_cmd) || 6679 return (rc == -EINTR) ? -ERESTARTSYS : rc;
6459 !acpi_ec_write(volume_offset, level + new_mute))
6460 return -EIO;
6461 }
6462 }
6463
6464 return 0;
6465} 6680}
6466 6681
6467static struct ibm_struct volume_driver_data = { 6682static struct ibm_struct volume_driver_data = {
6468 .name = "volume", 6683 .name = "volume",
6469 .read = volume_read, 6684 .read = volume_read,
6470 .write = volume_write, 6685 .write = volume_write,
6686 .exit = volume_exit,
6687 .suspend = volume_suspend,
6688 .shutdown = volume_shutdown,
6471}; 6689};
6472 6690
6473/************************************************************************* 6691/*************************************************************************
@@ -8121,6 +8339,7 @@ static struct ibm_init_struct ibms_init[] __initdata = {
8121 .data = &brightness_driver_data, 8339 .data = &brightness_driver_data,
8122 }, 8340 },
8123 { 8341 {
8342 .init = volume_init,
8124 .data = &volume_driver_data, 8343 .data = &volume_driver_data,
8125 }, 8344 },
8126 { 8345 {
@@ -8186,6 +8405,11 @@ MODULE_PARM_DESC(hotkey_report_mode,
8186 "used for backwards compatibility with userspace, " 8405 "used for backwards compatibility with userspace, "
8187 "see documentation"); 8406 "see documentation");
8188 8407
8408module_param_named(volume_mode, volume_mode, uint, 0444);
8409MODULE_PARM_DESC(volume_mode,
8410 "Selects volume control strategy: "
8411 "0=auto, 1=EC, 2=N/A, 3=EC+NVRAM");
8412
8189#define TPACPI_PARAM(feature) \ 8413#define TPACPI_PARAM(feature) \
8190 module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ 8414 module_param_call(feature, set_ibm_param, NULL, NULL, 0); \
8191 MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ 8415 MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \