aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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)
41 snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); 41 snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
42 snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); 42 snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
43 snd_array_init(&spec->paths, sizeof(struct nid_path), 8); 43 snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
44 mutex_init(&spec->pcm_mutex);
44 return 0; 45 return 0;
45} 46}
46EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init); 47EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init);
@@ -1485,6 +1486,79 @@ static int create_speaker_out_ctls(struct hda_codec *codec)
1485} 1486}
1486 1487
1487/* 1488/*
1489 * independent HP controls
1490 */
1491
1492static int indep_hp_info(struct snd_kcontrol *kcontrol,
1493 struct snd_ctl_elem_info *uinfo)
1494{
1495 return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
1496}
1497
1498static int indep_hp_get(struct snd_kcontrol *kcontrol,
1499 struct snd_ctl_elem_value *ucontrol)
1500{
1501 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1502 struct hda_gen_spec *spec = codec->spec;
1503 ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled;
1504 return 0;
1505}
1506
1507static int indep_hp_put(struct snd_kcontrol *kcontrol,
1508 struct snd_ctl_elem_value *ucontrol)
1509{
1510 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1511 struct hda_gen_spec *spec = codec->spec;
1512 unsigned int select = ucontrol->value.enumerated.item[0];
1513 int ret = 0;
1514
1515 mutex_lock(&spec->pcm_mutex);
1516 if (spec->active_streams) {
1517 ret = -EBUSY;
1518 goto unlock;
1519 }
1520
1521 if (spec->indep_hp_enabled != select) {
1522 spec->indep_hp_enabled = select;
1523 if (spec->indep_hp_enabled)
1524 spec->multiout.hp_out_nid[0] = 0;
1525 else
1526 spec->multiout.hp_out_nid[0] = spec->alt_dac_nid;
1527 ret = 1;
1528 }
1529 unlock:
1530 mutex_unlock(&spec->pcm_mutex);
1531 return ret;
1532}
1533
1534static const struct snd_kcontrol_new indep_hp_ctl = {
1535 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1536 .name = "Independent HP",
1537 .info = indep_hp_info,
1538 .get = indep_hp_get,
1539 .put = indep_hp_put,
1540};
1541
1542
1543static int create_indep_hp_ctls(struct hda_codec *codec)
1544{
1545 struct hda_gen_spec *spec = codec->spec;
1546
1547 if (!spec->indep_hp)
1548 return 0;
1549 if (!spec->multiout.hp_out_nid[0]) {
1550 spec->indep_hp = 0;
1551 return 0;
1552 }
1553
1554 spec->indep_hp_enabled = false;
1555 spec->alt_dac_nid = spec->multiout.hp_out_nid[0];
1556 if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl))
1557 return -ENOMEM;
1558 return 0;
1559}
1560
1561/*
1488 * channel mode enum control 1562 * channel mode enum control
1489 */ 1563 */
1490 1564
@@ -2905,6 +2979,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
2905 err = create_speaker_out_ctls(codec); 2979 err = create_speaker_out_ctls(codec);
2906 if (err < 0) 2980 if (err < 0)
2907 return err; 2981 return err;
2982 err = create_indep_hp_ctls(codec);
2983 if (err < 0)
2984 return err;
2908 err = create_shared_input(codec); 2985 err = create_shared_input(codec);
2909 if (err < 0) 2986 if (err < 0)
2910 return err; 2987 return err;
@@ -3057,8 +3134,16 @@ static int playback_pcm_open(struct hda_pcm_stream *hinfo,
3057 struct snd_pcm_substream *substream) 3134 struct snd_pcm_substream *substream)
3058{ 3135{
3059 struct hda_gen_spec *spec = codec->spec; 3136 struct hda_gen_spec *spec = codec->spec;
3060 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 3137 int err;
3138
3139 mutex_lock(&spec->pcm_mutex);
3140 err = snd_hda_multi_out_analog_open(codec,
3141 &spec->multiout, substream,
3061 hinfo); 3142 hinfo);
3143 if (!err)
3144 spec->active_streams |= 1 << STREAM_MULTI_OUT;
3145 mutex_unlock(&spec->pcm_mutex);
3146 return err;
3062} 3147}
3063 3148
3064static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, 3149static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -3080,6 +3165,44 @@ static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
3080 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 3165 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
3081} 3166}
3082 3167
3168static int playback_pcm_close(struct hda_pcm_stream *hinfo,
3169 struct hda_codec *codec,
3170 struct snd_pcm_substream *substream)
3171{
3172 struct hda_gen_spec *spec = codec->spec;
3173 mutex_lock(&spec->pcm_mutex);
3174 spec->active_streams &= ~(1 << STREAM_MULTI_OUT);
3175 mutex_unlock(&spec->pcm_mutex);
3176 return 0;
3177}
3178
3179static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
3180 struct hda_codec *codec,
3181 struct snd_pcm_substream *substream)
3182{
3183 struct hda_gen_spec *spec = codec->spec;
3184 int err = 0;
3185
3186 mutex_lock(&spec->pcm_mutex);
3187 if (!spec->indep_hp_enabled)
3188 err = -EBUSY;
3189 else
3190 spec->active_streams |= 1 << STREAM_INDEP_HP;
3191 mutex_unlock(&spec->pcm_mutex);
3192 return err;
3193}
3194
3195static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
3196 struct hda_codec *codec,
3197 struct snd_pcm_substream *substream)
3198{
3199 struct hda_gen_spec *spec = codec->spec;
3200 mutex_lock(&spec->pcm_mutex);
3201 spec->active_streams &= ~(1 << STREAM_INDEP_HP);
3202 mutex_unlock(&spec->pcm_mutex);
3203 return 0;
3204}
3205
3083/* 3206/*
3084 * Digital out 3207 * Digital out
3085 */ 3208 */
@@ -3154,6 +3277,7 @@ static const struct hda_pcm_stream pcm_analog_playback = {
3154 /* NID is set in build_pcms */ 3277 /* NID is set in build_pcms */
3155 .ops = { 3278 .ops = {
3156 .open = playback_pcm_open, 3279 .open = playback_pcm_open,
3280 .close = playback_pcm_close,
3157 .prepare = playback_pcm_prepare, 3281 .prepare = playback_pcm_prepare,
3158 .cleanup = playback_pcm_cleanup 3282 .cleanup = playback_pcm_cleanup
3159 }, 3283 },
@@ -3171,6 +3295,10 @@ static const struct hda_pcm_stream pcm_analog_alt_playback = {
3171 .channels_min = 2, 3295 .channels_min = 2,
3172 .channels_max = 2, 3296 .channels_max = 2,
3173 /* NID is set in build_pcms */ 3297 /* NID is set in build_pcms */
3298 .ops = {
3299 .open = alt_playback_pcm_open,
3300 .close = alt_playback_pcm_close
3301 },
3174}; 3302};
3175 3303
3176static const struct hda_pcm_stream pcm_analog_alt_capture = { 3304static 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 {
65 unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */ 65 unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */
66}; 66};
67 67
68/* active stream id */
69enum { STREAM_MULTI_OUT, STREAM_INDEP_HP };
70
68struct hda_gen_spec { 71struct hda_gen_spec {
69 char stream_name_analog[32]; /* analog PCM stream */ 72 char stream_name_analog[32]; /* analog PCM stream */
70 const struct hda_pcm_stream *stream_analog_playback; 73 const struct hda_pcm_stream *stream_analog_playback;
@@ -76,6 +79,10 @@ struct hda_gen_spec {
76 const struct hda_pcm_stream *stream_digital_playback; 79 const struct hda_pcm_stream *stream_digital_playback;
77 const struct hda_pcm_stream *stream_digital_capture; 80 const struct hda_pcm_stream *stream_digital_capture;
78 81
82 /* PCM */
83 unsigned int active_streams;
84 struct mutex pcm_mutex;
85
79 /* playback */ 86 /* playback */
80 struct hda_multi_out multiout; /* playback set-up 87 struct hda_multi_out multiout; /* playback set-up
81 * max_channels, dacs must be set 88 * max_channels, dacs must be set
@@ -150,6 +157,8 @@ struct hda_gen_spec {
150 unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ 157 unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
151 unsigned int own_eapd_ctl:1; /* set EAPD by own function */ 158 unsigned int own_eapd_ctl:1; /* set EAPD by own function */
152 unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */ 159 unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */
160 unsigned int indep_hp:1; /* independent HP supported */
161 unsigned int indep_hp_enabled:1; /* independent HP enabled */
153 162
154 /* for virtual master */ 163 /* for virtual master */
155 hda_nid_t vmaster_nid; 164 hda_nid_t vmaster_nid;