[alsa-devel] [PATCH 0/3] Headphone Mic patches
I'm preparing upstreaming of some new hardware, which has headphone mic support, i e, you can use the headphone jack to plug in a TRS/TS mic instead of plugging in a headphone in that jack.
For this hardware, the existing hp_mic functionality won't work as the headphone and mic are on different pins.
David Henningsson (3): ALSA: hda - allow "Headphone Mic" parser flag ALSA: hda - add some small convenience functions to auto parser ALSA: hda - Handle Headphone Mic jack more generic
sound/pci/hda/hda_auto_parser.c | 68 ++++++++++++++++++++++++++++++++------- sound/pci/hda/hda_auto_parser.h | 29 +++++++++++++++-- sound/pci/hda/hda_generic.c | 12 +------ sound/pci/hda/hda_jack.c | 43 +++++++++++++++++-------- 4 files changed, 113 insertions(+), 39 deletions(-)
This allows a specific mic to get the "Headphone Mic" name, in addition to the existing "Headset Mic" name.
Also, it allows for a special mark: if the sequence number is set to 0xc, that's an indication to prefer it for headset mic, and if it's set to 0xd, that's an indication to prefer it for headphone mic.
Signed-off-by: David Henningsson david.henningsson@canonical.com --- sound/pci/hda/hda_auto_parser.c | 68 ++++++++++++++++++++++++++++++++------- sound/pci/hda/hda_auto_parser.h | 8 +++-- sound/pci/hda/hda_generic.c | 1 + 3 files changed, 62 insertions(+), 15 deletions(-)
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 6b173b3..7c11d46 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -119,6 +119,32 @@ static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin, } }
+static bool can_be_headset_mic(struct hda_codec *codec, + struct auto_pin_cfg_item *item, + int seq_number) +{ + int attr; + unsigned int def_conf; + if (item->type != AUTO_PIN_MIC) + return false; + + if (item->is_headset_mic || item->is_headphone_mic) + return false; /* Already assigned */ + + def_conf = snd_hda_codec_get_pincfg(codec, item->pin); + attr = snd_hda_get_input_pin_attr(def_conf); + if (attr <= INPUT_PIN_ATTR_DOCK) + return false; + + if (seq_number >= 0) { + int seq = get_defcfg_sequence(def_conf); + if (seq != seq_number) + return false; + } + + return true; +} + /* * Parse all pin widgets and store the useful pin nids to cfg * @@ -260,20 +286,36 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, } }
- /* Take first mic to be a headset mic pin */ - if (cond_flags & HDA_PINCFG_HEADSET_MIC) { - for (i = 0; i < cfg->num_inputs; i++) { - int attr; - unsigned int def_conf; - if (cfg->inputs[i].type != AUTO_PIN_MIC) - continue; - def_conf = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin); - attr = snd_hda_get_input_pin_attr(def_conf); - if (attr <= INPUT_PIN_ATTR_DOCK) + /* Find a pin that could be a headset or headphone mic */ + if (cond_flags & HDA_PINCFG_HEADSET_MIC || cond_flags & HDA_PINCFG_HEADPHONE_MIC) { + bool hsmic = !!(cond_flags & HDA_PINCFG_HEADSET_MIC); + bool hpmic = !!(cond_flags & HDA_PINCFG_HEADPHONE_MIC); + for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++) + if (hsmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xc)) { + cfg->inputs[i].is_headset_mic = 1; + hsmic = false; + } else if (hpmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xd)) { + cfg->inputs[i].is_headphone_mic = 1; + hpmic = false; + } + + /* If we didn't find our sequence number mark, fall back to any sequence number */ + for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++) { + if (!can_be_headset_mic(codec, &cfg->inputs[i], -1)) continue; - cfg->inputs[i].is_headset_mic = 1; - break; + if (hsmic) { + cfg->inputs[i].is_headset_mic = 1; + hsmic = false; + } else if (hpmic) { + cfg->inputs[i].is_headphone_mic = 1; + hpmic = false; + } } + + if (hsmic) + snd_printdd("Told to look for a headset mic, but didn't find any.\n"); + if (hpmic) + snd_printdd("Told to look for a headphone mic, but didn't find any.\n"); }
/* FIX-UP: @@ -419,6 +461,8 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec, case AC_JACK_MIC_IN: if (item && item->is_headset_mic) return "Headset Mic"; + if (item && item->is_headphone_mic) + return "Headphone Mic"; if (!check_location) return "Mic"; attr = snd_hda_get_input_pin_attr(def_conf); diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h index c7826ce..fe2ba7f 100644 --- a/sound/pci/hda/hda_auto_parser.h +++ b/sound/pci/hda/hda_auto_parser.h @@ -37,6 +37,7 @@ struct auto_pin_cfg_item { hda_nid_t pin; int type; unsigned int is_headset_mic:1; + unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */ };
struct auto_pin_cfg; @@ -79,9 +80,10 @@ struct auto_pin_cfg { };
/* bit-flags for snd_hda_parse_pin_def_config() behavior */ -#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */ -#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */ -#define HDA_PINCFG_HEADSET_MIC (1 << 2) /* Take first mic as headset mic */ +#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */ +#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */ +#define HDA_PINCFG_HEADSET_MIC (1 << 2) /* Try to find headset mic; mark seq number as 0xc to trigger */ +#define HDA_PINCFG_HEADPHONE_MIC (1 << 3) /* Try to find headphone mic; mark seq number as 0xd to trigger */
int snd_hda_parse_pin_defcfg(struct hda_codec *codec, struct auto_pin_cfg *cfg, diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 8299e40..d7a8eb3 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2333,6 +2333,7 @@ static int create_hp_mic(struct hda_codec *codec)
cfg->inputs[cfg->num_inputs].pin = nid; cfg->inputs[cfg->num_inputs].type = AUTO_PIN_MIC; + cfg->inputs[cfg->num_inputs].is_headphone_mic = 1; cfg->num_inputs++; spec->hp_mic = 1; spec->hp_mic_pin = nid;
I never liked that we move our speaker and hp pins to line out if there are not any line outs; but now that we do, add some convenience functions to find hp and speaker pins even if they have been moved.
Signed-off-by: David Henningsson david.henningsson@canonical.com --- sound/pci/hda/hda_auto_parser.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h index fe2ba7f..e941f60 100644 --- a/sound/pci/hda/hda_auto_parser.h +++ b/sound/pci/hda/hda_auto_parser.h @@ -94,4 +94,25 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, #define snd_hda_parse_pin_def_config(codec, cfg, ignore) \ snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
+static inline int auto_cfg_hp_outs(const struct auto_pin_cfg *cfg) +{ + return (cfg->line_out_type == AUTO_PIN_HP_OUT) ? + cfg->line_outs : cfg->hp_outs; +} +static inline const hda_nid_t *auto_cfg_hp_pins(const struct auto_pin_cfg *cfg) +{ + return (cfg->line_out_type == AUTO_PIN_HP_OUT) ? + cfg->line_out_pins : cfg->hp_pins; +} +static inline int auto_cfg_speaker_outs(const struct auto_pin_cfg *cfg) +{ + return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ? + cfg->line_outs : cfg->speaker_outs; +} +static inline const hda_nid_t *auto_cfg_speaker_pins(const struct auto_pin_cfg *cfg) +{ + return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ? + cfg->line_out_pins : cfg->speaker_pins; +} + #endif /* __SOUND_HDA_AUTO_PARSER_H */
Now that we have a flag for headphone mics, we can use that flag in the jack creation instead of creating the jack manually.
Signed-off-by: David Henningsson david.henningsson@canonical.com --- sound/pci/hda/hda_generic.c | 11 ----------- sound/pci/hda/hda_jack.c | 43 ++++++++++++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 24 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index d7a8eb3..c7b59dd 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -4366,17 +4366,6 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
free_kctls(spec); /* no longer needed */
- if (spec->hp_mic_pin) { - int err; - int nid = spec->hp_mic_pin; - err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0); - if (err < 0) - return err; - err = snd_hda_jack_detect_enable(codec, nid, 0); - if (err < 0) - return err; - } - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); if (err < 0) return err; diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 1d035ef..9e0a952 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -394,7 +394,8 @@ static int get_unique_index(struct hda_codec *codec, const char *name, int idx) }
static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg) + const struct auto_pin_cfg *cfg, + const char *base_name) { unsigned int def_conf, conn; char name[44]; @@ -410,7 +411,11 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, phantom_jack = (conn != AC_JACK_PORT_COMPLEX) || !is_jack_detectable(codec, nid);
- snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx); + if (base_name) { + strlcpy(name, base_name, sizeof(name)); + idx = 0; + } else + snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx); if (phantom_jack) /* Example final name: "Internal Mic Phantom Jack" */ strncat(name, " Phantom", sizeof(name) - strlen(name) - 1); @@ -433,39 +438,51 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, const hda_nid_t *p; int i, err;
+ for (i = 0; i < cfg->num_inputs; i++) { + /* If we have headphone mics; make sure they get the right name + before grabbed by output pins */ + if (cfg->inputs[i].is_headphone_mic) { + if (auto_cfg_hp_outs(cfg) == 1) + err = add_jack_kctl(codec, auto_cfg_hp_pins(cfg)[0], + cfg, "Headphone Mic"); + else + err = add_jack_kctl(codec, cfg->inputs[i].pin, + cfg, "Headphone Mic"); + } else + err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, + NULL); + if (err < 0) + return err; + } + for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) { - err = add_jack_kctl(codec, *p, cfg); + err = add_jack_kctl(codec, *p, cfg, NULL); if (err < 0) return err; } for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) { if (*p == *cfg->line_out_pins) /* might be duplicated */ break; - err = add_jack_kctl(codec, *p, cfg); + err = add_jack_kctl(codec, *p, cfg, NULL); if (err < 0) return err; } for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) { if (*p == *cfg->line_out_pins) /* might be duplicated */ break; - err = add_jack_kctl(codec, *p, cfg); - if (err < 0) - return err; - } - for (i = 0; i < cfg->num_inputs; i++) { - err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg); + err = add_jack_kctl(codec, *p, cfg, NULL); if (err < 0) return err; } for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) { - err = add_jack_kctl(codec, *p, cfg); + err = add_jack_kctl(codec, *p, cfg, NULL); if (err < 0) return err; } - err = add_jack_kctl(codec, cfg->dig_in_pin, cfg); + err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, NULL); if (err < 0) return err; - err = add_jack_kctl(codec, cfg->mono_out_pin, cfg); + err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, NULL); if (err < 0) return err; return 0;
At Thu, 11 Apr 2013 11:30:27 +0200, David Henningsson wrote:
I'm preparing upstreaming of some new hardware, which has headphone mic support, i e, you can use the headphone jack to plug in a TRS/TS mic instead of plugging in a headphone in that jack.
For this hardware, the existing hp_mic functionality won't work as the headphone and mic are on different pins.
Applied all three patches now.
thanks,
Takashi
David Henningsson (3): ALSA: hda - allow "Headphone Mic" parser flag ALSA: hda - add some small convenience functions to auto parser ALSA: hda - Handle Headphone Mic jack more generic
sound/pci/hda/hda_auto_parser.c | 68 ++++++++++++++++++++++++++++++++------- sound/pci/hda/hda_auto_parser.h | 29 +++++++++++++++-- sound/pci/hda/hda_generic.c | 12 +------ sound/pci/hda/hda_jack.c | 43 +++++++++++++++++-------- 4 files changed, 113 insertions(+), 39 deletions(-)
-- 1.7.9.5
participants (2)
-
David Henningsson
-
Takashi Iwai