ALSA: hda - Implement independent HP control

Similar like the implementation in patch_analog.c and patch_via.c,
the generic parser can provide the independent HP PCM stream now.
It's enabled when spec->indep_hp is set by the caller while parsing.

Currently no dynamic PCM switching as in patch_via.c is implemented
yet.  The control returns -EBUSY when the value is changed during PCM
operations.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2012-12-21 14:09:42 +01:00
parent b3a8c74522
commit 38cf6f1a41
2 changed files with 138 additions and 1 deletions

View File

@ -41,6 +41,7 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec)
snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
mutex_init(&spec->pcm_mutex);
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init);
@ -1484,6 +1485,79 @@ static int create_speaker_out_ctls(struct hda_codec *codec)
"Speaker");
}
/*
* independent HP controls
*/
static int indep_hp_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
}
static int indep_hp_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_gen_spec *spec = codec->spec;
ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled;
return 0;
}
static int indep_hp_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_gen_spec *spec = codec->spec;
unsigned int select = ucontrol->value.enumerated.item[0];
int ret = 0;
mutex_lock(&spec->pcm_mutex);
if (spec->active_streams) {
ret = -EBUSY;
goto unlock;
}
if (spec->indep_hp_enabled != select) {
spec->indep_hp_enabled = select;
if (spec->indep_hp_enabled)
spec->multiout.hp_out_nid[0] = 0;
else
spec->multiout.hp_out_nid[0] = spec->alt_dac_nid;
ret = 1;
}
unlock:
mutex_unlock(&spec->pcm_mutex);
return ret;
}
static const struct snd_kcontrol_new indep_hp_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Independent HP",
.info = indep_hp_info,
.get = indep_hp_get,
.put = indep_hp_put,
};
static int create_indep_hp_ctls(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;
if (!spec->indep_hp)
return 0;
if (!spec->multiout.hp_out_nid[0]) {
spec->indep_hp = 0;
return 0;
}
spec->indep_hp_enabled = false;
spec->alt_dac_nid = spec->multiout.hp_out_nid[0];
if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl))
return -ENOMEM;
return 0;
}
/*
* channel mode enum control
*/
@ -2903,6 +2977,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
if (err < 0)
return err;
err = create_speaker_out_ctls(codec);
if (err < 0)
return err;
err = create_indep_hp_ctls(codec);
if (err < 0)
return err;
err = create_shared_input(codec);
@ -3057,8 +3134,16 @@ static int playback_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
int err;
mutex_lock(&spec->pcm_mutex);
err = snd_hda_multi_out_analog_open(codec,
&spec->multiout, substream,
hinfo);
if (!err)
spec->active_streams |= 1 << STREAM_MULTI_OUT;
mutex_unlock(&spec->pcm_mutex);
return err;
}
static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@ -3080,6 +3165,44 @@ static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
}
static int playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
mutex_lock(&spec->pcm_mutex);
spec->active_streams &= ~(1 << STREAM_MULTI_OUT);
mutex_unlock(&spec->pcm_mutex);
return 0;
}
static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
int err = 0;
mutex_lock(&spec->pcm_mutex);
if (!spec->indep_hp_enabled)
err = -EBUSY;
else
spec->active_streams |= 1 << STREAM_INDEP_HP;
mutex_unlock(&spec->pcm_mutex);
return err;
}
static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
mutex_lock(&spec->pcm_mutex);
spec->active_streams &= ~(1 << STREAM_INDEP_HP);
mutex_unlock(&spec->pcm_mutex);
return 0;
}
/*
* Digital out
*/
@ -3154,6 +3277,7 @@ static const struct hda_pcm_stream pcm_analog_playback = {
/* NID is set in build_pcms */
.ops = {
.open = playback_pcm_open,
.close = playback_pcm_close,
.prepare = playback_pcm_prepare,
.cleanup = playback_pcm_cleanup
},
@ -3171,6 +3295,10 @@ static const struct hda_pcm_stream pcm_analog_alt_playback = {
.channels_min = 2,
.channels_max = 2,
/* NID is set in build_pcms */
.ops = {
.open = alt_playback_pcm_open,
.close = alt_playback_pcm_close
},
};
static const struct hda_pcm_stream pcm_analog_alt_capture = {

View File

@ -65,6 +65,9 @@ struct automic_entry {
unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */
};
/* active stream id */
enum { STREAM_MULTI_OUT, STREAM_INDEP_HP };
struct hda_gen_spec {
char stream_name_analog[32]; /* analog PCM stream */
const struct hda_pcm_stream *stream_analog_playback;
@ -76,6 +79,10 @@ struct hda_gen_spec {
const struct hda_pcm_stream *stream_digital_playback;
const struct hda_pcm_stream *stream_digital_capture;
/* PCM */
unsigned int active_streams;
struct mutex pcm_mutex;
/* playback */
struct hda_multi_out multiout; /* playback set-up
* max_channels, dacs must be set
@ -150,6 +157,8 @@ struct hda_gen_spec {
unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
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 */
/* for virtual master */
hda_nid_t vmaster_nid;