14e01f54bSTakashi Iwai /* 24e01f54bSTakashi Iwai * HD audio interface patch for Creative X-Fi CA0110-IBG chip 34e01f54bSTakashi Iwai * 44e01f54bSTakashi Iwai * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de> 54e01f54bSTakashi Iwai * 64e01f54bSTakashi Iwai * This driver is free software; you can redistribute it and/or modify 74e01f54bSTakashi Iwai * it under the terms of the GNU General Public License as published by 84e01f54bSTakashi Iwai * the Free Software Foundation; either version 2 of the License, or 94e01f54bSTakashi Iwai * (at your option) any later version. 104e01f54bSTakashi Iwai * 114e01f54bSTakashi Iwai * This driver is distributed in the hope that it will be useful, 124e01f54bSTakashi Iwai * but WITHOUT ANY WARRANTY; without even the implied warranty of 134e01f54bSTakashi Iwai * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 144e01f54bSTakashi Iwai * GNU General Public License for more details. 154e01f54bSTakashi Iwai * 164e01f54bSTakashi Iwai * You should have received a copy of the GNU General Public License 174e01f54bSTakashi Iwai * along with this program; if not, write to the Free Software 184e01f54bSTakashi Iwai * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 194e01f54bSTakashi Iwai */ 204e01f54bSTakashi Iwai 214e01f54bSTakashi Iwai #include <linux/init.h> 224e01f54bSTakashi Iwai #include <linux/delay.h> 234e01f54bSTakashi Iwai #include <linux/slab.h> 244e01f54bSTakashi Iwai #include <linux/pci.h> 254e01f54bSTakashi Iwai #include <sound/core.h> 264e01f54bSTakashi Iwai #include "hda_codec.h" 274e01f54bSTakashi Iwai #include "hda_local.h" 284e01f54bSTakashi Iwai 294e01f54bSTakashi Iwai /* 304e01f54bSTakashi Iwai */ 314e01f54bSTakashi Iwai 324e01f54bSTakashi Iwai struct ca0110_spec { 334e01f54bSTakashi Iwai struct auto_pin_cfg autocfg; 344e01f54bSTakashi Iwai struct hda_multi_out multiout; 354e01f54bSTakashi Iwai hda_nid_t out_pins[AUTO_CFG_MAX_OUTS]; 364e01f54bSTakashi Iwai hda_nid_t dacs[AUTO_CFG_MAX_OUTS]; 374e01f54bSTakashi Iwai hda_nid_t hp_dac; 384e01f54bSTakashi Iwai hda_nid_t input_pins[AUTO_PIN_LAST]; 394e01f54bSTakashi Iwai hda_nid_t adcs[AUTO_PIN_LAST]; 404e01f54bSTakashi Iwai hda_nid_t dig_out; 414e01f54bSTakashi Iwai hda_nid_t dig_in; 424e01f54bSTakashi Iwai unsigned int num_inputs; 434e01f54bSTakashi Iwai const char *input_labels[AUTO_PIN_LAST]; 444e01f54bSTakashi Iwai struct hda_pcm pcm_rec[2]; /* PCM information */ 454e01f54bSTakashi Iwai }; 464e01f54bSTakashi Iwai 474e01f54bSTakashi Iwai /* 484e01f54bSTakashi Iwai * PCM callbacks 494e01f54bSTakashi Iwai */ 504e01f54bSTakashi Iwai static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo, 514e01f54bSTakashi Iwai struct hda_codec *codec, 524e01f54bSTakashi Iwai struct snd_pcm_substream *substream) 534e01f54bSTakashi Iwai { 544e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 554e01f54bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 564e01f54bSTakashi Iwai hinfo); 574e01f54bSTakashi Iwai } 584e01f54bSTakashi Iwai 594e01f54bSTakashi Iwai static int ca0110_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 604e01f54bSTakashi Iwai struct hda_codec *codec, 614e01f54bSTakashi Iwai unsigned int stream_tag, 624e01f54bSTakashi Iwai unsigned int format, 634e01f54bSTakashi Iwai struct snd_pcm_substream *substream) 644e01f54bSTakashi Iwai { 654e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 664e01f54bSTakashi Iwai return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, 674e01f54bSTakashi Iwai stream_tag, format, substream); 684e01f54bSTakashi Iwai } 694e01f54bSTakashi Iwai 704e01f54bSTakashi Iwai static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 714e01f54bSTakashi Iwai struct hda_codec *codec, 724e01f54bSTakashi Iwai struct snd_pcm_substream *substream) 734e01f54bSTakashi Iwai { 744e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 754e01f54bSTakashi Iwai return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 764e01f54bSTakashi Iwai } 774e01f54bSTakashi Iwai 784e01f54bSTakashi Iwai /* 794e01f54bSTakashi Iwai * Digital out 804e01f54bSTakashi Iwai */ 814e01f54bSTakashi Iwai static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 824e01f54bSTakashi Iwai struct hda_codec *codec, 834e01f54bSTakashi Iwai struct snd_pcm_substream *substream) 844e01f54bSTakashi Iwai { 854e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 864e01f54bSTakashi Iwai return snd_hda_multi_out_dig_open(codec, &spec->multiout); 874e01f54bSTakashi Iwai } 884e01f54bSTakashi Iwai 894e01f54bSTakashi Iwai static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 904e01f54bSTakashi Iwai struct hda_codec *codec, 914e01f54bSTakashi Iwai struct snd_pcm_substream *substream) 924e01f54bSTakashi Iwai { 934e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 944e01f54bSTakashi Iwai return snd_hda_multi_out_dig_close(codec, &spec->multiout); 954e01f54bSTakashi Iwai } 964e01f54bSTakashi Iwai 974e01f54bSTakashi Iwai static int ca0110_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 984e01f54bSTakashi Iwai struct hda_codec *codec, 994e01f54bSTakashi Iwai unsigned int stream_tag, 1004e01f54bSTakashi Iwai unsigned int format, 1014e01f54bSTakashi Iwai struct snd_pcm_substream *substream) 1024e01f54bSTakashi Iwai { 1034e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 1044e01f54bSTakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, 1054e01f54bSTakashi Iwai format, substream); 1064e01f54bSTakashi Iwai } 1074e01f54bSTakashi Iwai 1084e01f54bSTakashi Iwai /* 1094e01f54bSTakashi Iwai * Analog capture 1104e01f54bSTakashi Iwai */ 1114e01f54bSTakashi Iwai static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1124e01f54bSTakashi Iwai struct hda_codec *codec, 1134e01f54bSTakashi Iwai unsigned int stream_tag, 1144e01f54bSTakashi Iwai unsigned int format, 1154e01f54bSTakashi Iwai struct snd_pcm_substream *substream) 1164e01f54bSTakashi Iwai { 1174e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 1184e01f54bSTakashi Iwai 1194e01f54bSTakashi Iwai snd_hda_codec_setup_stream(codec, spec->adcs[substream->number], 1204e01f54bSTakashi Iwai stream_tag, 0, format); 1214e01f54bSTakashi Iwai return 0; 1224e01f54bSTakashi Iwai } 1234e01f54bSTakashi Iwai 1244e01f54bSTakashi Iwai static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1254e01f54bSTakashi Iwai struct hda_codec *codec, 1264e01f54bSTakashi Iwai struct snd_pcm_substream *substream) 1274e01f54bSTakashi Iwai { 1284e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 1294e01f54bSTakashi Iwai 1304e01f54bSTakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]); 1314e01f54bSTakashi Iwai return 0; 1324e01f54bSTakashi Iwai } 1334e01f54bSTakashi Iwai 1344e01f54bSTakashi Iwai /* 1354e01f54bSTakashi Iwai */ 1364e01f54bSTakashi Iwai 1374e01f54bSTakashi Iwai static char *dirstr[2] = { "Playback", "Capture" }; 1384e01f54bSTakashi Iwai 1394e01f54bSTakashi Iwai static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, 1404e01f54bSTakashi Iwai int chan, int dir) 1414e01f54bSTakashi Iwai { 1424e01f54bSTakashi Iwai char namestr[44]; 1434e01f54bSTakashi Iwai int type = dir ? HDA_INPUT : HDA_OUTPUT; 1444e01f54bSTakashi Iwai struct snd_kcontrol_new knew = 1454e01f54bSTakashi Iwai HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type); 1464e01f54bSTakashi Iwai sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); 1474e01f54bSTakashi Iwai return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); 1484e01f54bSTakashi Iwai } 1494e01f54bSTakashi Iwai 1504e01f54bSTakashi Iwai static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, 1514e01f54bSTakashi Iwai int chan, int dir) 1524e01f54bSTakashi Iwai { 1534e01f54bSTakashi Iwai char namestr[44]; 1544e01f54bSTakashi Iwai int type = dir ? HDA_INPUT : HDA_OUTPUT; 1554e01f54bSTakashi Iwai struct snd_kcontrol_new knew = 1564e01f54bSTakashi Iwai HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); 1574e01f54bSTakashi Iwai sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); 1584e01f54bSTakashi Iwai return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); 1594e01f54bSTakashi Iwai } 1604e01f54bSTakashi Iwai 1614e01f54bSTakashi Iwai #define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) 1624e01f54bSTakashi Iwai #define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0) 1634e01f54bSTakashi Iwai #define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1) 1644e01f54bSTakashi Iwai #define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1) 1654e01f54bSTakashi Iwai #define add_mono_switch(codec, nid, pfx, chan) \ 1664e01f54bSTakashi Iwai _add_switch(codec, nid, pfx, chan, 0) 1674e01f54bSTakashi Iwai #define add_mono_volume(codec, nid, pfx, chan) \ 1684e01f54bSTakashi Iwai _add_volume(codec, nid, pfx, chan, 0) 1694e01f54bSTakashi Iwai 1704e01f54bSTakashi Iwai static int ca0110_build_controls(struct hda_codec *codec) 1714e01f54bSTakashi Iwai { 1724e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 1734e01f54bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 1744e01f54bSTakashi Iwai static char *prefix[AUTO_CFG_MAX_OUTS] = { 1754e01f54bSTakashi Iwai "Front", "Surround", NULL, "Side", "Multi" 1764e01f54bSTakashi Iwai }; 1774e01f54bSTakashi Iwai hda_nid_t mutenid; 1784e01f54bSTakashi Iwai int i, err; 1794e01f54bSTakashi Iwai 1804e01f54bSTakashi Iwai for (i = 0; i < spec->multiout.num_dacs; i++) { 1814e01f54bSTakashi Iwai if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP) 1824e01f54bSTakashi Iwai mutenid = spec->out_pins[i]; 1834e01f54bSTakashi Iwai else 1844e01f54bSTakashi Iwai mutenid = spec->multiout.dac_nids[i]; 1854e01f54bSTakashi Iwai if (!prefix[i]) { 1864e01f54bSTakashi Iwai err = add_mono_switch(codec, mutenid, 1874e01f54bSTakashi Iwai "Center", 1); 1884e01f54bSTakashi Iwai if (err < 0) 1894e01f54bSTakashi Iwai return err; 1904e01f54bSTakashi Iwai err = add_mono_switch(codec, mutenid, 1914e01f54bSTakashi Iwai "LFE", 1); 1924e01f54bSTakashi Iwai if (err < 0) 1934e01f54bSTakashi Iwai return err; 1944e01f54bSTakashi Iwai err = add_mono_volume(codec, spec->multiout.dac_nids[i], 1954e01f54bSTakashi Iwai "Center", 1); 1964e01f54bSTakashi Iwai if (err < 0) 1974e01f54bSTakashi Iwai return err; 1984e01f54bSTakashi Iwai err = add_mono_volume(codec, spec->multiout.dac_nids[i], 1994e01f54bSTakashi Iwai "LFE", 1); 2004e01f54bSTakashi Iwai if (err < 0) 2014e01f54bSTakashi Iwai return err; 2024e01f54bSTakashi Iwai } else { 2034e01f54bSTakashi Iwai err = add_out_switch(codec, mutenid, 2044e01f54bSTakashi Iwai prefix[i]); 2054e01f54bSTakashi Iwai if (err < 0) 2064e01f54bSTakashi Iwai return err; 2074e01f54bSTakashi Iwai err = add_out_volume(codec, spec->multiout.dac_nids[i], 2084e01f54bSTakashi Iwai prefix[i]); 2094e01f54bSTakashi Iwai if (err < 0) 2104e01f54bSTakashi Iwai return err; 2114e01f54bSTakashi Iwai } 2124e01f54bSTakashi Iwai } 2134e01f54bSTakashi Iwai if (cfg->hp_outs) { 2144e01f54bSTakashi Iwai if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP) 2154e01f54bSTakashi Iwai mutenid = cfg->hp_pins[0]; 2164e01f54bSTakashi Iwai else 2174e01f54bSTakashi Iwai mutenid = spec->multiout.dac_nids[i]; 2184e01f54bSTakashi Iwai 2194e01f54bSTakashi Iwai err = add_out_switch(codec, mutenid, "Headphone"); 2204e01f54bSTakashi Iwai if (err < 0) 2214e01f54bSTakashi Iwai return err; 2224e01f54bSTakashi Iwai if (spec->hp_dac) { 2234e01f54bSTakashi Iwai err = add_out_volume(codec, spec->hp_dac, "Headphone"); 2244e01f54bSTakashi Iwai if (err < 0) 2254e01f54bSTakashi Iwai return err; 2264e01f54bSTakashi Iwai } 2274e01f54bSTakashi Iwai } 2284e01f54bSTakashi Iwai for (i = 0; i < spec->num_inputs; i++) { 2294e01f54bSTakashi Iwai const char *label = spec->input_labels[i]; 2304e01f54bSTakashi Iwai if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP) 2314e01f54bSTakashi Iwai mutenid = spec->input_pins[i]; 2324e01f54bSTakashi Iwai else 2334e01f54bSTakashi Iwai mutenid = spec->adcs[i]; 2344e01f54bSTakashi Iwai err = add_in_switch(codec, mutenid, label); 2354e01f54bSTakashi Iwai if (err < 0) 2364e01f54bSTakashi Iwai return err; 2374e01f54bSTakashi Iwai err = add_in_volume(codec, spec->adcs[i], label); 2384e01f54bSTakashi Iwai if (err < 0) 2394e01f54bSTakashi Iwai return err; 2404e01f54bSTakashi Iwai } 2414e01f54bSTakashi Iwai 2424e01f54bSTakashi Iwai if (spec->dig_out) { 2434e01f54bSTakashi Iwai err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out); 2444e01f54bSTakashi Iwai if (err < 0) 2454e01f54bSTakashi Iwai return err; 2464e01f54bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); 2474e01f54bSTakashi Iwai if (err < 0) 2484e01f54bSTakashi Iwai return err; 2494e01f54bSTakashi Iwai spec->multiout.share_spdif = 1; 2504e01f54bSTakashi Iwai } 2514e01f54bSTakashi Iwai if (spec->dig_in) { 2524e01f54bSTakashi Iwai err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); 2534e01f54bSTakashi Iwai if (err < 0) 2544e01f54bSTakashi Iwai return err; 2554e01f54bSTakashi Iwai err = add_in_volume(codec, spec->dig_in, "IEC958"); 2564e01f54bSTakashi Iwai } 2574e01f54bSTakashi Iwai return 0; 2584e01f54bSTakashi Iwai } 2594e01f54bSTakashi Iwai 2604e01f54bSTakashi Iwai /* 2614e01f54bSTakashi Iwai */ 2624e01f54bSTakashi Iwai static struct hda_pcm_stream ca0110_pcm_analog_playback = { 2634e01f54bSTakashi Iwai .substreams = 1, 2644e01f54bSTakashi Iwai .channels_min = 2, 2654e01f54bSTakashi Iwai .channels_max = 8, 2664e01f54bSTakashi Iwai .ops = { 2674e01f54bSTakashi Iwai .open = ca0110_playback_pcm_open, 2684e01f54bSTakashi Iwai .prepare = ca0110_playback_pcm_prepare, 2694e01f54bSTakashi Iwai .cleanup = ca0110_playback_pcm_cleanup 2704e01f54bSTakashi Iwai }, 2714e01f54bSTakashi Iwai }; 2724e01f54bSTakashi Iwai 2734e01f54bSTakashi Iwai static struct hda_pcm_stream ca0110_pcm_analog_capture = { 2744e01f54bSTakashi Iwai .substreams = 1, 2754e01f54bSTakashi Iwai .channels_min = 2, 2764e01f54bSTakashi Iwai .channels_max = 2, 2774e01f54bSTakashi Iwai .ops = { 2784e01f54bSTakashi Iwai .prepare = ca0110_capture_pcm_prepare, 2794e01f54bSTakashi Iwai .cleanup = ca0110_capture_pcm_cleanup 2804e01f54bSTakashi Iwai }, 2814e01f54bSTakashi Iwai }; 2824e01f54bSTakashi Iwai 2834e01f54bSTakashi Iwai static struct hda_pcm_stream ca0110_pcm_digital_playback = { 2844e01f54bSTakashi Iwai .substreams = 1, 2854e01f54bSTakashi Iwai .channels_min = 2, 2864e01f54bSTakashi Iwai .channels_max = 2, 2874e01f54bSTakashi Iwai .ops = { 2884e01f54bSTakashi Iwai .open = ca0110_dig_playback_pcm_open, 2894e01f54bSTakashi Iwai .close = ca0110_dig_playback_pcm_close, 2904e01f54bSTakashi Iwai .prepare = ca0110_dig_playback_pcm_prepare 2914e01f54bSTakashi Iwai }, 2924e01f54bSTakashi Iwai }; 2934e01f54bSTakashi Iwai 2944e01f54bSTakashi Iwai static struct hda_pcm_stream ca0110_pcm_digital_capture = { 2954e01f54bSTakashi Iwai .substreams = 1, 2964e01f54bSTakashi Iwai .channels_min = 2, 2974e01f54bSTakashi Iwai .channels_max = 2, 2984e01f54bSTakashi Iwai }; 2994e01f54bSTakashi Iwai 3004e01f54bSTakashi Iwai static int ca0110_build_pcms(struct hda_codec *codec) 3014e01f54bSTakashi Iwai { 3024e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 3034e01f54bSTakashi Iwai struct hda_pcm *info = spec->pcm_rec; 3044e01f54bSTakashi Iwai 3054e01f54bSTakashi Iwai codec->pcm_info = info; 3064e01f54bSTakashi Iwai codec->num_pcms = 0; 3074e01f54bSTakashi Iwai 3084e01f54bSTakashi Iwai info->name = "CA0110 Analog"; 3094e01f54bSTakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback; 3104e01f54bSTakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; 3114e01f54bSTakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 31267667263STakashi Iwai spec->multiout.max_channels; 3134e01f54bSTakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture; 3144e01f54bSTakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs; 3154e01f54bSTakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; 3164e01f54bSTakashi Iwai codec->num_pcms++; 3174e01f54bSTakashi Iwai 3184e01f54bSTakashi Iwai if (!spec->dig_out && !spec->dig_in) 3194e01f54bSTakashi Iwai return 0; 3204e01f54bSTakashi Iwai 3214e01f54bSTakashi Iwai info++; 3224e01f54bSTakashi Iwai info->name = "CA0110 Digital"; 3234e01f54bSTakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 3244e01f54bSTakashi Iwai if (spec->dig_out) { 3254e01f54bSTakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 3264e01f54bSTakashi Iwai ca0110_pcm_digital_playback; 3274e01f54bSTakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; 3284e01f54bSTakashi Iwai } 3294e01f54bSTakashi Iwai if (spec->dig_in) { 3304e01f54bSTakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = 3314e01f54bSTakashi Iwai ca0110_pcm_digital_capture; 3324e01f54bSTakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; 3334e01f54bSTakashi Iwai } 3344e01f54bSTakashi Iwai codec->num_pcms++; 3354e01f54bSTakashi Iwai 3364e01f54bSTakashi Iwai return 0; 3374e01f54bSTakashi Iwai } 3384e01f54bSTakashi Iwai 3394e01f54bSTakashi Iwai static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) 3404e01f54bSTakashi Iwai { 3414e01f54bSTakashi Iwai if (pin) { 3424e01f54bSTakashi Iwai snd_hda_codec_write(codec, pin, 0, 3434e01f54bSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); 3444e01f54bSTakashi Iwai if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) 3454e01f54bSTakashi Iwai snd_hda_codec_write(codec, pin, 0, 3464e01f54bSTakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 3474e01f54bSTakashi Iwai AMP_OUT_UNMUTE); 3484e01f54bSTakashi Iwai } 3494e01f54bSTakashi Iwai if (dac) 3504e01f54bSTakashi Iwai snd_hda_codec_write(codec, dac, 0, 3514e01f54bSTakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); 3524e01f54bSTakashi Iwai } 3534e01f54bSTakashi Iwai 3544e01f54bSTakashi Iwai static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) 3554e01f54bSTakashi Iwai { 3564e01f54bSTakashi Iwai if (pin) { 3574e01f54bSTakashi Iwai snd_hda_codec_write(codec, pin, 0, 3584e01f54bSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80); 3594e01f54bSTakashi Iwai if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) 3604e01f54bSTakashi Iwai snd_hda_codec_write(codec, pin, 0, 3614e01f54bSTakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 3624e01f54bSTakashi Iwai AMP_IN_UNMUTE(0)); 3634e01f54bSTakashi Iwai } 3644e01f54bSTakashi Iwai if (adc) 3654e01f54bSTakashi Iwai snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE, 3664e01f54bSTakashi Iwai AMP_IN_UNMUTE(0)); 3674e01f54bSTakashi Iwai } 3684e01f54bSTakashi Iwai 3694e01f54bSTakashi Iwai static int ca0110_init(struct hda_codec *codec) 3704e01f54bSTakashi Iwai { 3714e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 3724e01f54bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 3734e01f54bSTakashi Iwai int i; 3744e01f54bSTakashi Iwai 3754e01f54bSTakashi Iwai for (i = 0; i < spec->multiout.num_dacs; i++) 3764e01f54bSTakashi Iwai init_output(codec, spec->out_pins[i], 3774e01f54bSTakashi Iwai spec->multiout.dac_nids[i]); 3784e01f54bSTakashi Iwai init_output(codec, cfg->hp_pins[0], spec->hp_dac); 3794e01f54bSTakashi Iwai init_output(codec, cfg->dig_out_pins[0], spec->dig_out); 3804e01f54bSTakashi Iwai 3814e01f54bSTakashi Iwai for (i = 0; i < spec->num_inputs; i++) 3824e01f54bSTakashi Iwai init_input(codec, spec->input_pins[i], spec->adcs[i]); 3834e01f54bSTakashi Iwai init_input(codec, cfg->dig_in_pin, spec->dig_in); 3844e01f54bSTakashi Iwai return 0; 3854e01f54bSTakashi Iwai } 3864e01f54bSTakashi Iwai 3874e01f54bSTakashi Iwai static void ca0110_free(struct hda_codec *codec) 3884e01f54bSTakashi Iwai { 3894e01f54bSTakashi Iwai kfree(codec->spec); 3904e01f54bSTakashi Iwai } 3914e01f54bSTakashi Iwai 3924e01f54bSTakashi Iwai static struct hda_codec_ops ca0110_patch_ops = { 3934e01f54bSTakashi Iwai .build_controls = ca0110_build_controls, 3944e01f54bSTakashi Iwai .build_pcms = ca0110_build_pcms, 3954e01f54bSTakashi Iwai .init = ca0110_init, 3964e01f54bSTakashi Iwai .free = ca0110_free, 3974e01f54bSTakashi Iwai }; 3984e01f54bSTakashi Iwai 3994e01f54bSTakashi Iwai 4004e01f54bSTakashi Iwai static void parse_line_outs(struct hda_codec *codec) 4014e01f54bSTakashi Iwai { 4024e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 4034e01f54bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 4044e01f54bSTakashi Iwai int i, n; 4054e01f54bSTakashi Iwai unsigned int def_conf; 4064e01f54bSTakashi Iwai hda_nid_t nid; 4074e01f54bSTakashi Iwai 4084e01f54bSTakashi Iwai n = 0; 4094e01f54bSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 4104e01f54bSTakashi Iwai nid = cfg->line_out_pins[i]; 4117670dc41STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 4124e01f54bSTakashi Iwai if (!def_conf) 4134e01f54bSTakashi Iwai continue; /* invalid pin */ 4144e01f54bSTakashi Iwai if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1) 4154e01f54bSTakashi Iwai continue; 4164e01f54bSTakashi Iwai spec->out_pins[n++] = nid; 4174e01f54bSTakashi Iwai } 4184e01f54bSTakashi Iwai spec->multiout.dac_nids = spec->dacs; 4194e01f54bSTakashi Iwai spec->multiout.num_dacs = n; 42067667263STakashi Iwai spec->multiout.max_channels = n * 2; 4214e01f54bSTakashi Iwai } 4224e01f54bSTakashi Iwai 4234e01f54bSTakashi Iwai static void parse_hp_out(struct hda_codec *codec) 4244e01f54bSTakashi Iwai { 4254e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 4264e01f54bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 4274e01f54bSTakashi Iwai int i; 4284e01f54bSTakashi Iwai unsigned int def_conf; 4294e01f54bSTakashi Iwai hda_nid_t nid, dac; 4304e01f54bSTakashi Iwai 4314e01f54bSTakashi Iwai if (!cfg->hp_outs) 4324e01f54bSTakashi Iwai return; 4334e01f54bSTakashi Iwai nid = cfg->hp_pins[0]; 4347670dc41STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 4354e01f54bSTakashi Iwai if (!def_conf) { 4364e01f54bSTakashi Iwai cfg->hp_outs = 0; 4374e01f54bSTakashi Iwai return; 4384e01f54bSTakashi Iwai } 4394e01f54bSTakashi Iwai if (snd_hda_get_connections(codec, nid, &dac, 1) != 1) 4404e01f54bSTakashi Iwai return; 4414e01f54bSTakashi Iwai 4424e01f54bSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) 4434e01f54bSTakashi Iwai if (dac == spec->dacs[i]) 4444e01f54bSTakashi Iwai break; 4454e01f54bSTakashi Iwai if (i >= cfg->line_outs) { 4464e01f54bSTakashi Iwai spec->hp_dac = dac; 4474e01f54bSTakashi Iwai spec->multiout.hp_nid = dac; 4484e01f54bSTakashi Iwai } 4494e01f54bSTakashi Iwai } 4504e01f54bSTakashi Iwai 4514e01f54bSTakashi Iwai static void parse_input(struct hda_codec *codec) 4524e01f54bSTakashi Iwai { 4534e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 4544e01f54bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 4554e01f54bSTakashi Iwai hda_nid_t nid, pin; 4564e01f54bSTakashi Iwai int n, i, j; 4574e01f54bSTakashi Iwai 4584e01f54bSTakashi Iwai n = 0; 4594e01f54bSTakashi Iwai nid = codec->start_nid; 4604e01f54bSTakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 4614e01f54bSTakashi Iwai unsigned int wcaps = get_wcaps(codec, nid); 4624e01f54bSTakashi Iwai unsigned int type = (wcaps & AC_WCAP_TYPE) >> 4634e01f54bSTakashi Iwai AC_WCAP_TYPE_SHIFT; 4644e01f54bSTakashi Iwai if (type != AC_WID_AUD_IN) 4654e01f54bSTakashi Iwai continue; 4664e01f54bSTakashi Iwai if (snd_hda_get_connections(codec, nid, &pin, 1) != 1) 4674e01f54bSTakashi Iwai continue; 4684e01f54bSTakashi Iwai if (pin == cfg->dig_in_pin) { 4694e01f54bSTakashi Iwai spec->dig_in = nid; 4704e01f54bSTakashi Iwai continue; 4714e01f54bSTakashi Iwai } 4724e01f54bSTakashi Iwai for (j = 0; j < AUTO_PIN_LAST; j++) 4734e01f54bSTakashi Iwai if (cfg->input_pins[j] == pin) 4744e01f54bSTakashi Iwai break; 4754e01f54bSTakashi Iwai if (j >= AUTO_PIN_LAST) 4764e01f54bSTakashi Iwai continue; 4774e01f54bSTakashi Iwai spec->input_pins[n] = pin; 4784e01f54bSTakashi Iwai spec->input_labels[n] = auto_pin_cfg_labels[j]; 4794e01f54bSTakashi Iwai spec->adcs[n] = nid; 4804e01f54bSTakashi Iwai n++; 4814e01f54bSTakashi Iwai } 4824e01f54bSTakashi Iwai spec->num_inputs = n; 4834e01f54bSTakashi Iwai } 4844e01f54bSTakashi Iwai 4854e01f54bSTakashi Iwai static void parse_digital(struct hda_codec *codec) 4864e01f54bSTakashi Iwai { 4874e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 4884e01f54bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 4894e01f54bSTakashi Iwai 4904e01f54bSTakashi Iwai if (cfg->dig_outs && 4914e01f54bSTakashi Iwai snd_hda_get_connections(codec, cfg->dig_out_pins[0], 4924e01f54bSTakashi Iwai &spec->dig_out, 1) == 1) 4934e01f54bSTakashi Iwai spec->multiout.dig_out_nid = cfg->dig_out_pins[0]; 4944e01f54bSTakashi Iwai } 4954e01f54bSTakashi Iwai 4964e01f54bSTakashi Iwai static int ca0110_parse_auto_config(struct hda_codec *codec) 4974e01f54bSTakashi Iwai { 4984e01f54bSTakashi Iwai struct ca0110_spec *spec = codec->spec; 4994e01f54bSTakashi Iwai int err; 5004e01f54bSTakashi Iwai 5014e01f54bSTakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5024e01f54bSTakashi Iwai if (err < 0) 5034e01f54bSTakashi Iwai return err; 5044e01f54bSTakashi Iwai 5054e01f54bSTakashi Iwai parse_line_outs(codec); 5064e01f54bSTakashi Iwai parse_hp_out(codec); 5074e01f54bSTakashi Iwai parse_digital(codec); 5084e01f54bSTakashi Iwai parse_input(codec); 5094e01f54bSTakashi Iwai return 0; 5104e01f54bSTakashi Iwai } 5114e01f54bSTakashi Iwai 5124e01f54bSTakashi Iwai 513*1e168953STakashi Iwai static int patch_ca0110(struct hda_codec *codec) 5144e01f54bSTakashi Iwai { 5154e01f54bSTakashi Iwai struct ca0110_spec *spec; 5164e01f54bSTakashi Iwai int err; 5174e01f54bSTakashi Iwai 5184e01f54bSTakashi Iwai spec = kzalloc(sizeof(*spec), GFP_KERNEL); 5194e01f54bSTakashi Iwai if (!spec) 5204e01f54bSTakashi Iwai return -ENOMEM; 5214e01f54bSTakashi Iwai codec->spec = spec; 5224e01f54bSTakashi Iwai 5234e01f54bSTakashi Iwai codec->bus->needs_damn_long_delay = 1; 5244e01f54bSTakashi Iwai 5254e01f54bSTakashi Iwai err = ca0110_parse_auto_config(codec); 5264e01f54bSTakashi Iwai if (err < 0) 5274e01f54bSTakashi Iwai goto error; 5284e01f54bSTakashi Iwai 5294e01f54bSTakashi Iwai codec->patch_ops = ca0110_patch_ops; 5304e01f54bSTakashi Iwai 5314e01f54bSTakashi Iwai return 0; 5324e01f54bSTakashi Iwai 5334e01f54bSTakashi Iwai error: 5344e01f54bSTakashi Iwai kfree(codec->spec); 5354e01f54bSTakashi Iwai codec->spec = NULL; 5364e01f54bSTakashi Iwai return err; 5374e01f54bSTakashi Iwai } 5384e01f54bSTakashi Iwai 5394e01f54bSTakashi Iwai 5404e01f54bSTakashi Iwai /* 5414e01f54bSTakashi Iwai * patch entries 5424e01f54bSTakashi Iwai */ 5434e01f54bSTakashi Iwai static struct hda_codec_preset snd_hda_preset_ca0110[] = { 5444e01f54bSTakashi Iwai { .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 }, 5454e01f54bSTakashi Iwai { .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 }, 5464e01f54bSTakashi Iwai { .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 }, 5474e01f54bSTakashi Iwai {} /* terminator */ 5484e01f54bSTakashi Iwai }; 5494e01f54bSTakashi Iwai 5504e01f54bSTakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1102000a"); 5514e01f54bSTakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1102000b"); 5524e01f54bSTakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1102000d"); 5534e01f54bSTakashi Iwai 5544e01f54bSTakashi Iwai MODULE_LICENSE("GPL"); 5554e01f54bSTakashi Iwai MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec"); 5564e01f54bSTakashi Iwai 5574e01f54bSTakashi Iwai static struct hda_codec_preset_list ca0110_list = { 5584e01f54bSTakashi Iwai .preset = snd_hda_preset_ca0110, 5594e01f54bSTakashi Iwai .owner = THIS_MODULE, 5604e01f54bSTakashi Iwai }; 5614e01f54bSTakashi Iwai 5624e01f54bSTakashi Iwai static int __init patch_ca0110_init(void) 5634e01f54bSTakashi Iwai { 5644e01f54bSTakashi Iwai return snd_hda_add_codec_preset(&ca0110_list); 5654e01f54bSTakashi Iwai } 5664e01f54bSTakashi Iwai 5674e01f54bSTakashi Iwai static void __exit patch_ca0110_exit(void) 5684e01f54bSTakashi Iwai { 5694e01f54bSTakashi Iwai snd_hda_delete_codec_preset(&ca0110_list); 5704e01f54bSTakashi Iwai } 5714e01f54bSTakashi Iwai 5724e01f54bSTakashi Iwai module_init(patch_ca0110_init) 5734e01f54bSTakashi Iwai module_exit(patch_ca0110_exit) 574