[alsa-devel] [RFC PATCH] Inverted internal mic

Takashi Iwai tiwai at suse.de
Fri Jun 22 11:33:04 CEST 2012


At Thu, 21 Jun 2012 16:23:48 +0200,
David Henningsson wrote:
> 
> On 06/21/2012 03:19 PM, Takashi Iwai wrote:
> > At Thu, 21 Jun 2012 15:04:44 +0200,
> > David Henningsson wrote:
> >>
> >> On 06/21/2012 02:52 PM, Takashi Iwai wrote:
> >>> At Thu, 21 Jun 2012 03:15:27 +0200,
> >>> David Henningsson wrote:
> >>>>
> >>>> On 06/20/2012 03:31 PM, Takashi Iwai wrote:
> >>>>> At Tue, 19 Jun 2012 09:43:07 +0200,
> >>>>> David Henningsson wrote:
> >>>>>>
> >>>>>> On 06/19/2012 05:07 AM, Eliot Blennerhassett wrote:
> >>>>>>> David Henningsson<david.henningsson<at>     canonical.com>     writes:
> >>>>>>>> On 02/28/2012 02:22 PM, Takashi Iwai wrote:
> >>>>>>>>> At Tue, 28 Feb 2012 14:07:59 +0100,
> >>>>>>>>> David Henningsson wrote:
> >>>>>>>>> Is there a way we can
> >>>>>>>>>> know the corresponding processing coefficients to set for ALC268 and
> >>>>>>>>>> ALC272X as well?
> >>>>>>>>>
> >>>>>>>>> AFAIK, no, it was specific to the codec model.
> >>>>>>>>
> >>>>>>>> Ok, then we can only hope for Kailang to supply this information if
> >>>>>>>> possible. And if not possible we could attempt the workaround (when/if
> >>>>>>>> we agree on it...) for these devices as well?
> >>>>>>>
> >>>>>>> Greetings,
> >>>>>>>
> >>>>>>> Any chance that there has been any progress on this?
> >>>>>>> I have a machine with dmic and ALC272X (details below) that exhibits this
> >>>>>>> problem, and can test any proposed patch.
> >>>>>>
> >>>>>> We have a patch in for the Thinkpad U300s, but that one had a Conexant
> >>>>>> codec.
> >>>>>> I haven't had time to start working on kernel patches for the Realtek
> >>>>>> ones yet, but meanwhile, I'm tracking known machines here:
> >>>>>>
> >>>>>> https://bugs.launchpad.net/ubuntu/+source/alsa-driver/+bug/1002978
> >>>>>
> >>>>> Looking at the codec, it's not so trivial to port the inverted switch
> >>>>> to Realtek.  In the input path of Realtek codecs, there is no
> >>>>> individual capture volume/switch but only a central ADC volume and a
> >>>>> MUX (or a mixer).
> >>>>
> >>>> Yeah, that's part of why I haven't done it myself yet ;-)
> >>>>
> >>>>    >   I can think of a new boolean switch or an enum to choose whether to
> >>>>    >   shut off the right channel of the input-mux and the loopback volume.
> >>>>    >   But it's feasible only if it make sense to PA.
> >>>>
> >>>> It seems possible that for ALC269 [1], you could switch path entirely
> >>>> (including ADC). I e, the internal mic would go 0x12 ->   0x23 ->   0x08 and
> >>>> the external mic would go 0x18 ->   0x24 ->   0x07. That way you could then
> >>>> label the volume control on 0x08 "Internal Mic Capture Volume" and
> >>>> "Inverted Internal Mic Capture Volume".
> >>>>
> >>>> Do you think this is a good strategy, or would it lead to other problems
> >>>> (i e, what happens when you plug your mic in while actively recording)?
> >>>
> >>> If the i-mic is the only user for ADC 0x08, this would work.
> >>> But when ADC 0x09 has multiple sources like e-mic and line-in,
> >>> ADC 0x09 would be named as "Capture" (because it's not only "Mic"),
> >>> and this becomes exclusive with "Internal Mic Capture".  It's a bit
> >>> confusing, IMO.
> >>
> >> Yes, this logic can only be used when there are two inputs - mic and
> >> internal mic. That is, the same conditions we today have for determining
> >> when to have auto-switching on plug/unplug.
> >>
> >> Then 0x08 would have "Internal Mic Capture Volume|Switch" and 0x09 would
> >> be "Mic Capture Volume|Switch". "Capture Volume|Switch" cannot be used
> >> (unless we try to implement some kind of vmaster on the input side, but
> >> I don't think that would be necessary).
> >>
> >> The alsa-info's I've looked at so far all have had one internal mic and
> >> one external mic on the input side. At least the Realtek ones.
> >
> > Well, it's a bit risky to bet that.  I won't be surprised by any
> > largish machines with one more jack for supporting 5.1 output and a
> > digital built-in mic, for example.
> 
> It is always difficult to bet on the future, but sure, that's a 
> drawback. So what were you suggesting instead, in a little more detail?

Well, I thought of a mixer switch or enum to specify the inverted mic
right-channel on/off.  If right channel is off, the ADC right channel
mute is set autotmatically when the d-mic is selected as the input
source.

A test patch is below.  It seems working with hda-emu.

(Actually in the case of ALC662 / ALC272x, it could be done in the
 mixer widget; but it's more generic to fiddle with ADC mute.)


Takashi

---
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 41475ae..dcc77d0 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -170,6 +170,7 @@ struct alc_spec {
 	hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
 	unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
 	int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
+	hda_nid_t inv_dmic_pin;
 
 	/* hooks */
 	void (*init_hook)(struct hda_codec *codec);
@@ -201,6 +202,8 @@ struct alc_spec {
 	unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */
 	unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
 	unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
+	unsigned int inv_dmic_fixup:1;
+	unsigned int inv_dmic_enabled:1;
 
 	/* auto-mute control */
 	int automute_mode;
@@ -298,6 +301,7 @@ static inline hda_nid_t get_capsrc(struct alc_spec *spec, int idx)
 }
 
 static void call_update_outputs(struct hda_codec *codec);
+static void alc_inv_dmic_sync(struct hda_codec *codec, bool force);
 
 /* select the given imux item; either unmute exclusively or select the route */
 static 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,
 					  AC_VERB_SET_CONNECT_SEL,
 					  imux->items[idx].index);
 	}
+	alc_inv_dmic_sync(codec, true);
 	return 1;
 }
 
@@ -1556,14 +1561,14 @@ typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
 
 static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol,
-				 getput_call_t func, bool check_adc_switch)
+				 getput_call_t func, bool is_put)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
 	int i, err = 0;
 
 	mutex_lock(&codec->control_mutex);
-	if (check_adc_switch && spec->dyn_adc_switch) {
+	if (is_put && spec->dyn_adc_switch) {
 		for (i = 0; i < spec->num_adc_nids; i++) {
 			kcontrol->private_value =
 				HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
@@ -1584,6 +1589,8 @@ static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
 						    3, 0, HDA_INPUT);
 		err = func(kcontrol, ucontrol);
 	}
+	if (err >= 0 && is_put)
+		alc_inv_dmic_sync(codec, false);
  error:
 	mutex_unlock(&codec->control_mutex);
 	return err;
@@ -1676,6 +1683,93 @@ DEFINE_CAPMIX_NOSRC(2);
 DEFINE_CAPMIX_NOSRC(3);
 
 /*
+ * Inverted digital-mic handling
+ */
+static void alc_inv_dmic_sync(struct hda_codec *codec, bool force)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	if (!spec->inv_dmic_fixup)
+		return;
+	if (spec->inv_dmic_enabled && !force)
+		return;
+	for (i = 0; i < spec->num_adc_nids; i++) {
+		int src = spec->dyn_adc_switch ? 0 : i;
+		bool dmic_fixup = false;
+		hda_nid_t nid;
+		int parm, dir, v;
+
+		if (!spec->inv_dmic_enabled &&
+		    spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin)
+			dmic_fixup = true;
+		if (!dmic_fixup && !force)
+			continue;
+		if (spec->vol_in_capsrc) {
+			nid = spec->capsrc_nids[i];
+			parm = AC_AMP_SET_RIGHT | AC_AMP_SET_OUTPUT;
+			dir = HDA_OUTPUT;
+		} else {
+			nid = spec->adc_nids[i];
+			parm = AC_AMP_SET_RIGHT | AC_AMP_SET_INPUT;
+			dir = HDA_INPUT;
+		}
+		v = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
+		if (v & 0x80) /* if already muted, we don't need to touch */
+			continue;
+		if (dmic_fixup) /* mute for d-mic required */
+			v |= 0x80;
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+				    parm | v);
+	}
+}
+
+static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	
+	ucontrol->value.integer.value[0] = spec->inv_dmic_enabled;
+	return 0;
+}
+
+static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	unsigned int val = !!ucontrol->value.integer.value[0];
+
+	if (val == spec->inv_dmic_enabled)
+		return 0;
+	spec->inv_dmic_enabled = val;
+	alc_inv_dmic_sync(codec, true);
+	return 0;
+}
+
+static const struct snd_kcontrol_new alc_inv_dmic_sw = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = snd_ctl_boolean_mono_info,
+	.get = alc_inv_dmic_sw_get,
+	.put = alc_inv_dmic_sw_put,
+};
+
+static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct alc_spec *spec = codec->spec;
+	struct snd_kcontrol_new *knew = alc_kcontrol_new(spec);
+	if (!knew)
+		return -ENOMEM;
+	*knew = alc_inv_dmic_sw;
+	knew->name = kstrdup("Inverted Mic Capture Switch", GFP_KERNEL);
+	spec->inv_dmic_fixup = 1;
+	spec->inv_dmic_enabled = 1;
+	spec->inv_dmic_pin = nid;
+	return 0;
+}
+
+/*
  * virtual master controls
  */
 
@@ -2316,6 +2410,7 @@ static int alc_resume(struct hda_codec *codec)
 	codec->patch_ops.init(codec);
 	snd_hda_codec_resume_amp(codec);
 	snd_hda_codec_resume_cache(codec);
+	alc_inv_dmic_sync(codec, true);
 	hda_call_check_power_status(codec, 0x01);
 	return 0;
 }
@@ -6424,6 +6519,13 @@ static void alc272_fixup_mario(struct hda_codec *codec,
 		       "hda_codec: failed to override amp caps for NID 0x2\n");
 }
 
+static void alc662_fixup_inv_dmic(struct hda_codec *codec,
+				  const struct alc_fixup *fix, int action)
+{
+	if (action == ALC_FIXUP_ACT_PROBE)
+		alc_add_inv_dmic_mixer(codec, 0x12);
+}
+
 enum {
 	ALC662_FIXUP_ASPIRE,
 	ALC662_FIXUP_IDEAPAD,
@@ -6441,6 +6543,7 @@ enum {
 	ALC662_FIXUP_ASUS_MODE8,
 	ALC662_FIXUP_NO_JACK_DETECT,
 	ALC662_FIXUP_ZOTAC_Z68,
+	ALC662_FIXUP_INV_DMIC,
 };
 
 static const struct alc_fixup alc662_fixups[] = {
@@ -6597,12 +6700,17 @@ static const struct alc_fixup alc662_fixups[] = {
 			{ }
 		}
 	},
+	[ALC662_FIXUP_INV_DMIC] = {
+		.type = ALC_FIXUP_FUNC,
+		.v.func = alc662_fixup_inv_dmic,
+	},
 };
 
 static const struct snd_pci_quirk alc662_fixup_tbl[] = {
 	SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2),
 	SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),
+	SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),
 	SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
 	SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
 	SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),


More information about the Alsa-devel mailing list