From c66ddf32dda0d5bcf9db7b4cc42ef5da7baadd3e Mon Sep 17 00:00:00 2001 From: Raymond Yau Date: Mon, 17 Jan 2011 11:19:03 +0100 Subject: 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 Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 182 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 4 deletions(-) (limited to 'sound/pci/hda/patch_analog.c') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 46780670162..34ee1169f2e 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -46,6 +46,9 @@ struct ad198x_spec { unsigned int cur_eapd; unsigned int need_dac_fix; + hda_nid_t *alt_dac_nid; + struct hda_pcm_stream *stream_analog_alt_playback; + /* capture */ unsigned int num_adc_nids; hda_nid_t *adc_nids; @@ -156,6 +159,25 @@ static const char *ad_slave_sws[] = { NULL }; +static const char *ad1988_6stack_fp_slave_vols[] = { + "Front Playback Volume", + "Surround Playback Volume", + "Center Playback Volume", + "LFE Playback Volume", + "Side Playback Volume", + "IEC958 Playback Volume", + NULL +}; + +static const char *ad1988_6stack_fp_slave_sws[] = { + "Front Playback Switch", + "Surround Playback Switch", + "Center Playback Switch", + "LFE Playback Switch", + "Side Playback Switch", + "IEC958 Playback Switch", + NULL +}; static void ad198x_free_kctls(struct hda_codec *codec); #ifdef CONFIG_SND_HDA_INPUT_BEEP @@ -309,6 +331,38 @@ static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); } +static int ad198x_alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ad198x_spec *spec = codec->spec; + snd_hda_codec_setup_stream(codec, spec->alt_dac_nid[0], stream_tag, + 0, format); + return 0; +} + +static int ad198x_alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ad198x_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->alt_dac_nid[0]); + return 0; +} + +static struct hda_pcm_stream ad198x_pcm_analog_alt_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in ad198x_build_pcms */ + .ops = { + .prepare = ad198x_alt_playback_pcm_prepare, + .cleanup = ad198x_alt_playback_pcm_cleanup + }, +}; + /* * Digital out */ @@ -446,6 +500,17 @@ static int ad198x_build_pcms(struct hda_codec *codec) } } + if (spec->alt_dac_nid && spec->stream_analog_alt_playback) { + codec->num_pcms++; + info = spec->pcm_rec + 2; + info->name = "AD198x Headphone"; + info->pcm_type = HDA_PCM_TYPE_AUDIO; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + *spec->stream_analog_alt_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->alt_dac_nid[0]; + } + return 0; } @@ -2015,6 +2080,7 @@ static int patch_ad1981(struct hda_codec *codec) enum { AD1988_6STACK, AD1988_6STACK_DIG, + AD1988_6STACK_DIG_FP, AD1988_3STACK, AD1988_3STACK_DIG, AD1988_LAPTOP, @@ -2047,6 +2113,10 @@ static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = { 0x04, 0x05, 0x0a, 0x06 }; +static hda_nid_t ad1988_alt_dac_nid[1] = { + 0x03 +}; + static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = { 0x04, 0x0a, 0x06 }; @@ -2166,6 +2236,35 @@ static struct snd_kcontrol_new ad1988_6stack_mixers2[] = { { } /* end */ }; +static struct snd_kcontrol_new ad1988_6stack_fp_mixers[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + + HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT), + HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT), + + HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT), + + { } /* end */ +}; + /* 3-stack mode */ static struct snd_kcontrol_new ad1988_3stack_mixers1[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), @@ -2445,6 +2544,68 @@ static struct hda_verb ad1988_6stack_init_verbs[] = { { } }; +static struct hda_verb ad1988_6stack_fp_init_verbs[] = { + /* Front, Surround, CLFE, side DAC; unmute as default */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Headphone; unmute as default */ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Port-A front headphon path */ + {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Port-D line-out path */ + {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* Port-F surround path */ + {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* Port-G CLFE path */ + {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* Port-H side path */ + {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* Mono out path */ + {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */ + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */ + /* Port-B front mic-in path */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* Port-C line-in path */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x33, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* Port-E mic-in path */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x34, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* Analog CD Input */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + /* Analog Mix output amp */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ + + { } +}; + static struct hda_verb ad1988_capture_init_verbs[] = { /* mute analog mix */ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, @@ -3074,13 +3235,13 @@ static int ad1988_auto_init(struct hda_codec *codec) return 0; } - /* */ static const char *ad1988_models[AD1988_MODEL_LAST] = { [AD1988_6STACK] = "6stack", [AD1988_6STACK_DIG] = "6stack-dig", + [AD1988_6STACK_DIG_FP] = "6stack-dig-fp", [AD1988_3STACK] = "3stack", [AD1988_3STACK_DIG] = "3stack-dig", [AD1988_LAPTOP] = "laptop", @@ -3140,6 +3301,7 @@ static int patch_ad1988(struct hda_codec *codec) switch (board_config) { case AD1988_6STACK: case AD1988_6STACK_DIG: + case AD1988_6STACK_DIG_FP: spec->multiout.max_channels = 8; spec->multiout.num_dacs = 4; if (is_rev2(codec)) @@ -3152,10 +3314,22 @@ static int patch_ad1988(struct hda_codec *codec) spec->mixers[0] = ad1988_6stack_mixers1_rev2; else spec->mixers[0] = ad1988_6stack_mixers1; - spec->mixers[1] = ad1988_6stack_mixers2; + if (board_config == AD1988_6STACK_DIG_FP) { + spec->mixers[1] = ad1988_6stack_fp_mixers; + spec->slave_vols = ad1988_6stack_fp_slave_vols; + spec->slave_sws = ad1988_6stack_fp_slave_sws; + spec->alt_dac_nid = ad1988_alt_dac_nid; + spec->stream_analog_alt_playback = + &ad198x_pcm_analog_alt_playback; + } else + spec->mixers[1] = ad1988_6stack_mixers2; spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1988_6stack_init_verbs; - if (board_config == AD1988_6STACK_DIG) { + if (board_config == AD1988_6STACK_DIG_FP) + spec->init_verbs[0] = ad1988_6stack_fp_init_verbs; + else + spec->init_verbs[0] = ad1988_6stack_init_verbs; + if ((board_config == AD1988_6STACK_DIG) || + (board_config == AD1988_6STACK_DIG_FP)) { spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; spec->dig_in_nid = AD1988_SPDIF_IN; } -- cgit v1.2.3