aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/patch_realtek.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2011-04-28 11:36:18 -0400
committerTakashi Iwai <tiwai@suse.de>2011-04-28 11:55:53 -0400
commit1a1455de10d89b9f2107fe5ad1746e7c18838492 (patch)
treee33941e2d9e4729f4a2e0256eb8c12ae9d728cdd /sound/pci/hda/patch_realtek.c
parent0f0f391c730228d1fd1c3933275ed75ee96e4db2 (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.c146
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,
1129static void update_speakers(struct hda_codec *codec) 1129static 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
1146static void alc_hp_automute(struct hda_codec *codec) 1156static 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
1427static 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
1444static 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
1460static 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
1491static 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
1499static struct snd_kcontrol_new *alc_kcontrol_new(struct alc_spec *spec);
1500
1501static 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
1417static void alc_init_auto_hp(struct hda_codec *codec) 1516static 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/*