| changeset 5092: | 958b39f3e8dd |
| parent: | 45179b325c8e |
| child: | 47ca87407c84 |
| author: | tiwai |
| date: | Mon Jun 04 18:32:23 2007 +0200 (4 years ago) |
| permissions: | -rw-r--r-- |
| description: | hda-codec - Fix Oops with AD1984 thinkpad model Fixed Oops with AD1984 thinkpad model. Also fixed the wrong init verbs for NID 0x03 and 0x04, which have apparently no mute bit. |
1/*2 * HD audio interface patch for AD1884, AD1981HD, AD1983, AD1984, AD1986A,3 * AD19884 *5 * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>6 *7 * This driver is free software; you can redistribute it and/or modify8 * it under the terms of the GNU General Public License as published by9 * the Free Software Foundation; either version 2 of the License, or10 * (at your option) any later version.11 *12 * This driver is distributed in the hope that it will be useful,13 * but WITHOUT ANY WARRANTY; without even the implied warranty of14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15 * GNU General Public License for more details.16 *17 * You should have received a copy of the GNU General Public License18 * along with this program; if not, write to the Free Software19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA20 */2122#include <sound/driver.h>23#include <linux/init.h>24#include <linux/delay.h>25#include <linux/slab.h>26#include <linux/pci.h>27#include <linux/mutex.h>2829#include <sound/core.h>30#include "hda_codec.h"31#include "hda_local.h"3233struct ad198x_spec {34 struct snd_kcontrol_new *mixers[5];35 int num_mixers;3637 const struct hda_verb *init_verbs[5]; /* initialization verbs38 * don't forget NULL termination!39 */40 unsigned int num_init_verbs;4142 /* playback */43 struct hda_multi_out multiout; /* playback set-up44 * max_channels, dacs must be set45 * dig_out_nid and hp_nid are optional46 */47 unsigned int cur_eapd;48 unsigned int need_dac_fix;4950 /* capture */51 unsigned int num_adc_nids;52 hda_nid_t *adc_nids;53 hda_nid_t dig_in_nid; /* digital-in NID; optional */5455 /* capture source */56 const struct hda_input_mux *input_mux;57 hda_nid_t *capsrc_nids;58 unsigned int cur_mux[3];5960 /* channel model */61 const struct hda_channel_mode *channel_mode;62 int num_channel_mode;6364 /* PCM information */65 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */6667 struct mutex amp_mutex; /* PCM volume/mute control mutex */68 unsigned int spdif_route;6970 /* dynamic controls, init_verbs and input_mux */71 struct auto_pin_cfg autocfg;72 unsigned int num_kctl_alloc, num_kctl_used;73 struct snd_kcontrol_new *kctl_alloc;74 struct hda_input_mux private_imux;75 hda_nid_t private_dac_nids[4];76};7778/*79 * input MUX handling (common part)80 */81static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)82{83 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);84 struct ad198x_spec *spec = codec->spec;8586 return snd_hda_input_mux_info(spec->input_mux, uinfo);87}8889static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)90{91 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);92 struct ad198x_spec *spec = codec->spec;93 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);9495 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];96 return 0;97}9899static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)100{101 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);102 struct ad198x_spec *spec = codec->spec;103 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);104105 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,106 spec->capsrc_nids[adc_idx],107 &spec->cur_mux[adc_idx]);108}109110/*111 * initialization (common callbacks)112 */113static int ad198x_init(struct hda_codec *codec)114{115 struct ad198x_spec *spec = codec->spec;116 int i;117118 for (i = 0; i < spec->num_init_verbs; i++)119 snd_hda_sequence_write(codec, spec->init_verbs[i]);120 return 0;121}122123static int ad198x_build_controls(struct hda_codec *codec)124{125 struct ad198x_spec *spec = codec->spec;126 unsigned int i;127 int err;128129 for (i = 0; i < spec->num_mixers; i++) {130 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);131 if (err < 0)132 return err;133 }134 if (spec->multiout.dig_out_nid) {135 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);136 if (err < 0)137 return err;138 }139 if (spec->dig_in_nid) {140 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);141 if (err < 0)142 return err;143 }144 return 0;145}146147/*148 * Analog playback callbacks149 */150static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,151 struct hda_codec *codec,152 struct snd_pcm_substream *substream)153{154 struct ad198x_spec *spec = codec->spec;155 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);156}157158static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,159 struct hda_codec *codec,160 unsigned int stream_tag,161 unsigned int format,162 struct snd_pcm_substream *substream)163{164 struct ad198x_spec *spec = codec->spec;165 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,166 format, substream);167}168169static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,170 struct hda_codec *codec,171 struct snd_pcm_substream *substream)172{173 struct ad198x_spec *spec = codec->spec;174 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);175}176177/*178 * Digital out179 */180static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,181 struct hda_codec *codec,182 struct snd_pcm_substream *substream)183{184 struct ad198x_spec *spec = codec->spec;185 return snd_hda_multi_out_dig_open(codec, &spec->multiout);186}187188static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,189 struct hda_codec *codec,190 struct snd_pcm_substream *substream)191{192 struct ad198x_spec *spec = codec->spec;193 return snd_hda_multi_out_dig_close(codec, &spec->multiout);194}195196static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,197 struct hda_codec *codec,198 unsigned int stream_tag,199 unsigned int format,200 struct snd_pcm_substream *substream)201{202 struct ad198x_spec *spec = codec->spec;203 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,204 format, substream);205}206207/*208 * Analog capture209 */210static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,211 struct hda_codec *codec,212 unsigned int stream_tag,213 unsigned int format,214 struct snd_pcm_substream *substream)215{216 struct ad198x_spec *spec = codec->spec;217 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],218 stream_tag, 0, format);219 return 0;220}221222static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,223 struct hda_codec *codec,224 struct snd_pcm_substream *substream)225{226 struct ad198x_spec *spec = codec->spec;227 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],228 0, 0, 0);229 return 0;230}231232233/*234 */235static struct hda_pcm_stream ad198x_pcm_analog_playback = {236 .substreams = 1,237 .channels_min = 2,238 .channels_max = 6, /* changed later */239 .nid = 0, /* fill later */240 .ops = {241 .open = ad198x_playback_pcm_open,242 .prepare = ad198x_playback_pcm_prepare,243 .cleanup = ad198x_playback_pcm_cleanup244 },245};246247static struct hda_pcm_stream ad198x_pcm_analog_capture = {248 .substreams = 1,249 .channels_min = 2,250 .channels_max = 2,251 .nid = 0, /* fill later */252 .ops = {253 .prepare = ad198x_capture_pcm_prepare,254 .cleanup = ad198x_capture_pcm_cleanup255 },256};257258static struct hda_pcm_stream ad198x_pcm_digital_playback = {259 .substreams = 1,260 .channels_min = 2,261 .channels_max = 2,262 .nid = 0, /* fill later */263 .ops = {264 .open = ad198x_dig_playback_pcm_open,265 .close = ad198x_dig_playback_pcm_close,266 .prepare = ad198x_dig_playback_pcm_prepare267 },268};269270static struct hda_pcm_stream ad198x_pcm_digital_capture = {271 .substreams = 1,272 .channels_min = 2,273 .channels_max = 2,274 /* NID is set in alc_build_pcms */275};276277static int ad198x_build_pcms(struct hda_codec *codec)278{279 struct ad198x_spec *spec = codec->spec;280 struct hda_pcm *info = spec->pcm_rec;281282 codec->num_pcms = 1;283 codec->pcm_info = info;284285 info->name = "AD198x Analog";286 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;287 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;288 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];289 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;290 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;291 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];292293 if (spec->multiout.dig_out_nid) {294 info++;295 codec->num_pcms++;296 info->name = "AD198x Digital";297 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;298 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;299 if (spec->dig_in_nid) {300 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;301 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;302 }303 }304305 return 0;306}307308static void ad198x_free(struct hda_codec *codec)309{310 struct ad198x_spec *spec = codec->spec;311 unsigned int i;312313 if (spec->kctl_alloc) {314 for (i = 0; i < spec->num_kctl_used; i++)315 kfree(spec->kctl_alloc[i].name);316 kfree(spec->kctl_alloc);317 }318 kfree(codec->spec);319}320321#ifdef CONFIG_PM322static int ad198x_resume(struct hda_codec *codec)323{324 struct ad198x_spec *spec = codec->spec;325 int i;326327 codec->patch_ops.init(codec);328 for (i = 0; i < spec->num_mixers; i++)329 snd_hda_resume_ctls(codec, spec->mixers[i]);330 if (spec->multiout.dig_out_nid)331 snd_hda_resume_spdif_out(codec);332 if (spec->dig_in_nid)333 snd_hda_resume_spdif_in(codec);334 return 0;335}336#endif337338static struct hda_codec_ops ad198x_patch_ops = {339 .build_controls = ad198x_build_controls,340 .build_pcms = ad198x_build_pcms,341 .init = ad198x_init,342 .free = ad198x_free,343#ifdef CONFIG_PM344 .resume = ad198x_resume,345#endif346};347348349/*350 * EAPD control351 * the private value = nid | (invert << 8)352 */353static int ad198x_eapd_info(struct snd_kcontrol *kcontrol,354 struct snd_ctl_elem_info *uinfo)355{356 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;357 uinfo->count = 1;358 uinfo->value.integer.min = 0;359 uinfo->value.integer.max = 1;360 return 0;361}362363static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,364 struct snd_ctl_elem_value *ucontrol)365{366 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);367 struct ad198x_spec *spec = codec->spec;368 int invert = (kcontrol->private_value >> 8) & 1;369 if (invert)370 ucontrol->value.integer.value[0] = ! spec->cur_eapd;371 else372 ucontrol->value.integer.value[0] = spec->cur_eapd;373 return 0;374}375376static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,377 struct snd_ctl_elem_value *ucontrol)378{379 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);380 struct ad198x_spec *spec = codec->spec;381 int invert = (kcontrol->private_value >> 8) & 1;382 hda_nid_t nid = kcontrol->private_value & 0xff;383 unsigned int eapd;384 eapd = ucontrol->value.integer.value[0];385 if (invert)386 eapd = !eapd;387 if (eapd == spec->cur_eapd && ! codec->in_resume)388 return 0;389 spec->cur_eapd = eapd;390 snd_hda_codec_write(codec, nid,391 0, AC_VERB_SET_EAPD_BTLENABLE,392 eapd ? 0x02 : 0x00);393 return 1;394}395396static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,397 struct snd_ctl_elem_info *uinfo);398static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,399 struct snd_ctl_elem_value *ucontrol);400static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,401 struct snd_ctl_elem_value *ucontrol);402403404/*405 * AD1986A specific406 */407408#define AD1986A_SPDIF_OUT 0x02409#define AD1986A_FRONT_DAC 0x03410#define AD1986A_SURR_DAC 0x04411#define AD1986A_CLFE_DAC 0x05412#define AD1986A_ADC 0x06413414static hda_nid_t ad1986a_dac_nids[3] = {415 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC416};417static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };418static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };419420static struct hda_input_mux ad1986a_capture_source = {421 .num_items = 7,422 .items = {423 { "Mic", 0x0 },424 { "CD", 0x1 },425 { "Aux", 0x3 },426 { "Line", 0x4 },427 { "Mix", 0x5 },428 { "Mono", 0x6 },429 { "Phone", 0x7 },430 },431};432433/*434 * PCM control435 *436 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity437 */438439#define ad1986a_pcm_amp_vol_info snd_hda_mixer_amp_volume_info440441static int ad1986a_pcm_amp_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)442{443 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);444 struct ad198x_spec *ad = codec->spec;445446 mutex_lock(&ad->amp_mutex);447 snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);448 mutex_unlock(&ad->amp_mutex);449 return 0;450}451452static int ad1986a_pcm_amp_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)453{454 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);455 struct ad198x_spec *ad = codec->spec;456 int i, change = 0;457458 mutex_lock(&ad->amp_mutex);459 for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {460 kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);461 change |= snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);462 }463 kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);464 mutex_unlock(&ad->amp_mutex);465 return change;466}467468#define ad1986a_pcm_amp_sw_info snd_hda_mixer_amp_switch_info469470static int ad1986a_pcm_amp_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)471{472 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);473 struct ad198x_spec *ad = codec->spec;474475 mutex_lock(&ad->amp_mutex);476 snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);477 mutex_unlock(&ad->amp_mutex);478 return 0;479}480481static int ad1986a_pcm_amp_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)482{483 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);484 struct ad198x_spec *ad = codec->spec;485 int i, change = 0;486487 mutex_lock(&ad->amp_mutex);488 for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {489 kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);490 change |= snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);491 }492 kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);493 mutex_unlock(&ad->amp_mutex);494 return change;495}496497/*498 * mixers499 */500static struct snd_kcontrol_new ad1986a_mixers[] = {501 {502 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,503 .name = "PCM Playback Volume",504 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |505 SNDRV_CTL_ELEM_ACCESS_TLV_READ |506 SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,507 .info = ad1986a_pcm_amp_vol_info,508 .get = ad1986a_pcm_amp_vol_get,509 .put = ad1986a_pcm_amp_vol_put,510 .tlv = { .c = snd_hda_mixer_amp_tlv },511 .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)512 },513 {514 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,515 .name = "PCM Playback Switch",516 .info = ad1986a_pcm_amp_sw_info,517 .get = ad1986a_pcm_amp_sw_get,518 .put = ad1986a_pcm_amp_sw_put,519 .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)520 },521 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),522 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),523 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),524 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),525 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),526 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),527 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),528 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),529 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),530 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),531 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),532 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),533 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),534 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),535 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),536 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),537 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),538 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),539 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),540 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),541 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),542 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),543 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),544 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),545 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),546 {547 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,548 .name = "Capture Source",549 .info = ad198x_mux_enum_info,550 .get = ad198x_mux_enum_get,551 .put = ad198x_mux_enum_put,552 },553 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),554 { } /* end */555};556557/* additional mixers for 3stack mode */558static struct snd_kcontrol_new ad1986a_3st_mixers[] = {559 {560 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,561 .name = "Channel Mode",562 .info = ad198x_ch_mode_info,563 .get = ad198x_ch_mode_get,564 .put = ad198x_ch_mode_put,565 },566 { } /* end */567};568569/* laptop model - 2ch only */570static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };571572static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {573 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),574 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),575 HDA_CODEC_VOLUME("Master Playback Volume", 0x1b, 0x0, HDA_OUTPUT),576 HDA_CODEC_MUTE("Master Playback Switch", 0x1b, 0x0, HDA_OUTPUT),577 /* HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),578 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT), */579 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),580 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),581 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),582 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),583 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),584 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),585 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),586 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),587 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),588 /* HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),589 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),590 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),591 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */592 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),593 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),594 {595 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,596 .name = "Capture Source",597 .info = ad198x_mux_enum_info,598 .get = ad198x_mux_enum_get,599 .put = ad198x_mux_enum_put,600 },601 { } /* end */602};603604/* laptop-eapd model - 2ch only */605606/* master controls both pins 0x1a and 0x1b */607static int ad1986a_laptop_master_vol_put(struct snd_kcontrol *kcontrol,608 struct snd_ctl_elem_value *ucontrol)609{610 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);611 long *valp = ucontrol->value.integer.value;612 int change;613614 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,615 0x7f, valp[0] & 0x7f);616 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,617 0x7f, valp[1] & 0x7f);618 snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,619 0x7f, valp[0] & 0x7f);620 snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,621 0x7f, valp[1] & 0x7f);622 return change;623}624625static int ad1986a_laptop_master_sw_put(struct snd_kcontrol *kcontrol,626 struct snd_ctl_elem_value *ucontrol)627{628 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);629 long *valp = ucontrol->value.integer.value;630 int change;631632 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,633 0x80, valp[0] ? 0 : 0x80);634 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,635 0x80, valp[1] ? 0 : 0x80);636 snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,637 0x80, valp[0] ? 0 : 0x80);638 snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,639 0x80, valp[1] ? 0 : 0x80);640 return change;641}642643static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {644 .num_items = 3,645 .items = {646 { "Mic", 0x0 },647 { "Internal Mic", 0x4 },648 { "Mix", 0x5 },649 },650};651652static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {653 {654 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,655 .name = "Master Playback Volume",656 .info = snd_hda_mixer_amp_volume_info,657 .get = snd_hda_mixer_amp_volume_get,658 .put = ad1986a_laptop_master_vol_put,659 .tlv = { .c = snd_hda_mixer_amp_tlv },660 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),661 },662 {663 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,664 .name = "Master Playback Switch",665 .info = snd_hda_mixer_amp_switch_info,666 .get = snd_hda_mixer_amp_switch_get,667 .put = ad1986a_laptop_master_sw_put,668 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),669 },670 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),671 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),672 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),673 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT),674 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),675 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),676 HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),677 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),678 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),679 {680 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,681 .name = "Capture Source",682 .info = ad198x_mux_enum_info,683 .get = ad198x_mux_enum_get,684 .put = ad198x_mux_enum_put,685 },686 {687 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,688 .name = "External Amplifier",689 .info = ad198x_eapd_info,690 .get = ad198x_eapd_get,691 .put = ad198x_eapd_put,692 .private_value = 0x1b | (1 << 8), /* port-D, inversed */693 },694 { } /* end */695};696697/*698 * initialization verbs699 */700static struct hda_verb ad1986a_init_verbs[] = {701 /* Front, Surround, CLFE DAC; mute as default */702 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},703 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},704 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},705 /* Downmix - off */706 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},707 /* HP, Line-Out, Surround, CLFE selectors */708 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},709 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},710 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},711 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},712 /* Mono selector */713 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},714 /* Mic selector: Mic 1/2 pin */715 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},716 /* Line-in selector: Line-in */717 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},718 /* Mic 1/2 swap */719 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},720 /* Record selector: mic */721 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},722 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */723 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},724 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},725 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},726 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},727 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},728 /* PC beep */729 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},730 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */731 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},732 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},733 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},734 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},735 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},736 /* HP Pin */737 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },738 /* Front, Surround, CLFE Pins */739 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },740 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },741 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },742 /* Mono Pin */743 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },744 /* Mic Pin */745 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },746 /* Line, Aux, CD, Beep-In Pin */747 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },748 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },749 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },750 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },751 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },752 { } /* end */753};754755static struct hda_verb ad1986a_ch2_init[] = {756 /* Surround out -> Line In */757 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },758 /* Line-in selectors */759 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },760 /* CLFE -> Mic in */761 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },762 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */763 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },764 { } /* end */765};766767static struct hda_verb ad1986a_ch4_init[] = {768 /* Surround out -> Surround */769 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },770 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },771 /* CLFE -> Mic in */772 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },773 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },774 { } /* end */775};776777static struct hda_verb ad1986a_ch6_init[] = {778 /* Surround out -> Surround out */779 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },780 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },781 /* CLFE -> CLFE */782 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },783 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },784 { } /* end */785};786787static struct hda_channel_mode ad1986a_modes[3] = {788 { 2, ad1986a_ch2_init },789 { 4, ad1986a_ch4_init },790 { 6, ad1986a_ch6_init },791};792793/* eapd initialization */794static struct hda_verb ad1986a_eapd_init_verbs[] = {795 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },796 {}797};798799/* Ultra initialization */800static struct hda_verb ad1986a_ultra_init[] = {801 /* eapd initialization */802 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },803 /* CLFE -> Mic in */804 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },805 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },806 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },807 { } /* end */808};809810/* models */811enum {812 AD1986A_6STACK,813 AD1986A_3STACK,814 AD1986A_LAPTOP,815 AD1986A_LAPTOP_EAPD,816 AD1986A_ULTRA,817 AD1986A_MODELS818};819820static const char *ad1986a_models[AD1986A_MODELS] = {821 [AD1986A_6STACK] = "6stack",822 [AD1986A_3STACK] = "3stack",823 [AD1986A_LAPTOP] = "laptop",824 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",825 [AD1986A_ULTRA] = "ultra",826};827828static struct snd_pci_quirk ad1986a_cfg_tbl[] = {829 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),830 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),831 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),832 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),833 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),834 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),835 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),836 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),837 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),838 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),839 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),840 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),841 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),842 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),843 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),844 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),845 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),846 SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),847 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),848 SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),849 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),850 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),851 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),852 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),853 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_EAPD),854 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),855 {}856};857858static int patch_ad1986a(struct hda_codec *codec)859{860 struct ad198x_spec *spec;861 int board_config;862863 spec = kzalloc(sizeof(*spec), GFP_KERNEL);864 if (spec == NULL)865 return -ENOMEM;866867 mutex_init(&spec->amp_mutex);868 codec->spec = spec;869870 spec->multiout.max_channels = 6;871 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);872 spec->multiout.dac_nids = ad1986a_dac_nids;873 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;874 spec->num_adc_nids = 1;875 spec->adc_nids = ad1986a_adc_nids;876 spec->capsrc_nids = ad1986a_capsrc_nids;877 spec->input_mux = &ad1986a_capture_source;878 spec->num_mixers = 1;879 spec->mixers[0] = ad1986a_mixers;880 spec->num_init_verbs = 1;881 spec->init_verbs[0] = ad1986a_init_verbs;882883 codec->patch_ops = ad198x_patch_ops;884885 /* override some parameters */886 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,887 ad1986a_models,888 ad1986a_cfg_tbl);889 switch (board_config) {890 case AD1986A_3STACK:891 spec->num_mixers = 2;892 spec->mixers[1] = ad1986a_3st_mixers;893 spec->num_init_verbs = 2;894 spec->init_verbs[1] = ad1986a_ch2_init;895 spec->channel_mode = ad1986a_modes;896 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);897 spec->need_dac_fix = 1;898 spec->multiout.max_channels = 2;899 spec->multiout.num_dacs = 1;900 break;901 case AD1986A_LAPTOP:902 spec->mixers[0] = ad1986a_laptop_mixers;903 spec->multiout.max_channels = 2;904 spec->multiout.num_dacs = 1;905 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;906 break;907 case AD1986A_LAPTOP_EAPD:908 spec->mixers[0] = ad1986a_laptop_eapd_mixers;909 spec->num_init_verbs = 2;910 spec->init_verbs[1] = ad1986a_eapd_init_verbs;911 spec->multiout.max_channels = 2;912 spec->multiout.num_dacs = 1;913 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;914 spec->multiout.dig_out_nid = 0;915 spec->input_mux = &ad1986a_laptop_eapd_capture_source;916 break;917 case AD1986A_ULTRA:918 spec->mixers[0] = ad1986a_laptop_eapd_mixers;919 spec->num_init_verbs = 2;920 spec->init_verbs[1] = ad1986a_ultra_init;921 spec->multiout.max_channels = 2;922 spec->multiout.num_dacs = 1;923 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;924 spec->multiout.dig_out_nid = 0;925 break;926 }927928 return 0;929}930931/*932 * AD1983 specific933 */934935#define AD1983_SPDIF_OUT 0x02936#define AD1983_DAC 0x03937#define AD1983_ADC 0x04938939static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };940static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };941static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };942943static struct hda_input_mux ad1983_capture_source = {944 .num_items = 4,945 .items = {946 { "Mic", 0x0 },947 { "Line", 0x1 },948 { "Mix", 0x2 },949 { "Mix Mono", 0x3 },950 },951};952953/*954 * SPDIF playback route955 */956static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)957{958 static char *texts[] = { "PCM", "ADC" };959960 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;961 uinfo->count = 1;962 uinfo->value.enumerated.items = 2;963 if (uinfo->value.enumerated.item > 1)964 uinfo->value.enumerated.item = 1;965 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);966 return 0;967}968969static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)970{971 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);972 struct ad198x_spec *spec = codec->spec;973974 ucontrol->value.enumerated.item[0] = spec->spdif_route;975 return 0;976}977978static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)979{980 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);981 struct ad198x_spec *spec = codec->spec;982983 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {984 spec->spdif_route = ucontrol->value.enumerated.item[0];985 snd_hda_codec_write(codec, spec->multiout.dig_out_nid, 0,986 AC_VERB_SET_CONNECT_SEL, spec->spdif_route);987 return 1;988 }989 return 0;990}991992static struct snd_kcontrol_new ad1983_mixers[] = {993 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),994 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),995 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),996 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),997 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),998 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),999 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),1000 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),1001 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),1002 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),1003 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),1004 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),1005 HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT),1006 HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT),1007 HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),1008 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),1009 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),1010 {1011 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,1012 .name = "Capture Source",1013 .info = ad198x_mux_enum_info,1014 .get = ad198x_mux_enum_get,1015 .put = ad198x_mux_enum_put,1016 },1017 {1018 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,1019 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",1020 .info = ad1983_spdif_route_info,1021 .get = ad1983_spdif_route_get,1022 .put = ad1983_spdif_route_put,1023 },1024 { } /* end */1025};10261027static struct hda_verb ad1983_init_verbs[] = {1028 /* Front, HP, Mono; mute as default */1029 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1030 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1031 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1032 /* Beep, PCM, Mic, Line-In: mute */1033 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1034 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1035 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1036 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1037 /* Front, HP selectors; from Mix */1038 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},1039 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},1040 /* Mono selector; from Mix */1041 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},1042 /* Mic selector; Mic */1043 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},1044 /* Line-in selector: Line-in */1045 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},1046 /* Mic boost: 0dB */1047 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},1048 /* Record selector: mic */1049 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},1050 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1051 /* SPDIF route: PCM */1052 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},1053 /* Front Pin */1054 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },1055 /* HP Pin */1056 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },1057 /* Mono Pin */1058 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },1059 /* Mic Pin */1060 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },1061 /* Line Pin */1062 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },1063 { } /* end */1064};106510661067static int patch_ad1983(struct hda_codec *codec)1068{1069 struct ad198x_spec *spec;10701071 spec = kzalloc(sizeof(*spec), GFP_KERNEL);1072 if (spec == NULL)1073 return -ENOMEM;10741075 mutex_init(&spec->amp_mutex);1076 codec->spec = spec;10771078 spec->multiout.max_channels = 2;1079 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);1080 spec->multiout.dac_nids = ad1983_dac_nids;1081 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;1082 spec->num_adc_nids = 1;1083 spec->adc_nids = ad1983_adc_nids;1084 spec->capsrc_nids = ad1983_capsrc_nids;1085 spec->input_mux = &ad1983_capture_source;1086 spec->num_mixers = 1;1087 spec->mixers[0] = ad1983_mixers;1088 spec->num_init_verbs = 1;1089 spec->init_verbs[0] = ad1983_init_verbs;1090 spec->spdif_route = 0;10911092 codec->patch_ops = ad198x_patch_ops;10931094 return 0;1095}109610971098/*1099 * AD1981 HD specific1100 */11011102#define AD1981_SPDIF_OUT 0x021103#define AD1981_DAC 0x031104#define AD1981_ADC 0x0411051106static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };1107static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };1108static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };11091110/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */1111static struct hda_input_mux ad1981_capture_source = {1112 .num_items = 7,1113 .items = {1114 { "Front Mic", 0x0 },1115 { "Line", 0x1 },1116 { "Mix", 0x2 },1117 { "Mix Mono", 0x3 },1118 { "CD", 0x4 },1119 { "Mic", 0x6 },1120 { "Aux", 0x7 },1121 },1122};11231124static struct snd_kcontrol_new ad1981_mixers[] = {1125 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),1126 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),1127 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),1128 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),1129 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),1130 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),1131 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),1132 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),1133 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),1134 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),1135 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),1136 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),1137 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),1138 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),1139 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),1140 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),1141 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),1142 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),1143 HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),1144 HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT),1145 HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),1146 HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),1147 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),1148 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),1149 {1150 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,1151 .name = "Capture Source",1152 .info = ad198x_mux_enum_info,1153 .get = ad198x_mux_enum_get,1154 .put = ad198x_mux_enum_put,1155 },1156 /* identical with AD1983 */1157 {1158 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,1159 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",1160 .info = ad1983_spdif_route_info,1161 .get = ad1983_spdif_route_get,1162 .put = ad1983_spdif_route_put,1163 },1164 { } /* end */1165};11661167static struct hda_verb ad1981_init_verbs[] = {1168 /* Front, HP, Mono; mute as default */1169 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1170 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1171 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1172 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */1173 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1174 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1175 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1176 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1177 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1178 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1179 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1180 /* Front, HP selectors; from Mix */1181 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},1182 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},1183 /* Mono selector; from Mix */1184 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},1185 /* Mic Mixer; select Front Mic */1186 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},1187 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1188 /* Mic boost: 0dB */1189 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},1190 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},1191 /* Record selector: Front mic */1192 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},1193 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1194 /* SPDIF route: PCM */1195 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},1196 /* Front Pin */1197 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },1198 /* HP Pin */1199 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },1200 /* Mono Pin */1201 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },1202 /* Front & Rear Mic Pins */1203 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },1204 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },1205 /* Line Pin */1206 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },1207 /* Digital Beep */1208 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},1209 /* Line-Out as Input: disabled */1210 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1211 { } /* end */1212};12131214/*1215 * Patch for HP nx63201216 *1217 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal1218 * speaker output enabled _and_ mute-LED off.1219 */12201221#define AD1981_HP_EVENT 0x371222#define AD1981_MIC_EVENT 0x3812231224static struct hda_verb ad1981_hp_init_verbs[] = {1225 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */1226 /* pin sensing on HP and Mic jacks */1227 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},1228 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},1229 {}1230};12311232/* turn on/off EAPD (+ mute HP) as a master switch */1233static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,1234 struct snd_ctl_elem_value *ucontrol)1235{1236 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);1237 struct ad198x_spec *spec = codec->spec;12381239 if (! ad198x_eapd_put(kcontrol, ucontrol))1240 return 0;12411242 /* toggle HP mute appropriately */1243 snd_hda_codec_amp_update(codec, 0x06, 0, HDA_OUTPUT, 0,1244 0x80, spec->cur_eapd ? 0 : 0x80);1245 snd_hda_codec_amp_update(codec, 0x06, 1, HDA_OUTPUT, 0,1246 0x80, spec->cur_eapd ? 0 : 0x80);1247 return 1;1248}12491250/* bind volumes of both NID 0x05 and 0x06 */1251static int ad1981_hp_master_vol_put(struct snd_kcontrol *kcontrol,1252 struct snd_ctl_elem_value *ucontrol)1253{1254 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);1255 long *valp = ucontrol->value.integer.value;1256 int change;12571258 change = snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0,1259 0x7f, valp[0] & 0x7f);1260 change |= snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0,1261 0x7f, valp[1] & 0x7f);1262 snd_hda_codec_amp_update(codec, 0x06, 0, HDA_OUTPUT, 0,1263 0x7f, valp[0] & 0x7f);1264 snd_hda_codec_amp_update(codec, 0x06, 1, HDA_OUTPUT, 0,1265 0x7f, valp[1] & 0x7f);1266 return change;1267}12681269/* mute internal speaker if HP is plugged */1270static void ad1981_hp_automute(struct hda_codec *codec)1271{1272 unsigned int present;12731274 present = snd_hda_codec_read(codec, 0x06, 0,1275 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;1276 snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0,1277 0x80, present ? 0x80 : 0);1278 snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0,1279 0x80, present ? 0x80 : 0);1280}12811282/* toggle input of built-in and mic jack appropriately */1283static void ad1981_hp_automic(struct hda_codec *codec)1284{1285 static struct hda_verb mic_jack_on[] = {1286 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1287 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},1288 {}1289 };1290 static struct hda_verb mic_jack_off[] = {1291 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},1292 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},1293 {}1294 };1295 unsigned int present;12961297 present = snd_hda_codec_read(codec, 0x08, 0,1298 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;1299 if (present)1300 snd_hda_sequence_write(codec, mic_jack_on);1301 else1302 snd_hda_sequence_write(codec, mic_jack_off);1303}13041305/* unsolicited event for HP jack sensing */1306static void ad1981_hp_unsol_event(struct hda_codec *codec,1307 unsigned int res)1308{1309 res >>= 26;1310 switch (res) {1311 case AD1981_HP_EVENT:1312 ad1981_hp_automute(codec);1313 break;1314 case AD1981_MIC_EVENT:1315 ad1981_hp_automic(codec);1316 break;1317 }1318}13191320static struct hda_input_mux ad1981_hp_capture_source = {1321 .num_items = 3,1322 .items = {1323 { "Mic", 0x0 },1324 { "Docking-Station", 0x1 },1325 { "Mix", 0x2 },1326 },1327};13281329static struct snd_kcontrol_new ad1981_hp_mixers[] = {1330 {1331 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,1332 .name = "Master Playback Volume",1333 .info = snd_hda_mixer_amp_volume_info,1334 .get = snd_hda_mixer_amp_volume_get,1335 .put = ad1981_hp_master_vol_put,1336 .private_value = HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),1337 },1338 {1339 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,1340 .name = "Master Playback Switch",1341 .info = ad198x_eapd_info,1342 .get = ad198x_eapd_get,1343 .put = ad1981_hp_master_sw_put,1344 .private_value = 0x05,1345 },1346 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),1347 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),1348#if 01349 /* FIXME: analog mic/line loopback doesn't work with my tests...1350 * (although recording is OK)1351 */1352 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),1353 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),1354 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),1355 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),1356 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),1357 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),1358 /* FIXME: does this laptop have analog CD connection? */1359 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),1360 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),1361#endif1362 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),1363 HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),1364 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),1365 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),1366 {1367 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,1368 .name = "Capture Source",1369 .info = ad198x_mux_enum_info,1370 .get = ad198x_mux_enum_get,1371 .put = ad198x_mux_enum_put,1372 },1373 { } /* end */1374};13751376/* initialize jack-sensing, too */1377static int ad1981_hp_init(struct hda_codec *codec)1378{1379 ad198x_init(codec);1380 ad1981_hp_automute(codec);1381 ad1981_hp_automic(codec);1382 return 0;1383}13841385/* configuration for Toshiba Laptops */1386static struct hda_verb ad1981_toshiba_init_verbs[] = {1387 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */1388 /* pin sensing on HP and Mic jacks */1389 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},1390 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},1391 {}1392};13931394static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {1395 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),1396 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),1397 { }1398};13991400/* configuration for Lenovo Thinkpad T60 */1401static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {1402 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),1403 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),1404 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),1405 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),1406 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),1407 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),1408 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),1409 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),1410 HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),1411 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),1412 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),1413 {1414 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,1415 .name = "Capture Source",1416 .info = ad198x_mux_enum_info,1417 .get = ad198x_mux_enum_get,1418 .put = ad198x_mux_enum_put,1419 },1420 /* identical with AD1983 */1421 {1422 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,1423 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",1424 .info = ad1983_spdif_route_info,1425 .get = ad1983_spdif_route_get,1426 .put = ad1983_spdif_route_put,1427 },1428 { } /* end */1429};14301431static struct hda_input_mux ad1981_thinkpad_capture_source = {1432 .num_items = 3,1433 .items = {1434 { "Mic", 0x0 },1435 { "Mix", 0x2 },1436 { "CD", 0x4 },1437 },1438};14391440/* models */1441enum {1442 AD1981_BASIC,1443 AD1981_HP,1444 AD1981_THINKPAD,1445 AD1981_TOSHIBA,1446 AD1981_MODELS1447};14481449static const char *ad1981_models[AD1981_MODELS] = {1450 [AD1981_HP] = "hp",1451 [AD1981_THINKPAD] = "thinkpad",1452 [AD1981_BASIC] = "basic",1453 [AD1981_TOSHIBA] = "toshiba"1454};14551456static struct snd_pci_quirk ad1981_cfg_tbl[] = {1457 /* All HP models */1458 SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),1459 /* HP nx6320 (reversed SSID, H/W bug) */1460 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),1461 /* Lenovo Thinkpad T60/X60/Z6xx */1462 SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),1463 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),1464 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),1465 {}1466};14671468static int patch_ad1981(struct hda_codec *codec)1469{1470 struct ad198x_spec *spec;1471 int board_config;14721473 spec = kzalloc(sizeof(*spec), GFP_KERNEL);1474 if (spec == NULL)1475 return -ENOMEM;14761477 mutex_init(&spec->amp_mutex);1478 codec->spec = spec;14791480 spec->multiout.max_channels = 2;1481 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);1482 spec->multiout.dac_nids = ad1981_dac_nids;1483 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;1484 spec->num_adc_nids = 1;1485 spec->adc_nids = ad1981_adc_nids;1486 spec->capsrc_nids = ad1981_capsrc_nids;1487 spec->input_mux = &ad1981_capture_source;1488 spec->num_mixers = 1;1489 spec->mixers[0] = ad1981_mixers;1490 spec->num_init_verbs = 1;1491 spec->init_verbs[0] = ad1981_init_verbs;1492 spec->spdif_route = 0;14931494 codec->patch_ops = ad198x_patch_ops;14951496 /* override some parameters */1497 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,1498 ad1981_models,1499 ad1981_cfg_tbl);1500 switch (board_config) {1501 case AD1981_HP:1502 spec->mixers[0] = ad1981_hp_mixers;1503 spec->num_init_verbs = 2;1504 spec->init_verbs[1] = ad1981_hp_init_verbs;1505 spec->multiout.dig_out_nid = 0;1506 spec->input_mux = &ad1981_hp_capture_source;15071508 codec->patch_ops.init = ad1981_hp_init;1509 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;1510 break;1511 case AD1981_THINKPAD:1512 spec->mixers[0] = ad1981_thinkpad_mixers;1513 spec->input_mux = &ad1981_thinkpad_capture_source;1514 break;1515 case AD1981_TOSHIBA:1516 spec->mixers[0] = ad1981_hp_mixers;1517 spec->mixers[1] = ad1981_toshiba_mixers;1518 spec->num_init_verbs = 2;1519 spec->init_verbs[1] = ad1981_toshiba_init_verbs;1520 spec->multiout.dig_out_nid = 0;1521 spec->input_mux = &ad1981_hp_capture_source;1522 codec->patch_ops.init = ad1981_hp_init;1523 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;1524 break;1525 }1526 return 0;1527}152815291530/*1531 * AD19881532 *1533 * Output pins and routes1534 *1535 * Pin Mix Sel DAC (*)1536 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/061537 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/061538 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a1539 * port-D 0x12 (mute/hp) <- 0x29 <- 041540 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a1541 * port-F 0x16 (mute) <- 0x2a <- 061542 * port-G 0x24 (mute) <- 0x27 <- 051543 * port-H 0x25 (mute) <- 0x28 <- 0a1544 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/061545 *1546 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah1547 * (*) DAC2/3/4 are swapped to DAC3/4/2 on AD198A rev.2 due to a h/w bug.1548 *1549 * Input pins and routes1550 *1551 * pin boost mix input # / adc input #1552 * port-A 0x11 -> 0x38 -> mix 2, ADC 01553 * port-B 0x14 -> 0x39 -> mix 0, ADC 11554 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 21555 * port-D 0x12 -> 0x3d -> mix 3, ADC 81556 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 41557 * port-F 0x16 -> 0x3b -> mix 5, ADC 31558 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 61559 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 71560 *1561 *1562 * DAC assignment1563 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/031564 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/031565 *1566 * Inputs of Analog Mix (0x20)1567 * 0:Port-B (front mic)1568 * 1:Port-C/G/H (line-in)1569 * 2:Port-A1570 * 3:Port-D (line-in/2)1571 * 4:Port-E/G/H (mic-in)1572 * 5:Port-F (mic2-in)1573 * 6:CD1574 * 7:Beep1575 *1576 * ADC selection1577 * 0:Port-A1578 * 1:Port-B (front mic-in)1579 * 2:Port-C (line-in)1580 * 3:Port-F (mic2-in)1581 * 4:Port-E (mic-in)1582 * 5:CD1583 * 6:Port-G1584 * 7:Port-H1585 * 8:Port-D (line-in/2)1586 * 9:Mix1587 *1588 * Proposed pin assignments by the datasheet1589 *1590 * 6-stack1591 * Port-A front headphone1592 * B front mic-in1593 * C rear line-in1594 * D rear front-out1595 * E rear mic-in1596 * F rear surround1597 * G rear CLFE1598 * H rear side1599 *1600 * 3-stack1601 * Port-A front headphone1602 * B front mic1603 * C rear line-in/surround1604 * D rear front-out1605 * E rear mic-in/CLFE1606 *1607 * laptop1608 * Port-A headphone1609 * B mic-in1610 * C docking station1611 * D internal speaker (with EAPD)1612 * E/F quad mic array1613 */161416151616/* models */1617enum {1618 AD1988_6STACK,1619 AD1988_6STACK_DIG,1620 AD1988_3STACK,1621 AD1988_3STACK_DIG,1622 AD1988_LAPTOP,1623 AD1988_LAPTOP_DIG,1624 AD1988_AUTO,1625 AD1988_MODEL_LAST,1626};16271628/* reivision id to check workarounds */1629#define AD1988A_REV2 0x10020016301631#define is_rev2(codec) \1632 ((codec)->vendor_id == 0x11d41988 && \1633 (codec)->revision_id == AD1988A_REV2)16341635/*1636 * mixers1637 */16381639static hda_nid_t ad1988_6stack_dac_nids[4] = {1640 0x04, 0x06, 0x05, 0x0a1641};16421643static hda_nid_t ad1988_3stack_dac_nids[3] = {1644 0x04, 0x05, 0x0a1645};16461647/* for AD1988A revision-2, DAC2-4 are swapped */1648static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {1649 0x04, 0x05, 0x0a, 0x061650};16511652static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {1653 0x04, 0x0a, 0x061654};16551656static hda_nid_t ad1988_adc_nids[3] = {1657 0x08, 0x09, 0x0f1658};16591660static hda_nid_t ad1988_capsrc_nids[3] = {1661 0x0c, 0x0d, 0x0e1662};16631664#define AD1988_SPDIF_OUT 0x021665#define AD1988_SPDIF_IN 0x0716661667static struct hda_input_mux ad1988_6stack_capture_source = {1668 .num_items = 5,1669 .items = {1670 { "Front Mic", 0x0 },1671 { "Line", 0x1 },1672 { "Mic", 0x4 },1673 { "CD", 0x5 },1674 { "Mix", 0x9 },1675 },1676};16771678static struct hda_input_mux ad1988_laptop_capture_source = {1679 .num_items = 3,1680 .items = {1681 { "Mic/Line", 0x0 },1682 { "CD", 0x5 },1683 { "Mix", 0x9 },1684 },1685};16861687/*1688 */1689static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,1690 struct snd_ctl_elem_info *uinfo)1691{1692 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);1693 struct ad198x_spec *spec = codec->spec;1694 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,1695 spec->num_channel_mode);1696}16971698static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,1699 struct snd_ctl_elem_value *ucontrol)1700{1701 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);1702 struct ad198x_spec *spec = codec->spec;1703 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,1704 spec->num_channel_mode, spec->multiout.max_channels);1705}17061707static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,1708 struct snd_ctl_elem_value *ucontrol)1709{1710 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);1711 struct ad198x_spec *spec = codec->spec;1712 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,1713 spec->num_channel_mode,1714 &spec->multiout.max_channels);1715 if (err >= 0 && spec->need_dac_fix)1716 spec->multiout.num_dacs = spec->multiout.max_channels / 2;1717 return err;1718}17191720/* 6-stack mode */1721static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {1722 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),1723 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),1724 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),1725 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),1726 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),1727 { } /* end */1728};17291730static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {1731 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),1732 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),1733 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),1734 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),1735 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),1736 { } /* end */1737};17381739static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {1740 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),1741 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),1742 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),1743 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),1744 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),1745 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),1746 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),17471748 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),1749 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),1750 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),1751 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),1752 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),1753 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),1754 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),1755 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),17561757 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),1758 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),17591760 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),1761 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),17621763 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),1764 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),17651766 { } /* end */1767};17681769/* 3-stack mode */1770static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {1771 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),1772 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),1773 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),1774 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),1775 { } /* end */1776};17771778static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {1779 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),1780 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),1781 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),1782 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),1783 { } /* end */1784};17851786static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {1787 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),1788 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),1789 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),1790 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),1791 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),1792 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),17931794 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),1795 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),1796 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),1797 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),1798 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),1799 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),1800 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),1801 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),18021803 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),1804 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),18051806 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),1807 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),18081809 HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),1810 HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),1811 {1812 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,1813 .name = "Channel Mode",1814 .info = ad198x_ch_mode_info,1815 .get = ad198x_ch_mode_get,1816 .put = ad198x_ch_mode_put,1817 },18181819 { } /* end */1820};18211822/* laptop mode */1823static struct snd_kcontrol_new ad1988_laptop_mixers[] = {1824 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),1825 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),1826 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),18271828 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),1829 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),1830 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),1831 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),1832 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),1833 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),18341835 HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),1836 HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),18371838 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),1839 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),18401841 HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),18421843 {1844 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,1845 .name = "External Amplifier",1846 .info = ad198x_eapd_info,1847 .get = ad198x_eapd_get,1848 .put = ad198x_eapd_put,1849 .private_value = 0x12 | (1 << 8), /* port-D, inversed */1850 },18511852 { } /* end */1853};18541855/* capture */1856static struct snd_kcontrol_new ad1988_capture_mixers[] = {1857 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),1858 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),1859 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),1860 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),1861 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),1862 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),1863 {1864 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,1865 /* The multiple "Capture Source" controls confuse alsamixer1866 * So call somewhat different..1867 * FIXME: the controls appear in the "playback" view!1868 */1869 /* .name = "Capture Source", */1870 .name = "Input Source",1871 .count = 3,1872 .info = ad198x_mux_enum_info,1873 .get = ad198x_mux_enum_get,1874 .put = ad198x_mux_enum_put,1875 },1876 { } /* end */1877};18781879static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,1880 struct snd_ctl_elem_info *uinfo)1881{1882 static char *texts[] = {1883 "PCM", "ADC1", "ADC2", "ADC3"1884 };1885 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;1886 uinfo->count = 1;1887 uinfo->value.enumerated.items = 4;1888 if (uinfo->value.enumerated.item >= 4)1889 uinfo->value.enumerated.item = 3;1890 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);1891 return 0;1892}18931894static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,1895 struct snd_ctl_elem_value *ucontrol)1896{1897 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);1898 unsigned int sel;18991900 sel = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONNECT_SEL, 0);1901 if (sel > 0) {1902 sel = snd_hda_codec_read(codec, 0x0b, 0,1903 AC_VERB_GET_CONNECT_SEL, 0);1904 if (sel < 3)1905 sel++;1906 else1907 sel = 0;1908 }1909 ucontrol->value.enumerated.item[0] = sel;1910 return 0;1911}19121913static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,1914 struct snd_ctl_elem_value *ucontrol)1915{1916 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);1917 unsigned int val, sel;1918 int change;19191920 val = ucontrol->value.enumerated.item[0];1921 sel = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONNECT_SEL, 0);1922 if (!val) {1923 change = sel != 0;1924 if (change || codec->in_resume)1925 snd_hda_codec_write(codec, 0x02, 0,1926 AC_VERB_SET_CONNECT_SEL, 0);1927 } else {1928 change = sel == 0;1929 if (change || codec->in_resume)1930 snd_hda_codec_write(codec, 0x02, 0,1931 AC_VERB_SET_CONNECT_SEL, 1);1932 sel = snd_hda_codec_read(codec, 0x0b, 0,1933 AC_VERB_GET_CONNECT_SEL, 0) + 1;1934 change |= sel != val;1935 if (change || codec->in_resume)1936 snd_hda_codec_write(codec, 0x0b, 0,1937 AC_VERB_SET_CONNECT_SEL, val - 1);1938 }1939 return change;1940}19411942static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {1943 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),1944 {1945 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,1946 .name = "IEC958 Playback Source",1947 .info = ad1988_spdif_playback_source_info,1948 .get = ad1988_spdif_playback_source_get,1949 .put = ad1988_spdif_playback_source_put,1950 },1951 { } /* end */1952};19531954static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {1955 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),1956 { } /* end */1957};195819591960/*1961 * initialization verbs1962 */19631964/*1965 * for 6-stack (+dig)1966 */1967static struct hda_verb ad1988_6stack_init_verbs[] = {1968 /* Front, Surround, CLFE, side DAC; unmute as default */1969 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},1970 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},1971 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},1972 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},1973 /* Port-A front headphon path */1974 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */1975 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},1976 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},1977 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},1978 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},1979 /* Port-D line-out path */1980 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},1981 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},1982 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},1983 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},1984 /* Port-F surround path */1985 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},1986 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},1987 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},1988 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},1989 /* Port-G CLFE path */1990 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},1991 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},1992 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},1993 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},1994 /* Port-H side path */1995 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},1996 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},1997 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},1998 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},1999 /* Mono out path */2000 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */2001 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},2002 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},2003 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},2004 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */2005 /* Port-B front mic-in path */2006 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2007 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},2008 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},2009 /* Port-C line-in path */2010 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2011 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},2012 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},2013 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},2014 /* Port-E mic-in path */2015 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2016 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},2017 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},2018 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},20192020 { }2021};20222023static struct hda_verb ad1988_capture_init_verbs[] = {2024 /* mute analog mix */2025 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},2026 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},2027 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},2028 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},2029 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},2030 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},2031 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},2032 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},2033 /* select ADCs - front-mic */2034 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},2035 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},2036 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},2037 /* ADCs; muted */2038 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2039 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2040 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},20412042 { }2043};20442045static struct hda_verb ad1988_spdif_init_verbs[] = {2046 /* SPDIF out sel */2047 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */2048 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */2049 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},2050 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},2051 /* SPDIF out pin */2052 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */2053 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x17}, /* 0dB */20542055 { }2056};20572058/*2059 * verbs for 3stack (+dig)2060 */2061static struct hda_verb ad1988_3stack_ch2_init[] = {2062 /* set port-C to line-in */2063 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },2064 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },2065 /* set port-E to mic-in */2066 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },2067 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },2068 { } /* end */2069};20702071static struct hda_verb ad1988_3stack_ch6_init[] = {2072 /* set port-C to surround out */2073 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },2074 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },2075 /* set port-E to CLFE out */2076 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },2077 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },2078 { } /* end */2079};20802081static struct hda_channel_mode ad1988_3stack_modes[2] = {2082 { 2, ad1988_3stack_ch2_init },2083 { 6, ad1988_3stack_ch6_init },2084};20852086static struct hda_verb ad1988_3stack_init_verbs[] = {2087 /* Front, Surround, CLFE, side DAC; unmute as default */2088 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2089 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2090 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2091 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2092 /* Port-A front headphon path */2093 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */2094 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},2095 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},2096 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2097 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},2098 /* Port-D line-out path */2099 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},2100 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},2101 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2102 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},2103 /* Mono out path */2104 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */2105 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},2106 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},2107 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},2108 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */2109 /* Port-B front mic-in path */2110 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2111 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},2112 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},2113 /* Port-C line-in/surround path - 6ch mode as default */2114 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},2115 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2116 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},2117 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */2118 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},2119 /* Port-E mic-in/CLFE path - 6ch mode as default */2120 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},2121 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2122 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},2123 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */2124 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},2125 /* mute analog mix */2126 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},2127 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},2128 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},2129 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},2130 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},2131 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},2132 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},2133 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},2134 /* select ADCs - front-mic */2135 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},2136 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},2137 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},2138 /* ADCs; muted */2139 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2140 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2141 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2142 { }2143};21442145/*2146 * verbs for laptop mode (+dig)2147 */2148static struct hda_verb ad1988_laptop_hp_on[] = {2149 /* unmute port-A and mute port-D */2150 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },2151 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },2152 { } /* end */2153};2154static struct hda_verb ad1988_laptop_hp_off[] = {2155 /* mute port-A and unmute port-D */2156 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },2157 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },2158 { } /* end */2159};21602161#define AD1988_HP_EVENT 0x0121622163static struct hda_verb ad1988_laptop_init_verbs[] = {2164 /* Front, Surround, CLFE, side DAC; unmute as default */2165 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2166 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2167 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2168 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2169 /* Port-A front headphon path */2170 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */2171 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},2172 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},2173 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2174 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},2175 /* unsolicited event for pin-sense */2176 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },2177 /* Port-D line-out path + EAPD */2178 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},2179 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},2180 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2181 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},2182 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */2183 /* Mono out path */2184 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */2185 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},2186 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},2187 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},2188 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */2189 /* Port-B mic-in path */2190 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2191 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},2192 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},2193 /* Port-C docking station - try to output */2194 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},2195 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},2196 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},2197 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},2198 /* mute analog mix */2199 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},2200 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},2201 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},2202 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},2203 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},2204 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},2205 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},2206 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},2207 /* select ADCs - mic */2208 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},2209 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},2210 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},2211 /* ADCs; muted */2212 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2213 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2214 {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2215 { }2216};22172218static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)2219{2220 if ((res >> 26) != AD1988_HP_EVENT)2221 return;2222 if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))2223 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);2224 else2225 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);2226}222722282229/*2230 * Automatic parse of I/O pins from the BIOS configuration2231 */22322233#define NUM_CONTROL_ALLOC 322234#define NUM_VERB_ALLOC 3222352236enum {2237 AD_CTL_WIDGET_VOL,2238 AD_CTL_WIDGET_MUTE,2239 AD_CTL_BIND_MUTE,2240};2241static struct snd_kcontrol_new ad1988_control_templates[] = {2242 HDA_CODEC_VOLUME(NULL, 0, 0, 0),2243 HDA_CODEC_MUTE(NULL, 0, 0, 0),2244 HDA_BIND_MUTE(NULL, 0, 0, 0),2245};22462247/* add dynamic controls */2248static int add_control(struct ad198x_spec *spec, int type, const char *name,2249 unsigned long val)2250{2251 struct snd_kcontrol_new *knew;22522253 if (spec->num_kctl_used >= spec->num_kctl_alloc) {2254 int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;22552256 knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */2257 if (! knew)2258 return -ENOMEM;2259 if (spec->kctl_alloc) {2260 memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);2261 kfree(spec->kctl_alloc);2262 }2263 spec->kctl_alloc = knew;2264 spec->num_kctl_alloc = num;2265 }22662267 knew = &spec->kctl_alloc[spec->num_kctl_used];2268 *knew = ad1988_control_templates[type];2269 knew->name = kstrdup(name, GFP_KERNEL);2270 if (! knew->name)2271 return -ENOMEM;2272 knew->private_value = val;2273 spec->num_kctl_used++;2274 return 0;2275}22762277#define AD1988_PIN_CD_NID 0x182278#define AD1988_PIN_BEEP_NID 0x1022792280static hda_nid_t ad1988_mixer_nids[8] = {2281 /* A B C D E F G H */2282 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x282283};22842285static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)2286{2287 static hda_nid_t idx_to_dac[8] = {2288 /* A B C D E F G H */2289 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a2290 };2291 static hda_nid_t idx_to_dac_rev2[8] = {2292 /* A B C D E F G H */2293 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x062294 };2295 if (is_rev2(codec))2296 return idx_to_dac_rev2[idx];2297 else2298 return idx_to_dac[idx];2299}23002301static hda_nid_t ad1988_boost_nids[8] = {2302 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 02303};23042305static int ad1988_pin_idx(hda_nid_t nid)2306{2307 static hda_nid_t ad1988_io_pins[8] = {2308 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x252309 };2310 int i;2311 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)2312 if (ad1988_io_pins[i] == nid)2313 return i;2314 return 0; /* should be -1 */2315}23162317static int ad1988_pin_to_loopback_idx(hda_nid_t nid)2318{2319 static int loopback_idx[8] = {2320 2, 0, 1, 3, 4, 5, 1, 42321 };2322 switch (nid) {2323 case AD1988_PIN_CD_NID:2324 return 6;2325 default:2326 return loopback_idx[ad1988_pin_idx(nid)];2327 }2328}23292330static int ad1988_pin_to_adc_idx(hda_nid_t nid)2331{2332 static int adc_idx[8] = {2333 0, 1, 2, 8, 4, 3, 6, 72334 };2335 switch (nid) {2336 case AD1988_PIN_CD_NID:2337 return 5;2338 default:2339 return adc_idx[ad1988_pin_idx(nid)];2340 }2341}23422343/* fill in the dac_nids table from the parsed pin configuration */2344static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,2345 const struct auto_pin_cfg *cfg)2346{2347 struct ad198x_spec *spec = codec->spec;2348 int i, idx;23492350 spec->multiout.dac_nids = spec->private_dac_nids;23512352 /* check the pins hardwired to audio widget */2353 for (i = 0; i < cfg->line_outs; i++) {2354 idx = ad1988_pin_idx(cfg->line_out_pins[i]);2355 spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);2356 }2357 spec->multiout.num_dacs = cfg->line_outs;2358 return 0;2359}23602361/* add playback controls from the parsed DAC table */2362static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,2363 const struct auto_pin_cfg *cfg)2364{2365 char name[32];2366 static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };2367 hda_nid_t nid;2368 int i, err;23692370 for (i = 0; i < cfg->line_outs; i++) {2371 hda_nid_t dac = spec->multiout.dac_nids[i];2372 if (! dac)2373 continue;2374 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];2375 if (i == 2) {2376 /* Center/LFE */2377 err = add_control(spec, AD_CTL_WIDGET_VOL,2378 "Center Playback Volume",2379 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));2380 if (err < 0)2381 return err;2382 err = add_control(spec, AD_CTL_WIDGET_VOL,2383 "LFE Playback Volume",2384 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));2385 if (err < 0)2386 return err;2387 err = add_control(spec, AD_CTL_BIND_MUTE,2388 "Center Playback Switch",2389 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));2390 if (err < 0)2391 return err;2392 err = add_control(spec, AD_CTL_BIND_MUTE,2393 "LFE Playback Switch",2394 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));2395 if (err < 0)2396 return err;2397 } else {2398 sprintf(name, "%s Playback Volume", chname[i]);2399 err = add_control(spec, AD_CTL_WIDGET_VOL, name,2400 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));2401 if (err < 0)2402 return err;2403 sprintf(name, "%s Playback Switch", chname[i]);2404 err = add_control(spec, AD_CTL_BIND_MUTE, name,2405 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));2406 if (err < 0)2407 return err;2408 }2409 }2410 return 0;2411}24122413/* add playback controls for speaker and HP outputs */2414static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,2415 const char *pfx)2416{2417 struct ad198x_spec *spec = codec->spec;2418 hda_nid_t nid;2419 int idx, err;2420 char name[32];24212422 if (! pin)2423 return 0;24242425 idx = ad1988_pin_idx(pin);2426 nid = ad1988_idx_to_dac(codec, idx);2427 /* specify the DAC as the extra output */2428 if (! spec->multiout.hp_nid)2429 spec->multiout.hp_nid = nid;2430 else2431 spec->multiout.extra_out_nid[0] = nid;2432 /* control HP volume/switch on the output mixer amp */2433 sprintf(name, "%s Playback Volume", pfx);2434 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,2435 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)2436 return err;2437 nid = ad1988_mixer_nids[idx];2438 sprintf(name, "%s Playback Switch", pfx);2439 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,2440 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)2441 return err;2442 return 0;2443}24442445/* create input playback/capture controls for the given pin */2446static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,2447 const char *ctlname, int boost)2448{2449 char name[32];2450 int err, idx;24512452 sprintf(name, "%s Playback Volume", ctlname);2453 idx = ad1988_pin_to_loopback_idx(pin);2454 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,2455 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)2456 return err;2457 sprintf(name, "%s Playback Switch", ctlname);2458 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,2459 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)2460 return err;2461 if (boost) {2462 hda_nid_t bnid;2463 idx = ad1988_pin_idx(pin);2464 bnid = ad1988_boost_nids[idx];2465 if (bnid) {2466 sprintf(name, "%s Boost", ctlname);2467 return add_control(spec, AD_CTL_WIDGET_VOL, name,2468 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));24692470 }2471 }2472 return 0;2473}24742475/* create playback/capture controls for input pins */2476static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,2477 const struct auto_pin_cfg *cfg)2478{2479 struct hda_input_mux *imux = &spec->private_imux;2480 int i, err;24812482 for (i = 0; i < AUTO_PIN_LAST; i++) {2483 err = new_analog_input(spec, cfg->input_pins[i],2484 auto_pin_cfg_labels[i],2485 i <= AUTO_PIN_FRONT_MIC);2486 if (err < 0)2487 return err;2488 imux->items[imux->num_items].label = auto_pin_cfg_labels[i];2489 imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);2490 imux->num_items++;2491 }2492 imux->items[imux->num_items].label = "Mix";2493 imux->items[imux->num_items].index = 9;2494 imux->num_items++;24952496 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,2497 "Analog Mix Playback Volume",2498 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)2499 return err;2500 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,2501 "Analog Mix Playback Switch",2502 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)2503 return err;25042505 return 0;2506}25072508static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,2509 hda_nid_t nid, int pin_type,2510 int dac_idx)2511{2512 /* set as output */2513 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);2514 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);2515 switch (nid) {2516 case 0x11: /* port-A - DAC 04 */2517 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);2518 break;2519 case 0x14: /* port-B - DAC 06 */2520 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);2521 break;2522 case 0x15: /* port-C - DAC 05 */2523 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);2524 break;2525 case 0x17: /* port-E - DAC 0a */2526 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);2527 break;2528 case 0x13: /* mono - DAC 04 */2529 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);2530 break;2531 }2532}25332534static void ad1988_auto_init_multi_out(struct hda_codec *codec)2535{2536 struct ad198x_spec *spec = codec->spec;2537 int i;25382539 for (i = 0; i < spec->autocfg.line_outs; i++) {2540 hda_nid_t nid = spec->autocfg.line_out_pins[i];2541 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);2542 }2543}25442545static void ad1988_auto_init_extra_out(struct hda_codec *codec)2546{2547 struct ad198x_spec *spec = codec->spec;2548 hda_nid_t pin;25492550 pin = spec->autocfg.speaker_pins[0];2551 if (pin) /* connect to front */2552 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);2553 pin = spec->autocfg.hp_pins[0];2554 if (pin) /* connect to front */2555 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);2556}25572558static void ad1988_auto_init_analog_input(struct hda_codec *codec)2559{2560 struct ad198x_spec *spec = codec->spec;2561 int i, idx;25622563 for (i = 0; i < AUTO_PIN_LAST; i++) {2564 hda_nid_t nid = spec->autocfg.input_pins[i];2565 if (! nid)2566 continue;2567 switch (nid) {2568 case 0x15: /* port-C */2569 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);2570 break;2571 case 0x17: /* port-E */2572 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);2573 break;2574 }2575 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,2576 i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);2577 if (nid != AD1988_PIN_CD_NID)2578 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,2579 AMP_OUT_MUTE);2580 idx = ad1988_pin_idx(nid);2581 if (ad1988_boost_nids[idx])2582 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,2583 AC_VERB_SET_AMP_GAIN_MUTE,2584 AMP_OUT_ZERO);2585 }2586}25872588/* parse the BIOS configuration and set up the alc_spec */2589/* return 1 if successful, 0 if the proper config is not found, or a negative error code */2590static int ad1988_parse_auto_config(struct hda_codec *codec)2591{2592 struct ad198x_spec *spec = codec->spec;2593 int err;25942595 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)2596 return err;2597 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)2598 return err;2599 if (! spec->autocfg.line_outs)2600 return 0; /* can't find valid BIOS pin config */2601 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||2602 (err = ad1988_auto_create_extra_out(codec,2603 spec->autocfg.speaker_pins[0],2604 "Speaker")) < 0 ||2605 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],2606 "Headphone")) < 0 ||2607 (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)2608 return err;26092610 spec->multiout.max_channels = spec->multiout.num_dacs * 2;26112612 if (spec->autocfg.dig_out_pin)2613 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;2614 if (spec->autocfg.dig_in_pin)2615 spec->dig_in_nid = AD1988_SPDIF_IN;26162617 if (spec->kctl_alloc)2618 spec->mixers[spec->num_mixers++] = spec->kctl_alloc;26192620 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;26212622 spec->input_mux = &spec->private_imux;26232624 return 1;2625}26262627/* init callback for auto-configuration model -- overriding the default init */2628static int ad1988_auto_init(struct hda_codec *codec)2629{2630 ad198x_init(codec);2631 ad1988_auto_init_multi_out(codec);2632 ad1988_auto_init_extra_out(codec);2633 ad1988_auto_init_analog_input(codec);2634 return 0;2635}263626372638/*2639 */26402641static const char *ad1988_models[AD1988_MODEL_LAST] = {2642 [AD1988_6STACK] = "6stack",2643 [AD1988_6STACK_DIG] = "6stack-dig",2644 [AD1988_3STACK] = "3stack",2645 [AD1988_3STACK_DIG] = "3stack-dig",2646 [AD1988_LAPTOP] = "laptop",2647 [AD1988_LAPTOP_DIG] = "laptop-dig",2648 [AD1988_AUTO] = "auto",2649};26502651static struct snd_pci_quirk ad1988_cfg_tbl[] = {2652 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),2653 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),2654 {}2655};26562657static int patch_ad1988(struct hda_codec *codec)2658{2659 struct ad198x_spec *spec;2660 int board_config;26612662 spec = kzalloc(sizeof(*spec), GFP_KERNEL);2663 if (spec == NULL)2664 return -ENOMEM;26652666 mutex_init(&spec->amp_mutex);2667 codec->spec = spec;26682669 if (is_rev2(codec))2670 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");26712672 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,2673 ad1988_models, ad1988_cfg_tbl);2674 if (board_config < 0) {2675 printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");2676 board_config = AD1988_AUTO;2677 }26782679 if (board_config == AD1988_AUTO) {2680 /* automatic parse from the BIOS config */2681 int err = ad1988_parse_auto_config(codec);2682 if (err < 0) {2683 ad198x_free(codec);2684 return err;2685 } else if (! err) {2686 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");2687 board_config = AD1988_6STACK;2688 }2689 }26902691 switch (board_config) {2692 case AD1988_6STACK:2693 case AD1988_6STACK_DIG:2694 spec->multiout.max_channels = 8;2695 spec->multiout.num_dacs = 4;2696 if (is_rev2(codec))2697 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;2698 else2699 spec->multiout.dac_nids = ad1988_6stack_dac_nids;2700 spec->input_mux = &ad1988_6stack_capture_source;2701 spec->num_mixers = 2;2702 if (is_rev2(codec))2703 spec->mixers[0] = ad1988_6stack_mixers1_rev2;2704 else2705 spec->mixers[0] = ad1988_6stack_mixers1;2706 spec->mixers[1] = ad1988_6stack_mixers2;2707 spec->num_init_verbs = 1;2708 spec->init_verbs[0] = ad1988_6stack_init_verbs;2709 if (board_config == AD1988_6STACK_DIG) {2710 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;2711 spec->dig_in_nid = AD1988_SPDIF_IN;2712 }2713 break;2714 case AD1988_3STACK:2715 case AD1988_3STACK_DIG:2716 spec->multiout.max_channels = 6;2717 spec->multiout.num_dacs = 3;2718 if (is_rev2(codec))2719 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;2720 else2721 spec->multiout.dac_nids = ad1988_3stack_dac_nids;2722 spec->input_mux = &ad1988_6stack_capture_source;2723 spec->channel_mode = ad1988_3stack_modes;2724 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);2725 spec->num_mixers = 2;2726 if (is_rev2(codec))2727 spec->mixers[0] = ad1988_3stack_mixers1_rev2;2728 else2729 spec->mixers[0] = ad1988_3stack_mixers1;2730 spec->mixers[1] = ad1988_3stack_mixers2;2731 spec->num_init_verbs = 1;2732 spec->init_verbs[0] = ad1988_3stack_init_verbs;2733 if (board_config == AD1988_3STACK_DIG)2734 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;2735 break;2736 case AD1988_LAPTOP:2737 case AD1988_LAPTOP_DIG:2738 spec->multiout.max_channels = 2;2739 spec->multiout.num_dacs = 1;2740 spec->multiout.dac_nids = ad1988_3stack_dac_nids;2741 spec->input_mux = &ad1988_laptop_capture_source;2742 spec->num_mixers = 1;2743 spec->mixers[0] = ad1988_laptop_mixers;2744 spec->num_init_verbs = 1;2745 spec->init_verbs[0] = ad1988_laptop_init_verbs;2746 if (board_config == AD1988_LAPTOP_DIG)2747 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;2748 break;2749 }27502751 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);2752 spec->adc_nids = ad1988_adc_nids;2753 spec->capsrc_nids = ad1988_capsrc_nids;2754 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;2755 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;2756 if (spec->multiout.dig_out_nid) {2757 spec->mixers[spec->num_mixers++] = ad1988_spdif_out_mixers;2758 spec->init_verbs[spec->num_init_verbs++] = ad1988_spdif_init_verbs;2759 }2760 if (spec->dig_in_nid)2761 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;27622763 codec->patch_ops = ad198x_patch_ops;2764 switch (board_config) {2765 case AD1988_AUTO:2766 codec->patch_ops.init = ad1988_auto_init;2767 break;2768 case AD1988_LAPTOP:2769 case AD1988_LAPTOP_DIG:2770 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;2771 break;2772 }27732774 return 0;2775}277627772778/*2779 * AD1884 / AD19842780 *2781 * port-B - front line/mic-in2782 * port-E - aux in/out2783 * port-F - aux in/out2784 * port-C - rear line/mic-in2785 * port-D - rear line/hp-out2786 * port-A - front line/hp-out2787 *2788 * AD1984 = AD1884 + two digital mic-ins2789 *2790 * FIXME:2791 * For simplicity, we share the single DAC for both HP and line-outs2792 * right now. The inidividual playbacks could be easily implemented,2793 * but no build-up framework is given, so far.2794 */27952796static hda_nid_t ad1884_dac_nids[1] = {2797 0x04,2798};27992800static hda_nid_t ad1884_adc_nids[2] = {2801 0x08, 0x09,2802};28032804static hda_nid_t ad1884_capsrc_nids[2] = {2805 0x0c, 0x0d,2806};28072808#define AD1884_SPDIF_OUT 0x0228092810static struct hda_input_mux ad1884_capture_source = {2811 .num_items = 4,2812 .items = {2813 { "Front Mic", 0x0 },2814 { "Mic", 0x1 },2815 { "CD", 0x2 },2816 { "Mix", 0x3 },2817 },2818};28192820static struct snd_kcontrol_new ad1884_base_mixers[] = {2821 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),2822 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */2823 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),2824 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),2825 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),2826 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),2827 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),2828 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),2829 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),2830 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),2831 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),2832 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),2833 /*2834 HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),2835 HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),2836 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),2837 HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),2838 */2839 HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),2840 HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),2841 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),2842 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),2843 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),2844 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),2845 {2846 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,2847 /* The multiple "Capture Source" controls confuse alsamixer2848 * So call somewhat different..2849 * FIXME: the controls appear in the "playback" view!2850 */2851 /* .name = "Capture Source", */2852 .name = "Input Source",2853 .count = 2,2854 .info = ad198x_mux_enum_info,2855 .get = ad198x_mux_enum_get,2856 .put = ad198x_mux_enum_put,2857 },2858 /* SPDIF controls */2859 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),2860 {2861 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,2862 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",2863 /* identical with ad1983 */2864 .info = ad1983_spdif_route_info,2865 .get = ad1983_spdif_route_get,2866 .put = ad1983_spdif_route_put,2867 },2868 { } /* end */2869};28702871static struct snd_kcontrol_new ad1984_dmic_mixers[] = {2872 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),2873 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),2874 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,2875 HDA_OUTPUT),2876 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,2877 HDA_OUTPUT),2878 { } /* end */2879};28802881/*2882 * initialization verbs2883 */2884static struct hda_verb ad1884_init_verbs[] = {2885 /* DACs; mute as default */2886 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},2887 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},2888 /* Port-A (HP) mixer */2889 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},2890 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},2891 /* Port-A pin */2892 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},2893 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2894 /* HP selector - select DAC2 */2895 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},2896 /* Port-D (Line-out) mixer */2897 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},2898 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},2899 /* Port-D pin */2900 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},2901 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2902 /* Mono-out mixer */2903 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},2904 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},2905 /* Mono-out pin */2906 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},2907 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2908 /* Mono selector */2909 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},2910 /* Port-B (front mic) pin */2911 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},2912 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2913 /* Port-C (rear mic) pin */2914 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},2915 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},2916 /* Analog mixer; mute as default */2917 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},2918 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},2919 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},2920 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},2921 /* Analog Mix output amp */2922 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */2923 /* SPDIF output selector */2924 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */2925 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */2926 { } /* end */2927};29282929static int patch_ad1884(struct hda_codec *codec)2930{2931 struct ad198x_spec *spec;29322933 spec = kzalloc(sizeof(*spec), GFP_KERNEL);2934 if (spec == NULL)2935 return -ENOMEM;29362937 mutex_init(&spec->amp_mutex);2938 codec->spec = spec;29392940 spec->multiout.max_channels = 2;2941 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);2942 spec->multiout.dac_nids = ad1884_dac_nids;2943 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;2944 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);2945 spec->adc_nids = ad1884_adc_nids;2946 spec->capsrc_nids = ad1884_capsrc_nids;2947 spec->input_mux = &ad1884_capture_source;2948 spec->num_mixers = 1;2949 spec->mixers[0] = ad1884_base_mixers;2950 spec->num_init_verbs = 1;2951 spec->init_verbs[0] = ad1884_init_verbs;2952 spec->spdif_route = 0;29532954 codec->patch_ops = ad198x_patch_ops;29552956 return 0;2957}29582959/*2960 * Lenovo Thinkpad T61/X612961 */2962static struct hda_input_mux ad1984_thinkpad_capture_source = {2963 .num_items = 3,2964 .items = {2965 { "Mic", 0x0 },2966 { "Internal Mic", 0x1 },2967 { "Mix", 0x3 },2968 },2969};29702971static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {2972 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),2973 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */2974 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),2975 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),2976 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),2977 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),2978 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),2979 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),2980 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),2981 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),2982 HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),2983 HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),2984 HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),2985 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),2986 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),2987 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),2988 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),2989 {2990 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,2991 /* The multiple "Capture Source" controls confuse alsamixer2992 * So call somewhat different..2993 * FIXME: the controls appear in the "playback" view!2994 */2995 /* .name = "Capture Source", */2996 .name = "Input Source",2997 .count = 2,2998 .info = ad198x_mux_enum_info,2999 .get = ad198x_mux_enum_get,3000 .put = ad198x_mux_enum_put,3001 },3002 { } /* end */3003};30043005/* additional verbs */3006static struct hda_verb ad1984_thinkpad_init_verbs[] = {3007 /* Port-E (docking station mic) pin */3008 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},3009 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},3010 /* docking mic boost */3011 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},3012 /* Analog mixer - docking mic; mute as default */3013 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},3014 { } /* end */3015};30163017/* Digial MIC ADC NID 0x05 + 0x06 */3018static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,3019 struct hda_codec *codec,3020 unsigned int stream_tag,3021 unsigned int format,3022 struct snd_pcm_substream *substream)3023{3024 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,3025 stream_tag, 0, format);3026 return 0;3027}30283029static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,3030 struct hda_codec *codec,3031 struct snd_pcm_substream *substream)3032{3033 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,3034 0, 0, 0);3035 return 0;3036}30373038static struct hda_pcm_stream ad1984_pcm_dmic_capture = {3039 .substreams = 2,3040 .channels_min = 2,3041 .channels_max = 2,3042 .nid = 0x05,3043 .ops = {3044 .prepare = ad1984_pcm_dmic_prepare,3045 .cleanup = ad1984_pcm_dmic_cleanup3046 },3047};30483049static int ad1984_build_pcms(struct hda_codec *codec)3050{3051 struct ad198x_spec *spec = codec->spec;3052 struct hda_pcm *info;3053 int err;30543055 err = ad198x_build_pcms(codec);3056 if (err < 0)3057 return err;30583059 info = spec->pcm_rec + codec->num_pcms;3060 codec->num_pcms++;3061 info->name = "AD1984 Digital Mic";3062 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;3063 return 0;3064}30653066/* models */3067enum {3068 AD1984_BASIC,3069 AD1984_THINKPAD,3070 AD1984_MODELS3071};30723073static const char *ad1984_models[AD1984_MODELS] = {3074 [AD1984_BASIC] = "basic",3075 [AD1984_THINKPAD] = "thinkpad",3076};30773078static struct snd_pci_quirk ad1984_cfg_tbl[] = {3079 /* Lenovo Thinkpad T61/X61 */3080 SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),3081 {}3082};30833084static int patch_ad1984(struct hda_codec *codec)3085{3086 struct ad198x_spec *spec;3087 int board_config, err;30883089 err = patch_ad1884(codec);3090 if (err < 0)3091 return err;3092 spec = codec->spec;3093 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,3094 ad1984_models, ad1984_cfg_tbl);3095 switch (board_config) {3096 case AD1984_BASIC:3097 /* additional digital mics */3098 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;3099 codec->patch_ops.build_pcms = ad1984_build_pcms;3100 break;3101 case AD1984_THINKPAD:3102 spec->multiout.dig_out_nid = 0;3103 spec->input_mux = &ad1984_thinkpad_capture_source;3104 spec->mixers[0] = ad1984_thinkpad_mixers;3105 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;3106 break;3107 }3108 return 0;3109}311031113112/*3113 * patch entries3114 */3115struct hda_codec_preset snd_hda_preset_analog[] = {3116 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },3117 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },3118 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },3119 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },3120 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },3121 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },3122 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },3123 {} /* terminator */3124};