diff options
author | Takashi Iwai <tiwai@suse.de> | 2011-08-19 03:05:35 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-08-19 03:05:35 -0400 |
commit | 23c09b00900c3fa6672148738cad29d6fc6ded7c (patch) | |
tree | a41e345353bfe4e3b0eb91e01b2bf0da2deef5f0 /sound/pci/hda/patch_realtek.c | |
parent | 2996bdbaa40c52c76ec9b981dfa1c9f3a6191fc3 (diff) |
ALSA: hda - Support multiple speakers by Realtek auto-parser
Add the support of multiple speakers by Realtek auto-parser.
When all speaker pins have individual DACs, create each speaker volume
control. Otherwise, create a bind-volume control for all speaker outs.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_realtek.c')
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 196 |
1 files changed, 164 insertions, 32 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d330e9717432..e0ecf5a5b097 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c | |||
@@ -203,6 +203,9 @@ struct alc_spec { | |||
203 | /* multi-io */ | 203 | /* multi-io */ |
204 | int multi_ios; | 204 | int multi_ios; |
205 | struct alc_multi_io multi_io[4]; | 205 | struct alc_multi_io multi_io[4]; |
206 | |||
207 | /* bind volumes */ | ||
208 | struct snd_array bind_ctls; | ||
206 | }; | 209 | }; |
207 | 210 | ||
208 | #define ALC_MODEL_AUTO 0 /* common for all chips */ | 211 | #define ALC_MODEL_AUTO 0 /* common for all chips */ |
@@ -2369,6 +2372,18 @@ static void alc_free_kctls(struct hda_codec *codec) | |||
2369 | snd_array_free(&spec->kctls); | 2372 | snd_array_free(&spec->kctls); |
2370 | } | 2373 | } |
2371 | 2374 | ||
2375 | static void alc_free_bind_ctls(struct hda_codec *codec) | ||
2376 | { | ||
2377 | struct alc_spec *spec = codec->spec; | ||
2378 | if (spec->bind_ctls.list) { | ||
2379 | struct hda_bind_ctls **ctl = spec->bind_ctls.list; | ||
2380 | int i; | ||
2381 | for (i = 0; i < spec->bind_ctls.used; i++) | ||
2382 | kfree(ctl[i]); | ||
2383 | } | ||
2384 | snd_array_free(&spec->bind_ctls); | ||
2385 | } | ||
2386 | |||
2372 | static void alc_free(struct hda_codec *codec) | 2387 | static void alc_free(struct hda_codec *codec) |
2373 | { | 2388 | { |
2374 | struct alc_spec *spec = codec->spec; | 2389 | struct alc_spec *spec = codec->spec; |
@@ -2379,6 +2394,7 @@ static void alc_free(struct hda_codec *codec) | |||
2379 | alc_shutup(codec); | 2394 | alc_shutup(codec); |
2380 | snd_hda_input_jack_free(codec); | 2395 | snd_hda_input_jack_free(codec); |
2381 | alc_free_kctls(codec); | 2396 | alc_free_kctls(codec); |
2397 | alc_free_bind_ctls(codec); | ||
2382 | kfree(spec); | 2398 | kfree(spec); |
2383 | snd_hda_detach_beep_device(codec); | 2399 | snd_hda_detach_beep_device(codec); |
2384 | } | 2400 | } |
@@ -2449,11 +2465,15 @@ enum { | |||
2449 | ALC_CTL_WIDGET_VOL, | 2465 | ALC_CTL_WIDGET_VOL, |
2450 | ALC_CTL_WIDGET_MUTE, | 2466 | ALC_CTL_WIDGET_MUTE, |
2451 | ALC_CTL_BIND_MUTE, | 2467 | ALC_CTL_BIND_MUTE, |
2468 | ALC_CTL_BIND_VOL, | ||
2469 | ALC_CTL_BIND_SW, | ||
2452 | }; | 2470 | }; |
2453 | static const struct snd_kcontrol_new alc_control_templates[] = { | 2471 | static const struct snd_kcontrol_new alc_control_templates[] = { |
2454 | HDA_CODEC_VOLUME(NULL, 0, 0, 0), | 2472 | HDA_CODEC_VOLUME(NULL, 0, 0, 0), |
2455 | HDA_CODEC_MUTE(NULL, 0, 0, 0), | 2473 | HDA_CODEC_MUTE(NULL, 0, 0, 0), |
2456 | HDA_BIND_MUTE(NULL, 0, 0, 0), | 2474 | HDA_BIND_MUTE(NULL, 0, 0, 0), |
2475 | HDA_BIND_VOL(NULL, 0), | ||
2476 | HDA_BIND_SW(NULL, 0), | ||
2457 | }; | 2477 | }; |
2458 | 2478 | ||
2459 | /* add dynamic controls */ | 2479 | /* add dynamic controls */ |
@@ -2494,13 +2514,14 @@ static int add_control_with_pfx(struct alc_spec *spec, int type, | |||
2494 | #define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ | 2514 | #define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ |
2495 | add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) | 2515 | add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) |
2496 | 2516 | ||
2517 | static const char * const channel_name[4] = { | ||
2518 | "Front", "Surround", "CLFE", "Side" | ||
2519 | }; | ||
2520 | |||
2497 | static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, | 2521 | static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, |
2498 | bool can_be_master, int *index) | 2522 | bool can_be_master, int *index) |
2499 | { | 2523 | { |
2500 | struct auto_pin_cfg *cfg = &spec->autocfg; | 2524 | struct auto_pin_cfg *cfg = &spec->autocfg; |
2501 | static const char * const chname[4] = { | ||
2502 | "Front", "Surround", NULL /*CLFE*/, "Side" | ||
2503 | }; | ||
2504 | 2525 | ||
2505 | *index = 0; | 2526 | *index = 0; |
2506 | if (cfg->line_outs == 1 && !spec->multi_ios && | 2527 | if (cfg->line_outs == 1 && !spec->multi_ios && |
@@ -2523,7 +2544,10 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, | |||
2523 | return "PCM"; | 2544 | return "PCM"; |
2524 | break; | 2545 | break; |
2525 | } | 2546 | } |
2526 | return chname[ch]; | 2547 | if (snd_BUG_ON(ch >= ARRAY_SIZE(channel_name))) |
2548 | return "PCM"; | ||
2549 | |||
2550 | return channel_name[ch]; | ||
2527 | } | 2551 | } |
2528 | 2552 | ||
2529 | /* create input playback/capture controls for the given pin */ | 2553 | /* create input playback/capture controls for the given pin */ |
@@ -2869,6 +2893,28 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) | |||
2869 | return 0; | 2893 | return 0; |
2870 | } | 2894 | } |
2871 | 2895 | ||
2896 | /* fill in the dac_nids table for surround speakers, etc */ | ||
2897 | static int alc_auto_fill_extra_dacs(struct hda_codec *codec) | ||
2898 | { | ||
2899 | struct alc_spec *spec = codec->spec; | ||
2900 | const struct auto_pin_cfg *cfg = &spec->autocfg; | ||
2901 | int i; | ||
2902 | |||
2903 | if (cfg->speaker_outs < 2 || !spec->multiout.extra_out_nid[0]) | ||
2904 | return 0; | ||
2905 | |||
2906 | for (i = 1; i < cfg->speaker_outs; i++) | ||
2907 | spec->multiout.extra_out_nid[i] = | ||
2908 | get_dac_if_single(codec, cfg->speaker_pins[i]); | ||
2909 | for (i = 1; i < cfg->speaker_outs; i++) { | ||
2910 | if (spec->multiout.extra_out_nid[i]) | ||
2911 | continue; | ||
2912 | spec->multiout.extra_out_nid[i] = | ||
2913 | alc_auto_look_for_dac(codec, cfg->speaker_pins[0]); | ||
2914 | } | ||
2915 | return 0; | ||
2916 | } | ||
2917 | |||
2872 | static int alc_auto_add_vol_ctl(struct hda_codec *codec, | 2918 | static int alc_auto_add_vol_ctl(struct hda_codec *codec, |
2873 | const char *pfx, int cidx, | 2919 | const char *pfx, int cidx, |
2874 | hda_nid_t nid, unsigned int chs) | 2920 | hda_nid_t nid, unsigned int chs) |
@@ -2991,16 +3037,13 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, | |||
2991 | return 0; | 3037 | return 0; |
2992 | } | 3038 | } |
2993 | 3039 | ||
2994 | /* add playback controls for speaker and HP outputs */ | ||
2995 | static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, | 3040 | static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, |
2996 | hda_nid_t dac, const char *pfx) | 3041 | hda_nid_t dac, const char *pfx) |
2997 | { | 3042 | { |
2998 | struct alc_spec *spec = codec->spec; | 3043 | struct alc_spec *spec = codec->spec; |
2999 | hda_nid_t sw, vol; | 3044 | hda_nid_t sw, vol; |
3000 | int err; | 3045 | int err; |
3001 | 3046 | ||
3002 | if (!pin) | ||
3003 | return 0; | ||
3004 | if (!dac) { | 3047 | if (!dac) { |
3005 | /* the corresponding DAC is already occupied */ | 3048 | /* the corresponding DAC is already occupied */ |
3006 | if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) | 3049 | if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) |
@@ -3021,6 +3064,92 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, | |||
3021 | return 0; | 3064 | return 0; |
3022 | } | 3065 | } |
3023 | 3066 | ||
3067 | static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec, | ||
3068 | unsigned int nums, | ||
3069 | struct hda_ctl_ops *ops) | ||
3070 | { | ||
3071 | struct alc_spec *spec = codec->spec; | ||
3072 | struct hda_bind_ctls **ctlp, *ctl; | ||
3073 | snd_array_init(&spec->bind_ctls, sizeof(ctl), 8); | ||
3074 | ctlp = snd_array_new(&spec->bind_ctls); | ||
3075 | if (!ctlp) | ||
3076 | return NULL; | ||
3077 | ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL); | ||
3078 | *ctlp = ctl; | ||
3079 | if (ctl) | ||
3080 | ctl->ops = ops; | ||
3081 | return ctl; | ||
3082 | } | ||
3083 | |||
3084 | /* add playback controls for speaker and HP outputs */ | ||
3085 | static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins, | ||
3086 | const hda_nid_t *pins, | ||
3087 | const hda_nid_t *dacs, | ||
3088 | const char *pfx) | ||
3089 | { | ||
3090 | struct alc_spec *spec = codec->spec; | ||
3091 | struct hda_bind_ctls *ctl; | ||
3092 | char name[32]; | ||
3093 | int i, n, err; | ||
3094 | |||
3095 | if (!num_pins || !pins[0]) | ||
3096 | return 0; | ||
3097 | |||
3098 | if (num_pins == 1) | ||
3099 | return alc_auto_create_extra_out(codec, *pins, *dacs, pfx); | ||
3100 | |||
3101 | if (dacs[num_pins - 1]) { | ||
3102 | /* OK, we have a multi-output system with individual volumes */ | ||
3103 | for (i = 0; i < num_pins; i++) { | ||
3104 | snprintf(name, sizeof(name), "%s %s", | ||
3105 | pfx, channel_name[i]); | ||
3106 | err = alc_auto_create_extra_out(codec, pins[i], dacs[i], | ||
3107 | name); | ||
3108 | if (err < 0) | ||
3109 | return err; | ||
3110 | } | ||
3111 | return 0; | ||
3112 | } | ||
3113 | |||
3114 | /* Let's create a bind-controls */ | ||
3115 | ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_sw); | ||
3116 | if (!ctl) | ||
3117 | return -ENOMEM; | ||
3118 | n = 0; | ||
3119 | for (i = 0; i < num_pins; i++) { | ||
3120 | if (get_wcaps(codec, pins[i]) & AC_WCAP_OUT_AMP) | ||
3121 | ctl->values[n++] = | ||
3122 | HDA_COMPOSE_AMP_VAL(pins[i], 3, 0, HDA_OUTPUT); | ||
3123 | } | ||
3124 | if (n) { | ||
3125 | snprintf(name, sizeof(name), "%s Playback Switch", pfx); | ||
3126 | err = add_control(spec, ALC_CTL_BIND_SW, name, 0, (long)ctl); | ||
3127 | if (err < 0) | ||
3128 | return err; | ||
3129 | } | ||
3130 | |||
3131 | ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol); | ||
3132 | if (!ctl) | ||
3133 | return -ENOMEM; | ||
3134 | n = 0; | ||
3135 | for (i = 0; i < num_pins; i++) { | ||
3136 | hda_nid_t vol; | ||
3137 | if (!pins[i] || !dacs[i]) | ||
3138 | continue; | ||
3139 | vol = alc_look_for_out_vol_nid(codec, pins[i], dacs[i]); | ||
3140 | if (vol) | ||
3141 | ctl->values[n++] = | ||
3142 | HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT); | ||
3143 | } | ||
3144 | if (n) { | ||
3145 | snprintf(name, sizeof(name), "%s Playback Volume", pfx); | ||
3146 | err = add_control(spec, ALC_CTL_BIND_VOL, name, 0, (long)ctl); | ||
3147 | if (err < 0) | ||
3148 | return err; | ||
3149 | } | ||
3150 | return 0; | ||
3151 | } | ||
3152 | |||
3024 | static int alc_auto_create_hp_out(struct hda_codec *codec) | 3153 | static int alc_auto_create_hp_out(struct hda_codec *codec) |
3025 | { | 3154 | { |
3026 | struct alc_spec *spec = codec->spec; | 3155 | struct alc_spec *spec = codec->spec; |
@@ -3032,9 +3161,10 @@ static int alc_auto_create_hp_out(struct hda_codec *codec) | |||
3032 | static int alc_auto_create_speaker_out(struct hda_codec *codec) | 3161 | static int alc_auto_create_speaker_out(struct hda_codec *codec) |
3033 | { | 3162 | { |
3034 | struct alc_spec *spec = codec->spec; | 3163 | struct alc_spec *spec = codec->spec; |
3035 | return alc_auto_create_extra_out(codec, spec->autocfg.speaker_pins[0], | 3164 | return alc_auto_create_extra_outs(codec, spec->autocfg.speaker_outs, |
3036 | spec->multiout.extra_out_nid[0], | 3165 | spec->autocfg.speaker_pins, |
3037 | "Speaker"); | 3166 | spec->multiout.extra_out_nid, |
3167 | "Speaker"); | ||
3038 | } | 3168 | } |
3039 | 3169 | ||
3040 | static void alc_auto_set_output_and_unmute(struct hda_codec *codec, | 3170 | static void alc_auto_set_output_and_unmute(struct hda_codec *codec, |
@@ -3225,27 +3355,13 @@ static const struct snd_kcontrol_new alc_auto_channel_mode_enum = { | |||
3225 | .put = alc_auto_ch_mode_put, | 3355 | .put = alc_auto_ch_mode_put, |
3226 | }; | 3356 | }; |
3227 | 3357 | ||
3228 | static int alc_auto_add_multi_channel_mode(struct hda_codec *codec, | 3358 | static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) |
3229 | int (*fill_dac)(struct hda_codec *)) | ||
3230 | { | 3359 | { |
3231 | struct alc_spec *spec = codec->spec; | 3360 | struct alc_spec *spec = codec->spec; |
3232 | struct auto_pin_cfg *cfg = &spec->autocfg; | 3361 | struct auto_pin_cfg *cfg = &spec->autocfg; |
3233 | unsigned int location, defcfg; | 3362 | unsigned int location, defcfg; |
3234 | int num_pins; | 3363 | int num_pins; |
3235 | 3364 | ||
3236 | if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && cfg->hp_outs == 1) { | ||
3237 | /* use HP as primary out */ | ||
3238 | cfg->speaker_outs = cfg->line_outs; | ||
3239 | memcpy(cfg->speaker_pins, cfg->line_out_pins, | ||
3240 | sizeof(cfg->speaker_pins)); | ||
3241 | cfg->line_outs = cfg->hp_outs; | ||
3242 | memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); | ||
3243 | cfg->hp_outs = 0; | ||
3244 | memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); | ||
3245 | cfg->line_out_type = AUTO_PIN_HP_OUT; | ||
3246 | if (fill_dac) | ||
3247 | fill_dac(codec); | ||
3248 | } | ||
3249 | if (cfg->line_outs != 1 || | 3365 | if (cfg->line_outs != 1 || |
3250 | cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) | 3366 | cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) |
3251 | return 0; | 3367 | return 0; |
@@ -3550,27 +3666,43 @@ static int alc_parse_auto_config(struct hda_codec *codec, | |||
3550 | const hda_nid_t *ssid_nids) | 3666 | const hda_nid_t *ssid_nids) |
3551 | { | 3667 | { |
3552 | struct alc_spec *spec = codec->spec; | 3668 | struct alc_spec *spec = codec->spec; |
3669 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3553 | int err; | 3670 | int err; |
3554 | 3671 | ||
3555 | err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, | 3672 | err = snd_hda_parse_pin_def_config(codec, cfg, ignore_nids); |
3556 | ignore_nids); | ||
3557 | if (err < 0) | 3673 | if (err < 0) |
3558 | return err; | 3674 | return err; |
3559 | if (!spec->autocfg.line_outs) { | 3675 | if (!cfg->line_outs) { |
3560 | if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) { | 3676 | if (cfg->dig_outs || cfg->dig_in_pin) { |
3561 | spec->multiout.max_channels = 2; | 3677 | spec->multiout.max_channels = 2; |
3562 | spec->no_analog = 1; | 3678 | spec->no_analog = 1; |
3563 | goto dig_only; | 3679 | goto dig_only; |
3564 | } | 3680 | } |
3565 | return 0; /* can't find valid BIOS pin config */ | 3681 | return 0; /* can't find valid BIOS pin config */ |
3566 | } | 3682 | } |
3683 | |||
3684 | if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && cfg->hp_outs == 1) { | ||
3685 | /* use HP as primary out */ | ||
3686 | cfg->speaker_outs = cfg->line_outs; | ||
3687 | memcpy(cfg->speaker_pins, cfg->line_out_pins, | ||
3688 | sizeof(cfg->speaker_pins)); | ||
3689 | cfg->line_outs = cfg->hp_outs; | ||
3690 | memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); | ||
3691 | cfg->hp_outs = 0; | ||
3692 | memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); | ||
3693 | cfg->line_out_type = AUTO_PIN_HP_OUT; | ||
3694 | } | ||
3695 | |||
3567 | err = alc_auto_fill_dac_nids(codec); | 3696 | err = alc_auto_fill_dac_nids(codec); |
3568 | if (err < 0) | 3697 | if (err < 0) |
3569 | return err; | 3698 | return err; |
3570 | err = alc_auto_add_multi_channel_mode(codec, alc_auto_fill_dac_nids); | 3699 | err = alc_auto_add_multi_channel_mode(codec); |
3700 | if (err < 0) | ||
3701 | return err; | ||
3702 | err = alc_auto_fill_extra_dacs(codec); | ||
3571 | if (err < 0) | 3703 | if (err < 0) |
3572 | return err; | 3704 | return err; |
3573 | err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg); | 3705 | err = alc_auto_create_multi_out_ctls(codec, cfg); |
3574 | if (err < 0) | 3706 | if (err < 0) |
3575 | return err; | 3707 | return err; |
3576 | err = alc_auto_create_hp_out(codec); | 3708 | err = alc_auto_create_hp_out(codec); |