diff options
author | Takashi Iwai <tiwai@suse.de> | 2010-08-05 11:16:56 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2010-08-05 11:16:56 +0200 |
commit | 2603798070a80d76e7e6d2992ba4ec74addcec90 (patch) | |
tree | 7a1cdc33bd9bbf3807db239205de02d6beb58e30 /sound/pci | |
parent | 9fe6206f400646a2322096b56c59891d530e8d51 (diff) | |
parent | fc091769a5aa65c045bfbda149c424ba33d0abbb (diff) |
Merge branch 'topic/hda' into for-linus
Diffstat (limited to 'sound/pci')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 87 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 27 | ||||
-rw-r--r-- | sound/pci/hda/hda_hwdep.c | 4 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 5 | ||||
-rw-r--r-- | sound/pci/hda/patch_analog.c | 7 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 95 | ||||
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 43 | ||||
-rw-r--r-- | sound/pci/hda/patch_intelhdmi.c | 3 | ||||
-rw-r--r-- | sound/pci/hda/patch_nvhdmi.c | 3 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 720 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 12 | ||||
-rw-r--r-- | sound/pci/hda/patch_via.c | 32 |
12 files changed, 891 insertions, 147 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index ba2098d20cc..05e8995f9ae 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -396,15 +396,18 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, } for (n = prev_nid + 1; n <= val; n++) { if (conns >= max_conns) { - snd_printk(KERN_ERR - "Too many connections\n"); + snd_printk(KERN_ERR "hda_codec: " + "Too many connections %d for NID 0x%x\n", + conns, nid); return -EINVAL; } conn_list[conns++] = n; } } else { if (conns >= max_conns) { - snd_printk(KERN_ERR "Too many connections\n"); + snd_printk(KERN_ERR "hda_codec: " + "Too many connections %d for NID 0x%x\n", + conns, nid); return -EINVAL; } conn_list[conns++] = val; @@ -1565,6 +1568,17 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); #endif /* SND_HDA_NEEDS_RESUME */ +static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int ofs) +{ + u32 caps = query_amp_caps(codec, nid, dir); + /* get num steps */ + caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + if (ofs < caps) + caps -= ofs; + return caps; +} + /** * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer * @@ -1579,23 +1593,17 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, u8 chs = get_amp_channels(kcontrol); int dir = get_amp_direction(kcontrol); unsigned int ofs = get_amp_offset(kcontrol); - u32 caps; - caps = query_amp_caps(codec, nid, dir); - /* num steps */ - caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - if (!caps) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = chs == 3 ? 2 : 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs); + if (!uinfo->value.integer.max) { printk(KERN_WARNING "hda_codec: " "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid, kcontrol->id.name); return -EINVAL; } - if (ofs < caps) - caps -= ofs; - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = chs == 3 ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = caps; return 0; } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info); @@ -1620,8 +1628,14 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid, int ch, int dir, int idx, unsigned int ofs, unsigned int val) { + unsigned int maxval; + if (val > 0) val += ofs; + /* ofs = 0: raw max value */ + maxval = get_amp_max_value(codec, nid, dir, 0); + if (val > maxval) + val = maxval; return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, HDA_AMP_VOLMASK, val); } @@ -2999,26 +3013,31 @@ struct hda_rate_tbl { unsigned int hda_fmt; }; +/* rate = base * mult / div */ +#define HDA_RATE(base, mult, div) \ + (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \ + (((div) - 1) << AC_FMT_DIV_SHIFT)) + static struct hda_rate_tbl rate_bits[] = { /* rate in Hz, ALSA rate bitmask, HDA format value */ /* autodetected value used in snd_hda_query_supported_pcm */ - { 8000, SNDRV_PCM_RATE_8000, 0x0500 }, /* 1/6 x 48 */ - { 11025, SNDRV_PCM_RATE_11025, 0x4300 }, /* 1/4 x 44 */ - { 16000, SNDRV_PCM_RATE_16000, 0x0200 }, /* 1/3 x 48 */ - { 22050, SNDRV_PCM_RATE_22050, 0x4100 }, /* 1/2 x 44 */ - { 32000, SNDRV_PCM_RATE_32000, 0x0a00 }, /* 2/3 x 48 */ - { 44100, SNDRV_PCM_RATE_44100, 0x4000 }, /* 44 */ - { 48000, SNDRV_PCM_RATE_48000, 0x0000 }, /* 48 */ - { 88200, SNDRV_PCM_RATE_88200, 0x4800 }, /* 2 x 44 */ - { 96000, SNDRV_PCM_RATE_96000, 0x0800 }, /* 2 x 48 */ - { 176400, SNDRV_PCM_RATE_176400, 0x5800 },/* 4 x 44 */ - { 192000, SNDRV_PCM_RATE_192000, 0x1800 }, /* 4 x 48 */ + { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) }, + { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) }, + { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) }, + { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) }, + { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) }, + { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) }, + { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) }, + { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) }, + { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) }, + { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) }, + { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) }, #define AC_PAR_PCM_RATE_BITS 11 /* up to bits 10, 384kHZ isn't supported properly */ /* not autodetected value */ - { 9600, SNDRV_PCM_RATE_KNOT, 0x0400 }, /* 1/5 x 48 */ + { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) }, { 0 } /* terminator */ }; @@ -3037,7 +3056,8 @@ static struct hda_rate_tbl rate_bits[] = { unsigned int snd_hda_calc_stream_format(unsigned int rate, unsigned int channels, unsigned int format, - unsigned int maxbps) + unsigned int maxbps, + unsigned short spdif_ctls) { int i; unsigned int val = 0; @@ -3060,20 +3080,20 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, switch (snd_pcm_format_width(format)) { case 8: - val |= 0x00; + val |= AC_FMT_BITS_8; break; case 16: - val |= 0x10; + val |= AC_FMT_BITS_16; break; case 20: case 24: case 32: if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) - val |= 0x40; + val |= AC_FMT_BITS_32; else if (maxbps >= 24) - val |= 0x30; + val |= AC_FMT_BITS_24; else - val |= 0x20; + val |= AC_FMT_BITS_20; break; default: snd_printdd("invalid format width %d\n", @@ -3081,6 +3101,9 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, return 0; } + if (spdif_ctls & AC_DIG1_NONAUDIO) + val |= AC_FMT_TYPE_NON_PCM; + return val; } EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 5991d14e1ec..46f75bccf0d 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -224,6 +224,27 @@ enum { /* Input converter SDI select */ #define AC_SDI_SELECT (0xf<<0) +/* stream format id */ +#define AC_FMT_CHAN_SHIFT 0 +#define AC_FMT_CHAN_MASK (0x0f << 0) +#define AC_FMT_BITS_SHIFT 4 +#define AC_FMT_BITS_MASK (7 << 4) +#define AC_FMT_BITS_8 (0 << 4) +#define AC_FMT_BITS_16 (1 << 4) +#define AC_FMT_BITS_20 (2 << 4) +#define AC_FMT_BITS_24 (3 << 4) +#define AC_FMT_BITS_32 (4 << 4) +#define AC_FMT_DIV_SHIFT 8 +#define AC_FMT_DIV_MASK (7 << 8) +#define AC_FMT_MULT_SHIFT 11 +#define AC_FMT_MULT_MASK (7 << 11) +#define AC_FMT_BASE_SHIFT 14 +#define AC_FMT_BASE_48K (0 << 14) +#define AC_FMT_BASE_44K (1 << 14) +#define AC_FMT_TYPE_SHIFT 15 +#define AC_FMT_TYPE_PCM (0 << 15) +#define AC_FMT_TYPE_NON_PCM (1 << 15) + /* Unsolicited response control */ #define AC_UNSOL_TAG (0x3f<<0) #define AC_UNSOL_ENABLED (1<<7) @@ -364,6 +385,9 @@ enum { #define AC_DIG2_CC (0x7f<<0) /* Pin widget control - 8bit */ +#define AC_PINCTL_EPT (0x3<<0) +#define AC_PINCTL_EPT_NATIVE 0 +#define AC_PINCTL_EPT_HBR 3 #define AC_PINCTL_VREFEN (0x7<<0) #define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */ #define AC_PINCTL_VREF_50 1 /* 50% */ @@ -928,7 +952,8 @@ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid); unsigned int snd_hda_calc_stream_format(unsigned int rate, unsigned int channels, unsigned int format, - unsigned int maxbps); + unsigned int maxbps, + unsigned short spdif_ctls); int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, unsigned int format); diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index a1fc83753cc..bf3ced51e0f 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -649,7 +649,9 @@ static void parse_codec_mode(char *buf, struct hda_bus *bus, *codecp = NULL; if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) { list_for_each_entry(codec, &bus->codec_list, list) { - if (codec->addr == caddr) { + if (codec->vendor_id == vendorid && + codec->subsystem_id == subid && + codec->addr == caddr) { *codecp = codec; break; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 1df25cf5ce3..66d420212d9 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1653,7 +1653,8 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) format_val = snd_hda_calc_stream_format(runtime->rate, runtime->channels, runtime->format, - hinfo->maxbps); + hinfo->maxbps, + apcm->codec->spdif_ctls); if (!format_val) { snd_printk(KERN_ERR SFX "invalid format_val, rate=%d, ch=%d, format=%d\n", @@ -1960,7 +1961,7 @@ static void azx_irq_pending_work(struct work_struct *work) spin_unlock_irq(&chip->reg_lock); if (!pending) return; - cond_resched(); + msleep(1); } } diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index afbe314a5bf..b697fd2a6f8 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -3662,7 +3662,12 @@ static int patch_ad1984(struct hda_codec *codec) codec->patch_ops.build_pcms = ad1984_build_pcms; break; case AD1984_THINKPAD: - spec->multiout.dig_out_nid = AD1884_SPDIF_OUT; + if (codec->subsystem_id == 0x17aa20fb) { + /* Thinpad X300 does not have the ability to do SPDIF, + or attach to docking station to use SPDIF */ + spec->multiout.dig_out_nid = 0; + } else + spec->multiout.dig_out_nid = AD1884_SPDIF_OUT; spec->input_mux = &ad1984_thinkpad_capture_source; spec->mixers[0] = ad1984_thinkpad_mixers; spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 2bf2cb5da95..df8b19b1730 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -131,6 +131,8 @@ struct conexant_spec { unsigned int dc_enable; unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */ unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */ + + unsigned int beep_amp; }; static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, @@ -515,6 +517,15 @@ static struct snd_kcontrol_new cxt_capture_mixers[] = { {} }; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +/* additional beep mixers; the actual parameters are overwritten at build */ +static struct snd_kcontrol_new cxt_beep_mixer[] = { + HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), + { } /* end */ +}; +#endif + static const char *slave_vols[] = { "Headphone Playback Volume", "Speaker Playback Volume", @@ -580,16 +591,52 @@ static int conexant_build_controls(struct hda_codec *codec) return err; } +#ifdef CONFIG_SND_HDA_INPUT_BEEP + /* create beep controls if needed */ + if (spec->beep_amp) { + struct snd_kcontrol_new *knew; + for (knew = cxt_beep_mixer; knew->name; knew++) { + struct snd_kcontrol *kctl; + kctl = snd_ctl_new1(knew, codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = spec->beep_amp; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; + } + } +#endif + + return 0; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static int conexant_suspend(struct hda_codec *codec, pm_message_t state) +{ + snd_hda_shutup_pins(codec); return 0; } +#endif static struct hda_codec_ops conexant_patch_ops = { .build_controls = conexant_build_controls, .build_pcms = conexant_build_pcms, .init = conexant_init, .free = conexant_free, +#ifdef CONFIG_SND_HDA_POWER_SAVE + .suspend = conexant_suspend, +#endif + .reboot_notify = snd_hda_shutup_pins, }; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +#define set_beep_amp(spec, nid, idx, dir) \ + ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#endif + /* * EAPD control * the private value = nid | (invert << 8) @@ -1130,9 +1177,10 @@ static int patch_cxt5045(struct hda_codec *codec) spec->num_init_verbs = 1; spec->init_verbs[0] = cxt5045_init_verbs; spec->spdif_route = 0; - spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes), - spec->channel_mode = cxt5045_modes, + spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes); + spec->channel_mode = cxt5045_modes; + set_beep_amp(spec, 0x16, 0, 1); codec->patch_ops = conexant_patch_ops; @@ -1211,6 +1259,9 @@ static int patch_cxt5045(struct hda_codec *codec) break; } + if (spec->beep_amp) + snd_hda_attach_beep_device(codec, spec->beep_amp); + return 0; } @@ -1632,6 +1683,11 @@ static void cxt5051_update_speaker(struct hda_codec *codec) pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); + /* on ideapad there is an aditional speaker (subwoofer) to mute */ + if (spec->ideapad) + snd_hda_codec_write(codec, 0x1b, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl); } /* turn on/off EAPD (+ mute HP) as a master switch */ @@ -1888,6 +1944,13 @@ static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid, #endif } +static struct hda_verb cxt5051_ideapad_init_verbs[] = { + /* Subwoofer */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } /* end */ +}; + /* initialize jack-sensing, too */ static int cxt5051_init(struct hda_codec *codec) { @@ -1917,6 +1980,7 @@ enum { CXT5051_LENOVO_X200, /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */ CXT5051_F700, /* HP Compaq Presario F700 */ CXT5051_TOSHIBA, /* Toshiba M300 & co */ + CXT5051_IDEAPAD, /* Lenovo IdeaPad Y430 */ CXT5051_MODELS }; @@ -1927,6 +1991,7 @@ static const char *cxt5051_models[CXT5051_MODELS] = { [CXT5051_LENOVO_X200] = "lenovo-x200", [CXT5051_F700] = "hp-700", [CXT5051_TOSHIBA] = "toshiba", + [CXT5051_IDEAPAD] = "ideapad", }; static struct snd_pci_quirk cxt5051_cfg_tbl[] = { @@ -1938,6 +2003,7 @@ static struct snd_pci_quirk cxt5051_cfg_tbl[] = { CXT5051_LAPTOP), SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo IdeaPad", CXT5051_IDEAPAD), {} }; @@ -1972,6 +2038,8 @@ static int patch_cxt5051(struct hda_codec *codec) spec->cur_adc = 0; spec->cur_adc_idx = 0; + set_beep_amp(spec, 0x13, 0, HDA_OUTPUT); + codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, @@ -1989,6 +2057,10 @@ static int patch_cxt5051(struct hda_codec *codec) break; case CXT5051_LENOVO_X200: spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs; + /* Thinkpad X301 does not have S/PDIF wired and no ability + to use a docking station. */ + if (codec->subsystem_id == 0x17aa211f) + spec->multiout.dig_out_nid = 0; break; case CXT5051_F700: spec->init_verbs[0] = cxt5051_f700_init_verbs; @@ -1999,8 +2071,16 @@ static int patch_cxt5051(struct hda_codec *codec) spec->mixers[0] = cxt5051_toshiba_mixers; spec->auto_mic = AUTO_MIC_PORTB; break; + case CXT5051_IDEAPAD: + spec->init_verbs[spec->num_init_verbs++] = + cxt5051_ideapad_init_verbs; + spec->ideapad = 1; + break; } + if (spec->beep_amp) + snd_hda_attach_beep_device(codec, spec->beep_amp); + return 0; } @@ -2616,7 +2696,6 @@ static struct snd_kcontrol_new cxt5066_vostro_mixers[] = { .put = cxt5066_mic_boost_mux_enum_put, .private_value = 0x23 | 0x100, }, - HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), {} }; @@ -2977,8 +3056,10 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x21b4, "Thinkpad Edge", CXT5066_IDEAPAD), + SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G series", CXT5066_IDEAPAD), + SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G series (AMD)", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD), - SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), {} }; @@ -3014,6 +3095,8 @@ static int patch_cxt5066(struct hda_codec *codec) spec->cur_adc = 0; spec->cur_adc_idx = 0; + set_beep_amp(spec, 0x13, 0, HDA_OUTPUT); + board_config = snd_hda_check_board_config(codec, CXT5066_MODELS, cxt5066_models, cxt5066_cfg_tbl); switch (board_config) { @@ -3062,7 +3145,6 @@ static int patch_cxt5066(struct hda_codec *codec) spec->port_d_mode = 0; spec->dell_vostro = 1; spec->mic_boost = 3; /* default 30dB gain */ - snd_hda_attach_beep_device(codec, 0x13); /* no S/PDIF out */ spec->multiout.dig_out_nid = 0; @@ -3104,6 +3186,9 @@ static int patch_cxt5066(struct hda_codec *codec) break; } + if (spec->beep_amp) + snd_hda_attach_beep_device(codec, spec->beep_amp); + return 0; } diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 2fc53961054..522e0748ee9 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -698,11 +698,51 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res) * Callbacks */ -static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, +/* HBR should be Non-PCM, 8 channels */ +#define is_hbr_format(format) \ + ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7) + +static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, int format) { + struct hdmi_spec *spec = codec->spec; int tag; int fmt; + int pinctl; + int new_pinctl = 0; + int i; + + for (i = 0; i < spec->num_pins; i++) { + if (spec->pin_cvt[i] != nid) + continue; + if (!(snd_hda_query_pin_caps(codec, spec->pin[i]) & AC_PINCAP_HBR)) + continue; + + pinctl = snd_hda_codec_read(codec, spec->pin[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + + new_pinctl = pinctl & ~AC_PINCTL_EPT; + if (is_hbr_format(format)) + new_pinctl |= AC_PINCTL_EPT_HBR; + else + new_pinctl |= AC_PINCTL_EPT_NATIVE; + + snd_printdd("hdmi_setup_stream: " + "NID=0x%x, %spinctl=0x%x\n", + spec->pin[i], + pinctl == new_pinctl ? "" : "new-", + new_pinctl); + + if (pinctl != new_pinctl) + snd_hda_codec_write(codec, spec->pin[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + new_pinctl); + } + + if (is_hbr_format(format) && !new_pinctl) { + snd_printdd("hdmi_setup_stream: HBR is not supported\n"); + return -EINVAL; + } tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4; fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0); @@ -722,6 +762,7 @@ static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, if (fmt != format) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format); + return 0; } /* diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index b81d23e42ac..5972d5e7d01 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -66,8 +66,7 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, hdmi_setup_audio_infoframe(codec, hinfo->nid, substream); - hdmi_setup_stream(codec, hinfo->nid, stream_tag, format); - return 0; + return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format); } static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c index b0652acee9b..a281836fd47 100644 --- a/sound/pci/hda/patch_nvhdmi.c +++ b/sound/pci/hda/patch_nvhdmi.c @@ -202,8 +202,7 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch_89(struct hda_pcm_stream *hinfo, hdmi_setup_audio_infoframe(codec, hinfo->nid, substream); - hdmi_setup_stream(codec, hinfo->nid, stream_tag, format); - return 0; + return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format); } static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo, diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 596ea2f12cf..6ac53f7de54 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -256,6 +256,13 @@ enum { ALC882_MODEL_LAST, }; +/* ALC680 models */ +enum { + ALC680_BASE, + ALC680_AUTO, + ALC680_MODEL_LAST, +}; + /* for GPIO Poll */ #define GPIO_MASK 0x03 @@ -326,6 +333,12 @@ struct alc_spec { hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ + /* capture setup for dynamic dual-adc switch */ + unsigned int cur_adc_idx; + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + /* capture source */ unsigned int num_mux_defs; const struct hda_input_mux *input_mux; @@ -367,6 +380,7 @@ struct alc_spec { /* other flags */ unsigned int no_analog :1; /* digital I/O only */ + unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */ int init_amp; /* for virtual master */ @@ -833,9 +847,13 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, if (auto_pin_type <= AUTO_PIN_FRONT_MIC) { unsigned int pincap; + unsigned int oldval; + oldval = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); pincap = snd_hda_query_pin_caps(codec, nid); pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - if (pincap & AC_PINCAP_VREF_80) + /* if the default pin setup is vref50, we give it priority */ + if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50) val = PIN_VREF80; else if (pincap & AC_PINCAP_VREF_50) val = PIN_VREF50; @@ -1003,6 +1021,29 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, return -1; } +/* switch the current ADC according to the jack state */ +static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int present; + hda_nid_t new_adc; + + present = snd_hda_jack_detect(codec, spec->ext_mic.pin); + if (present) + spec->cur_adc_idx = 1; + else + spec->cur_adc_idx = 0; + new_adc = spec->adc_nids[spec->cur_adc_idx]; + if (spec->cur_adc && spec->cur_adc != new_adc) { + /* stream is running, let's swap the current ADC */ + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = new_adc; + snd_hda_codec_setup_stream(codec, new_adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + } +} + static void alc_mic_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1017,6 +1058,11 @@ static void alc_mic_automute(struct hda_codec *codec) if (snd_BUG_ON(!spec->adc_nids)) return; + if (spec->dual_adc_switch) { + alc_dual_mic_adc_auto_switch(codec); + return; + } + cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0]; present = snd_hda_jack_detect(codec, spec->ext_mic.pin); @@ -1499,6 +1545,63 @@ static int alc_read_coef_idx(struct hda_codec *codec, return val; } +/* set right pin controls for digital I/O */ +static void alc_auto_init_digital(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + hda_nid_t pin; + + for (i = 0; i < spec->autocfg.dig_outs; i++) { + pin = spec->autocfg.dig_out_pins[i]; + if (pin) { + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_OUT); + } + } + pin = spec->autocfg.dig_in_pin; + if (pin) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_IN); +} + +/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */ +static void alc_auto_parse_digital(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i, err; + hda_nid_t dig_nid; + + /* support multiple SPDIFs; the secondary is set up as a slave */ + for (i = 0; i < spec->autocfg.dig_outs; i++) { + err = snd_hda_get_connections(codec, + spec->autocfg.dig_out_pins[i], + &dig_nid, 1); + if (err < 0) + continue; + if (!i) { + spec->multiout.dig_out_nid = dig_nid; + spec->dig_out_type = spec->autocfg.dig_out_type[0]; + } else { + spec->multiout.slave_dig_outs = spec->slave_dig_outs; + if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1) + break; + spec->slave_dig_outs[i - 1] = dig_nid; + } + } + + if (spec->autocfg.dig_in_pin) { + hda_nid_t dig_nid; + err = snd_hda_get_connections(codec, + spec->autocfg.dig_in_pin, + &dig_nid, 1); + if (err > 0) + spec->dig_in_nid = dig_nid; + } +} + /* * ALC888 */ @@ -3607,6 +3710,41 @@ static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, return 0; } +/* analog capture with dynamic dual-adc changes */ +static int dualmic_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + spec->cur_adc = spec->adc_nids[spec->cur_adc_idx]; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); + return 0; +} + +static int dualmic_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; + return 0; +} + +static struct hda_pcm_stream dualmic_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .prepare = dualmic_capture_pcm_prepare, + .cleanup = dualmic_capture_pcm_cleanup + }, +}; /* */ @@ -4936,7 +5074,7 @@ static void alc880_auto_init_input_src(struct hda_codec *codec) static int alc880_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int i, err; + int err; static hda_nid_t alc880_ignore[] = { 0x1d, 0 }; err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, @@ -4967,25 +5105,7 @@ static int alc880_parse_auto_config(struct hda_codec *codec) spec->multiout.max_channels = spec->multiout.num_dacs * 2; - /* check multiple SPDIF-out (for recent codecs) */ - for (i = 0; i < spec->autocfg.dig_outs; i++) { - hda_nid_t dig_nid; - err = snd_hda_get_connections(codec, - spec->autocfg.dig_out_pins[i], - &dig_nid, 1); - if (err < 0) - continue; - if (!i) - spec->multiout.dig_out_nid = dig_nid; - else { - spec->multiout.slave_dig_outs = spec->slave_dig_outs; - if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1) - break; - spec->slave_dig_outs[i - 1] = dig_nid; - } - } - if (spec->autocfg.dig_in_pin) - spec->dig_in_nid = ALC880_DIGIN_NID; + alc_auto_parse_digital(codec); if (spec->kctls.list) add_mixer(spec, spec->kctls.list); @@ -5008,6 +5128,7 @@ static void alc880_auto_init(struct hda_codec *codec) alc880_auto_init_extra_out(codec); alc880_auto_init_analog_input(codec); alc880_auto_init_input_src(codec); + alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); } @@ -5045,6 +5166,39 @@ static void fixup_automic_adc(struct hda_codec *codec) spec->auto_mic = 0; /* disable auto-mic to be sure */ } +/* select or unmute the given capsrc route */ +static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap, + int idx) +{ + if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) { + snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx, + HDA_AMP_MUTE, 0); + } else { + snd_hda_codec_write_cache(codec, cap, 0, + AC_VERB_SET_CONNECT_SEL, idx); + } +} + +/* set the default connection to that pin */ +static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t cap = spec->capsrc_nids ? + spec->capsrc_nids[i] : spec->adc_nids[i]; + int idx; + + idx = get_connection_index(codec, cap, pin); + if (idx < 0) + continue; + select_or_unmute_capsrc(codec, cap, idx); + return i; /* return the found index */ + } + return -1; /* not found */ +} + /* choose the ADC/MUX containing the input pin and initialize the setup */ static void fixup_single_adc(struct hda_codec *codec) { @@ -5061,33 +5215,24 @@ static void fixup_single_adc(struct hda_codec *codec) } if (!pin) return; - - /* set the default connection to that pin */ - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t cap = spec->capsrc_nids ? - spec->capsrc_nids[i] : spec->adc_nids[i]; - int idx; - - idx = get_connection_index(codec, cap, pin); - if (idx < 0) - continue; + i = init_capsrc_for_pin(codec, pin); + if (i >= 0) { /* use only this ADC */ if (spec->capsrc_nids) spec->capsrc_nids += i; spec->adc_nids += i; spec->num_adc_nids = 1; - /* select or unmute this route */ - if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) { - snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx, - HDA_AMP_MUTE, 0); - } else { - snd_hda_codec_write_cache(codec, cap, 0, - AC_VERB_SET_CONNECT_SEL, idx); - } - return; } } +/* initialize dual adcs */ +static void fixup_dual_adc_switch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + init_capsrc_for_pin(codec, spec->ext_mic.pin); + init_capsrc_for_pin(codec, spec->int_mic.pin); +} + static void set_capture_mixer(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -5101,7 +5246,10 @@ static void set_capture_mixer(struct hda_codec *codec) }; if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) { int mux = 0; - if (spec->auto_mic) + int num_adcs = spec->num_adc_nids; + if (spec->dual_adc_switch) + fixup_dual_adc_switch(codec); + else if (spec->auto_mic) fixup_automic_adc(codec); else if (spec->input_mux) { if (spec->input_mux->num_items > 1) @@ -5109,7 +5257,9 @@ static void set_capture_mixer(struct hda_codec *codec) else if (spec->input_mux->num_items == 1) fixup_single_adc(codec); } - spec->cap_mixer = caps[mux][spec->num_adc_nids |