[alsa-devel] [PATCH RFC 0/9] Final bits for HD-audio generic parser
Hi,
this is the final bits for improving / fixing the HD-audio generic parser for migrating patch_sigmatel.c. After these, patch_sigmatel.c is converted to use the generic parser, and all done!
As usual, the patches are found in sound-unstable git tree test/hda-gen-parser branch (it's already merged into master and test/hda-migrate branch, too).
Takashi
... and a little bit of code refactoring.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 73900d9..0d6c4f7 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1826,6 +1826,8 @@ get_multiio_path(struct hda_codec *codec, int idx) spec->out_paths[spec->autocfg.line_outs + idx]); }
+static void update_automute_all(struct hda_codec *codec); + static int set_multi_io(struct hda_codec *codec, int idx, bool output) { struct hda_gen_spec *spec = codec->spec; @@ -1850,9 +1852,7 @@ static int set_multi_io(struct hda_codec *codec, int idx, bool output) }
/* update jack retasking in case it modifies any of them */ - snd_hda_gen_hp_automute(codec, NULL); - snd_hda_gen_line_automute(codec, NULL); - snd_hda_gen_mic_autoswitch(codec, NULL); + update_automute_all(codec);
return 0; } @@ -3131,6 +3131,25 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *ja } EXPORT_SYMBOL_HDA(snd_hda_gen_mic_autoswitch);
+/* update jack retasking */ +static void update_automute_all(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (spec->hp_automute_hook) + spec->hp_automute_hook(codec, NULL); + else + snd_hda_gen_hp_automute(codec, NULL); + if (spec->line_automute_hook) + spec->line_automute_hook(codec, NULL); + else + snd_hda_gen_line_automute(codec, NULL); + if (spec->mic_autoswitch_hook) + spec->mic_autoswitch_hook(codec, NULL); + else + snd_hda_gen_mic_autoswitch(codec, NULL); +} + /* * Auto-Mute mode mixer enum support */ @@ -4281,9 +4300,7 @@ int snd_hda_gen_init(struct hda_codec *codec) clear_unsol_on_unused_pins(codec);
/* call init functions of standard auto-mute helpers */ - snd_hda_gen_hp_automute(codec, NULL); - snd_hda_gen_line_automute(codec, NULL); - snd_hda_gen_mic_autoswitch(codec, NULL); + update_automute_all(codec);
snd_hda_codec_flush_amp_cache(codec); snd_hda_codec_flush_cmd_cache(codec);
... to be referred by the codec driver.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 2 ++ sound/pci/hda/hda_generic.h | 2 ++ 2 files changed, 4 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 0d6c4f7..186a554 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3047,6 +3047,7 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec) else on = spec->hp_jack_present | spec->line_jack_present; on |= spec->master_mute; + spec->speaker_muted = on; do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), spec->autocfg.speaker_pins, on);
@@ -3060,6 +3061,7 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec) else on = spec->hp_jack_present; on |= spec->master_mute; + spec->line_out_muted = on; do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), spec->autocfg.line_out_pins, on); } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 6ba5805..d4abf87 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -170,6 +170,8 @@ struct hda_gen_spec { unsigned int auto_mic:1; unsigned int automute_speaker:1; /* automute speaker outputs */ unsigned int automute_lo:1; /* automute LO outputs */ + unsigned int speaker_muted:1; /* current status of speaker mute */ + unsigned int line_out_muted:1; /* current status of LO mute */ unsigned int detect_hp:1; /* Headphone detection enabled */ unsigned int detect_lo:1; /* Line-out detection enabled */ unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */
A new flag to skip the auto-mute handling in the generic parser, just like suppress_auto_mic flag. It has to be set before calling snd_hda_gen_parse_auto_config().
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 6 ++++++ sound/pci/hda/hda_generic.h | 1 + 2 files changed, 7 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 186a554..1ea9c2d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -119,6 +119,9 @@ static void parse_user_hints(struct hda_codec *codec) if (val >= 0) codec->single_adc_amp = !!val;
+ val = snd_hda_get_bool_hint(codec, "auto_mute"); + if (val >= 0) + spec->suppress_auto_mute = !val; val = snd_hda_get_bool_hint(codec, "auto_mic"); if (val >= 0) spec->suppress_auto_mic = !val; @@ -3253,6 +3256,9 @@ static int check_auto_mute_availability(struct hda_codec *codec) int present = 0; int i, err;
+ if (spec->suppress_auto_mute) + return 0; + if (cfg->hp_pins[0]) present++; if (cfg->line_out_pins[0]) diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index d4abf87..59d08c6 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -177,6 +177,7 @@ struct hda_gen_spec { unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ unsigned int automute_lo_possible:1; /* there are line outs and HP */ unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ + unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */ unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */ unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
Since we have many bit flags in hda_gen_spec, rearrange in sections and give more comments there.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.h | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 59d08c6..dd0818b 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -164,27 +164,34 @@ struct hda_gen_spec { struct automic_entry am_entry[MAX_AUTO_MIC_PINS];
/* for pin sensing */ + /* current status; set in hda_geneic.c */ unsigned int hp_jack_present:1; unsigned int line_jack_present:1; - unsigned int master_mute:1; + unsigned int speaker_muted:1; /* current status of speaker mute */ + unsigned int line_out_muted:1; /* current status of LO mute */ + + /* internal states of automute / autoswitch behavior */ unsigned int auto_mic:1; unsigned int automute_speaker:1; /* automute speaker outputs */ unsigned int automute_lo:1; /* automute LO outputs */ - unsigned int speaker_muted:1; /* current status of speaker mute */ - unsigned int line_out_muted:1; /* current status of LO mute */ + + /* capabilities detected by parser */ unsigned int detect_hp:1; /* Headphone detection enabled */ unsigned int detect_lo:1; /* Line-out detection enabled */ unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ unsigned int automute_lo_possible:1; /* there are line outs and HP */ + + /* additional parameters set by codec drivers */ + unsigned int master_mute:1; /* master mute over all */ unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ + unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ + + /* parser behavior flags; set before snd_hda_gen_parse_auto_config() */ unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */ unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */ - unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
- /* other flags */ + /* other parse behavior flags */ unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */ - unsigned int no_analog:1; /* digital I/O only */ - unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ @@ -192,11 +199,15 @@ struct hda_gen_spec { unsigned int own_eapd_ctl:1; /* set EAPD by own function */ unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */ unsigned int indep_hp:1; /* independent HP supported */ - unsigned int indep_hp_enabled:1; /* independent HP enabled */ unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */ unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */
+ /* other internal flags */ + unsigned int no_analog:1; /* digital I/O only */ + unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ + unsigned int indep_hp_enabled:1; /* independent HP enabled */ + /* loopback mixing mode */ bool aamix_mode;
Just like the jack mode enum ctls for output jacks, add the support for similar enum ctls for input pins to control the bias Vref. The new controls will be added when spec->add_in_jack_modes is set either by the codec driver or by a hint string.
Note that ground and 100% vrefs are excluded from the list for simplicity, currently. We may add a new flag to allow them, too. But I guess it's easier to put a value override in the pinfix in such a case.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 140 ++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_generic.h | 1 + 2 files changed, 141 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1ea9c2d..878556b 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -26,6 +26,7 @@ #include <linux/sort.h> #include <linux/ctype.h> #include <linux/string.h> +#include <linux/bitops.h> #include <sound/core.h> #include <sound/jack.h> #include "hda_codec.h" @@ -149,6 +150,9 @@ static void parse_user_hints(struct hda_codec *codec) val = snd_hda_get_bool_hint(codec, "add_out_jack_modes"); if (val >= 0) spec->add_out_jack_modes = !!val; + val = snd_hda_get_bool_hint(codec, "add_in_jack_modes"); + if (val >= 0) + spec->add_in_jack_modes = !!val;
if (!snd_hda_get_int_hint(codec, "mixer_nid", &val)) spec->mixer_nid = val; @@ -2138,6 +2142,136 @@ static int create_out_jack_modes(struct hda_codec *codec, int num_pins, return 0; }
+/* + * input jack mode + */ + +/* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */ +#define NUM_VREFS 6 + +static const char * const vref_texts[NUM_VREFS] = { + "Line In", "Mic 50pc Bias", "Mic 0V Bias", + "", "Mic 80pc Bias", "Mic 100pc Bias" +}; + +static unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin) +{ + unsigned int pincap; + + pincap = snd_hda_query_pin_caps(codec, pin); + pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; + /* filter out unusual vrefs */ + pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100); + return pincap; +} + +/* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */ +static int get_vref_idx(unsigned int vref_caps, unsigned int item_idx) +{ + unsigned int i, n = 0; + + for (i = 0; i < NUM_VREFS; i++) { + if (vref_caps & (1 << i)) { + if (n == item_idx) + return i; + n++; + } + } + return 0; +} + +/* convert back from the vref ctl index to the enum item index */ +static int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx) +{ + unsigned int i, n = 0; + + for (i = 0; i < NUM_VREFS; i++) { + if (i == idx) + return n; + if (vref_caps & (1 << i)) + n++; + } + return 0; +} + +static int in_jack_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int vref_caps = get_vref_caps(codec, nid); + + snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps), + vref_texts); + /* set the right text */ + strcpy(uinfo->value.enumerated.name, + vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]); + return 0; +} + +static int in_jack_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int vref_caps = get_vref_caps(codec, nid); + unsigned int idx; + + idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN; + ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx); + return 0; +} + +static int in_jack_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int vref_caps = get_vref_caps(codec, nid); + unsigned int val, idx; + + val = snd_hda_codec_get_pin_target(codec, nid); + idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN); + if (idx == ucontrol->value.enumerated.item[0]) + return 0; + + val &= ~AC_PINCTL_VREFEN; + val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]); + snd_hda_set_pin_ctl_cache(codec, nid, val); + return 1; +} + +static const struct snd_kcontrol_new in_jack_mode_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = in_jack_mode_info, + .get = in_jack_mode_get, + .put = in_jack_mode_put, +}; + +static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) +{ + struct hda_gen_spec *spec = codec->spec; + unsigned int defcfg; + struct snd_kcontrol_new *knew; + char name[44]; + + /* no jack mode for fixed pins */ + defcfg = snd_hda_codec_get_pincfg(codec, pin); + if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) + return 0; + + /* no multiple vref caps? */ + if (hweight32(get_vref_caps(codec, pin)) <= 1) + return 0; + + get_jack_mode_name(codec, pin, name, sizeof(name)); + knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum); + if (!knew) + return -ENOMEM; + knew->private_value = pin; + return 0; +} +
/* * Parse input paths @@ -2392,6 +2526,12 @@ static int create_input_ctls(struct hda_codec *codec) err = parse_capture_source(codec, pin, num_adcs, label, -mixer); if (err < 0) return err; + + if (spec->add_in_jack_modes) { + err = create_in_jack_mode(codec, pin); + if (err < 0) + return err; + } }
if (mixer && spec->add_stereo_mix_input) { diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index dd0818b..142a571 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -202,6 +202,7 @@ struct hda_gen_spec { unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */ unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */ + unsigned int add_in_jack_modes:1; /* add input jack mode enum ctls */
/* other internal flags */ unsigned int no_analog:1; /* digital I/O only */
Add vmaster_tlv[] to hda_gen_spec and store the suggested TLV data in snd_hda_gen_parse_auto_config(). This allows the codec driver to correct the TLV data (e.g. mute capability) before actually creating vmaster instance.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 8 ++++---- sound/pci/hda/hda_generic.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 878556b..9c06749 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1601,6 +1601,9 @@ static int parse_output_paths(struct hda_codec *codec) path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]); if (path) spec->vmaster_nid = look_for_out_vol_nid(codec, path); + if (spec->vmaster_nid) + snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, + HDA_OUTPUT, spec->vmaster_tlv); }
kfree(best_cfg); @@ -3752,11 +3755,8 @@ int snd_hda_gen_build_controls(struct hda_codec *codec) /* if we have no master control, let's create it */ if (!spec->no_analog && !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - unsigned int vmaster_tlv[4]; - snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, vmaster_tlv); err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, slave_pfxs, + spec->vmaster_tlv, slave_pfxs, "Playback Volume"); if (err < 0) return err; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 142a571..43a8482 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -214,6 +214,7 @@ struct hda_gen_spec {
/* for virtual master */ hda_nid_t vmaster_nid; + unsigned int vmaster_tlv[4]; struct hda_vmaster_mute_hook vmaster_mute; #ifdef CONFIG_PM struct hda_loopback_check loopback;
Since the generic parser reduces the ADC list, copy the list of the all detected ADCs and keep it.
This list can be later referred by the codec driver for finer power controls.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 5 +++++ sound/pci/hda/hda_generic.h | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 9c06749..fc3f241 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2368,6 +2368,11 @@ static int fill_adc_nids(struct hda_codec *codec) break; } spec->num_adc_nids = nums; + + /* copy the detected ADCs to all_adcs[] */ + spec->num_all_adcs = nums; + memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t)); + return nums; }
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 43a8482..a0486b1 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -142,9 +142,11 @@ struct hda_gen_spec { unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; hda_nid_t shared_mic_vref_pin;
- /* DAC list */ + /* DAC/ADC lists */ int num_all_dacs; hda_nid_t all_dacs[16]; + int num_all_adcs; + hda_nid_t all_adcs[AUTO_CFG_MAX_OUTS];
/* path list */ struct snd_array paths;
Not only PCM playback, a hook for PCM capture would be required for power controls in codec drivers.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 63 +++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_generic.h | 6 ++++- 2 files changed, 68 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index fc3f241..f946714 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3815,6 +3815,16 @@ static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo, spec->pcm_playback_hook(hinfo, codec, substream, action); }
+static void call_pcm_capture_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + struct hda_gen_spec *spec = codec->spec; + if (spec->pcm_capture_hook) + spec->pcm_capture_hook(hinfo, codec, substream, action); +} + /* * Analog playback callbacks */ @@ -3882,6 +3892,44 @@ static int playback_pcm_close(struct hda_pcm_stream *hinfo, return 0; }
+static int capture_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN); + return 0; +} + +static int capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + call_pcm_capture_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_PREPARE); + return 0; +} + +static int capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + call_pcm_capture_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_CLEANUP); + return 0; +} + +static int capture_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE); + return 0; +} + static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) @@ -3976,6 +4024,9 @@ static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo, /* * Analog capture */ +#define alt_capture_pcm_open capture_pcm_open +#define alt_capture_pcm_close capture_pcm_close + static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, @@ -3986,6 +4037,8 @@ static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], stream_tag, 0, format); + call_pcm_capture_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_PREPARE); return 0; }
@@ -3997,6 +4050,8 @@ static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number + 1]); + call_pcm_capture_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_CLEANUP); return 0; }
@@ -4020,6 +4075,12 @@ static const struct hda_pcm_stream pcm_analog_capture = { .channels_min = 2, .channels_max = 2, /* NID is set in build_pcms */ + .ops = { + .open = capture_pcm_open, + .close = capture_pcm_close, + .prepare = capture_pcm_prepare, + .cleanup = capture_pcm_cleanup + }, };
static const struct hda_pcm_stream pcm_analog_alt_playback = { @@ -4041,6 +4102,8 @@ static const struct hda_pcm_stream pcm_analog_alt_capture = { .channels_max = 2, /* NID is set in build_pcms */ .ops = { + .open = alt_capture_pcm_open, + .close = alt_capture_pcm_close, .prepare = alt_capture_pcm_prepare, .cleanup = alt_capture_pcm_cleanup }, diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index a0486b1..7b14e9c 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -233,11 +233,15 @@ struct hda_gen_spec { void (*automute_hook)(struct hda_codec *codec); void (*cap_sync_hook)(struct hda_codec *codec);
- /* PCM playback hook */ + /* PCM hooks */ void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream, int action); + void (*pcm_capture_hook)(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action);
/* automute / autoswitch hooks */ void (*hp_automute_hook)(struct hda_codec *codec,
When the volume or mute control of the primary output is shared with other (headphone or speaker) outputs, we shouldn't name it as a specific output type but rather name it with the channel name or a generic name like "PCM".
Also, this check should be performed individually for the volume and the mute controls because some codecs may have shared volumes but separate mute controls.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 59 ++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 17 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f946714..ef4c04a 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -825,19 +825,27 @@ static int add_stereo_sw(struct hda_codec *codec, const char *pfx, return add_sw_ctl(codec, pfx, cidx, chs, path); }
+/* any ctl assigned to the path with the given index? */ +static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type) +{ + struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx); + return path && path->ctls[ctl_type]; +} + static const char * const channel_name[4] = { "Front", "Surround", "CLFE", "Side" };
/* give some appropriate ctl name prefix for the given line out channel */ -static const char *get_line_out_pfx(struct hda_gen_spec *spec, int ch, - bool can_be_master, int *index) +static const char *get_line_out_pfx(struct hda_codec *codec, int ch, + int *index, int ctl_type) { + struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg;
*index = 0; if (cfg->line_outs == 1 && !spec->multi_ios && - !cfg->hp_outs && !cfg->speaker_outs && can_be_master) + !cfg->hp_outs && !cfg->speaker_outs) return spec->vmaster_mute.hook ? "PCM" : "Master";
/* if there is really a single DAC used in the whole output paths, @@ -847,24 +855,41 @@ static const char *get_line_out_pfx(struct hda_gen_spec *spec, int ch, !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) return spec->vmaster_mute.hook ? "PCM" : "Master";
+ /* multi-io channels */ + if (ch >= cfg->line_outs) + return channel_name[ch]; + switch (cfg->line_out_type) { case AUTO_PIN_SPEAKER_OUT: + /* if the primary channel vol/mute is shared with HP volume, + * don't name it as Speaker + */ + if (!ch && cfg->hp_outs && + !path_has_mixer(codec, spec->hp_paths[0], ctl_type)) + break; if (cfg->line_outs == 1) return "Speaker"; if (cfg->line_outs == 2) return ch ? "Bass Speaker" : "Speaker"; break; case AUTO_PIN_HP_OUT: + /* if the primary channel vol/mute is shared with spk volume, + * don't name it as Headphone + */ + if (!ch && cfg->speaker_outs && + !path_has_mixer(codec, spec->speaker_paths[0], ctl_type)) + break; /* for multi-io case, only the primary out */ if (ch && spec->multi_ios) break; *index = ch; return "Headphone"; - default: - if (cfg->line_outs == 1 && !spec->multi_ios) - return "PCM"; - break; } + + /* for a single channel output, we don't have to name the channel */ + if (cfg->line_outs == 1 && !spec->multi_ios) + return "PCM"; + if (ch >= ARRAY_SIZE(channel_name)) { snd_BUG(); return "PCM"; @@ -1626,16 +1651,11 @@ static int create_multi_out_ctls(struct hda_codec *codec, int index; struct nid_path *path;
- if (i >= cfg->line_outs) { - index = 0; - name = channel_name[i]; - } else { - name = get_line_out_pfx(spec, i, true, &index); - } - path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); if (!path) continue; + + name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL); if (!name || !strcmp(name, "CLFE")) { /* Center/LFE */ err = add_vol_ctl(codec, "Center", 0, 1, path); @@ -1644,6 +1664,14 @@ static int create_multi_out_ctls(struct hda_codec *codec, err = add_vol_ctl(codec, "LFE", 0, 2, path); if (err < 0) return err; + } else { + err = add_stereo_vol(codec, name, index, path); + if (err < 0) + return err; + } + + name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL); + if (!name || !strcmp(name, "CLFE")) { err = add_sw_ctl(codec, "Center", 0, 1, path); if (err < 0) return err; @@ -1651,9 +1679,6 @@ static int create_multi_out_ctls(struct hda_codec *codec, if (err < 0) return err; } else { - err = add_stereo_vol(codec, name, index, path); - if (err < 0) - return err; err = add_stereo_sw(codec, name, index, path); if (err < 0) return err;
participants (1)
-
Takashi Iwai