diff options
author | Takashi Iwai <tiwai@suse.de> | 2011-04-28 11:36:18 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-04-28 11:55:53 -0400 |
commit | 1a1455de10d89b9f2107fe5ad1746e7c18838492 (patch) | |
tree | e33941e2d9e4729f4a2e0256eb8c12ae9d728cdd /sound/pci/hda/patch_realtek.c | |
parent | 0f0f391c730228d1fd1c3933275ed75ee96e4db2 (diff) |
ALSA: hda - Add support for Line-Out automute to Realtek auto-parser
By popular demands, I add the functionality to mute / unmute the
line-out jacks per the headphone plug / unplug. For achieving this
and keeping the compatibility with the old behavior, the new mixer
enum "Auto-Mute Mode" is added. With this, user can control the
auto-mute behavior either disabled, speaker-only or lineout+speaker.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_realtek.c')
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 146 |
1 files changed, 133 insertions, 13 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b76d3b3fb63c..9e2594d6db96 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c | |||
@@ -1129,18 +1129,28 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, | |||
1129 | static void update_speakers(struct hda_codec *codec) | 1129 | static void update_speakers(struct hda_codec *codec) |
1130 | { | 1130 | { |
1131 | struct alc_spec *spec = codec->spec; | 1131 | struct alc_spec *spec = codec->spec; |
1132 | int on; | ||
1132 | 1133 | ||
1134 | if (!spec->automute) | ||
1135 | on = 0; | ||
1136 | else | ||
1137 | on = spec->jack_present | spec->line_jack_present; | ||
1138 | on |= spec->master_mute; | ||
1133 | do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), | 1139 | do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), |
1134 | spec->autocfg.speaker_pins, | 1140 | spec->autocfg.speaker_pins, on, false); |
1135 | spec->jack_present | spec->line_jack_present | | ||
1136 | spec->master_mute, false); | ||
1137 | 1141 | ||
1138 | /* toggle line-out mutes if needed, too */ | 1142 | /* toggle line-out mutes if needed, too */ |
1139 | if (!spec->automute_lines) | 1143 | /* if LO is a copy of either HP or Speaker, don't need to handle it */ |
1144 | if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || | ||
1145 | spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) | ||
1140 | return; | 1146 | return; |
1147 | if (!spec->automute_lines || !spec->automute) | ||
1148 | on = 0; | ||
1149 | else | ||
1150 | on = spec->jack_present; | ||
1151 | on |= spec->master_mute; | ||
1141 | do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), | 1152 | do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), |
1142 | spec->autocfg.line_out_pins, | 1153 | spec->autocfg.line_out_pins, on, false); |
1143 | spec->jack_present | spec->master_mute, false); | ||
1144 | } | 1154 | } |
1145 | 1155 | ||
1146 | static void alc_hp_automute(struct hda_codec *codec) | 1156 | static void alc_hp_automute(struct hda_codec *codec) |
@@ -1414,6 +1424,95 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) | |||
1414 | } | 1424 | } |
1415 | } | 1425 | } |
1416 | 1426 | ||
1427 | static int alc_automute_mode_info(struct snd_kcontrol *kcontrol, | ||
1428 | struct snd_ctl_elem_info *uinfo) | ||
1429 | { | ||
1430 | static const char * const texts[] = { | ||
1431 | "Disabled", "Speaker Only", "Line-Out+Speaker" | ||
1432 | }; | ||
1433 | |||
1434 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
1435 | uinfo->count = 1; | ||
1436 | uinfo->value.enumerated.items = 3; | ||
1437 | if (uinfo->value.enumerated.item >= 3) | ||
1438 | uinfo->value.enumerated.item = 2; | ||
1439 | strcpy(uinfo->value.enumerated.name, | ||
1440 | texts[uinfo->value.enumerated.item]); | ||
1441 | return 0; | ||
1442 | } | ||
1443 | |||
1444 | static int alc_automute_mode_get(struct snd_kcontrol *kcontrol, | ||
1445 | struct snd_ctl_elem_value *ucontrol) | ||
1446 | { | ||
1447 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
1448 | struct alc_spec *spec = codec->spec; | ||
1449 | unsigned int val; | ||
1450 | if (!spec->automute) | ||
1451 | val = 0; | ||
1452 | else if (!spec->automute_lines) | ||
1453 | val = 1; | ||
1454 | else | ||
1455 | val = 2; | ||
1456 | ucontrol->value.enumerated.item[0] = val; | ||
1457 | return 0; | ||
1458 | } | ||
1459 | |||
1460 | static int alc_automute_mode_put(struct snd_kcontrol *kcontrol, | ||
1461 | struct snd_ctl_elem_value *ucontrol) | ||
1462 | { | ||
1463 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
1464 | struct alc_spec *spec = codec->spec; | ||
1465 | |||
1466 | switch (ucontrol->value.enumerated.item[0]) { | ||
1467 | case 0: | ||
1468 | if (!spec->automute) | ||
1469 | return 0; | ||
1470 | spec->automute = 0; | ||
1471 | break; | ||
1472 | case 1: | ||
1473 | if (spec->automute && !spec->automute_lines) | ||
1474 | return 0; | ||
1475 | spec->automute = 1; | ||
1476 | spec->automute_lines = 0; | ||
1477 | break; | ||
1478 | case 2: | ||
1479 | if (spec->automute && spec->automute_lines) | ||
1480 | return 0; | ||
1481 | spec->automute = 1; | ||
1482 | spec->automute_lines = 1; | ||
1483 | break; | ||
1484 | default: | ||
1485 | return -EINVAL; | ||
1486 | } | ||
1487 | update_speakers(codec); | ||
1488 | return 1; | ||
1489 | } | ||
1490 | |||
1491 | static struct snd_kcontrol_new alc_automute_mode_enum = { | ||
1492 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1493 | .name = "Auto-Mute Mode", | ||
1494 | .info = alc_automute_mode_info, | ||
1495 | .get = alc_automute_mode_get, | ||
1496 | .put = alc_automute_mode_put, | ||
1497 | }; | ||
1498 | |||
1499 | static struct snd_kcontrol_new *alc_kcontrol_new(struct alc_spec *spec); | ||
1500 | |||
1501 | static int alc_add_automute_mode_enum(struct hda_codec *codec) | ||
1502 | { | ||
1503 | struct alc_spec *spec = codec->spec; | ||
1504 | struct snd_kcontrol_new *knew; | ||
1505 | |||
1506 | knew = alc_kcontrol_new(spec); | ||
1507 | if (!knew) | ||
1508 | return -ENOMEM; | ||
1509 | *knew = alc_automute_mode_enum; | ||
1510 | knew->name = kstrdup("Auto-Mute Mode", GFP_KERNEL); | ||
1511 | if (!knew->name) | ||
1512 | return -ENOMEM; | ||
1513 | return 0; | ||
1514 | } | ||
1515 | |||
1417 | static void alc_init_auto_hp(struct hda_codec *codec) | 1516 | static void alc_init_auto_hp(struct hda_codec *codec) |
1418 | { | 1517 | { |
1419 | struct alc_spec *spec = codec->spec; | 1518 | struct alc_spec *spec = codec->spec; |
@@ -1440,14 +1539,37 @@ static void alc_init_auto_hp(struct hda_codec *codec) | |||
1440 | } | 1539 | } |
1441 | 1540 | ||
1442 | for (i = 0; i < cfg->hp_outs; i++) { | 1541 | for (i = 0; i < cfg->hp_outs; i++) { |
1542 | hda_nid_t nid = cfg->hp_pins[i]; | ||
1543 | if (!(snd_hda_query_pin_caps(codec, nid) & | ||
1544 | AC_PINCAP_PRES_DETECT)) | ||
1545 | continue; | ||
1443 | snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n", | 1546 | snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n", |
1444 | cfg->hp_pins[i]); | 1547 | nid); |
1445 | snd_hda_codec_write_cache(codec, cfg->hp_pins[i], 0, | 1548 | snd_hda_codec_write_cache(codec, nid, 0, |
1446 | AC_VERB_SET_UNSOLICITED_ENABLE, | 1549 | AC_VERB_SET_UNSOLICITED_ENABLE, |
1447 | AC_USRSP_EN | ALC880_HP_EVENT); | 1550 | AC_USRSP_EN | ALC880_HP_EVENT); |
1448 | spec->automute = 1; | 1551 | spec->automute = 1; |
1449 | spec->automute_mode = ALC_AUTOMUTE_PIN; | 1552 | spec->automute_mode = ALC_AUTOMUTE_PIN; |
1450 | } | 1553 | } |
1554 | if (spec->automute && cfg->line_out_pins[0] && | ||
1555 | cfg->line_out_pins[0] != cfg->hp_pins[0] && | ||
1556 | cfg->line_out_pins[0] != cfg->speaker_pins[0]) { | ||
1557 | for (i = 0; i < cfg->line_outs; i++) { | ||
1558 | hda_nid_t nid = cfg->line_out_pins[i]; | ||
1559 | if (!(snd_hda_query_pin_caps(codec, nid) & | ||
1560 | AC_PINCAP_PRES_DETECT)) | ||
1561 | continue; | ||
1562 | snd_printdd("realtek: Enable Line-Out auto-muting " | ||
1563 | "on NID 0x%x\n", nid); | ||
1564 | snd_hda_codec_write_cache(codec, nid, 0, | ||
1565 | AC_VERB_SET_UNSOLICITED_ENABLE, | ||
1566 | AC_USRSP_EN | ALC880_FRONT_EVENT); | ||
1567 | spec->detect_line = 1; | ||
1568 | } | ||
1569 | /* create a control for automute mode */ | ||
1570 | alc_add_automute_mode_enum(codec); | ||
1571 | spec->automute_lines = 1; | ||
1572 | } | ||
1451 | spec->unsol_event = alc_sku_unsol_event; | 1573 | spec->unsol_event = alc_sku_unsol_event; |
1452 | } | 1574 | } |
1453 | 1575 | ||
@@ -1684,9 +1806,6 @@ do_sku: | |||
1684 | return 1; | 1806 | return 1; |
1685 | spec->autocfg.hp_pins[0] = nid; | 1807 | spec->autocfg.hp_pins[0] = nid; |
1686 | } | 1808 | } |
1687 | |||
1688 | alc_init_auto_hp(codec); | ||
1689 | alc_init_auto_mic(codec); | ||
1690 | return 1; | 1809 | return 1; |
1691 | } | 1810 | } |
1692 | 1811 | ||
@@ -1699,9 +1818,10 @@ static void alc_ssid_check(struct hda_codec *codec, | |||
1699 | snd_printd("realtek: " | 1818 | snd_printd("realtek: " |
1700 | "Enable default setup for auto mode as fallback\n"); | 1819 | "Enable default setup for auto mode as fallback\n"); |
1701 | spec->init_amp = ALC_INIT_DEFAULT; | 1820 | spec->init_amp = ALC_INIT_DEFAULT; |
1702 | alc_init_auto_hp(codec); | ||
1703 | alc_init_auto_mic(codec); | ||
1704 | } | 1821 | } |
1822 | |||
1823 | alc_init_auto_hp(codec); | ||
1824 | alc_init_auto_mic(codec); | ||
1705 | } | 1825 | } |
1706 | 1826 | ||
1707 | /* | 1827 | /* |