mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Zhang Heng <zhangheng@kylinos.cn>
To: perex@perex.cz, tiwai@suse.com, linux-sound@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Fwd: [PATCH] ALSA: hda/generic: Add mic autoswitch support for dyn_adc_switch mode
Date: Wed, 6 May 2026 15:03:52 +0800	[thread overview]
Message-ID: <a7daf8a3-559b-4532-a2ee-a211a7e0e0b0@kylinos.cn> (raw)
In-Reply-To: <5b215802-4c94-4f94-9cee-f8e497b31337@kylinos.cn>




-------- 转发的消息 --------
主题: 	Re: [PATCH] ALSA: hda/generic: Add mic autoswitch support for 
dyn_adc_switch mode
日期: 	Wed, 6 May 2026 14:19:14 +0800
发件人: 	Zhang Heng <zhangheng@kylinos.cn>
收件人: 	Takashi Iwai <tiwai@suse.de>




在 2026/5/5 0:25, Takashi Iwai 写道:
> On Sun, 03 May 2026 13:45:12 +0200,
> Zhang Heng wrote:
>> When auto_mic is not available but dyn_adc_switch mode is enabled
>> (e.g., on laptops with both front and rear mic jacks), this patch
>> enables automatic microphone switching based on jack detection.
>>
>> The patch includes three changes:
>>
>> 1. In check_dyn_adc_switch(): Register jack detect callbacks for
>> all input pins when dyn_adc_switch is enabled and auto_mic is
>> not available.
>>
>> 2. In call_mic_autoswitch(): Add handling for dyn_adc_switch mode
>> to check imux_pins[] for jack presence, searching from back to
>> front (last inserted wins).
>>
>> 3. In mux_select(): Notify Capture Source controls after switching
>> to sync with user-space (PulseAudio/PipeWire).
>>
>> Problem description:
>> On Ubuntu 20.04 (with older PulseAudio/PipeWire):
>> - Front mic is unplugged
>> - Plug in rear mic
>> - Jack event is reported correctly
>> - Volume control (PulseAudio/PipeWire) recognizes the rear mic
>> - But alsamixer does NOT switch to rear mic
>> - Codec also does NOT perform the switch
>>
>> The root cause is that in dyn_adc_switch mode without auto_mic,
>> the jack detect callback was not properly set up to trigger the
>> mic autoswitch. The call_mic_autoswitch() function only calls
>> mic_autoswitch_hook or snd_hda_gen_mic_autoswitch(), which rely
>> on auto_mic being enabled.
>>
>> Additionally, after mux_select() performs the switch, user-space
>> (PulseAudio/PipeWire) may not be aware of the path change,
>> causing Capture Switch to show 'off'.
>>
>> This patch fixes both issues by:
>> 1. Registering jack detect callbacks for all input pins in
>> dyn_adc_switch mode
>> 2. Notifying Capture Source controls after switching
>>
>> Tested on SN6186 codec with Ubuntu 20.04 and 25.10.
>>
>> Question to the community: Is this approach correct? Should additional
>> changes be made to handle mute state preservation, or is this purely
>> a user-space issue that requires updating PulseAudio/PipeWire?
>>
>> Testing and feedback are welcome.
> I think the basic idea is OK. We can treat the auto-mic with dynamic
> ADC switches, too.
>
> But it's not clear what's the actual intent in your code changes:
>
>
>> Signed-off-by: Zhang Heng <zhangheng@kylinos.cn>
>> ---
>> sound/hda/codecs/generic.c | 82 ++++++++++++++++++++++++++++++++++++--
>> 1 file changed, 79 insertions(+), 3 deletions(-)
>>
>> diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c
>> index 660a9f2c0ded..c536de10d8b8 100644
>> --- a/sound/hda/codecs/generic.c
>> +++ b/sound/hda/codecs/generic.c
>> @@ -27,6 +27,10 @@
>> #include "hda_beep.h"
>> #include "generic.h"
>> +/* Forward declaration */
>> +static void call_mic_autoswitch(struct hda_codec *codec,
>> + struct hda_jack_callback *jack);
>> +
>> /**
>> * snd_hda_gen_spec_init - initialize hda_gen_spec struct
>> @@ -3238,6 +3242,19 @@ static int check_dyn_adc_switch(struct 
>> hda_codec *codec)
>> if (!spec->dyn_adc_switch && spec->multi_cap_vol)
>> spec->num_adc_nids = 1;
>> + /* Enable mic autoswitch for dyn_adc_switch mode when auto_mic is 
>> not available */
>> + if (!spec->auto_mic && imux->num_items > 1) {
>> + int i;
>> + for (i = 0; i < imux->num_items; i++) {
>> + hda_nid_t pin = spec->imux_pins[i];
>> + if (!is_jack_detectable(codec, pin))
>> + continue;
>> + snd_hda_jack_detect_enable_callback(codec, pin,
>> + call_mic_autoswitch);
>> + }
>> + codec_dbg(codec, "Enable mic autoswitch for input sources\n");
>> + }
> IMO, this should be rather put at auto_mic_check_imux() instead,
> something like:
>
> @@ -4789,13 +4789,15 @@ static bool auto_mic_check_imux(struct 
> hda_codec *codec)
> const struct hda_input_mux *imux;
> int i;
> - imux = &spec->input_mux;
> - for (i = 0; i < spec->am_num_entries; i++) {
> - spec->am_entry[i].idx =
> - find_idx_in_nid_list(spec->am_entry[i].pin,
> - spec->imux_pins, imux->num_items);
> - if (spec->am_entry[i].idx < 0)
> - return false; /* no corresponding imux */
> + if (!spec->dyn_adc_switch) {
> + imux = &spec->input_mux;
> + for (i = 0; i < spec->am_num_entries; i++) {
> + spec->am_entry[i].idx =
> + find_idx_in_nid_list(spec->am_entry[i].pin,
> + spec->imux_pins, imux->num_items);
> + if (spec->am_entry[i].idx < 0)
> + return false; /* no corresponding imux */
> + }
> }
> /* we don't need the jack detection for the first pin */
>
In fact, auto_ic_check_imux cannot be executed on desktop computers at 
all. You can take a look at the check_outo_ic_availability section
for (int i = 0; i < cfg->num_inputs; i++) {
     hda_nid_t nid = cfg->inputs[i].pin;
     unsigned int attr;
     attr = snd_hda_codec_get_pincfg(codec, nid);
     attr = snd_hda_get_input_pin_attr(attr);
     if (types & (1 << attr))        For desktop computers, the rear mic 
and line are an attr, which will be directly returned here.
         return 0;  /* already occupied */
}

>> return 0;
>> }
>> @@ -4107,9 +4124,38 @@ static int mux_select(struct hda_codec *codec, 
>> unsigned int adc_idx,
>> path = get_input_path(codec, adc_idx, idx);
>> if (!path)
>> return 0;
>> - if (path->active)
>> - return 0;
>> - snd_hda_activate_path(codec, path, true, false);
>> + if (path->active) {
>> + codec_err(codec, "[MUX] mux_select: path already active, skip 
>> activate but still notify\n");
>> + } else {
>> + snd_hda_activate_path(codec, path, true, false);
>> + }
>> +
>> + /* Notify Input Source / Capture Source controls that the path has 
>> changed */
>> + if (!spec->auto_mic && spec->input_mux.num_items > 1) {
>> + struct snd_card *card = codec->card;
>> + struct snd_kcontrol *kctl;
>> + struct snd_ctl_elem_id id;
>> + int notified = 0;
>> +
>> + codec_err(codec, "[MUX] mux_select: notifying controls, 
>> num_items=%d\n", spec->input_mux.num_items);
>> +
>> + if (card) {
>> + list_for_each_entry(kctl, &card->controls, list) {
>> + if (kctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
>> + continue;
>> + codec_err(codec, "[MUX] mux_select: found control '%s'\n", 
>> kctl->id.name);
>> + if (strncmp(kctl->id.name, "Capture Source", 14) == 0) {
>> + id = kctl->id;
>> + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id);
>> + codec_err(codec, "[MUX] mux_select: notified '%s'\n", kctl->id.name);
>> + notified = 1;
>> + }
>> + }
>> + }
>> + if (!notified)
>> + codec_err(codec, "[MUX] mux_select: no Input/Capture Source control 
>> found!\n");
>> + }
>> +
> Hmm, I can't follow the logic here. What does this code change try to
> achieve...?

If I open Alsamixer first without adding snd_ctl_notify, the ADC 
switching caused

by inserting and unplugging mic will not notify Alsamixer in real time, 
resulting

in asynchronous information and still retaining the state when it was 
opened.


I will create another patch for this notification later to prevent 
confusion in understanding.

>> if (spec->cap_sync_hook)
>> spec->cap_sync_hook(codec, NULL, NULL);
>> path_power_down_sync(codec, old_path);
>> @@ -4602,6 +4648,36 @@ static void call_mic_autoswitch(struct 
>> hda_codec *codec,
>> struct hda_jack_callback *jack)
>> {
>> struct hda_gen_spec *spec = codec->spec;
>> + const struct hda_input_mux *imux;
>> + int i;
>> +
>> + /* Handle dyn_adc_switch mode - use imux_pins[] */
>> + if (!spec->auto_mic && spec->dyn_adc_switch) {
>> + imux = &spec->input_mux;
>> + if (imux->num_items <= 1)
>> + goto fallback;
>> +
>> + codec_err(codec, "[AUTO_MIC] call_mic_autoswitch: dyn_adc_switch 
>> mode, checking %d inputs\n", imux->num_items);
>> +
>> + /* Search from back to front (last inserted wins) */
>> + for (i = imux->num_items - 1; i >= 0; i--) {
>> + hda_nid_t pin = spec->imux_pins[i];
>> + int det = is_jack_detectable(codec, pin);
>> + int state = snd_hda_jack_detect_state(codec, pin);
>> + codec_err(codec, "[AUTO_MIC] call_mic_autoswitch: imux[%d] 
>> pin=0x%02x, det=%d, state=%s\n",
>> + i, pin, det, state == HDA_JACK_PRESENT ? "PRESENT" : "ABSENT");
>> + if (det && state == HDA_JACK_PRESENT) {
>> + codec_err(codec, "[AUTO_MIC] call_mic_autoswitch: switching to imux 
>> %d\n", i);
>> + mux_select(codec, 0, i);
>> + return;
>> + }
>> + }
>> + /* No jack present, keep current selection (don't switch to imux 0) */
>> + codec_err(codec, "[AUTO_MIC] call_mic_autoswitch: no jack present, 
>> keeping current\n");
>> + return;
>> + }
>> +
>> +fallback:
>> if (spec->mic_autoswitch_hook)
>> spec->mic_autoswitch_hook(codec, jack);
>> else
> Ditto. Something too complex here.
> mux_select() should handle the dynamic ADC switching, per se.
>
> And, with the auto-mic mode, the driver shouldn't expose the Capture
> Source mixer control. That said, enabling the auto-mic would have a
> clear downside for the user of PA/PW, too.

I have added a lot of debugging information here, which will be 
simplified when submitting v2 later.


       reply	other threads:[~2026-05-06  7:04 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <5b215802-4c94-4f94-9cee-f8e497b31337@kylinos.cn>
2026-05-06  7:03 ` Zhang Heng [this message]
2026-05-07 15:05   ` Takashi Iwai
2026-05-08  2:21     ` Zhang Heng
2026-05-08  2:50       ` Zhang Heng
2026-05-15  9:24         ` Takashi Iwai
2026-05-15 11:19           ` Jaroslav Kysela
2026-05-15 11:29             ` Takashi Iwai
2026-05-18  7:03             ` Zhang Heng
2026-05-18  7:07             ` Zhang Heng

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a7daf8a3-559b-4532-a2ee-a211a7e0e0b0@kylinos.cn \
    --to=zhangheng@kylinos.cn \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®