diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-12-21 08:09:42 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2013-01-12 02:42:56 -0500 |
commit | 38cf6f1a41e40a33d80924554b356fcd5b5d2751 (patch) | |
tree | 7c027e9fc6a552bea88e1738c2cbd3a153ed609a | |
parent | b3a8c74522ae7c992cd916c3bd4b685d742c0e2a (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>
-rw-r--r-- | sound/pci/hda/hda_generic.c | 130 | ||||
-rw-r--r-- | sound/pci/hda/hda_generic.h | 9 |
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 | } |
46 | EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init); | 47 | EXPORT_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 | |||
1492 | static 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 | |||
1498 | static 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 | |||
1507 | static 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 | |||
1534 | static 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 | |||
1543 | static 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 | ||
3064 | static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, | 3149 | static 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 | ||
3168 | static 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 | |||
3179 | static 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 | |||
3195 | static 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 | ||
3176 | static const struct hda_pcm_stream pcm_analog_alt_capture = { | 3304 | 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 { | |||
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 */ | ||
69 | enum { STREAM_MULTI_OUT, STREAM_INDEP_HP }; | ||
70 | |||
68 | struct hda_gen_spec { | 71 | struct 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; |