提交 3d21d3f7 编写于 作者: T Takashi Iwai

ALSA: hda - Support auto-mic switching with IDT/STAC codec

Support the automatic mic-switching with some devices with IDT/STAC
codecs.  The condition is that the device has only two inputs, one
for an external mic and one for an internal mic.
Signed-off-by: NTakashi Iwai <tiwai@suse.de>
上级 62558ce1
...@@ -40,6 +40,7 @@ enum { ...@@ -40,6 +40,7 @@ enum {
STAC_INSERT_EVENT, STAC_INSERT_EVENT,
STAC_PWR_EVENT, STAC_PWR_EVENT,
STAC_HP_EVENT, STAC_HP_EVENT,
STAC_MIC_EVENT,
}; };
enum { enum {
...@@ -176,6 +177,12 @@ struct sigmatel_jack { ...@@ -176,6 +177,12 @@ struct sigmatel_jack {
struct snd_jack *jack; struct snd_jack *jack;
}; };
struct sigmatel_mic_route {
hda_nid_t pin;
unsigned char mux_idx;
unsigned char dmux_idx;
};
struct sigmatel_spec { struct sigmatel_spec {
struct snd_kcontrol_new *mixers[4]; struct snd_kcontrol_new *mixers[4];
unsigned int num_mixers; unsigned int num_mixers;
...@@ -187,6 +194,7 @@ struct sigmatel_spec { ...@@ -187,6 +194,7 @@ struct sigmatel_spec {
unsigned int hp_detect: 1; unsigned int hp_detect: 1;
unsigned int spdif_mute: 1; unsigned int spdif_mute: 1;
unsigned int check_volume_offset:1; unsigned int check_volume_offset:1;
unsigned int auto_mic:1;
/* gpio lines */ /* gpio lines */
unsigned int eapd_mask; unsigned int eapd_mask;
...@@ -243,6 +251,9 @@ struct sigmatel_spec { ...@@ -243,6 +251,9 @@ struct sigmatel_spec {
unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */ unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */
unsigned int num_caps; /* number of capture volume/switch elements */ unsigned int num_caps; /* number of capture volume/switch elements */
struct sigmatel_mic_route ext_mic;
struct sigmatel_mic_route int_mic;
const char **spdif_labels; const char **spdif_labels;
hda_nid_t dig_in_nid; hda_nid_t dig_in_nid;
...@@ -1304,7 +1315,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) ...@@ -1304,7 +1315,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
if (err < 0) if (err < 0)
return err; return err;
} }
if (spec->num_dmuxes > 0) { if (!spec->auto_mic && spec->num_dmuxes > 0) {
stac_dmux_mixer.count = spec->num_dmuxes; stac_dmux_mixer.count = spec->num_dmuxes;
err = snd_hda_ctl_add(codec, err = snd_hda_ctl_add(codec,
snd_ctl_new1(&stac_dmux_mixer, codec)); snd_ctl_new1(&stac_dmux_mixer, codec));
...@@ -2950,6 +2961,8 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec) ...@@ -2950,6 +2961,8 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec)
struct snd_kcontrol_new *knew; struct snd_kcontrol_new *knew;
struct hda_input_mux *imux = &spec->private_imux; struct hda_input_mux *imux = &spec->private_imux;
if (spec->auto_mic)
return 0; /* no need for input source */
if (!spec->num_adcs || imux->num_items <= 1) if (!spec->num_adcs || imux->num_items <= 1)
return 0; /* no need for input source control */ return 0; /* no need for input source control */
knew = stac_control_new(spec, &stac_input_src_temp, knew = stac_control_new(spec, &stac_input_src_temp,
...@@ -3557,14 +3570,26 @@ static const char *stac92xx_dmic_labels[5] = { ...@@ -3557,14 +3570,26 @@ static const char *stac92xx_dmic_labels[5] = {
"Digital Mic 3", "Digital Mic 4" "Digital Mic 3", "Digital Mic 4"
}; };
static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid)
{
hda_nid_t conn[HDA_MAX_NUM_INPUTS];
int i, nums;
nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
for (i = 0; i < nums; i++)
if (conn[i] == nid)
return i;
return -1;
}
/* create playback/capture controls for input pins on dmic capable codecs */ /* create playback/capture controls for input pins on dmic capable codecs */
static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg) const struct auto_pin_cfg *cfg)
{ {
struct sigmatel_spec *spec = codec->spec; struct sigmatel_spec *spec = codec->spec;
struct hda_input_mux *dimux = &spec->private_dimux; struct hda_input_mux *dimux = &spec->private_dimux;
hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; int err, i;
int err, i, j;
char name[32]; char name[32];
dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0]; dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
...@@ -3574,7 +3599,6 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, ...@@ -3574,7 +3599,6 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
for (i = 0; i < spec->num_dmics; i++) { for (i = 0; i < spec->num_dmics; i++) {
hda_nid_t nid; hda_nid_t nid;
int index; int index;
int num_cons;
unsigned int wcaps; unsigned int wcaps;
unsigned int def_conf; unsigned int def_conf;
...@@ -3583,17 +3607,10 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, ...@@ -3583,17 +3607,10 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
continue; continue;
nid = spec->dmic_nids[i]; nid = spec->dmic_nids[i];
num_cons = snd_hda_get_connections(codec, index = get_connection_index(codec, spec->dmux_nids[0], nid);
spec->dmux_nids[0], if (index < 0)
con_lst, continue;
HDA_MAX_NUM_INPUTS);
for (j = 0; j < num_cons; j++)
if (con_lst[j] == nid) {
index = j;
goto found;
}
continue;
found:
wcaps = get_wcaps(codec, nid) & wcaps = get_wcaps(codec, nid) &
(AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP); (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);
...@@ -3620,6 +3637,88 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, ...@@ -3620,6 +3637,88 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
return 0; return 0;
} }
static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *fixed, hda_nid_t *ext)
{
unsigned int cfg;
if (!nid)
return 0;
cfg = snd_hda_codec_get_pincfg(codec, nid);
switch (get_defcfg_connect(cfg)) {
case AC_JACK_PORT_FIXED:
if (*fixed)
return 1; /* already occupied */
*fixed = nid;
break;
case AC_JACK_PORT_COMPLEX:
if (*ext)
return 1; /* already occupied */
*ext = nid;
break;
}
return 0;
}
static int set_mic_route(struct hda_codec *codec,
struct sigmatel_mic_route *mic,
hda_nid_t pin)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
mic->pin = pin;
for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
if (pin == cfg->input_pins[i])
break;
if (i <= AUTO_PIN_FRONT_MIC) {
/* analog pin */
mic->dmux_idx = 0;
i = get_connection_index(codec, spec->mux_nids[0], pin);
if (i < 0)
return -1;
mic->mux_idx = i;
} else {
/* digital pin */
mic->mux_idx = 0;
i = get_connection_index(codec, spec->dmux_nids[0], pin);
if (i < 0)
return -1;
mic->dmux_idx = i;
}
return 0;
}
/* return non-zero if the device is for automatic mic switch */
static int stac_check_auto_mic(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t fixed, ext;
int i;
for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) {
if (cfg->input_pins[i])
return 0; /* must be exclusively mics */
}
fixed = ext = 0;
for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext))
return 0;
for (i = 0; i < spec->num_dmics; i++)
if (check_mic_pin(codec, spec->dmic_nids[i], &fixed, &ext))
return 0;
if (!fixed || !ext)
return 0;
if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
return 0; /* no unsol support */
if (set_mic_route(codec, &spec->ext_mic, ext) ||
set_mic_route(codec, &spec->int_mic, fixed))
return 0; /* something is wrong */
return 1;
}
/* create playback/capture controls for input pins */ /* create playback/capture controls for input pins */
static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
{ {
...@@ -3837,6 +3936,14 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out ...@@ -3837,6 +3936,14 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
spec->autocfg.line_outs = 0; spec->autocfg.line_outs = 0;
} }
if (stac_check_auto_mic(codec)) {
spec->auto_mic = 1;
/* only one capture for auto-mic */
spec->num_adcs = 1;
spec->num_caps = 1;
spec->num_muxes = 1;
}
for (i = 0; i < spec->num_caps; i++) { for (i = 0; i < spec->num_caps; i++) {
err = stac92xx_add_capvol_ctls(codec, spec->capvols[i], err = stac92xx_add_capvol_ctls(codec, spec->capvols[i],
spec->capsws[i], i); spec->capsws[i], i);
...@@ -4264,6 +4371,10 @@ static int stac92xx_init(struct hda_codec *codec) ...@@ -4264,6 +4371,10 @@ static int stac92xx_init(struct hda_codec *codec)
for (i = 0; i < cfg->hp_outs; i++) for (i = 0; i < cfg->hp_outs; i++)
stac_toggle_power_map(codec, cfg->hp_pins[i], 1); stac_toggle_power_map(codec, cfg->hp_pins[i], 1);
} }
if (spec->auto_mic) {
if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
stac_issue_unsol_event(codec, spec->ext_mic.pin);
}
for (i = 0; i < AUTO_PIN_LAST; i++) { for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = cfg->input_pins[i]; hda_nid_t nid = cfg->input_pins[i];
if (nid) { if (nid) {
...@@ -4601,6 +4712,25 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid) ...@@ -4601,6 +4712,25 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
} }
} }
static void stac92xx_mic_detect(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
struct sigmatel_mic_route *mic;
if (get_pin_presence(codec, spec->ext_mic.pin))
mic = &spec->ext_mic;
else
mic = &spec->int_mic;
if (mic->dmux_idx)
snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
AC_VERB_SET_CONNECT_SEL,
mic->dmux_idx);
else
snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0,
AC_VERB_SET_CONNECT_SEL,
mic->mux_idx);
}
static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid)
{ {
struct sigmatel_event *event = stac_get_event(codec, nid); struct sigmatel_event *event = stac_get_event(codec, nid);
...@@ -4623,7 +4753,15 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) ...@@ -4623,7 +4753,15 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
switch (event->type) { switch (event->type) {
case STAC_HP_EVENT: case STAC_HP_EVENT:
stac92xx_hp_detect(codec); stac92xx_hp_detect(codec);
/* fallthru */ break;
case STAC_MIC_EVENT:
stac92xx_mic_detect(codec);
break;
}
switch (event->type) {
case STAC_HP_EVENT:
case STAC_MIC_EVENT:
case STAC_INSERT_EVENT: case STAC_INSERT_EVENT:
case STAC_PWR_EVENT: case STAC_PWR_EVENT:
if (spec->num_pwrs > 0) if (spec->num_pwrs > 0)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册