diff options
author | Takashi Iwai <tiwai@suse.de> | 2006-09-06 08:03:14 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2006-09-23 04:46:27 -0400 |
commit | a7da6ce564a80952d9c0b210deca5a8cd3474a31 (patch) | |
tree | e0520ce4255be7a8692e39241e086334a232b6a8 /sound/pci/hda/hda_generic.c | |
parent | 160ea0dc6b86e2c0c4d325c06bf402bfdde7c1c7 (diff) |
[ALSA] hda-codec - Add independent headphone volume control
This patch addes the support of the independent 'Headphone' volume
control to the generic codec parser. Some codecs (e.g. Conexant)
have separate connections to the headphone and the independent amp
adjustment is needed.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound/pci/hda/hda_generic.c')
-rw-r--r-- | sound/pci/hda/hda_generic.c | 71 |
1 files changed, 52 insertions, 19 deletions
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index dedfc5b1083a..97e9af130b71 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c | |||
@@ -46,11 +46,18 @@ struct hda_gnode { | |||
46 | }; | 46 | }; |
47 | 47 | ||
48 | /* patch-specific record */ | 48 | /* patch-specific record */ |
49 | |||
50 | #define MAX_PCM_VOLS 2 | ||
51 | struct pcm_vol { | ||
52 | struct hda_gnode *node; /* Node for PCM volume */ | ||
53 | unsigned int index; /* connection of PCM volume */ | ||
54 | }; | ||
55 | |||
49 | struct hda_gspec { | 56 | struct hda_gspec { |
50 | struct hda_gnode *dac_node[2]; /* DAC node */ | 57 | struct hda_gnode *dac_node[2]; /* DAC node */ |
51 | struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */ | 58 | struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */ |
52 | struct hda_gnode *pcm_vol_node[2]; /* Node for PCM volume */ | 59 | struct pcm_vol pcm_vol[MAX_PCM_VOLS]; /* PCM volumes */ |
53 | unsigned int pcm_vol_index[2]; /* connection of PCM volume */ | 60 | unsigned int pcm_vol_nodes; /* number of PCM volumes */ |
54 | 61 | ||
55 | struct hda_gnode *adc_node; /* ADC node */ | 62 | struct hda_gnode *adc_node; /* ADC node */ |
56 | struct hda_gnode *cap_vol_node; /* Node for capture volume */ | 63 | struct hda_gnode *cap_vol_node; /* Node for capture volume */ |
@@ -285,9 +292,11 @@ static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec, | |||
285 | return node == spec->dac_node[dac_idx]; | 292 | return node == spec->dac_node[dac_idx]; |
286 | } | 293 | } |
287 | spec->dac_node[dac_idx] = node; | 294 | spec->dac_node[dac_idx] = node; |
288 | if (node->wid_caps & AC_WCAP_OUT_AMP) { | 295 | if ((node->wid_caps & AC_WCAP_OUT_AMP) && |
289 | spec->pcm_vol_node[dac_idx] = node; | 296 | spec->pcm_vol_nodes < MAX_PCM_VOLS) { |
290 | spec->pcm_vol_index[dac_idx] = 0; | 297 | spec->pcm_vol[spec->pcm_vol_nodes].node = node; |
298 | spec->pcm_vol[spec->pcm_vol_nodes].index = 0; | ||
299 | spec->pcm_vol_nodes++; | ||
291 | } | 300 | } |
292 | return 1; /* found */ | 301 | return 1; /* found */ |
293 | } | 302 | } |
@@ -307,13 +316,16 @@ static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec, | |||
307 | select_input_connection(codec, node, i); | 316 | select_input_connection(codec, node, i); |
308 | unmute_input(codec, node, i); | 317 | unmute_input(codec, node, i); |
309 | unmute_output(codec, node); | 318 | unmute_output(codec, node); |
310 | if (! spec->pcm_vol_node[dac_idx]) { | 319 | if (spec->dac_node[dac_idx] && |
311 | if (node->wid_caps & AC_WCAP_IN_AMP) { | 320 | spec->pcm_vol_nodes < MAX_PCM_VOLS && |
312 | spec->pcm_vol_node[dac_idx] = node; | 321 | !(spec->dac_node[dac_idx]->wid_caps & |
313 | spec->pcm_vol_index[dac_idx] = i; | 322 | AC_WCAP_OUT_AMP)) { |
314 | } else if (node->wid_caps & AC_WCAP_OUT_AMP) { | 323 | if ((node->wid_caps & AC_WCAP_IN_AMP) || |
315 | spec->pcm_vol_node[dac_idx] = node; | 324 | (node->wid_caps & AC_WCAP_OUT_AMP)) { |
316 | spec->pcm_vol_index[dac_idx] = 0; | 325 | int n = spec->pcm_vol_nodes; |
326 | spec->pcm_vol[n].node = node; | ||
327 | spec->pcm_vol[n].index = i; | ||
328 | spec->pcm_vol_nodes++; | ||
317 | } | 329 | } |
318 | } | 330 | } |
319 | return 1; | 331 | return 1; |
@@ -370,7 +382,9 @@ static struct hda_gnode *parse_output_jack(struct hda_codec *codec, | |||
370 | /* set PIN-Out enable */ | 382 | /* set PIN-Out enable */ |
371 | snd_hda_codec_write(codec, node->nid, 0, | 383 | snd_hda_codec_write(codec, node->nid, 0, |
372 | AC_VERB_SET_PIN_WIDGET_CONTROL, | 384 | AC_VERB_SET_PIN_WIDGET_CONTROL, |
373 | AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); | 385 | AC_PINCTL_OUT_EN | |
386 | ((node->pin_caps & AC_PINCAP_HP_DRV) ? | ||
387 | AC_PINCTL_HP_EN : 0)); | ||
374 | return node; | 388 | return node; |
375 | } | 389 | } |
376 | } | 390 | } |
@@ -745,22 +759,41 @@ static int check_existing_control(struct hda_codec *codec, const char *type, con | |||
745 | /* | 759 | /* |
746 | * build output mixer controls | 760 | * build output mixer controls |
747 | */ | 761 | */ |
748 | static int build_output_controls(struct hda_codec *codec) | 762 | static int create_output_mixers(struct hda_codec *codec, const char **names) |
749 | { | 763 | { |
750 | struct hda_gspec *spec = codec->spec; | 764 | struct hda_gspec *spec = codec->spec; |
751 | static const char *types[2] = { "Master", "Headphone" }; | ||
752 | int i, err; | 765 | int i, err; |
753 | 766 | ||
754 | for (i = 0; i < 2 && spec->pcm_vol_node[i]; i++) { | 767 | for (i = 0; i < spec->pcm_vol_nodes; i++) { |
755 | err = create_mixer(codec, spec->pcm_vol_node[i], | 768 | err = create_mixer(codec, spec->pcm_vol[i].node, |
756 | spec->pcm_vol_index[i], | 769 | spec->pcm_vol[i].index, |
757 | types[i], "Playback"); | 770 | names[i], "Playback"); |
758 | if (err < 0) | 771 | if (err < 0) |
759 | return err; | 772 | return err; |
760 | } | 773 | } |
761 | return 0; | 774 | return 0; |
762 | } | 775 | } |
763 | 776 | ||
777 | static int build_output_controls(struct hda_codec *codec) | ||
778 | { | ||
779 | struct hda_gspec *spec = codec->spec; | ||
780 | static const char *types_speaker[] = { "Speaker", "Headphone" }; | ||
781 | static const char *types_line[] = { "Front", "Headphone" }; | ||
782 | |||
783 | switch (spec->pcm_vol_nodes) { | ||
784 | case 1: | ||
785 | return create_mixer(codec, spec->pcm_vol[0].node, | ||
786 | spec->pcm_vol[0].index, | ||
787 | "Master", "Playback"); | ||
788 | case 2: | ||
789 | if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER) | ||
790 | return create_output_mixers(codec, types_speaker); | ||
791 | else | ||
792 | return create_output_mixers(codec, types_line); | ||
793 | } | ||
794 | return 0; | ||
795 | } | ||
796 | |||
764 | /* create capture volume/switch */ | 797 | /* create capture volume/switch */ |
765 | static int build_input_controls(struct hda_codec *codec) | 798 | static int build_input_controls(struct hda_codec *codec) |
766 | { | 799 | { |