mirror of https://gitee.com/openkylin/linux.git
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->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 = {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue