linux/sound/soc/codecs/wm8940.c
<<
>>
Prefs
   1/*
   2 * wm8940.c  --  WM8940 ALSA Soc Audio driver
   3 *
   4 * Author: Jonathan Cameron <jic23@cam.ac.uk>
   5 *
   6 * Based on wm8510.c
   7 *    Copyright  2006 Wolfson Microelectronics PLC.
   8 *    Author:  Liam Girdwood <lrg@slimlogic.co.uk>
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License version 2 as
  12 * published by the Free Software Foundation.
  13 *
  14 * Not currently handled:
  15 * Notch filter control
  16 * AUXMode (inverting vs mixer)
  17 * No means to obtain current gain if alc enabled.
  18 * No use made of gpio
  19 * Fast VMID discharge for power down
  20 * Soft Start
  21 * DLR and ALR Swaps not enabled
  22 * Digital Sidetone not supported
  23 */
  24#include <linux/module.h>
  25#include <linux/moduleparam.h>
  26#include <linux/kernel.h>
  27#include <linux/init.h>
  28#include <linux/delay.h>
  29#include <linux/pm.h>
  30#include <linux/i2c.h>
  31#include <linux/platform_device.h>
  32#include <linux/spi/spi.h>
  33#include <linux/slab.h>
  34#include <sound/core.h>
  35#include <sound/pcm.h>
  36#include <sound/pcm_params.h>
  37#include <sound/soc.h>
  38#include <sound/initval.h>
  39#include <sound/tlv.h>
  40
  41#include "wm8940.h"
  42
  43struct wm8940_priv {
  44        unsigned int sysclk;
  45        enum snd_soc_control_type control_type;
  46};
  47
  48static int wm8940_volatile_register(struct snd_soc_codec *codec,
  49                                    unsigned int reg)
  50{
  51        switch (reg) {
  52        case WM8940_SOFTRESET:
  53                return 1;
  54        default:
  55                return 0;
  56        }
  57}
  58
  59static u16 wm8940_reg_defaults[] = {
  60        0x8940, /* Soft Reset */
  61        0x0000, /* Power 1 */
  62        0x0000, /* Power 2 */
  63        0x0000, /* Power 3 */
  64        0x0010, /* Interface Control */
  65        0x0000, /* Companding Control */
  66        0x0140, /* Clock Control */
  67        0x0000, /* Additional Controls */
  68        0x0000, /* GPIO Control */
  69        0x0002, /* Auto Increment Control */
  70        0x0000, /* DAC Control */
  71        0x00FF, /* DAC Volume */
  72        0,
  73        0,
  74        0x0100, /* ADC Control */
  75        0x00FF, /* ADC Volume */
  76        0x0000, /* Notch Filter 1 Control 1 */
  77        0x0000, /* Notch Filter 1 Control 2 */
  78        0x0000, /* Notch Filter 2 Control 1 */
  79        0x0000, /* Notch Filter 2 Control 2 */
  80        0x0000, /* Notch Filter 3 Control 1 */
  81        0x0000, /* Notch Filter 3 Control 2 */
  82        0x0000, /* Notch Filter 4 Control 1 */
  83        0x0000, /* Notch Filter 4 Control 2 */
  84        0x0032, /* DAC Limit Control 1 */
  85        0x0000, /* DAC Limit Control 2 */
  86        0,
  87        0,
  88        0,
  89        0,
  90        0,
  91        0,
  92        0x0038, /* ALC Control 1 */
  93        0x000B, /* ALC Control 2 */
  94        0x0032, /* ALC Control 3 */
  95        0x0000, /* Noise Gate */
  96        0x0041, /* PLLN */
  97        0x000C, /* PLLK1 */
  98        0x0093, /* PLLK2 */
  99        0x00E9, /* PLLK3 */
 100        0,
 101        0,
 102        0x0030, /* ALC Control 4 */
 103        0,
 104        0x0002, /* Input Control */
 105        0x0050, /* PGA Gain */
 106        0,
 107        0x0002, /* ADC Boost Control */
 108        0,
 109        0x0002, /* Output Control */
 110        0x0000, /* Speaker Mixer Control */
 111        0,
 112        0,
 113        0,
 114        0x0079, /* Speaker Volume */
 115        0,
 116        0x0000, /* Mono Mixer Control */
 117};
 118
 119static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" };
 120static const struct soc_enum wm8940_adc_companding_enum
 121= SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 1, 4, wm8940_companding);
 122static const struct soc_enum wm8940_dac_companding_enum
 123= SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 3, 4, wm8940_companding);
 124
 125static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"};
 126static const struct soc_enum wm8940_alc_mode_enum
 127= SOC_ENUM_SINGLE(WM8940_ALC3, 8, 2, wm8940_alc_mode_text);
 128
 129static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"};
 130static const struct soc_enum wm8940_mic_bias_level_enum
 131= SOC_ENUM_SINGLE(WM8940_INPUTCTL, 8, 2, wm8940_mic_bias_level_text);
 132
 133static const char *wm8940_filter_mode_text[] = {"Audio", "Application"};
 134static const struct soc_enum wm8940_filter_mode_enum
 135= SOC_ENUM_SINGLE(WM8940_ADC, 7, 2, wm8940_filter_mode_text);
 136
 137static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1);
 138static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0);
 139static DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0);
 140static DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0);
 141static DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0);
 142static DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0);
 143static DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0);
 144static DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0);
 145static DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1);
 146static DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0);
 147
 148static const struct snd_kcontrol_new wm8940_snd_controls[] = {
 149        SOC_SINGLE("Digital Loopback Switch", WM8940_COMPANDINGCTL,
 150                   6, 1, 0),
 151        SOC_ENUM("DAC Companding", wm8940_dac_companding_enum),
 152        SOC_ENUM("ADC Companding", wm8940_adc_companding_enum),
 153
 154        SOC_ENUM("ALC Mode", wm8940_alc_mode_enum),
 155        SOC_SINGLE("ALC Switch", WM8940_ALC1, 8, 1, 0),
 156        SOC_SINGLE_TLV("ALC Capture Max Gain", WM8940_ALC1,
 157                       3, 7, 1, wm8940_alc_max_tlv),
 158        SOC_SINGLE_TLV("ALC Capture Min Gain", WM8940_ALC1,
 159                       0, 7, 0, wm8940_alc_min_tlv),
 160        SOC_SINGLE_TLV("ALC Capture Target", WM8940_ALC2,
 161                       0, 14, 0, wm8940_alc_tar_tlv),
 162        SOC_SINGLE("ALC Capture Hold", WM8940_ALC2, 4, 10, 0),
 163        SOC_SINGLE("ALC Capture Decay", WM8940_ALC3, 4, 10, 0),
 164        SOC_SINGLE("ALC Capture Attach", WM8940_ALC3, 0, 10, 0),
 165        SOC_SINGLE("ALC ZC Switch", WM8940_ALC4, 1, 1, 0),
 166        SOC_SINGLE("ALC Capture Noise Gate Switch", WM8940_NOISEGATE,
 167                   3, 1, 0),
 168        SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8940_NOISEGATE,
 169                   0, 7, 0),
 170
 171        SOC_SINGLE("DAC Playback Limiter Switch", WM8940_DACLIM1, 8, 1, 0),
 172        SOC_SINGLE("DAC Playback Limiter Attack", WM8940_DACLIM1, 0, 9, 0),
 173        SOC_SINGLE("DAC Playback Limiter Decay", WM8940_DACLIM1, 4, 11, 0),
 174        SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8940_DACLIM2,
 175                       4, 9, 1, wm8940_lim_thresh_tlv),
 176        SOC_SINGLE_TLV("DAC Playback Limiter Boost", WM8940_DACLIM2,
 177                       0, 12, 0, wm8940_lim_boost_tlv),
 178
 179        SOC_SINGLE("Capture PGA ZC Switch", WM8940_PGAGAIN, 7, 1, 0),
 180        SOC_SINGLE_TLV("Capture PGA Volume", WM8940_PGAGAIN,
 181                       0, 63, 0, wm8940_pga_vol_tlv),
 182        SOC_SINGLE_TLV("Digital Playback Volume", WM8940_DACVOL,
 183                       0, 255, 0, wm8940_adc_tlv),
 184        SOC_SINGLE_TLV("Digital Capture Volume", WM8940_ADCVOL,
 185                       0, 255, 0, wm8940_adc_tlv),
 186        SOC_ENUM("Mic Bias Level", wm8940_mic_bias_level_enum),
 187        SOC_SINGLE_TLV("Capture Boost Volue", WM8940_ADCBOOST,
 188                       8, 1, 0, wm8940_capture_boost_vol_tlv),
 189        SOC_SINGLE_TLV("Speaker Playback Volume", WM8940_SPKVOL,
 190                       0, 63, 0, wm8940_spk_vol_tlv),
 191        SOC_SINGLE("Speaker Playback Switch", WM8940_SPKVOL,  6, 1, 1),
 192
 193        SOC_SINGLE_TLV("Speaker Mixer Line Bypass Volume", WM8940_SPKVOL,
 194                       8, 1, 1, wm8940_att_tlv),
 195        SOC_SINGLE("Speaker Playback ZC Switch", WM8940_SPKVOL, 7, 1, 0),
 196
 197        SOC_SINGLE("Mono Out Switch", WM8940_MONOMIX, 6, 1, 1),
 198        SOC_SINGLE_TLV("Mono Mixer Line Bypass Volume", WM8940_MONOMIX,
 199                       7, 1, 1, wm8940_att_tlv),
 200
 201        SOC_SINGLE("High Pass Filter Switch", WM8940_ADC, 8, 1, 0),
 202        SOC_ENUM("High Pass Filter Mode", wm8940_filter_mode_enum),
 203        SOC_SINGLE("High Pass Filter Cut Off", WM8940_ADC, 4, 7, 0),
 204        SOC_SINGLE("ADC Inversion Switch", WM8940_ADC, 0, 1, 0),
 205        SOC_SINGLE("DAC Inversion Switch", WM8940_DAC, 0, 1, 0),
 206        SOC_SINGLE("DAC Auto Mute Switch", WM8940_DAC, 2, 1, 0),
 207        SOC_SINGLE("ZC Timeout Clock Switch", WM8940_ADDCNTRL, 0, 1, 0),
 208};
 209
 210static const struct snd_kcontrol_new wm8940_speaker_mixer_controls[] = {
 211        SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_SPKMIX, 1, 1, 0),
 212        SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_SPKMIX, 5, 1, 0),
 213        SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_SPKMIX, 0, 1, 0),
 214};
 215
 216static const struct snd_kcontrol_new wm8940_mono_mixer_controls[] = {
 217        SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_MONOMIX, 1, 1, 0),
 218        SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_MONOMIX, 2, 1, 0),
 219        SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_MONOMIX, 0, 1, 0),
 220};
 221
 222static DECLARE_TLV_DB_SCALE(wm8940_boost_vol_tlv, -1500, 300, 1);
 223static const struct snd_kcontrol_new wm8940_input_boost_controls[] = {
 224        SOC_DAPM_SINGLE("Mic PGA Switch", WM8940_PGAGAIN, 6, 1, 1),
 225        SOC_DAPM_SINGLE_TLV("Aux Volume", WM8940_ADCBOOST,
 226                            0, 7, 0, wm8940_boost_vol_tlv),
 227        SOC_DAPM_SINGLE_TLV("Mic Volume", WM8940_ADCBOOST,
 228                            4, 7, 0, wm8940_boost_vol_tlv),
 229};
 230
 231static const struct snd_kcontrol_new wm8940_micpga_controls[] = {
 232        SOC_DAPM_SINGLE("AUX Switch", WM8940_INPUTCTL, 2, 1, 0),
 233        SOC_DAPM_SINGLE("MICP Switch", WM8940_INPUTCTL, 0, 1, 0),
 234        SOC_DAPM_SINGLE("MICN Switch", WM8940_INPUTCTL, 1, 1, 0),
 235};
 236
 237static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = {
 238        SND_SOC_DAPM_MIXER("Speaker Mixer", WM8940_POWER3, 2, 0,
 239                           &wm8940_speaker_mixer_controls[0],
 240                           ARRAY_SIZE(wm8940_speaker_mixer_controls)),
 241        SND_SOC_DAPM_MIXER("Mono Mixer", WM8940_POWER3, 3, 0,
 242                           &wm8940_mono_mixer_controls[0],
 243                           ARRAY_SIZE(wm8940_mono_mixer_controls)),
 244        SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8940_POWER3, 0, 0),
 245
 246        SND_SOC_DAPM_PGA("SpkN Out", WM8940_POWER3, 5, 0, NULL, 0),
 247        SND_SOC_DAPM_PGA("SpkP Out", WM8940_POWER3, 6, 0, NULL, 0),
 248        SND_SOC_DAPM_PGA("Mono Out", WM8940_POWER3, 7, 0, NULL, 0),
 249        SND_SOC_DAPM_OUTPUT("MONOOUT"),
 250        SND_SOC_DAPM_OUTPUT("SPKOUTP"),
 251        SND_SOC_DAPM_OUTPUT("SPKOUTN"),
 252
 253        SND_SOC_DAPM_PGA("Aux Input", WM8940_POWER1, 6, 0, NULL, 0),
 254        SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8940_POWER2, 0, 0),
 255        SND_SOC_DAPM_MIXER("Mic PGA", WM8940_POWER2, 2, 0,
 256                           &wm8940_micpga_controls[0],
 257                           ARRAY_SIZE(wm8940_micpga_controls)),
 258        SND_SOC_DAPM_MIXER("Boost Mixer", WM8940_POWER2, 4, 0,
 259                           &wm8940_input_boost_controls[0],
 260                           ARRAY_SIZE(wm8940_input_boost_controls)),
 261        SND_SOC_DAPM_MICBIAS("Mic Bias", WM8940_POWER1, 4, 0),
 262
 263        SND_SOC_DAPM_INPUT("MICN"),
 264        SND_SOC_DAPM_INPUT("MICP"),
 265        SND_SOC_DAPM_INPUT("AUX"),
 266};
 267
 268static const struct snd_soc_dapm_route audio_map[] = {
 269        /* Mono output mixer */
 270        {"Mono Mixer", "PCM Playback Switch", "DAC"},
 271        {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
 272        {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
 273
 274        /* Speaker output mixer */
 275        {"Speaker Mixer", "PCM Playback Switch", "DAC"},
 276        {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
 277        {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
 278
 279        /* Outputs */
 280        {"Mono Out", NULL, "Mono Mixer"},
 281        {"MONOOUT", NULL, "Mono Out"},
 282        {"SpkN Out", NULL, "Speaker Mixer"},
 283        {"SpkP Out", NULL, "Speaker Mixer"},
 284        {"SPKOUTN", NULL, "SpkN Out"},
 285        {"SPKOUTP", NULL, "SpkP Out"},
 286
 287        /*  Microphone PGA */
 288        {"Mic PGA", "MICN Switch", "MICN"},
 289        {"Mic PGA", "MICP Switch", "MICP"},
 290        {"Mic PGA", "AUX Switch", "AUX"},
 291
 292        /* Boost Mixer */
 293        {"Boost Mixer", "Mic PGA Switch", "Mic PGA"},
 294        {"Boost Mixer", "Mic Volume",  "MICP"},
 295        {"Boost Mixer", "Aux Volume", "Aux Input"},
 296
 297        {"ADC", NULL, "Boost Mixer"},
 298};
 299
 300static int wm8940_add_widgets(struct snd_soc_codec *codec)
 301{
 302        struct snd_soc_dapm_context *dapm = &codec->dapm;
 303        int ret;
 304
 305        ret = snd_soc_dapm_new_controls(dapm, wm8940_dapm_widgets,
 306                                        ARRAY_SIZE(wm8940_dapm_widgets));
 307        if (ret)
 308                goto error_ret;
 309        ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
 310
 311error_ret:
 312        return ret;
 313}
 314
 315#define wm8940_reset(c) snd_soc_write(c, WM8940_SOFTRESET, 0);
 316
 317static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
 318                              unsigned int fmt)
 319{
 320        struct snd_soc_codec *codec = codec_dai->codec;
 321        u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFE67;
 322        u16 clk = snd_soc_read(codec, WM8940_CLOCK) & 0x1fe;
 323
 324        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 325        case SND_SOC_DAIFMT_CBM_CFM:
 326                clk |= 1;
 327                break;
 328        case SND_SOC_DAIFMT_CBS_CFS:
 329                break;
 330        default:
 331                return -EINVAL;
 332        }
 333        snd_soc_write(codec, WM8940_CLOCK, clk);
 334
 335        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 336        case SND_SOC_DAIFMT_I2S:
 337                iface |= (2 << 3);
 338                break;
 339        case SND_SOC_DAIFMT_LEFT_J:
 340                iface |= (1 << 3);
 341                break;
 342        case SND_SOC_DAIFMT_RIGHT_J:
 343                break;
 344        case SND_SOC_DAIFMT_DSP_A:
 345                iface |= (3 << 3);
 346                break;
 347        case SND_SOC_DAIFMT_DSP_B:
 348                iface |= (3 << 3) | (1 << 7);
 349                break;
 350        }
 351
 352        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 353        case SND_SOC_DAIFMT_NB_NF:
 354                break;
 355        case SND_SOC_DAIFMT_NB_IF:
 356                iface |= (1 << 7);
 357                break;
 358        case SND_SOC_DAIFMT_IB_NF:
 359                iface |= (1 << 8);
 360                break;
 361        case SND_SOC_DAIFMT_IB_IF:
 362                iface |= (1 << 8) | (1 << 7);
 363                break;
 364        }
 365
 366        snd_soc_write(codec, WM8940_IFACE, iface);
 367
 368        return 0;
 369}
 370
 371static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
 372                                struct snd_pcm_hw_params *params,
 373                                struct snd_soc_dai *dai)
 374{
 375        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 376        struct snd_soc_codec *codec = rtd->codec;
 377        u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFD9F;
 378        u16 addcntrl = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFF1;
 379        u16 companding =  snd_soc_read(codec,
 380                                                WM8940_COMPANDINGCTL) & 0xFFDF;
 381        int ret;
 382
 383        /* LoutR control */
 384        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
 385            && params_channels(params) == 2)
 386                iface |= (1 << 9);
 387
 388        switch (params_rate(params)) {
 389        case 8000:
 390                addcntrl |= (0x5 << 1);
 391                break;
 392        case 11025:
 393                addcntrl |= (0x4 << 1);
 394                break;
 395        case 16000:
 396                addcntrl |= (0x3 << 1);
 397                break;
 398        case 22050:
 399                addcntrl |= (0x2 << 1);
 400                break;
 401        case 32000:
 402                addcntrl |= (0x1 << 1);
 403                break;
 404        case 44100:
 405        case 48000:
 406                break;
 407        }
 408        ret = snd_soc_write(codec, WM8940_ADDCNTRL, addcntrl);
 409        if (ret)
 410                goto error_ret;
 411
 412        switch (params_format(params)) {
 413        case SNDRV_PCM_FORMAT_S8:
 414                companding = companding | (1 << 5);
 415                break;
 416        case SNDRV_PCM_FORMAT_S16_LE:
 417                break;
 418        case SNDRV_PCM_FORMAT_S20_3LE:
 419                iface |= (1 << 5);
 420                break;
 421        case SNDRV_PCM_FORMAT_S24_LE:
 422                iface |= (2 << 5);
 423                break;
 424        case SNDRV_PCM_FORMAT_S32_LE:
 425                iface |= (3 << 5);
 426                break;
 427        }
 428        ret = snd_soc_write(codec, WM8940_COMPANDINGCTL, companding);
 429        if (ret)
 430                goto error_ret;
 431        ret = snd_soc_write(codec, WM8940_IFACE, iface);
 432
 433error_ret:
 434        return ret;
 435}
 436
 437static int wm8940_mute(struct snd_soc_dai *dai, int mute)
 438{
 439        struct snd_soc_codec *codec = dai->codec;
 440        u16 mute_reg = snd_soc_read(codec, WM8940_DAC) & 0xffbf;
 441
 442        if (mute)
 443                mute_reg |= 0x40;
 444
 445        return snd_soc_write(codec, WM8940_DAC, mute_reg);
 446}
 447
 448static int wm8940_set_bias_level(struct snd_soc_codec *codec,
 449                                 enum snd_soc_bias_level level)
 450{
 451        u16 val;
 452        u16 pwr_reg = snd_soc_read(codec, WM8940_POWER1) & 0x1F0;
 453        int ret = 0;
 454
 455        switch (level) {
 456        case SND_SOC_BIAS_ON:
 457                /* ensure bufioen and biasen */
 458                pwr_reg |= (1 << 2) | (1 << 3);
 459                /* Enable thermal shutdown */
 460                val = snd_soc_read(codec, WM8940_OUTPUTCTL);
 461                ret = snd_soc_write(codec, WM8940_OUTPUTCTL, val | 0x2);
 462                if (ret)
 463                        break;
 464                /* set vmid to 75k */
 465                ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
 466                break;
 467        case SND_SOC_BIAS_PREPARE:
 468                /* ensure bufioen and biasen */
 469                pwr_reg |= (1 << 2) | (1 << 3);
 470                ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
 471                break;
 472        case SND_SOC_BIAS_STANDBY:
 473                if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
 474                        ret = snd_soc_cache_sync(codec);
 475                        if (ret < 0) {
 476                                dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
 477                                return ret;
 478                        }
 479                }
 480
 481                /* ensure bufioen and biasen */
 482                pwr_reg |= (1 << 2) | (1 << 3);
 483                /* set vmid to 300k for standby */
 484                ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x2);
 485                break;
 486        case SND_SOC_BIAS_OFF:
 487                ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg);
 488                break;
 489        }
 490
 491        codec->dapm.bias_level = level;
 492
 493        return ret;
 494}
 495
 496struct pll_ {
 497        unsigned int pre_scale:2;
 498        unsigned int n:4;
 499        unsigned int k;
 500};
 501
 502static struct pll_ pll_div;
 503
 504/* The size in bits of the pll divide multiplied by 10
 505 * to allow rounding later */
 506#define FIXED_PLL_SIZE ((1 << 24) * 10)
 507static void pll_factors(unsigned int target, unsigned int source)
 508{
 509        unsigned long long Kpart;
 510        unsigned int K, Ndiv, Nmod;
 511        /* The left shift ist to avoid accuracy loss when right shifting */
 512        Ndiv = target / source;
 513
 514        if (Ndiv > 12) {
 515                source <<= 1;
 516                /* Multiply by 2 */
 517                pll_div.pre_scale = 0;
 518                Ndiv = target / source;
 519        } else if (Ndiv < 3) {
 520                source >>= 2;
 521                /* Divide by 4 */
 522                pll_div.pre_scale = 3;
 523                Ndiv = target / source;
 524        } else if (Ndiv < 6) {
 525                source >>= 1;
 526                /* divide by 2 */
 527                pll_div.pre_scale = 2;
 528                Ndiv = target / source;
 529        } else
 530                pll_div.pre_scale = 1;
 531
 532        if ((Ndiv < 6) || (Ndiv > 12))
 533                printk(KERN_WARNING
 534                        "WM8940 N value %d outwith recommended range!d\n",
 535                        Ndiv);
 536
 537        pll_div.n = Ndiv;
 538        Nmod = target % source;
 539        Kpart = FIXED_PLL_SIZE * (long long)Nmod;
 540
 541        do_div(Kpart, source);
 542
 543        K = Kpart & 0xFFFFFFFF;
 544
 545        /* Check if we need to round */
 546        if ((K % 10) >= 5)
 547                K += 5;
 548
 549        /* Move down to proper range now rounding is done */
 550        K /= 10;
 551
 552        pll_div.k = K;
 553}
 554
 555/* Untested at the moment */
 556static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
 557                int source, unsigned int freq_in, unsigned int freq_out)
 558{
 559        struct snd_soc_codec *codec = codec_dai->codec;
 560        u16 reg;
 561
 562        /* Turn off PLL */
 563        reg = snd_soc_read(codec, WM8940_POWER1);
 564        snd_soc_write(codec, WM8940_POWER1, reg & 0x1df);
 565
 566        if (freq_in == 0 || freq_out == 0) {
 567                /* Clock CODEC directly from MCLK */
 568                reg = snd_soc_read(codec, WM8940_CLOCK);
 569                snd_soc_write(codec, WM8940_CLOCK, reg & 0x0ff);
 570                /* Pll power down */
 571                snd_soc_write(codec, WM8940_PLLN, (1 << 7));
 572                return 0;
 573        }
 574
 575        /* Pll is followed by a frequency divide by 4 */
 576        pll_factors(freq_out*4, freq_in);
 577        if (pll_div.k)
 578                snd_soc_write(codec, WM8940_PLLN,
 579                             (pll_div.pre_scale << 4) | pll_div.n | (1 << 6));
 580        else /* No factional component */
 581                snd_soc_write(codec, WM8940_PLLN,
 582                             (pll_div.pre_scale << 4) | pll_div.n);
 583        snd_soc_write(codec, WM8940_PLLK1, pll_div.k >> 18);
 584        snd_soc_write(codec, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff);
 585        snd_soc_write(codec, WM8940_PLLK3, pll_div.k & 0x1ff);
 586        /* Enable the PLL */
 587        reg = snd_soc_read(codec, WM8940_POWER1);
 588        snd_soc_write(codec, WM8940_POWER1, reg | 0x020);
 589
 590        /* Run CODEC from PLL instead of MCLK */
 591        reg = snd_soc_read(codec, WM8940_CLOCK);
 592        snd_soc_write(codec, WM8940_CLOCK, reg | 0x100);
 593
 594        return 0;
 595}
 596
 597static int wm8940_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 598                                 int clk_id, unsigned int freq, int dir)
 599{
 600        struct snd_soc_codec *codec = codec_dai->codec;
 601        struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
 602
 603        switch (freq) {
 604        case 11289600:
 605        case 12000000:
 606        case 12288000:
 607        case 16934400:
 608        case 18432000:
 609                wm8940->sysclk = freq;
 610                return 0;
 611        }
 612        return -EINVAL;
 613}
 614
 615static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
 616                                 int div_id, int div)
 617{
 618        struct snd_soc_codec *codec = codec_dai->codec;
 619        u16 reg;
 620        int ret = 0;
 621
 622        switch (div_id) {
 623        case WM8940_BCLKDIV:
 624                reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFFE3;
 625                ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 2));
 626                break;
 627        case WM8940_MCLKDIV:
 628                reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFF1F;
 629                ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 5));
 630                break;
 631        case WM8940_OPCLKDIV:
 632                reg = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFCF;
 633                ret = snd_soc_write(codec, WM8940_ADDCNTRL, reg | (div << 4));
 634                break;
 635        }
 636        return ret;
 637}
 638
 639#define WM8940_RATES SNDRV_PCM_RATE_8000_48000
 640
 641#define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 |                           \
 642                        SNDRV_PCM_FMTBIT_S16_LE |                       \
 643                        SNDRV_PCM_FMTBIT_S20_3LE |                      \
 644                        SNDRV_PCM_FMTBIT_S24_LE |                       \
 645                        SNDRV_PCM_FMTBIT_S32_LE)
 646
 647static struct snd_soc_dai_ops wm8940_dai_ops = {
 648        .hw_params = wm8940_i2s_hw_params,
 649        .set_sysclk = wm8940_set_dai_sysclk,
 650        .digital_mute = wm8940_mute,
 651        .set_fmt = wm8940_set_dai_fmt,
 652        .set_clkdiv = wm8940_set_dai_clkdiv,
 653        .set_pll = wm8940_set_dai_pll,
 654};
 655
 656static struct snd_soc_dai_driver wm8940_dai = {
 657        .name = "wm8940-hifi",
 658        .playback = {
 659                .stream_name = "Playback",
 660                .channels_min = 1,
 661                .channels_max = 2,
 662                .rates = WM8940_RATES,
 663                .formats = WM8940_FORMATS,
 664        },
 665        .capture = {
 666                .stream_name = "Capture",
 667                .channels_min = 1,
 668                .channels_max = 2,
 669                .rates = WM8940_RATES,
 670                .formats = WM8940_FORMATS,
 671        },
 672        .ops = &wm8940_dai_ops,
 673        .symmetric_rates = 1,
 674};
 675
 676static int wm8940_suspend(struct snd_soc_codec *codec, pm_message_t state)
 677{
 678        return wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF);
 679}
 680
 681static int wm8940_resume(struct snd_soc_codec *codec)
 682{
 683        wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 684        return 0;
 685}
 686
 687static int wm8940_probe(struct snd_soc_codec *codec)
 688{
 689        struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
 690        struct wm8940_setup_data *pdata = codec->dev->platform_data;
 691        int ret;
 692        u16 reg;
 693
 694        ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8940->control_type);
 695        if (ret < 0) {
 696                dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
 697                return ret;
 698        }
 699
 700        ret = wm8940_reset(codec);
 701        if (ret < 0) {
 702                dev_err(codec->dev, "Failed to issue reset\n");
 703                return ret;
 704        }
 705
 706        wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 707
 708        ret = snd_soc_write(codec, WM8940_POWER1, 0x180);
 709        if (ret < 0)
 710                return ret;
 711
 712        if (!pdata)
 713                dev_warn(codec->dev, "No platform data supplied\n");
 714        else {
 715                reg = snd_soc_read(codec, WM8940_OUTPUTCTL);
 716                ret = snd_soc_write(codec, WM8940_OUTPUTCTL, reg | pdata->vroi);
 717                if (ret < 0)
 718                        return ret;
 719        }
 720
 721        ret = snd_soc_add_controls(codec, wm8940_snd_controls,
 722                             ARRAY_SIZE(wm8940_snd_controls));
 723        if (ret)
 724                return ret;
 725        ret = wm8940_add_widgets(codec);
 726        return ret;
 727}
 728
 729static int wm8940_remove(struct snd_soc_codec *codec)
 730{
 731        wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF);
 732        return 0;
 733}
 734
 735static struct snd_soc_codec_driver soc_codec_dev_wm8940 = {
 736        .probe =        wm8940_probe,
 737        .remove =       wm8940_remove,
 738        .suspend =      wm8940_suspend,
 739        .resume =       wm8940_resume,
 740        .set_bias_level = wm8940_set_bias_level,
 741        .reg_cache_size = ARRAY_SIZE(wm8940_reg_defaults),
 742        .reg_word_size = sizeof(u16),
 743        .reg_cache_default = wm8940_reg_defaults,
 744        .volatile_register = wm8940_volatile_register,
 745};
 746
 747#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 748static __devinit int wm8940_i2c_probe(struct i2c_client *i2c,
 749                                      const struct i2c_device_id *id)
 750{
 751        struct wm8940_priv *wm8940;
 752        int ret;
 753
 754        wm8940 = kzalloc(sizeof(struct wm8940_priv), GFP_KERNEL);
 755        if (wm8940 == NULL)
 756                return -ENOMEM;
 757
 758        i2c_set_clientdata(i2c, wm8940);
 759        wm8940->control_type = SND_SOC_I2C;
 760
 761        ret = snd_soc_register_codec(&i2c->dev,
 762                        &soc_codec_dev_wm8940, &wm8940_dai, 1);
 763        if (ret < 0)
 764                kfree(wm8940);
 765        return ret;
 766}
 767
 768static __devexit int wm8940_i2c_remove(struct i2c_client *client)
 769{
 770        snd_soc_unregister_codec(&client->dev);
 771        kfree(i2c_get_clientdata(client));
 772        return 0;
 773}
 774
 775static const struct i2c_device_id wm8940_i2c_id[] = {
 776        { "wm8940", 0 },
 777        { }
 778};
 779MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id);
 780
 781static struct i2c_driver wm8940_i2c_driver = {
 782        .driver = {
 783                .name = "wm8940-codec",
 784                .owner = THIS_MODULE,
 785        },
 786        .probe =    wm8940_i2c_probe,
 787        .remove =   __devexit_p(wm8940_i2c_remove),
 788        .id_table = wm8940_i2c_id,
 789};
 790#endif
 791
 792static int __init wm8940_modinit(void)
 793{
 794        int ret = 0;
 795#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 796        ret = i2c_add_driver(&wm8940_i2c_driver);
 797        if (ret != 0) {
 798                printk(KERN_ERR "Failed to register wm8940 I2C driver: %d\n",
 799                       ret);
 800        }
 801#endif
 802        return ret;
 803}
 804module_init(wm8940_modinit);
 805
 806static void __exit wm8940_exit(void)
 807{
 808#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 809        i2c_del_driver(&wm8940_i2c_driver);
 810#endif
 811}
 812module_exit(wm8940_exit);
 813
 814MODULE_DESCRIPTION("ASoC WM8940 driver");
 815MODULE_AUTHOR("Jonathan Cameron");
 816MODULE_LICENSE("GPL");
 817