diff options
author | Takashi Iwai <tiwai@suse.de> | 2006-03-21 05:29:07 -0500 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2006-03-22 04:40:49 -0500 |
commit | 97ec558a88fb03fa23aee0832a88964e76e4a4db (patch) | |
tree | 406aecd3632ae51d946a13509482cf75bf66b822 /sound/pci/hda/hda_generic.c | |
parent | 82bc955f6379135e6ce35ff90c7ac411fd412c4c (diff) |
[ALSA] hda-codec - Fix generic auto-configurator
Modules: HDA generic driver
Fixed the generic auto-configurator to check speaker pins in
addition.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_generic.c')
-rw-r--r-- | sound/pci/hda/hda_generic.c | 128 |
1 files changed, 92 insertions, 36 deletions
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 39edfcfd3abd..85ad164ada59 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c | |||
@@ -47,10 +47,10 @@ struct hda_gnode { | |||
47 | 47 | ||
48 | /* patch-specific record */ | 48 | /* patch-specific record */ |
49 | struct hda_gspec { | 49 | struct hda_gspec { |
50 | struct hda_gnode *dac_node; /* DAC node */ | 50 | struct hda_gnode *dac_node[2]; /* DAC node */ |
51 | struct hda_gnode *out_pin_node; /* Output pin (Line-Out) node */ | 51 | struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */ |
52 | struct hda_gnode *pcm_vol_node; /* Node for PCM volume */ | 52 | struct hda_gnode *pcm_vol_node[2]; /* Node for PCM volume */ |
53 | unsigned int pcm_vol_index; /* connection of PCM volume */ | 53 | unsigned int pcm_vol_index[2]; /* connection of PCM volume */ |
54 | 54 | ||
55 | struct hda_gnode *adc_node; /* ADC node */ | 55 | struct hda_gnode *adc_node; /* ADC node */ |
56 | struct hda_gnode *cap_vol_node; /* Node for capture volume */ | 56 | struct hda_gnode *cap_vol_node; /* Node for capture volume */ |
@@ -69,8 +69,12 @@ struct hda_gspec { | |||
69 | /* | 69 | /* |
70 | * retrieve the default device type from the default config value | 70 | * retrieve the default device type from the default config value |
71 | */ | 71 | */ |
72 | #define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) | 72 | #define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> \ |
73 | #define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) | 73 | AC_DEFCFG_DEVICE_SHIFT) |
74 | #define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> \ | ||
75 | AC_DEFCFG_LOCATION_SHIFT) | ||
76 | #define defcfg_port_conn(node) (((node)->def_cfg & AC_DEFCFG_PORT_CONN) >> \ | ||
77 | AC_DEFCFG_PORT_CONN_SHIFT) | ||
74 | 78 | ||
75 | /* | 79 | /* |
76 | * destructor | 80 | * destructor |
@@ -261,7 +265,7 @@ static void clear_check_flags(struct hda_gspec *spec) | |||
261 | * returns 0 if not found, 1 if found, or a negative error code. | 265 | * returns 0 if not found, 1 if found, or a negative error code. |
262 | */ | 266 | */ |
263 | static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec, | 267 | static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec, |
264 | struct hda_gnode *node) | 268 | struct hda_gnode *node, int dac_idx) |
265 | { | 269 | { |
266 | int i, err; | 270 | int i, err; |
267 | struct hda_gnode *child; | 271 | struct hda_gnode *child; |
@@ -276,14 +280,14 @@ static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec, | |||
276 | return 0; | 280 | return 0; |
277 | } | 281 | } |
278 | snd_printdd("AUD_OUT found %x\n", node->nid); | 282 | snd_printdd("AUD_OUT found %x\n", node->nid); |
279 | if (spec->dac_node) { | 283 | if (spec->dac_node[dac_idx]) { |
280 | /* already DAC node is assigned, just unmute & connect */ | 284 | /* already DAC node is assigned, just unmute & connect */ |
281 | return node == spec->dac_node; | 285 | return node == spec->dac_node[dac_idx]; |
282 | } | 286 | } |
283 | spec->dac_node = node; | 287 | spec->dac_node[dac_idx] = node; |
284 | if (node->wid_caps & AC_WCAP_OUT_AMP) { | 288 | if (node->wid_caps & AC_WCAP_OUT_AMP) { |
285 | spec->pcm_vol_node = node; | 289 | spec->pcm_vol_node[dac_idx] = node; |
286 | spec->pcm_vol_index = 0; | 290 | spec->pcm_vol_index[dac_idx] = 0; |
287 | } | 291 | } |
288 | return 1; /* found */ | 292 | return 1; /* found */ |
289 | } | 293 | } |
@@ -292,7 +296,7 @@ static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec, | |||
292 | child = hda_get_node(spec, node->conn_list[i]); | 296 | child = hda_get_node(spec, node->conn_list[i]); |
293 | if (! child) | 297 | if (! child) |
294 | continue; | 298 | continue; |
295 | err = parse_output_path(codec, spec, child); | 299 | err = parse_output_path(codec, spec, child, dac_idx); |
296 | if (err < 0) | 300 | if (err < 0) |
297 | return err; | 301 | return err; |
298 | else if (err > 0) { | 302 | else if (err > 0) { |
@@ -303,13 +307,13 @@ static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec, | |||
303 | select_input_connection(codec, node, i); | 307 | select_input_connection(codec, node, i); |
304 | unmute_input(codec, node, i); | 308 | unmute_input(codec, node, i); |
305 | unmute_output(codec, node); | 309 | unmute_output(codec, node); |
306 | if (! spec->pcm_vol_node) { | 310 | if (! spec->pcm_vol_node[dac_idx]) { |
307 | if (node->wid_caps & AC_WCAP_IN_AMP) { | 311 | if (node->wid_caps & AC_WCAP_IN_AMP) { |
308 | spec->pcm_vol_node = node; | 312 | spec->pcm_vol_node[dac_idx] = node; |
309 | spec->pcm_vol_index = i; | 313 | spec->pcm_vol_index[dac_idx] = i; |
310 | } else if (node->wid_caps & AC_WCAP_OUT_AMP) { | 314 | } else if (node->wid_caps & AC_WCAP_OUT_AMP) { |
311 | spec->pcm_vol_node = node; | 315 | spec->pcm_vol_node[dac_idx] = node; |
312 | spec->pcm_vol_index = 0; | 316 | spec->pcm_vol_index[dac_idx] = 0; |
313 | } | 317 | } |
314 | } | 318 | } |
315 | return 1; | 319 | return 1; |
@@ -339,6 +343,8 @@ static struct hda_gnode *parse_output_jack(struct hda_codec *codec, | |||
339 | /* output capable? */ | 343 | /* output capable? */ |
340 | if (! (node->pin_caps & AC_PINCAP_OUT)) | 344 | if (! (node->pin_caps & AC_PINCAP_OUT)) |
341 | continue; | 345 | continue; |
346 | if (defcfg_port_conn(node) == AC_JACK_PORT_NONE) | ||
347 | continue; /* unconnected */ | ||
342 | if (jack_type >= 0) { | 348 | if (jack_type >= 0) { |
343 | if (jack_type != defcfg_type(node)) | 349 | if (jack_type != defcfg_type(node)) |
344 | continue; | 350 | continue; |
@@ -350,10 +356,15 @@ static struct hda_gnode *parse_output_jack(struct hda_codec *codec, | |||
350 | continue; | 356 | continue; |
351 | } | 357 | } |
352 | clear_check_flags(spec); | 358 | clear_check_flags(spec); |
353 | err = parse_output_path(codec, spec, node); | 359 | err = parse_output_path(codec, spec, node, 0); |
354 | if (err < 0) | 360 | if (err < 0) |
355 | return NULL; | 361 | return NULL; |
356 | else if (err > 0) { | 362 | if (! err && spec->out_pin_node[0]) { |
363 | err = parse_output_path(codec, spec, node, 1); | ||
364 | if (err < 0) | ||
365 | return NULL; | ||
366 | } | ||
367 | if (err > 0) { | ||
357 | /* unmute the PIN output */ | 368 | /* unmute the PIN output */ |
358 | unmute_output(codec, node); | 369 | unmute_output(codec, node); |
359 | /* set PIN-Out enable */ | 370 | /* set PIN-Out enable */ |
@@ -381,20 +392,28 @@ static int parse_output(struct hda_codec *codec) | |||
381 | /* first, look for the line-out pin */ | 392 | /* first, look for the line-out pin */ |
382 | node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT); | 393 | node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT); |
383 | if (node) /* found, remember the PIN node */ | 394 | if (node) /* found, remember the PIN node */ |
384 | spec->out_pin_node = node; | 395 | spec->out_pin_node[0] = node; |
396 | else { | ||
397 | /* if no line-out is found, try speaker out */ | ||
398 | node = parse_output_jack(codec, spec, AC_JACK_SPEAKER); | ||
399 | if (node) | ||
400 | spec->out_pin_node[0] = node; | ||
401 | } | ||
385 | /* look for the HP-out pin */ | 402 | /* look for the HP-out pin */ |
386 | node = parse_output_jack(codec, spec, AC_JACK_HP_OUT); | 403 | node = parse_output_jack(codec, spec, AC_JACK_HP_OUT); |
387 | if (node) { | 404 | if (node) { |
388 | if (! spec->out_pin_node) | 405 | if (! spec->out_pin_node[0]) |
389 | spec->out_pin_node = node; | 406 | spec->out_pin_node[0] = node; |
407 | else | ||
408 | spec->out_pin_node[1] = node; | ||
390 | } | 409 | } |
391 | 410 | ||
392 | if (! spec->out_pin_node) { | 411 | if (! spec->out_pin_node[0]) { |
393 | /* no line-out or HP pins found, | 412 | /* no line-out or HP pins found, |
394 | * then choose for the first output pin | 413 | * then choose for the first output pin |
395 | */ | 414 | */ |
396 | spec->out_pin_node = parse_output_jack(codec, spec, -1); | 415 | spec->out_pin_node[0] = parse_output_jack(codec, spec, -1); |
397 | if (! spec->out_pin_node) | 416 | if (! spec->out_pin_node[0]) |
398 | snd_printd("hda_generic: no proper output path found\n"); | 417 | snd_printd("hda_generic: no proper output path found\n"); |
399 | } | 418 | } |
400 | 419 | ||
@@ -505,6 +524,9 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec, | |||
505 | if (! (node->pin_caps & AC_PINCAP_IN)) | 524 | if (! (node->pin_caps & AC_PINCAP_IN)) |
506 | return 0; | 525 | return 0; |
507 | 526 | ||
527 | if (defcfg_port_conn(node) == AC_JACK_PORT_NONE) | ||
528 | return 0; /* unconnected */ | ||
529 | |||
508 | if (node->wid_caps & AC_WCAP_DIGITAL) | 530 | if (node->wid_caps & AC_WCAP_DIGITAL) |
509 | return 0; /* skip SPDIF */ | 531 | return 0; /* skip SPDIF */ |
510 | 532 | ||
@@ -703,12 +725,16 @@ static int check_existing_control(struct hda_codec *codec, const char *type, con | |||
703 | static int build_output_controls(struct hda_codec *codec) | 725 | static int build_output_controls(struct hda_codec *codec) |
704 | { | 726 | { |
705 | struct hda_gspec *spec = codec->spec; | 727 | struct hda_gspec *spec = codec->spec; |
706 | int err; | 728 | static const char *types[2] = { "Master", "Headphone" }; |
729 | int i, err; | ||
707 | 730 | ||
708 | err = create_mixer(codec, spec->pcm_vol_node, spec->pcm_vol_index, | 731 | for (i = 0; i < 2 && spec->pcm_vol_node[i]; i++) { |
709 | "PCM", "Playback"); | 732 | err = create_mixer(codec, spec->pcm_vol_node[i], |
710 | if (err < 0) | 733 | spec->pcm_vol_index[i], |
711 | return err; | 734 | types[i], "Playback"); |
735 | if (err < 0) | ||
736 | return err; | ||
737 | } | ||
712 | return 0; | 738 | return 0; |
713 | } | 739 | } |
714 | 740 | ||
@@ -805,7 +831,7 @@ static int build_loopback_controls(struct hda_codec *codec) | |||
805 | int err; | 831 | int err; |
806 | const char *type; | 832 | const char *type; |
807 | 833 | ||
808 | if (! spec->out_pin_node) | 834 | if (! spec->out_pin_node[0]) |
809 | return 0; | 835 | return 0; |
810 | 836 | ||
811 | list_for_each(p, &spec->nid_list) { | 837 | list_for_each(p, &spec->nid_list) { |
@@ -820,7 +846,8 @@ static int build_loopback_controls(struct hda_codec *codec) | |||
820 | if (check_existing_control(codec, type, "Playback")) | 846 | if (check_existing_control(codec, type, "Playback")) |
821 | continue; | 847 | continue; |
822 | clear_check_flags(spec); | 848 | clear_check_flags(spec); |
823 | err = parse_loopback_path(codec, spec, spec->out_pin_node, | 849 | err = parse_loopback_path(codec, spec, |
850 | spec->out_pin_node[0], | ||
824 | node, type); | 851 | node, type); |
825 | if (err < 0) | 852 | if (err < 0) |
826 | return err; | 853 | return err; |
@@ -855,12 +882,37 @@ static struct hda_pcm_stream generic_pcm_playback = { | |||
855 | .channels_max = 2, | 882 | .channels_max = 2, |
856 | }; | 883 | }; |
857 | 884 | ||
885 | static int generic_pcm2_prepare(struct hda_pcm_stream *hinfo, | ||
886 | struct hda_codec *codec, | ||
887 | unsigned int stream_tag, | ||
888 | unsigned int format, | ||
889 | struct snd_pcm_substream *substream) | ||
890 | { | ||
891 | struct hda_gspec *spec = codec->spec; | ||
892 | |||
893 | snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); | ||
894 | snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid, | ||
895 | stream_tag, 0, format); | ||
896 | return 0; | ||
897 | } | ||
898 | |||
899 | static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo, | ||
900 | struct hda_codec *codec, | ||
901 | struct snd_pcm_substream *substream) | ||
902 | { | ||
903 | struct hda_gspec *spec = codec->spec; | ||
904 | |||
905 | snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0); | ||
906 | snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid, 0, 0, 0); | ||
907 | return 0; | ||
908 | } | ||
909 | |||
858 | static int build_generic_pcms(struct hda_codec *codec) | 910 | static int build_generic_pcms(struct hda_codec *codec) |
859 | { | 911 | { |
860 | struct hda_gspec *spec = codec->spec; | 912 | struct hda_gspec *spec = codec->spec; |
861 | struct hda_pcm *info = &spec->pcm_rec; | 913 | struct hda_pcm *info = &spec->pcm_rec; |
862 | 914 | ||
863 | if (! spec->dac_node && ! spec->adc_node) { | 915 | if (! spec->dac_node[0] && ! spec->adc_node) { |
864 | snd_printd("hda_generic: no PCM found\n"); | 916 | snd_printd("hda_generic: no PCM found\n"); |
865 | return 0; | 917 | return 0; |
866 | } | 918 | } |
@@ -869,9 +921,13 @@ static int build_generic_pcms(struct hda_codec *codec) | |||
869 | codec->pcm_info = info; | 921 | codec->pcm_info = info; |
870 | 922 | ||
871 | info->name = "HDA Generic"; | 923 | info->name = "HDA Generic"; |
872 | if (spec->dac_node) { | 924 | if (spec->dac_node[0]) { |
873 | info->stream[0] = generic_pcm_playback; | 925 | info->stream[0] = generic_pcm_playback; |
874 | info->stream[0].nid = spec->dac_node->nid; | 926 | info->stream[0].nid = spec->dac_node[0]->nid; |
927 | if (spec->dac_node[1]) { | ||
928 | info->stream[0].ops.prepare = generic_pcm2_prepare; | ||
929 | info->stream[0].ops.cleanup = generic_pcm2_cleanup; | ||
930 | } | ||
875 | } | 931 | } |
876 | if (spec->adc_node) { | 932 | if (spec->adc_node) { |
877 | info->stream[1] = generic_pcm_playback; | 933 | info->stream[1] = generic_pcm_playback; |