summaryrefslogtreecommitdiff
path: root/sound/pci/hda
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-12-21 14:09:42 +0100
committerTakashi Iwai <tiwai@suse.de>2013-01-12 08:42:56 +0100
commit38cf6f1a41e40a33d80924554b356fcd5b5d2751 (patch)
tree7c027e9fc6a552bea88e1738c2cbd3a153ed609a /sound/pci/hda
parentb3a8c74522ae7c992cd916c3bd4b685d742c0e2a (diff)
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>
Diffstat (limited to 'sound/pci/hda')
-rw-r--r--sound/pci/hda/hda_generic.c130
-rw-r--r--sound/pci/hda/hda_generic.h9
2 files changed, 138 insertions, 1 deletions
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index f3c6ace2c87f..ff15aea836da 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -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);
@@ -1485,6 +1486,79 @@ static int create_speaker_out_ctls(struct hda_codec *codec)
}
/*
+ * 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
*/
@@ -2905,6 +2979,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
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);
if (err < 0)
return err;
@@ -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 = {
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 85d138fc10b3..5c1569c69888 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -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;