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