linux/sound/soc/codecs/stac9766.c
<<
>>
Prefs
   1/*
   2 * stac9766.c  --  ALSA SoC STAC9766 codec support
   3 *
   4 * Copyright 2009 Jon Smirl, Digispeaker
   5 * Author: Jon Smirl <jonsmirl@gmail.com>
   6 *
   7 *  This program is free software; you can redistribute  it and/or modify it
   8 *  under  the terms of  the GNU General  Public License as published by the
   9 *  Free Software Foundation;  either version 2 of the  License, or (at your
  10 *  option) any later version.
  11 *
  12 *  Features:-
  13 *
  14 *   o Support for AC97 Codec, S/PDIF
  15 */
  16
  17#include <linux/init.h>
  18#include <linux/module.h>
  19#include <linux/device.h>
  20#include <sound/core.h>
  21#include <sound/pcm.h>
  22#include <sound/ac97_codec.h>
  23#include <sound/initval.h>
  24#include <sound/pcm_params.h>
  25#include <sound/soc.h>
  26#include <sound/tlv.h>
  27#include <sound/soc-of-simple.h>
  28
  29#include "stac9766.h"
  30
  31#define STAC9766_VERSION "0.10"
  32
  33/*
  34 * STAC9766 register cache
  35 */
  36static const u16 stac9766_reg[] = {
  37        0x6A90, 0x8000, 0x8000, 0x8000, /* 6 */
  38        0x0000, 0x0000, 0x8008, 0x8008, /* e */
  39        0x8808, 0x8808, 0x8808, 0x8808, /* 16 */
  40        0x8808, 0x0000, 0x8000, 0x0000, /* 1e */
  41        0x0000, 0x0000, 0x0000, 0x000f, /* 26 */
  42        0x0a05, 0x0400, 0xbb80, 0x0000, /* 2e */
  43        0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */
  44        0x0000, 0x2000, 0x0000, 0x0100, /* 3e */
  45        0x0000, 0x0000, 0x0080, 0x0000, /* 46 */
  46        0x0000, 0x0000, 0x0003, 0xffff, /* 4e */
  47        0x0000, 0x0000, 0x0000, 0x0000, /* 56 */
  48        0x4000, 0x0000, 0x0000, 0x0000, /* 5e */
  49        0x1201, 0xFFFF, 0xFFFF, 0x0000, /* 66 */
  50        0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
  51        0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
  52        0x0000, 0x0000, 0x0000, 0x0000, /* 7e */
  53};
  54
  55static const char *stac9766_record_mux[] = {"Mic", "CD", "Video", "AUX",
  56                        "Line", "Stereo Mix", "Mono Mix", "Phone"};
  57static const char *stac9766_mono_mux[] = {"Mix", "Mic"};
  58static const char *stac9766_mic_mux[] = {"Mic1", "Mic2"};
  59static const char *stac9766_SPDIF_mux[] = {"PCM", "ADC Record"};
  60static const char *stac9766_popbypass_mux[] = {"Normal", "Bypass Mixer"};
  61static const char *stac9766_record_all_mux[] = {"All analog",
  62        "Analog plus DAC"};
  63static const char *stac9766_boost1[] = {"0dB", "10dB"};
  64static const char *stac9766_boost2[] = {"0dB", "20dB"};
  65static const char *stac9766_stereo_mic[] = {"Off", "On"};
  66
  67static const struct soc_enum stac9766_record_enum =
  68        SOC_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, stac9766_record_mux);
  69static const struct soc_enum stac9766_mono_enum =
  70        SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 9, 2, stac9766_mono_mux);
  71static const struct soc_enum stac9766_mic_enum =
  72        SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, stac9766_mic_mux);
  73static const struct soc_enum stac9766_SPDIF_enum =
  74        SOC_ENUM_SINGLE(AC97_STAC_DA_CONTROL, 1, 2, stac9766_SPDIF_mux);
  75static const struct soc_enum stac9766_popbypass_enum =
  76        SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, stac9766_popbypass_mux);
  77static const struct soc_enum stac9766_record_all_enum =
  78        SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 12, 2,
  79                        stac9766_record_all_mux);
  80static const struct soc_enum stac9766_boost1_enum =
  81        SOC_ENUM_SINGLE(AC97_MIC, 6, 2, stac9766_boost1); /* 0/10dB */
  82static const struct soc_enum stac9766_boost2_enum =
  83        SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 2, 2, stac9766_boost2); /* 0/20dB */
  84static const struct soc_enum stac9766_stereo_mic_enum =
  85        SOC_ENUM_SINGLE(AC97_STAC_STEREO_MIC, 2, 1, stac9766_stereo_mic);
  86
  87static const DECLARE_TLV_DB_LINEAR(master_tlv, -4600, 0);
  88static const DECLARE_TLV_DB_LINEAR(record_tlv, 0, 2250);
  89static const DECLARE_TLV_DB_LINEAR(beep_tlv, -4500, 0);
  90static const DECLARE_TLV_DB_LINEAR(mix_tlv, -3450, 1200);
  91
  92static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
  93        SOC_DOUBLE_TLV("Speaker Volume", AC97_MASTER, 8, 0, 31, 1, master_tlv),
  94        SOC_SINGLE("Speaker Switch", AC97_MASTER, 15, 1, 1),
  95        SOC_DOUBLE_TLV("Headphone Volume", AC97_HEADPHONE, 8, 0, 31, 1,
  96                       master_tlv),
  97        SOC_SINGLE("Headphone Switch", AC97_HEADPHONE, 15, 1, 1),
  98        SOC_SINGLE_TLV("Mono Out Volume", AC97_MASTER_MONO, 0, 31, 1,
  99                       master_tlv),
 100        SOC_SINGLE("Mono Out Switch", AC97_MASTER_MONO, 15, 1, 1),
 101
 102        SOC_DOUBLE_TLV("Record Volume", AC97_REC_GAIN, 8, 0, 15, 0, record_tlv),
 103        SOC_SINGLE("Record Switch", AC97_REC_GAIN, 15, 1, 1),
 104
 105
 106        SOC_SINGLE_TLV("Beep Volume", AC97_PC_BEEP, 1, 15, 1, beep_tlv),
 107        SOC_SINGLE("Beep Switch", AC97_PC_BEEP, 15, 1, 1),
 108        SOC_SINGLE("Beep Frequency", AC97_PC_BEEP, 5, 127, 1),
 109        SOC_SINGLE_TLV("Phone Volume", AC97_PHONE, 0, 31, 1, mix_tlv),
 110        SOC_SINGLE("Phone Switch", AC97_PHONE, 15, 1, 1),
 111
 112        SOC_ENUM("Mic Boost1", stac9766_boost1_enum),
 113        SOC_ENUM("Mic Boost2", stac9766_boost2_enum),
 114        SOC_SINGLE_TLV("Mic Volume", AC97_MIC, 0, 31, 1, mix_tlv),
 115        SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1),
 116        SOC_ENUM("Stereo Mic", stac9766_stereo_mic_enum),
 117
 118        SOC_DOUBLE_TLV("Line Volume", AC97_LINE, 8, 0, 31, 1, mix_tlv),
 119        SOC_SINGLE("Line Switch", AC97_LINE, 15, 1, 1),
 120        SOC_DOUBLE_TLV("CD Volume", AC97_CD, 8, 0, 31, 1, mix_tlv),
 121        SOC_SINGLE("CD Switch", AC97_CD, 15, 1, 1),
 122        SOC_DOUBLE_TLV("AUX Volume", AC97_AUX, 8, 0, 31, 1, mix_tlv),
 123        SOC_SINGLE("AUX Switch", AC97_AUX, 15, 1, 1),
 124        SOC_DOUBLE_TLV("Video Volume", AC97_VIDEO, 8, 0, 31, 1, mix_tlv),
 125        SOC_SINGLE("Video Switch", AC97_VIDEO, 15, 1, 1),
 126
 127        SOC_DOUBLE_TLV("DAC Volume", AC97_PCM, 8, 0, 31, 1, mix_tlv),
 128        SOC_SINGLE("DAC Switch", AC97_PCM, 15, 1, 1),
 129        SOC_SINGLE("Loopback Test Switch", AC97_GENERAL_PURPOSE, 7, 1, 0),
 130        SOC_SINGLE("3D Volume", AC97_3D_CONTROL, 3, 2, 1),
 131        SOC_SINGLE("3D Switch", AC97_GENERAL_PURPOSE, 13, 1, 0),
 132
 133        SOC_ENUM("SPDIF Mux", stac9766_SPDIF_enum),
 134        SOC_ENUM("Mic1/2 Mux", stac9766_mic_enum),
 135        SOC_ENUM("Record All Mux", stac9766_record_all_enum),
 136        SOC_ENUM("Record Mux", stac9766_record_enum),
 137        SOC_ENUM("Mono Mux", stac9766_mono_enum),
 138        SOC_ENUM("Pop Bypass Mux", stac9766_popbypass_enum),
 139};
 140
 141static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 142                               unsigned int val)
 143{
 144        u16 *cache = codec->reg_cache;
 145
 146        if (reg > AC97_STAC_PAGE0) {
 147                stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
 148                soc_ac97_ops.write(codec->ac97, reg, val);
 149                stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
 150                return 0;
 151        }
 152        if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
 153                return -EIO;
 154
 155        soc_ac97_ops.write(codec->ac97, reg, val);
 156        cache[reg / 2] = val;
 157        return 0;
 158}
 159
 160static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
 161                                       unsigned int reg)
 162{
 163        u16 val = 0, *cache = codec->reg_cache;
 164
 165        if (reg > AC97_STAC_PAGE0) {
 166                stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
 167                val = soc_ac97_ops.read(codec->ac97, reg - AC97_STAC_PAGE0);
 168                stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
 169                return val;
 170        }
 171        if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
 172                return -EIO;
 173
 174        if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
 175                reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
 176                reg == AC97_VENDOR_ID2) {
 177
 178                val = soc_ac97_ops.read(codec->ac97, reg);
 179                return val;
 180        }
 181        return cache[reg / 2];
 182}
 183
 184static int ac97_analog_prepare(struct snd_pcm_substream *substream,
 185                               struct snd_soc_dai *dai)
 186{
 187        struct snd_soc_codec *codec = dai->codec;
 188        struct snd_pcm_runtime *runtime = substream->runtime;
 189        unsigned short reg, vra;
 190
 191        vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
 192
 193        vra |= 0x1; /* enable variable rate audio */
 194
 195        stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
 196
 197        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 198                reg = AC97_PCM_FRONT_DAC_RATE;
 199        else
 200                reg = AC97_PCM_LR_ADC_RATE;
 201
 202        return stac9766_ac97_write(codec, reg, runtime->rate);
 203}
 204
 205static int ac97_digital_prepare(struct snd_pcm_substream *substream,
 206                                struct snd_soc_dai *dai)
 207{
 208        struct snd_soc_codec *codec = dai->codec;
 209        struct snd_pcm_runtime *runtime = substream->runtime;
 210        unsigned short reg, vra;
 211
 212        stac9766_ac97_write(codec, AC97_SPDIF, 0x2002);
 213
 214        vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
 215        vra |= 0x5; /* Enable VRA and SPDIF out */
 216
 217        stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
 218
 219        reg = AC97_PCM_FRONT_DAC_RATE;
 220
 221        return stac9766_ac97_write(codec, reg, runtime->rate);
 222}
 223
 224static int ac97_digital_trigger(struct snd_pcm_substream *substream,
 225                                int cmd, struct snd_soc_dai *dai)
 226{
 227        struct snd_soc_codec *codec = dai->codec;
 228        unsigned short vra;
 229
 230        switch (cmd) {
 231        case SNDRV_PCM_TRIGGER_STOP:
 232                vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
 233                vra &= !0x04;
 234                stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
 235                break;
 236        }
 237        return 0;
 238}
 239
 240static int stac9766_set_bias_level(struct snd_soc_codec *codec,
 241                                   enum snd_soc_bias_level level)
 242{
 243        switch (level) {
 244        case SND_SOC_BIAS_ON: /* full On */
 245        case SND_SOC_BIAS_PREPARE: /* partial On */
 246        case SND_SOC_BIAS_STANDBY: /* Off, with power */
 247                stac9766_ac97_write(codec, AC97_POWERDOWN, 0x0000);
 248                break;
 249        case SND_SOC_BIAS_OFF: /* Off, without power */
 250                /* disable everything including AC link */
 251                stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff);
 252                break;
 253        }
 254        codec->bias_level = level;
 255        return 0;
 256}
 257
 258static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
 259{
 260        if (try_warm && soc_ac97_ops.warm_reset) {
 261                soc_ac97_ops.warm_reset(codec->ac97);
 262                if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
 263                        return 1;
 264        }
 265
 266        soc_ac97_ops.reset(codec->ac97);
 267        if (soc_ac97_ops.warm_reset)
 268                soc_ac97_ops.warm_reset(codec->ac97);
 269        if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
 270                return -EIO;
 271        return 0;
 272}
 273
 274static int stac9766_codec_suspend(struct platform_device *pdev,
 275                                  pm_message_t state)
 276{
 277        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 278        struct snd_soc_codec *codec = socdev->card->codec;
 279
 280        stac9766_set_bias_level(codec, SND_SOC_BIAS_OFF);
 281        return 0;
 282}
 283
 284static int stac9766_codec_resume(struct platform_device *pdev)
 285{
 286        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 287        struct snd_soc_codec *codec = socdev->card->codec;
 288        u16 id, reset;
 289
 290        reset = 0;
 291        /* give the codec an AC97 warm reset to start the link */
 292reset:
 293        if (reset > 5) {
 294                printk(KERN_ERR "stac9766 failed to resume");
 295                return -EIO;
 296        }
 297        codec->ac97->bus->ops->warm_reset(codec->ac97);
 298        id = soc_ac97_ops.read(codec->ac97, AC97_VENDOR_ID2);
 299        if (id != 0x4c13) {
 300                stac9766_reset(codec, 0);
 301                reset++;
 302                goto reset;
 303        }
 304        stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 305
 306        if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
 307                stac9766_set_bias_level(codec, SND_SOC_BIAS_ON);
 308
 309        return 0;
 310}
 311
 312static struct snd_soc_dai_ops stac9766_dai_ops_analog = {
 313        .prepare = ac97_analog_prepare,
 314};
 315
 316static struct snd_soc_dai_ops stac9766_dai_ops_digital = {
 317        .prepare = ac97_digital_prepare,
 318        .trigger = ac97_digital_trigger,
 319};
 320
 321struct snd_soc_dai stac9766_dai[] = {
 322{
 323        .name = "stac9766 analog",
 324        .id = 0,
 325        .ac97_control = 1,
 326
 327        /* stream cababilities */
 328        .playback = {
 329                .stream_name = "stac9766 analog",
 330                .channels_min = 1,
 331                .channels_max = 2,
 332                .rates = SNDRV_PCM_RATE_8000_48000,
 333                .formats = SND_SOC_STD_AC97_FMTS,
 334        },
 335        .capture = {
 336                .stream_name = "stac9766 analog",
 337                .channels_min = 1,
 338                .channels_max = 2,
 339                .rates = SNDRV_PCM_RATE_8000_48000,
 340                .formats = SND_SOC_STD_AC97_FMTS,
 341        },
 342        /* alsa ops */
 343        .ops = &stac9766_dai_ops_analog,
 344},
 345{
 346        .name = "stac9766 IEC958",
 347        .id = 1,
 348        .ac97_control = 1,
 349
 350        /* stream cababilities */
 351        .playback = {
 352                .stream_name = "stac9766 IEC958",
 353                .channels_min = 1,
 354                .channels_max = 2,
 355                .rates = SNDRV_PCM_RATE_32000 | \
 356                        SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
 357                .formats = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE,
 358        },
 359        /* alsa ops */
 360        .ops = &stac9766_dai_ops_digital,
 361}
 362};
 363EXPORT_SYMBOL_GPL(stac9766_dai);
 364
 365static int stac9766_codec_probe(struct platform_device *pdev)
 366{
 367        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 368        struct snd_soc_codec *codec;
 369        int ret = 0;
 370
 371        printk(KERN_INFO "STAC9766 SoC Audio Codec %s\n", STAC9766_VERSION);
 372
 373        socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
 374        if (socdev->card->codec == NULL)
 375                return -ENOMEM;
 376        codec = socdev->card->codec;
 377        mutex_init(&codec->mutex);
 378
 379        codec->reg_cache = kmemdup(stac9766_reg, sizeof(stac9766_reg),
 380                                   GFP_KERNEL);
 381        if (codec->reg_cache == NULL) {
 382                ret = -ENOMEM;
 383                goto cache_err;
 384        }
 385        codec->reg_cache_size = sizeof(stac9766_reg);
 386        codec->reg_cache_step = 2;
 387
 388        codec->name = "STAC9766";
 389        codec->owner = THIS_MODULE;
 390        codec->dai = stac9766_dai;
 391        codec->num_dai = ARRAY_SIZE(stac9766_dai);
 392        codec->write = stac9766_ac97_write;
 393        codec->read = stac9766_ac97_read;
 394        codec->set_bias_level = stac9766_set_bias_level;
 395        INIT_LIST_HEAD(&codec->dapm_widgets);
 396        INIT_LIST_HEAD(&codec->dapm_paths);
 397
 398        ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
 399        if (ret < 0)
 400                goto codec_err;
 401
 402        /* register pcms */
 403        ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
 404        if (ret < 0)
 405                goto pcm_err;
 406
 407        /* do a cold reset for the controller and then try
 408         * a warm reset followed by an optional cold reset for codec */
 409        stac9766_reset(codec, 0);
 410        ret = stac9766_reset(codec, 1);
 411        if (ret < 0) {
 412                printk(KERN_ERR "Failed to reset STAC9766: AC97 link error\n");
 413                goto reset_err;
 414        }
 415
 416        stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 417
 418        snd_soc_add_controls(codec, stac9766_snd_ac97_controls,
 419                             ARRAY_SIZE(stac9766_snd_ac97_controls));
 420
 421        ret = snd_soc_init_card(socdev);
 422        if (ret < 0)
 423                goto reset_err;
 424        return 0;
 425
 426reset_err:
 427        snd_soc_free_pcms(socdev);
 428pcm_err:
 429        snd_soc_free_ac97_codec(codec);
 430codec_err:
 431        kfree(codec->private_data);
 432cache_err:
 433        kfree(socdev->card->codec);
 434        socdev->card->codec = NULL;
 435        return ret;
 436}
 437
 438static int stac9766_codec_remove(struct platform_device *pdev)
 439{
 440        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 441        struct snd_soc_codec *codec = socdev->card->codec;
 442
 443        if (codec == NULL)
 444                return 0;
 445
 446        snd_soc_free_pcms(socdev);
 447        snd_soc_free_ac97_codec(codec);
 448        kfree(codec->reg_cache);
 449        kfree(codec);
 450        return 0;
 451}
 452
 453struct snd_soc_codec_device soc_codec_dev_stac9766 = {
 454        .probe = stac9766_codec_probe,
 455        .remove = stac9766_codec_remove,
 456        .suspend = stac9766_codec_suspend,
 457        .resume = stac9766_codec_resume,
 458};
 459EXPORT_SYMBOL_GPL(soc_codec_dev_stac9766);
 460
 461MODULE_DESCRIPTION("ASoC stac9766 driver");
 462MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
 463MODULE_LICENSE("GPL");
 464