diff options
author | Jeff Garzik <jeff@garzik.org> | 2006-09-24 01:52:47 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-09-24 01:52:47 -0400 |
commit | 23930fa1cebfea6f79881c588ccd1b0781e49e3f (patch) | |
tree | 36d29e3f83661c4f5f45b6f74ac0d5f9886867a8 /sound/pci/hda/hda_generic.c | |
parent | 36b35a5be0e4b406acd816e2122d153e875105be (diff) | |
parent | 4f5537de7c1531398e84e18a24f667e49cc94208 (diff) |
Merge branch 'master' into upstream
Diffstat (limited to 'sound/pci/hda/hda_generic.c')
-rw-r--r-- | sound/pci/hda/hda_generic.c | 199 |
1 files changed, 142 insertions, 57 deletions
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 85ad164ada59..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 | } |
@@ -461,14 +475,19 @@ static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl) | |||
461 | return "Front Line"; | 475 | return "Front Line"; |
462 | return "Line"; | 476 | return "Line"; |
463 | case AC_JACK_CD: | 477 | case AC_JACK_CD: |
478 | #if 0 | ||
464 | if (pinctl) | 479 | if (pinctl) |
465 | *pinctl |= AC_PINCTL_VREF_GRD; | 480 | *pinctl |= AC_PINCTL_VREF_GRD; |
481 | #endif | ||
466 | return "CD"; | 482 | return "CD"; |
467 | case AC_JACK_AUX: | 483 | case AC_JACK_AUX: |
468 | if ((location & 0x0f) == AC_JACK_LOC_FRONT) | 484 | if ((location & 0x0f) == AC_JACK_LOC_FRONT) |
469 | return "Front Aux"; | 485 | return "Front Aux"; |
470 | return "Aux"; | 486 | return "Aux"; |
471 | case AC_JACK_MIC_IN: | 487 | case AC_JACK_MIC_IN: |
488 | if (node->pin_caps & | ||
489 | (AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT)) | ||
490 | *pinctl |= AC_PINCTL_VREF_80; | ||
472 | if ((location & 0x0f) == AC_JACK_LOC_FRONT) | 491 | if ((location & 0x0f) == AC_JACK_LOC_FRONT) |
473 | return "Front Mic"; | 492 | return "Front Mic"; |
474 | return "Mic"; | 493 | return "Mic"; |
@@ -556,6 +575,29 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec, | |||
556 | return 1; /* found */ | 575 | return 1; /* found */ |
557 | } | 576 | } |
558 | 577 | ||
578 | /* add a capture source element */ | ||
579 | static void add_cap_src(struct hda_gspec *spec, int idx) | ||
580 | { | ||
581 | struct hda_input_mux_item *csrc; | ||
582 | char *buf; | ||
583 | int num, ocap; | ||
584 | |||
585 | num = spec->input_mux.num_items; | ||
586 | csrc = &spec->input_mux.items[num]; | ||
587 | buf = spec->cap_labels[num]; | ||
588 | for (ocap = 0; ocap < num; ocap++) { | ||
589 | if (! strcmp(buf, spec->cap_labels[ocap])) { | ||
590 | /* same label already exists, | ||
591 | * put the index number to be unique | ||
592 | */ | ||
593 | sprintf(buf, "%s %d", spec->cap_labels[ocap], num); | ||
594 | break; | ||
595 | } | ||
596 | } | ||
597 | csrc->index = idx; | ||
598 | spec->input_mux.num_items++; | ||
599 | } | ||
600 | |||
559 | /* | 601 | /* |
560 | * parse input | 602 | * parse input |
561 | */ | 603 | */ |
@@ -576,28 +618,26 @@ static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node) | |||
576 | * if it reaches to a proper input PIN, add the path as the | 618 | * if it reaches to a proper input PIN, add the path as the |
577 | * input path. | 619 | * input path. |
578 | */ | 620 | */ |
621 | /* first, check the direct connections to PIN widgets */ | ||
579 | for (i = 0; i < adc_node->nconns; i++) { | 622 | for (i = 0; i < adc_node->nconns; i++) { |
580 | node = hda_get_node(spec, adc_node->conn_list[i]); | 623 | node = hda_get_node(spec, adc_node->conn_list[i]); |
581 | if (! node) | 624 | if (node && node->type == AC_WID_PIN) { |
582 | continue; | 625 | err = parse_adc_sub_nodes(codec, spec, node); |
583 | err = parse_adc_sub_nodes(codec, spec, node); | 626 | if (err < 0) |
584 | if (err < 0) | 627 | return err; |
585 | return err; | 628 | else if (err > 0) |
586 | else if (err > 0) { | 629 | add_cap_src(spec, i); |
587 | struct hda_input_mux_item *csrc = &spec->input_mux.items[spec->input_mux.num_items]; | 630 | } |
588 | char *buf = spec->cap_labels[spec->input_mux.num_items]; | 631 | } |
589 | int ocap; | 632 | /* ... then check the rests, more complicated connections */ |
590 | for (ocap = 0; ocap < spec->input_mux.num_items; ocap++) { | 633 | for (i = 0; i < adc_node->nconns; i++) { |
591 | if (! strcmp(buf, spec->cap_labels[ocap])) { | 634 | node = hda_get_node(spec, adc_node->conn_list[i]); |
592 | /* same label already exists, | 635 | if (node && node->type != AC_WID_PIN) { |
593 | * put the index number to be unique | 636 | err = parse_adc_sub_nodes(codec, spec, node); |
594 | */ | 637 | if (err < 0) |
595 | sprintf(buf, "%s %d", spec->cap_labels[ocap], | 638 | return err; |
596 | spec->input_mux.num_items); | 639 | else if (err > 0) |
597 | } | 640 | add_cap_src(spec, i); |
598 | } | ||
599 | csrc->index = i; | ||
600 | spec->input_mux.num_items++; | ||
601 | } | 641 | } |
602 | } | 642 | } |
603 | 643 | ||
@@ -647,9 +687,6 @@ static int parse_input(struct hda_codec *codec) | |||
647 | /* | 687 | /* |
648 | * create mixer controls if possible | 688 | * create mixer controls if possible |
649 | */ | 689 | */ |
650 | #define DIR_OUT 0x1 | ||
651 | #define DIR_IN 0x2 | ||
652 | |||
653 | static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, | 690 | static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, |
654 | unsigned int index, const char *type, const char *dir_sfx) | 691 | unsigned int index, const char *type, const char *dir_sfx) |
655 | { | 692 | { |
@@ -722,49 +759,97 @@ static int check_existing_control(struct hda_codec *codec, const char *type, con | |||
722 | /* | 759 | /* |
723 | * build output mixer controls | 760 | * build output mixer controls |
724 | */ | 761 | */ |
725 | static int build_output_controls(struct hda_codec *codec) | 762 | static int create_output_mixers(struct hda_codec *codec, const char **names) |
726 | { | 763 | { |
727 | struct hda_gspec *spec = codec->spec; | 764 | struct hda_gspec *spec = codec->spec; |
728 | static const char *types[2] = { "Master", "Headphone" }; | ||
729 | int i, err; | 765 | int i, err; |
730 | 766 | ||
731 | for (i = 0; i < 2 && spec->pcm_vol_node[i]; i++) { | 767 | for (i = 0; i < spec->pcm_vol_nodes; i++) { |
732 | err = create_mixer(codec, spec->pcm_vol_node[i], | 768 | err = create_mixer(codec, spec->pcm_vol[i].node, |
733 | spec->pcm_vol_index[i], | 769 | spec->pcm_vol[i].index, |
734 | types[i], "Playback"); | 770 | names[i], "Playback"); |
735 | if (err < 0) | 771 | if (err < 0) |
736 | return err; | 772 | return err; |
737 | } | 773 | } |
738 | return 0; | 774 | return 0; |
739 | } | 775 | } |
740 | 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 | |||
741 | /* create capture volume/switch */ | 797 | /* create capture volume/switch */ |
742 | static int build_input_controls(struct hda_codec *codec) | 798 | static int build_input_controls(struct hda_codec *codec) |
743 | { | 799 | { |
744 | struct hda_gspec *spec = codec->spec; | 800 | struct hda_gspec *spec = codec->spec; |
745 | struct hda_gnode *adc_node = spec->adc_node; | 801 | struct hda_gnode *adc_node = spec->adc_node; |
746 | int err; | 802 | int i, err; |
747 | 803 | static struct snd_kcontrol_new cap_sel = { | |
748 | if (! adc_node) | 804 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
805 | .name = "Capture Source", | ||
806 | .info = capture_source_info, | ||
807 | .get = capture_source_get, | ||
808 | .put = capture_source_put, | ||
809 | }; | ||
810 | |||
811 | if (! adc_node || ! spec->input_mux.num_items) | ||
749 | return 0; /* not found */ | 812 | return 0; /* not found */ |
750 | 813 | ||
814 | spec->cur_cap_src = 0; | ||
815 | select_input_connection(codec, adc_node, | ||
816 | spec->input_mux.items[0].index); | ||
817 | |||
751 | /* create capture volume and switch controls if the ADC has an amp */ | 818 | /* create capture volume and switch controls if the ADC has an amp */ |
752 | err = create_mixer(codec, adc_node, 0, NULL, "Capture"); | 819 | /* do we have only a single item? */ |
820 | if (spec->input_mux.num_items == 1) { | ||
821 | err = create_mixer(codec, adc_node, | ||
822 | spec->input_mux.items[0].index, | ||
823 | NULL, "Capture"); | ||
824 | if (err < 0) | ||
825 | return err; | ||
826 | return 0; | ||
827 | } | ||
753 | 828 | ||
754 | /* create input MUX if multiple sources are available */ | 829 | /* create input MUX if multiple sources are available */ |
755 | if (spec->input_mux.num_items > 1) { | 830 | if ((err = snd_ctl_add(codec->bus->card, |
756 | static struct snd_kcontrol_new cap_sel = { | 831 | snd_ctl_new1(&cap_sel, codec))) < 0) |
757 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | 832 | return err; |
758 | .name = "Capture Source", | 833 | |
759 | .info = capture_source_info, | 834 | /* no volume control? */ |
760 | .get = capture_source_get, | 835 | if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) || |
761 | .put = capture_source_put, | 836 | ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) |
762 | }; | 837 | return 0; |
763 | if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&cap_sel, codec))) < 0) | 838 | |
839 | for (i = 0; i < spec->input_mux.num_items; i++) { | ||
840 | struct snd_kcontrol_new knew; | ||
841 | char name[32]; | ||
842 | sprintf(name, "%s Capture Volume", | ||
843 | spec->input_mux.items[i].label); | ||
844 | knew = (struct snd_kcontrol_new) | ||
845 | HDA_CODEC_VOLUME(name, adc_node->nid, | ||
846 | spec->input_mux.items[i].index, | ||
847 | HDA_INPUT); | ||
848 | if ((err = snd_ctl_add(codec->bus->card, | ||
849 | snd_ctl_new1(&knew, codec))) < 0) | ||
764 | return err; | 850 | return err; |
765 | spec->cur_cap_src = 0; | ||
766 | select_input_connection(codec, adc_node, spec->input_mux.items[0].index); | ||
767 | } | 851 | } |
852 | |||
768 | return 0; | 853 | return 0; |
769 | } | 854 | } |
770 | 855 | ||