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:
parent
b3a8c74522
commit
38cf6f1a41
@ -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->kctls, sizeof(struct snd_kcontrol_new), 32);
|
||||||
snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
|
snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
|
||||||
snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
|
snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
|
||||||
|
mutex_init(&spec->pcm_mutex);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init);
|
EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init);
|
||||||
@ -1484,6 +1485,79 @@ static int create_speaker_out_ctls(struct hda_codec *codec)
|
|||||||
"Speaker");
|
"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
|
* channel mode enum control
|
||||||
*/
|
*/
|
||||||
@ -2903,6 +2977,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
|
|||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
err = create_speaker_out_ctls(codec);
|
err = create_speaker_out_ctls(codec);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
err = create_indep_hp_ctls(codec);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
err = create_shared_input(codec);
|
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 snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct hda_gen_spec *spec = codec->spec;
|
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);
|
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,
|
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);
|
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
|
* Digital out
|
||||||
*/
|
*/
|
||||||
@ -3154,6 +3277,7 @@ static const struct hda_pcm_stream pcm_analog_playback = {
|
|||||||
/* NID is set in build_pcms */
|
/* NID is set in build_pcms */
|
||||||
.ops = {
|
.ops = {
|
||||||
.open = playback_pcm_open,
|
.open = playback_pcm_open,
|
||||||
|
.close = playback_pcm_close,
|
||||||
.prepare = playback_pcm_prepare,
|
.prepare = playback_pcm_prepare,
|
||||||
.cleanup = playback_pcm_cleanup
|
.cleanup = playback_pcm_cleanup
|
||||||
},
|
},
|
||||||
@ -3171,6 +3295,10 @@ static const struct hda_pcm_stream pcm_analog_alt_playback = {
|
|||||||
.channels_min = 2,
|
.channels_min = 2,
|
||||||
.channels_max = 2,
|
.channels_max = 2,
|
||||||
/* NID is set in build_pcms */
|
/* 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 = {
|
static const struct hda_pcm_stream pcm_analog_alt_capture = {
|
||||||
|
@ -65,6 +65,9 @@ struct automic_entry {
|
|||||||
unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */
|
unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* active stream id */
|
||||||
|
enum { STREAM_MULTI_OUT, STREAM_INDEP_HP };
|
||||||
|
|
||||||
struct hda_gen_spec {
|
struct hda_gen_spec {
|
||||||
char stream_name_analog[32]; /* analog PCM stream */
|
char stream_name_analog[32]; /* analog PCM stream */
|
||||||
const struct hda_pcm_stream *stream_analog_playback;
|
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_playback;
|
||||||
const struct hda_pcm_stream *stream_digital_capture;
|
const struct hda_pcm_stream *stream_digital_capture;
|
||||||
|
|
||||||
|
/* PCM */
|
||||||
|
unsigned int active_streams;
|
||||||
|
struct mutex pcm_mutex;
|
||||||
|
|
||||||
/* playback */
|
/* playback */
|
||||||
struct hda_multi_out multiout; /* playback set-up
|
struct hda_multi_out multiout; /* playback set-up
|
||||||
* max_channels, dacs must be set
|
* 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 inv_dmic_split:1; /* inverted dmic w/a for conexant */
|
||||||
unsigned int own_eapd_ctl:1; /* set EAPD by own function */
|
unsigned int own_eapd_ctl:1; /* set EAPD by own function */
|
||||||
unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */
|
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 */
|
/* for virtual master */
|
||||||
hda_nid_t vmaster_nid;
|
hda_nid_t vmaster_nid;
|
||||||
|
Loading…
Reference in New Issue
Block a user