aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/patch_realtek.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-06-22 08:30:29 -0400
committerTakashi Iwai <tiwai@suse.de>2012-06-25 03:45:54 -0400
commit125821ae539ab60f432b5e10dadfd7bbf069ca7a (patch)
treedc227300fe1fa38dfd66a8fdec17ac611b0c868a /sound/pci/hda/patch_realtek.c
parentb43d224767e426cf1a8b6622d1d172f2b2b0e857 (diff)
ALSA: hda - Add the inverted digital mic workaround to Realtek codecs
Some laptops are equipped with ForteMedia digital mics that give the differential input. With such devices, summing stereo streams into a mono (like PulseAudio does) results in almost silence. This patch provides a workaround for this bug by adding a new mixer switch to turn on/off the right channel of digital mic, just like a similar fix for Conexant codecs. When the new switch "Inverted Internal Mic Capture Switch" is off and the current input source is the digital mic, the right channel of the recording stream is muted. When another input source is selected, the right channel is restored. Tested-by: Eliot Blennerhassett <eliot@blennerhassett.gen.nz> 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.c127
1 files changed, 125 insertions, 2 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index f8f4906e498..a0a3cf95650 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -170,6 +170,7 @@ struct alc_spec {
170 hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; 170 hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
171 unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; 171 unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
172 int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ 172 int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
173 hda_nid_t inv_dmic_pin;
173 174
174 /* hooks */ 175 /* hooks */
175 void (*init_hook)(struct hda_codec *codec); 176 void (*init_hook)(struct hda_codec *codec);
@@ -201,6 +202,8 @@ struct alc_spec {
201 unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */ 202 unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */
202 unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ 203 unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
203 unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ 204 unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
205 unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
206 unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
204 207
205 /* auto-mute control */ 208 /* auto-mute control */
206 int automute_mode; 209 int automute_mode;
@@ -298,6 +301,7 @@ static inline hda_nid_t get_capsrc(struct alc_spec *spec, int idx)
298} 301}
299 302
300static void call_update_outputs(struct hda_codec *codec); 303static void call_update_outputs(struct hda_codec *codec);
304static void alc_inv_dmic_sync(struct hda_codec *codec, bool force);
301 305
302/* select the given imux item; either unmute exclusively or select the route */ 306/* select the given imux item; either unmute exclusively or select the route */
303static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, 307static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
@@ -368,6 +372,7 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
368 AC_VERB_SET_CONNECT_SEL, 372 AC_VERB_SET_CONNECT_SEL,
369 imux->items[idx].index); 373 imux->items[idx].index);
370 } 374 }
375 alc_inv_dmic_sync(codec, true);
371 return 1; 376 return 1;
372} 377}
373 378
@@ -1556,14 +1561,14 @@ typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
1556 1561
1557static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol, 1562static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
1558 struct snd_ctl_elem_value *ucontrol, 1563 struct snd_ctl_elem_value *ucontrol,
1559 getput_call_t func, bool check_adc_switch) 1564 getput_call_t func, bool is_put)
1560{ 1565{
1561 struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1566 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1562 struct alc_spec *spec = codec->spec; 1567 struct alc_spec *spec = codec->spec;
1563 int i, err = 0; 1568 int i, err = 0;
1564 1569
1565 mutex_lock(&codec->control_mutex); 1570 mutex_lock(&codec->control_mutex);
1566 if (check_adc_switch && spec->dyn_adc_switch) { 1571 if (is_put && spec->dyn_adc_switch) {
1567 for (i = 0; i < spec->num_adc_nids; i++) { 1572 for (i = 0; i < spec->num_adc_nids; i++) {
1568 kcontrol->private_value = 1573 kcontrol->private_value =
1569 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 1574 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
@@ -1584,6 +1589,8 @@ static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
1584 3, 0, HDA_INPUT); 1589 3, 0, HDA_INPUT);
1585 err = func(kcontrol, ucontrol); 1590 err = func(kcontrol, ucontrol);
1586 } 1591 }
1592 if (err >= 0 && is_put)
1593 alc_inv_dmic_sync(codec, false);
1587 error: 1594 error:
1588 mutex_unlock(&codec->control_mutex); 1595 mutex_unlock(&codec->control_mutex);
1589 return err; 1596 return err;
@@ -1676,6 +1683,108 @@ DEFINE_CAPMIX_NOSRC(2);
1676DEFINE_CAPMIX_NOSRC(3); 1683DEFINE_CAPMIX_NOSRC(3);
1677 1684
1678/* 1685/*
1686 * Inverted digital-mic handling
1687 *
1688 * First off, it's a bit tricky. The "Inverted Internal Mic Capture Switch"
1689 * gives the additional mute only to the right channel of the digital mic
1690 * capture stream. This is a workaround for avoiding the almost silence
1691 * by summing the stereo stream from some (known to be ForteMedia)
1692 * digital mic unit.
1693 *
1694 * The logic is to call alc_inv_dmic_sync() after each action (possibly)
1695 * modifying ADC amp. When the mute flag is set, it mutes the R-channel
1696 * without caching so that the cache can still keep the original value.
1697 * The cached value is then restored when the flag is set off or any other
1698 * than d-mic is used as the current input source.
1699 */
1700static void alc_inv_dmic_sync(struct hda_codec *codec, bool force)
1701{
1702 struct alc_spec *spec = codec->spec;
1703 int i;
1704
1705 if (!spec->inv_dmic_fixup)
1706 return;
1707 if (!spec->inv_dmic_muted && !force)
1708 return;
1709 for (i = 0; i < spec->num_adc_nids; i++) {
1710 int src = spec->dyn_adc_switch ? 0 : i;
1711 bool dmic_fixup = false;
1712 hda_nid_t nid;
1713 int parm, dir, v;
1714
1715 if (spec->inv_dmic_muted &&
1716 spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin)
1717 dmic_fixup = true;
1718 if (!dmic_fixup && !force)
1719 continue;
1720 if (spec->vol_in_capsrc) {
1721 nid = spec->capsrc_nids[i];
1722 parm = AC_AMP_SET_RIGHT | AC_AMP_SET_OUTPUT;
1723 dir = HDA_OUTPUT;
1724 } else {
1725 nid = spec->adc_nids[i];
1726 parm = AC_AMP_SET_RIGHT | AC_AMP_SET_INPUT;
1727 dir = HDA_INPUT;
1728 }
1729 /* we care only right channel */
1730 v = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
1731 if (v & 0x80) /* if already muted, we don't need to touch */
1732 continue;
1733 if (dmic_fixup) /* add mute for d-mic */
1734 v |= 0x80;
1735 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
1736 parm | v);
1737 }
1738}
1739
1740static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol,
1741 struct snd_ctl_elem_value *ucontrol)
1742{
1743 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1744 struct alc_spec *spec = codec->spec;
1745
1746 ucontrol->value.integer.value[0] = !spec->inv_dmic_muted;
1747 return 0;
1748}
1749
1750static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol,
1751 struct snd_ctl_elem_value *ucontrol)
1752{
1753 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1754 struct alc_spec *spec = codec->spec;
1755 unsigned int val = !ucontrol->value.integer.value[0];
1756
1757 if (val == spec->inv_dmic_muted)
1758 return 0;
1759 spec->inv_dmic_muted = val;
1760 alc_inv_dmic_sync(codec, true);
1761 return 0;
1762}
1763
1764static const struct snd_kcontrol_new alc_inv_dmic_sw = {
1765 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1766 .info = snd_ctl_boolean_mono_info,
1767 .get = alc_inv_dmic_sw_get,
1768 .put = alc_inv_dmic_sw_put,
1769};
1770
1771static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid)
1772{
1773 struct alc_spec *spec = codec->spec;
1774 struct snd_kcontrol_new *knew = alc_kcontrol_new(spec);
1775 if (!knew)
1776 return -ENOMEM;
1777 *knew = alc_inv_dmic_sw;
1778 knew->name = kstrdup("Inverted Internal Mic Capture Switch", GFP_KERNEL);
1779 if (!knew->name)
1780 return -ENOMEM;
1781 spec->inv_dmic_fixup = 1;
1782 spec->inv_dmic_muted = 0;
1783 spec->inv_dmic_pin = nid;
1784 return 0;
1785}
1786
1787/*
1679 * virtual master controls 1788 * virtual master controls
1680 */ 1789 */
1681 1790
@@ -2316,6 +2425,7 @@ static int alc_resume(struct hda_codec *codec)
2316 codec->patch_ops.init(codec); 2425 codec->patch_ops.init(codec);
2317 snd_hda_codec_resume_amp(codec); 2426 snd_hda_codec_resume_amp(codec);
2318 snd_hda_codec_resume_cache(codec); 2427 snd_hda_codec_resume_cache(codec);
2428 alc_inv_dmic_sync(codec, true);
2319 hda_call_check_power_status(codec, 0x01); 2429 hda_call_check_power_status(codec, 0x01);
2320 return 0; 2430 return 0;
2321} 2431}
@@ -6424,6 +6534,13 @@ static void alc272_fixup_mario(struct hda_codec *codec,
6424 "hda_codec: failed to override amp caps for NID 0x2\n"); 6534 "hda_codec: failed to override amp caps for NID 0x2\n");
6425} 6535}
6426 6536
6537static void alc662_fixup_inv_dmic(struct hda_codec *codec,
6538 const struct alc_fixup *fix, int action)
6539{
6540 if (action == ALC_FIXUP_ACT_PROBE)
6541 alc_add_inv_dmic_mixer(codec, 0x12);
6542}
6543
6427enum { 6544enum {
6428 ALC662_FIXUP_ASPIRE, 6545 ALC662_FIXUP_ASPIRE,
6429 ALC662_FIXUP_IDEAPAD, 6546 ALC662_FIXUP_IDEAPAD,
@@ -6441,6 +6558,7 @@ enum {
6441 ALC662_FIXUP_ASUS_MODE8, 6558 ALC662_FIXUP_ASUS_MODE8,
6442 ALC662_FIXUP_NO_JACK_DETECT, 6559 ALC662_FIXUP_NO_JACK_DETECT,
6443 ALC662_FIXUP_ZOTAC_Z68, 6560 ALC662_FIXUP_ZOTAC_Z68,
6561 ALC662_FIXUP_INV_DMIC,
6444}; 6562};
6445 6563
6446static const struct alc_fixup alc662_fixups[] = { 6564static const struct alc_fixup alc662_fixups[] = {
@@ -6597,12 +6715,17 @@ static const struct alc_fixup alc662_fixups[] = {
6597 { } 6715 { }
6598 } 6716 }
6599 }, 6717 },
6718 [ALC662_FIXUP_INV_DMIC] = {
6719 .type = ALC_FIXUP_FUNC,
6720 .v.func = alc662_fixup_inv_dmic,
6721 },
6600}; 6722};
6601 6723
6602static const struct snd_pci_quirk alc662_fixup_tbl[] = { 6724static const struct snd_pci_quirk alc662_fixup_tbl[] = {
6603 SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2), 6725 SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2),
6604 SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), 6726 SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
6605 SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), 6727 SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),
6728 SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),
6606 SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), 6729 SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
6607 SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), 6730 SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
6608 SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), 6731 SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),