linux/sound/pci/hda/patch_ca0110.c
<<
>>
Prefs
   1/*
   2 * HD audio interface patch for Creative X-Fi CA0110-IBG chip
   3 *
   4 * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
   5 *
   6 *  This driver is free software; you can redistribute it and/or modify
   7 *  it under the terms of the GNU General Public License as published by
   8 *  the Free Software Foundation; either version 2 of the License, or
   9 *  (at your option) any later version.
  10 *
  11 *  This driver is distributed in the hope that it will be useful,
  12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *  GNU General Public License for more details.
  15 *
  16 *  You should have received a copy of the GNU General Public License
  17 *  along with this program; if not, write to the Free Software
  18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19 */
  20
  21#include <linux/init.h>
  22#include <linux/delay.h>
  23#include <linux/slab.h>
  24#include <linux/pci.h>
  25#include <linux/module.h>
  26#include <sound/core.h>
  27#include "hda_codec.h"
  28#include "hda_local.h"
  29
  30/*
  31 */
  32
  33struct ca0110_spec {
  34        struct auto_pin_cfg autocfg;
  35        struct hda_multi_out multiout;
  36        hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
  37        hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
  38        hda_nid_t hp_dac;
  39        hda_nid_t input_pins[AUTO_PIN_LAST];
  40        hda_nid_t adcs[AUTO_PIN_LAST];
  41        hda_nid_t dig_out;
  42        hda_nid_t dig_in;
  43        unsigned int num_inputs;
  44        const char *input_labels[AUTO_PIN_LAST];
  45        struct hda_pcm pcm_rec[2];      /* PCM information */
  46};
  47
  48/*
  49 * PCM callbacks
  50 */
  51static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo,
  52                                    struct hda_codec *codec,
  53                                    struct snd_pcm_substream *substream)
  54{
  55        struct ca0110_spec *spec = codec->spec;
  56        return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
  57                                             hinfo);
  58}
  59
  60static int ca0110_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
  61                                       struct hda_codec *codec,
  62                                       unsigned int stream_tag,
  63                                       unsigned int format,
  64                                       struct snd_pcm_substream *substream)
  65{
  66        struct ca0110_spec *spec = codec->spec;
  67        return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
  68                                                stream_tag, format, substream);
  69}
  70
  71static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
  72                                       struct hda_codec *codec,
  73                                       struct snd_pcm_substream *substream)
  74{
  75        struct ca0110_spec *spec = codec->spec;
  76        return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
  77}
  78
  79/*
  80 * Digital out
  81 */
  82static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
  83                                        struct hda_codec *codec,
  84                                        struct snd_pcm_substream *substream)
  85{
  86        struct ca0110_spec *spec = codec->spec;
  87        return snd_hda_multi_out_dig_open(codec, &spec->multiout);
  88}
  89
  90static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
  91                                         struct hda_codec *codec,
  92                                         struct snd_pcm_substream *substream)
  93{
  94        struct ca0110_spec *spec = codec->spec;
  95        return snd_hda_multi_out_dig_close(codec, &spec->multiout);
  96}
  97
  98static int ca0110_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
  99                                           struct hda_codec *codec,
 100                                           unsigned int stream_tag,
 101                                           unsigned int format,
 102                                           struct snd_pcm_substream *substream)
 103{
 104        struct ca0110_spec *spec = codec->spec;
 105        return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
 106                                             format, substream);
 107}
 108
 109/*
 110 * Analog capture
 111 */
 112static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
 113                                      struct hda_codec *codec,
 114                                      unsigned int stream_tag,
 115                                      unsigned int format,
 116                                      struct snd_pcm_substream *substream)
 117{
 118        struct ca0110_spec *spec = codec->spec;
 119
 120        snd_hda_codec_setup_stream(codec, spec->adcs[substream->number],
 121                                   stream_tag, 0, format);
 122        return 0;
 123}
 124
 125static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
 126                                      struct hda_codec *codec,
 127                                      struct snd_pcm_substream *substream)
 128{
 129        struct ca0110_spec *spec = codec->spec;
 130
 131        snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]);
 132        return 0;
 133}
 134
 135/*
 136 */
 137
 138static const char * const dirstr[2] = { "Playback", "Capture" };
 139
 140static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
 141                       int chan, int dir)
 142{
 143        char namestr[44];
 144        int type = dir ? HDA_INPUT : HDA_OUTPUT;
 145        struct snd_kcontrol_new knew =
 146                HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
 147        sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
 148        return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
 149}
 150
 151static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
 152                       int chan, int dir)
 153{
 154        char namestr[44];
 155        int type = dir ? HDA_INPUT : HDA_OUTPUT;
 156        struct snd_kcontrol_new knew =
 157                HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
 158        sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
 159        return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
 160}
 161
 162#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
 163#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0)
 164#define add_in_switch(codec, nid, pfx)  _add_switch(codec, nid, pfx, 3, 1)
 165#define add_in_volume(codec, nid, pfx)  _add_volume(codec, nid, pfx, 3, 1)
 166#define add_mono_switch(codec, nid, pfx, chan) \
 167        _add_switch(codec, nid, pfx, chan, 0)
 168#define add_mono_volume(codec, nid, pfx, chan) \
 169        _add_volume(codec, nid, pfx, chan, 0)
 170
 171static int ca0110_build_controls(struct hda_codec *codec)
 172{
 173        struct ca0110_spec *spec = codec->spec;
 174        struct auto_pin_cfg *cfg = &spec->autocfg;
 175        static const char * const prefix[AUTO_CFG_MAX_OUTS] = {
 176                "Front", "Surround", NULL, "Side", "Multi"
 177        };
 178        hda_nid_t mutenid;
 179        int i, err;
 180
 181        for (i = 0; i < spec->multiout.num_dacs; i++) {
 182                if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP)
 183                        mutenid = spec->out_pins[i];
 184                else
 185                        mutenid = spec->multiout.dac_nids[i];
 186                if (!prefix[i]) {
 187                        err = add_mono_switch(codec, mutenid,
 188                                              "Center", 1);
 189                        if (err < 0)
 190                                return err;
 191                        err = add_mono_switch(codec, mutenid,
 192                                              "LFE", 1);
 193                        if (err < 0)
 194                                return err;
 195                        err = add_mono_volume(codec, spec->multiout.dac_nids[i],
 196                                              "Center", 1);
 197                        if (err < 0)
 198                                return err;
 199                        err = add_mono_volume(codec, spec->multiout.dac_nids[i],
 200                                              "LFE", 1);
 201                        if (err < 0)
 202                                return err;
 203                } else {
 204                        err = add_out_switch(codec, mutenid,
 205                                             prefix[i]);
 206                        if (err < 0)
 207                                return err;
 208                        err = add_out_volume(codec, spec->multiout.dac_nids[i],
 209                                         prefix[i]);
 210                        if (err < 0)
 211                                return err;
 212                }
 213        }
 214        if (cfg->hp_outs) {
 215                if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP)
 216                        mutenid = cfg->hp_pins[0];
 217                else
 218                        mutenid = spec->multiout.dac_nids[i];
 219
 220                err = add_out_switch(codec, mutenid, "Headphone");
 221                if (err < 0)
 222                        return err;
 223                if (spec->hp_dac) {
 224                        err = add_out_volume(codec, spec->hp_dac, "Headphone");
 225                        if (err < 0)
 226                                return err;
 227                }
 228        }
 229        for (i = 0; i < spec->num_inputs; i++) {
 230                const char *label = spec->input_labels[i];
 231                if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP)
 232                        mutenid = spec->input_pins[i];
 233                else
 234                        mutenid = spec->adcs[i];
 235                err = add_in_switch(codec, mutenid, label);
 236                if (err < 0)
 237                        return err;
 238                err = add_in_volume(codec, spec->adcs[i], label);
 239                if (err < 0)
 240                        return err;
 241        }
 242
 243        if (spec->dig_out) {
 244                err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
 245                                                    spec->dig_out);
 246                if (err < 0)
 247                        return err;
 248                err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
 249                if (err < 0)
 250                        return err;
 251                spec->multiout.share_spdif = 1;
 252        }
 253        if (spec->dig_in) {
 254                err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
 255                if (err < 0)
 256                        return err;
 257                err = add_in_volume(codec, spec->dig_in, "IEC958");
 258        }
 259        return 0;
 260}
 261
 262/*
 263 */
 264static const struct hda_pcm_stream ca0110_pcm_analog_playback = {
 265        .substreams = 1,
 266        .channels_min = 2,
 267        .channels_max = 8,
 268        .ops = {
 269                .open = ca0110_playback_pcm_open,
 270                .prepare = ca0110_playback_pcm_prepare,
 271                .cleanup = ca0110_playback_pcm_cleanup
 272        },
 273};
 274
 275static const struct hda_pcm_stream ca0110_pcm_analog_capture = {
 276        .substreams = 1,
 277        .channels_min = 2,
 278        .channels_max = 2,
 279        .ops = {
 280                .prepare = ca0110_capture_pcm_prepare,
 281                .cleanup = ca0110_capture_pcm_cleanup
 282        },
 283};
 284
 285static const struct hda_pcm_stream ca0110_pcm_digital_playback = {
 286        .substreams = 1,
 287        .channels_min = 2,
 288        .channels_max = 2,
 289        .ops = {
 290                .open = ca0110_dig_playback_pcm_open,
 291                .close = ca0110_dig_playback_pcm_close,
 292                .prepare = ca0110_dig_playback_pcm_prepare
 293        },
 294};
 295
 296static const struct hda_pcm_stream ca0110_pcm_digital_capture = {
 297        .substreams = 1,
 298        .channels_min = 2,
 299        .channels_max = 2,
 300};
 301
 302static int ca0110_build_pcms(struct hda_codec *codec)
 303{
 304        struct ca0110_spec *spec = codec->spec;
 305        struct hda_pcm *info = spec->pcm_rec;
 306
 307        codec->pcm_info = info;
 308        codec->num_pcms = 0;
 309
 310        info->name = "CA0110 Analog";
 311        info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback;
 312        info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
 313        info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
 314                spec->multiout.max_channels;
 315        info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture;
 316        info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
 317        info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
 318        codec->num_pcms++;
 319
 320        if (!spec->dig_out && !spec->dig_in)
 321                return 0;
 322
 323        info++;
 324        info->name = "CA0110 Digital";
 325        info->pcm_type = HDA_PCM_TYPE_SPDIF;
 326        if (spec->dig_out) {
 327                info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
 328                        ca0110_pcm_digital_playback;
 329                info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
 330        }
 331        if (spec->dig_in) {
 332                info->stream[SNDRV_PCM_STREAM_CAPTURE] =
 333                        ca0110_pcm_digital_capture;
 334                info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
 335        }
 336        codec->num_pcms++;
 337
 338        return 0;
 339}
 340
 341static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
 342{
 343        if (pin) {
 344                snd_hda_codec_write(codec, pin, 0,
 345                                    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
 346                if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
 347                        snd_hda_codec_write(codec, pin, 0,
 348                                            AC_VERB_SET_AMP_GAIN_MUTE,
 349                                            AMP_OUT_UNMUTE);
 350        }
 351        if (dac)
 352                snd_hda_codec_write(codec, dac, 0,
 353                                    AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
 354}
 355
 356static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
 357{
 358        if (pin) {
 359                snd_hda_codec_write(codec, pin, 0,
 360                                    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
 361                if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
 362                        snd_hda_codec_write(codec, pin, 0,
 363                                            AC_VERB_SET_AMP_GAIN_MUTE,
 364                                            AMP_IN_UNMUTE(0));
 365        }
 366        if (adc)
 367                snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
 368                                    AMP_IN_UNMUTE(0));
 369}
 370
 371static int ca0110_init(struct hda_codec *codec)
 372{
 373        struct ca0110_spec *spec = codec->spec;
 374        struct auto_pin_cfg *cfg = &spec->autocfg;
 375        int i;
 376
 377        for (i = 0; i < spec->multiout.num_dacs; i++)
 378                init_output(codec, spec->out_pins[i],
 379                            spec->multiout.dac_nids[i]);
 380        init_output(codec, cfg->hp_pins[0], spec->hp_dac);
 381        init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
 382
 383        for (i = 0; i < spec->num_inputs; i++)
 384                init_input(codec, spec->input_pins[i], spec->adcs[i]);
 385        init_input(codec, cfg->dig_in_pin, spec->dig_in);
 386        return 0;
 387}
 388
 389static void ca0110_free(struct hda_codec *codec)
 390{
 391        kfree(codec->spec);
 392}
 393
 394static const struct hda_codec_ops ca0110_patch_ops = {
 395        .build_controls = ca0110_build_controls,
 396        .build_pcms = ca0110_build_pcms,
 397        .init = ca0110_init,
 398        .free = ca0110_free,
 399};
 400
 401
 402static void parse_line_outs(struct hda_codec *codec)
 403{
 404        struct ca0110_spec *spec = codec->spec;
 405        struct auto_pin_cfg *cfg = &spec->autocfg;
 406        int i, n;
 407        unsigned int def_conf;
 408        hda_nid_t nid;
 409
 410        n = 0;
 411        for (i = 0; i < cfg->line_outs; i++) {
 412                nid = cfg->line_out_pins[i];
 413                def_conf = snd_hda_codec_get_pincfg(codec, nid);
 414                if (!def_conf)
 415                        continue; /* invalid pin */
 416                if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1)
 417                        continue;
 418                spec->out_pins[n++] = nid;
 419        }
 420        spec->multiout.dac_nids = spec->dacs;
 421        spec->multiout.num_dacs = n;
 422        spec->multiout.max_channels = n * 2;
 423}
 424
 425static void parse_hp_out(struct hda_codec *codec)
 426{
 427        struct ca0110_spec *spec = codec->spec;
 428        struct auto_pin_cfg *cfg = &spec->autocfg;
 429        int i;
 430        unsigned int def_conf;
 431        hda_nid_t nid, dac;
 432
 433        if (!cfg->hp_outs)
 434                return;
 435        nid = cfg->hp_pins[0];
 436        def_conf = snd_hda_codec_get_pincfg(codec, nid);
 437        if (!def_conf) {
 438                cfg->hp_outs = 0;
 439                return;
 440        }
 441        if (snd_hda_get_connections(codec, nid, &dac, 1) != 1)
 442                return;
 443
 444        for (i = 0; i < cfg->line_outs; i++)
 445                if (dac == spec->dacs[i])
 446                        break;
 447        if (i >= cfg->line_outs) {
 448                spec->hp_dac = dac;
 449                spec->multiout.hp_nid = dac;
 450        }
 451}
 452
 453static void parse_input(struct hda_codec *codec)
 454{
 455        struct ca0110_spec *spec = codec->spec;
 456        struct auto_pin_cfg *cfg = &spec->autocfg;
 457        hda_nid_t nid, pin;
 458        int n, i, j;
 459
 460        n = 0;
 461        nid = codec->start_nid;
 462        for (i = 0; i < codec->num_nodes; i++, nid++) {
 463                unsigned int wcaps = get_wcaps(codec, nid);
 464                unsigned int type = get_wcaps_type(wcaps);
 465                if (type != AC_WID_AUD_IN)
 466                        continue;
 467                if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)
 468                        continue;
 469                if (pin == cfg->dig_in_pin) {
 470                        spec->dig_in = nid;
 471                        continue;
 472                }
 473                for (j = 0; j < cfg->num_inputs; j++)
 474                        if (cfg->inputs[j].pin == pin)
 475                                break;
 476                if (j >= cfg->num_inputs)
 477                        continue;
 478                spec->input_pins[n] = pin;
 479                spec->input_labels[n] = hda_get_input_pin_label(codec, pin, 1);
 480                spec->adcs[n] = nid;
 481                n++;
 482        }
 483        spec->num_inputs = n;
 484}
 485
 486static void parse_digital(struct hda_codec *codec)
 487{
 488        struct ca0110_spec *spec = codec->spec;
 489        struct auto_pin_cfg *cfg = &spec->autocfg;
 490
 491        if (cfg->dig_outs &&
 492            snd_hda_get_connections(codec, cfg->dig_out_pins[0],
 493                                    &spec->dig_out, 1) == 1)
 494                spec->multiout.dig_out_nid = spec->dig_out;
 495}
 496
 497static int ca0110_parse_auto_config(struct hda_codec *codec)
 498{
 499        struct ca0110_spec *spec = codec->spec;
 500        int err;
 501
 502        err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
 503        if (err < 0)
 504                return err;
 505
 506        parse_line_outs(codec);
 507        parse_hp_out(codec);
 508        parse_digital(codec);
 509        parse_input(codec);
 510        return 0;
 511}
 512
 513
 514static int patch_ca0110(struct hda_codec *codec)
 515{
 516        struct ca0110_spec *spec;
 517        int err;
 518
 519        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 520        if (!spec)
 521                return -ENOMEM;
 522        codec->spec = spec;
 523
 524        codec->bus->needs_damn_long_delay = 1;
 525
 526        err = ca0110_parse_auto_config(codec);
 527        if (err < 0)
 528                goto error;
 529
 530        codec->patch_ops = ca0110_patch_ops;
 531
 532        return 0;
 533
 534 error:
 535        kfree(codec->spec);
 536        codec->spec = NULL;
 537        return err;
 538}
 539
 540
 541/*
 542 * patch entries
 543 */
 544static const struct hda_codec_preset snd_hda_preset_ca0110[] = {
 545        { .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
 546        { .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
 547        { .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
 548        {} /* terminator */
 549};
 550
 551MODULE_ALIAS("snd-hda-codec-id:1102000a");
 552MODULE_ALIAS("snd-hda-codec-id:1102000b");
 553MODULE_ALIAS("snd-hda-codec-id:1102000d");
 554
 555MODULE_LICENSE("GPL");
 556MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
 557
 558static struct hda_codec_preset_list ca0110_list = {
 559        .preset = snd_hda_preset_ca0110,
 560        .owner = THIS_MODULE,
 561};
 562
 563static int __init patch_ca0110_init(void)
 564{
 565        return snd_hda_add_codec_preset(&ca0110_list);
 566}
 567
 568static void __exit patch_ca0110_exit(void)
 569{
 570        snd_hda_delete_codec_preset(&ca0110_list);
 571}
 572
 573module_init(patch_ca0110_init)
 574module_exit(patch_ca0110_exit)
 575
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.