ALSA: hda - Add add multi-streaming playback for AD1988

Attached a patch which add a new model to support multi-streaming
playback for ad1988.

playback another stereo stream through the front panel headphone on
device 2 while playback through the speakers connected to rear panel
on device 0 at the same time.

Tested with ad1988a rev2 codec on asus P5B-V motherboard.

Signed-off-by: Raymond Yau <superquad.vortex2@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by Raymond Yau and committed by Takashi Iwai c66ddf32 4fe2ca14

+178 -4
+178 -4
sound/pci/hda/patch_analog.c
··· 46 unsigned int cur_eapd; 47 unsigned int need_dac_fix; 48 49 /* capture */ 50 unsigned int num_adc_nids; 51 hda_nid_t *adc_nids; ··· 159 NULL 160 }; 161 162 static void ad198x_free_kctls(struct hda_codec *codec); 163 164 #ifdef CONFIG_SND_HDA_INPUT_BEEP ··· 331 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 332 } 333 334 /* 335 * Digital out 336 */ ··· 498 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture; 499 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; 500 } 501 } 502 503 return 0; ··· 2080 enum { 2081 AD1988_6STACK, 2082 AD1988_6STACK_DIG, 2083 AD1988_3STACK, 2084 AD1988_3STACK_DIG, 2085 AD1988_LAPTOP, ··· 2111 /* for AD1988A revision-2, DAC2-4 are swapped */ 2112 static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = { 2113 0x04, 0x05, 0x0a, 0x06 2114 }; 2115 2116 static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = { ··· 2210 }; 2211 2212 static struct snd_kcontrol_new ad1988_6stack_mixers2[] = { 2213 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT), 2214 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT), 2215 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT), ··· 2492 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2493 /* Port-A front headphon path */ 2494 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */ 2495 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 2496 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 2497 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ··· 3235 return 0; 3236 } 3237 3238 - 3239 /* 3240 */ 3241 3242 static const char *ad1988_models[AD1988_MODEL_LAST] = { 3243 [AD1988_6STACK] = "6stack", 3244 [AD1988_6STACK_DIG] = "6stack-dig", 3245 [AD1988_3STACK] = "3stack", 3246 [AD1988_3STACK_DIG] = "3stack-dig", 3247 [AD1988_LAPTOP] = "laptop", ··· 3301 switch (board_config) { 3302 case AD1988_6STACK: 3303 case AD1988_6STACK_DIG: 3304 spec->multiout.max_channels = 8; 3305 spec->multiout.num_dacs = 4; 3306 if (is_rev2(codec)) ··· 3314 spec->mixers[0] = ad1988_6stack_mixers1_rev2; 3315 else 3316 spec->mixers[0] = ad1988_6stack_mixers1; 3317 - spec->mixers[1] = ad1988_6stack_mixers2; 3318 spec->num_init_verbs = 1; 3319 - spec->init_verbs[0] = ad1988_6stack_init_verbs; 3320 - if (board_config == AD1988_6STACK_DIG) { 3321 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; 3322 spec->dig_in_nid = AD1988_SPDIF_IN; 3323 }
··· 46 unsigned int cur_eapd; 47 unsigned int need_dac_fix; 48 49 + hda_nid_t *alt_dac_nid; 50 + struct hda_pcm_stream *stream_analog_alt_playback; 51 + 52 /* capture */ 53 unsigned int num_adc_nids; 54 hda_nid_t *adc_nids; ··· 156 NULL 157 }; 158 159 + static const char *ad1988_6stack_fp_slave_vols[] = { 160 + "Front Playback Volume", 161 + "Surround Playback Volume", 162 + "Center Playback Volume", 163 + "LFE Playback Volume", 164 + "Side Playback Volume", 165 + "IEC958 Playback Volume", 166 + NULL 167 + }; 168 + 169 + static const char *ad1988_6stack_fp_slave_sws[] = { 170 + "Front Playback Switch", 171 + "Surround Playback Switch", 172 + "Center Playback Switch", 173 + "LFE Playback Switch", 174 + "Side Playback Switch", 175 + "IEC958 Playback Switch", 176 + NULL 177 + }; 178 static void ad198x_free_kctls(struct hda_codec *codec); 179 180 #ifdef CONFIG_SND_HDA_INPUT_BEEP ··· 309 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 310 } 311 312 + static int ad198x_alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 313 + struct hda_codec *codec, 314 + unsigned int stream_tag, 315 + unsigned int format, 316 + struct snd_pcm_substream *substream) 317 + { 318 + struct ad198x_spec *spec = codec->spec; 319 + snd_hda_codec_setup_stream(codec, spec->alt_dac_nid[0], stream_tag, 320 + 0, format); 321 + return 0; 322 + } 323 + 324 + static int ad198x_alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 325 + struct hda_codec *codec, 326 + struct snd_pcm_substream *substream) 327 + { 328 + struct ad198x_spec *spec = codec->spec; 329 + snd_hda_codec_cleanup_stream(codec, spec->alt_dac_nid[0]); 330 + return 0; 331 + } 332 + 333 + static struct hda_pcm_stream ad198x_pcm_analog_alt_playback = { 334 + .substreams = 1, 335 + .channels_min = 2, 336 + .channels_max = 2, 337 + /* NID is set in ad198x_build_pcms */ 338 + .ops = { 339 + .prepare = ad198x_alt_playback_pcm_prepare, 340 + .cleanup = ad198x_alt_playback_pcm_cleanup 341 + }, 342 + }; 343 + 344 /* 345 * Digital out 346 */ ··· 444 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture; 445 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; 446 } 447 + } 448 + 449 + if (spec->alt_dac_nid && spec->stream_analog_alt_playback) { 450 + codec->num_pcms++; 451 + info = spec->pcm_rec + 2; 452 + info->name = "AD198x Headphone"; 453 + info->pcm_type = HDA_PCM_TYPE_AUDIO; 454 + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 455 + *spec->stream_analog_alt_playback; 456 + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 457 + spec->alt_dac_nid[0]; 458 } 459 460 return 0; ··· 2015 enum { 2016 AD1988_6STACK, 2017 AD1988_6STACK_DIG, 2018 + AD1988_6STACK_DIG_FP, 2019 AD1988_3STACK, 2020 AD1988_3STACK_DIG, 2021 AD1988_LAPTOP, ··· 2045 /* for AD1988A revision-2, DAC2-4 are swapped */ 2046 static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = { 2047 0x04, 0x05, 0x0a, 0x06 2048 + }; 2049 + 2050 + static hda_nid_t ad1988_alt_dac_nid[1] = { 2051 + 0x03 2052 }; 2053 2054 static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = { ··· 2140 }; 2141 2142 static struct snd_kcontrol_new ad1988_6stack_mixers2[] = { 2143 + HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT), 2144 + HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT), 2145 + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT), 2146 + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT), 2147 + HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT), 2148 + HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT), 2149 + HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT), 2150 + 2151 + HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT), 2152 + HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT), 2153 + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT), 2154 + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT), 2155 + HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), 2156 + HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), 2157 + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT), 2158 + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT), 2159 + 2160 + HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), 2161 + HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), 2162 + 2163 + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), 2164 + HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT), 2165 + 2166 + { } /* end */ 2167 + }; 2168 + 2169 + static struct snd_kcontrol_new ad1988_6stack_fp_mixers[] = { 2170 + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), 2171 + 2172 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT), 2173 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT), 2174 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT), ··· 2393 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2394 /* Port-A front headphon path */ 2395 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */ 2396 + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 2397 + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 2398 + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2399 + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, 2400 + /* Port-D line-out path */ 2401 + {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 2402 + {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 2403 + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2404 + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, 2405 + /* Port-F surround path */ 2406 + {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 2407 + {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 2408 + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2409 + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, 2410 + /* Port-G CLFE path */ 2411 + {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 2412 + {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 2413 + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2414 + {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, 2415 + /* Port-H side path */ 2416 + {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 2417 + {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 2418 + {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2419 + {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, 2420 + /* Mono out path */ 2421 + {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */ 2422 + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 2423 + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 2424 + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, 2425 + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */ 2426 + /* Port-B front mic-in path */ 2427 + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, 2428 + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, 2429 + {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2430 + /* Port-C line-in path */ 2431 + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, 2432 + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, 2433 + {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2434 + {0x33, AC_VERB_SET_CONNECT_SEL, 0x0}, 2435 + /* Port-E mic-in path */ 2436 + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, 2437 + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, 2438 + {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2439 + {0x34, AC_VERB_SET_CONNECT_SEL, 0x0}, 2440 + /* Analog CD Input */ 2441 + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, 2442 + /* Analog Mix output amp */ 2443 + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ 2444 + 2445 + { } 2446 + }; 2447 + 2448 + static struct hda_verb ad1988_6stack_fp_init_verbs[] = { 2449 + /* Front, Surround, CLFE, side DAC; unmute as default */ 2450 + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2451 + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2452 + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2453 + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2454 + /* Headphone; unmute as default */ 2455 + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 2456 + /* Port-A front headphon path */ 2457 + {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ 2458 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 2459 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 2460 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ··· 3074 return 0; 3075 } 3076 3077 /* 3078 */ 3079 3080 static const char *ad1988_models[AD1988_MODEL_LAST] = { 3081 [AD1988_6STACK] = "6stack", 3082 [AD1988_6STACK_DIG] = "6stack-dig", 3083 + [AD1988_6STACK_DIG_FP] = "6stack-dig-fp", 3084 [AD1988_3STACK] = "3stack", 3085 [AD1988_3STACK_DIG] = "3stack-dig", 3086 [AD1988_LAPTOP] = "laptop", ··· 3140 switch (board_config) { 3141 case AD1988_6STACK: 3142 case AD1988_6STACK_DIG: 3143 + case AD1988_6STACK_DIG_FP: 3144 spec->multiout.max_channels = 8; 3145 spec->multiout.num_dacs = 4; 3146 if (is_rev2(codec)) ··· 3152 spec->mixers[0] = ad1988_6stack_mixers1_rev2; 3153 else 3154 spec->mixers[0] = ad1988_6stack_mixers1; 3155 + if (board_config == AD1988_6STACK_DIG_FP) { 3156 + spec->mixers[1] = ad1988_6stack_fp_mixers; 3157 + spec->slave_vols = ad1988_6stack_fp_slave_vols; 3158 + spec->slave_sws = ad1988_6stack_fp_slave_sws; 3159 + spec->alt_dac_nid = ad1988_alt_dac_nid; 3160 + spec->stream_analog_alt_playback = 3161 + &ad198x_pcm_analog_alt_playback; 3162 + } else 3163 + spec->mixers[1] = ad1988_6stack_mixers2; 3164 spec->num_init_verbs = 1; 3165 + if (board_config == AD1988_6STACK_DIG_FP) 3166 + spec->init_verbs[0] = ad1988_6stack_fp_init_verbs; 3167 + else 3168 + spec->init_verbs[0] = ad1988_6stack_init_verbs; 3169 + if ((board_config == AD1988_6STACK_DIG) || 3170 + (board_config == AD1988_6STACK_DIG_FP)) { 3171 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; 3172 spec->dig_in_nid = AD1988_SPDIF_IN; 3173 }