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        void *control_data;
  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        struct snd_soc_dapm_context *dapm = &codec->dapm;
 293        int ret;
 294
 295        ret = snd_soc_dapm_new_controls(dapm, wm8940_dapm_widgets,
 296                                        ARRAY_SIZE(wm8940_dapm_widgets));
 297        if (ret)
 298                goto error_ret;
 299        ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
 300        if (ret)
 301                goto error_ret;
 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_codec *codec = rtd->codec;
 369        u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFD9F;
 370        u16 addcntrl = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFF1;
 371        u16 companding =  snd_soc_read(codec,
 372                                                WM8940_COMPANDINGCTL) & 0xFFDF;
 373        int ret;
 374
 375        /* LoutR control */
 376        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
 377            && params_channels(params) == 2)
 378                iface |= (1 << 9);
 379
 380        switch (params_rate(params)) {
 381        case 8000:
 382                addcntrl |= (0x5 << 1);
 383                break;
 384        case 11025:
 385                addcntrl |= (0x4 << 1);
 386                break;
 387        case 16000:
 388                addcntrl |= (0x3 << 1);
 389                break;
 390        case 22050:
 391                addcntrl |= (0x2 << 1);
 392                break;
 393        case 32000:
 394                addcntrl |= (0x1 << 1);
 395                break;
 396        case 44100:
 397        case 48000:
 398                break;
 399        }
 400        ret = snd_soc_write(codec, WM8940_ADDCNTRL, addcntrl);
 401        if (ret)
 402                goto error_ret;
 403
 404        switch (params_format(params)) {
 405        case SNDRV_PCM_FORMAT_S8:
 406                companding = companding | (1 << 5);
 407                break;
 408        case SNDRV_PCM_FORMAT_S16_LE:
 409                break;
 410        case SNDRV_PCM_FORMAT_S20_3LE:
 411                iface |= (1 << 5);
 412                break;
 413        case SNDRV_PCM_FORMAT_S24_LE:
 414                iface |= (2 << 5);
 415                break;
 416        case SNDRV_PCM_FORMAT_S32_LE:
 417                iface |= (3 << 5);
 418                break;
 419        }
 420        ret = snd_soc_write(codec, WM8940_COMPANDINGCTL, companding);
 421        if (ret)
 422                goto error_ret;
 423        ret = snd_soc_write(codec, WM8940_IFACE, iface);
 424
 425error_ret:
 426        return ret;
 427}
 428
 429static int wm8940_mute(struct snd_soc_dai *dai, int mute)
 430{
 431        struct snd_soc_codec *codec = dai->codec;
 432        u16 mute_reg = snd_soc_read(codec, WM8940_DAC) & 0xffbf;
 433
 434        if (mute)
 435                mute_reg |= 0x40;
 436
 437        return snd_soc_write(codec, WM8940_DAC, mute_reg);
 438}
 439
 440static int wm8940_set_bias_level(struct snd_soc_codec *codec,
 441                                 enum snd_soc_bias_level level)
 442{
 443        u16 val;
 444        u16 pwr_reg = snd_soc_read(codec, WM8940_POWER1) & 0x1F0;
 445        int ret = 0;
 446
 447        switch (level) {
 448        case SND_SOC_BIAS_ON:
 449                /* ensure bufioen and biasen */
 450                pwr_reg |= (1 << 2) | (1 << 3);
 451                /* Enable thermal shutdown */
 452                val = snd_soc_read(codec, WM8940_OUTPUTCTL);
 453                ret = snd_soc_write(codec, WM8940_OUTPUTCTL, val | 0x2);
 454                if (ret)
 455                        break;
 456                /* set vmid to 75k */
 457                ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
 458                break;
 459        case SND_SOC_BIAS_PREPARE:
 460                /* ensure bufioen and biasen */
 461                pwr_reg |= (1 << 2) | (1 << 3);
 462                ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
 463                break;
 464        case SND_SOC_BIAS_STANDBY:
 465                /* ensure bufioen and biasen */
 466                pwr_reg |= (1 << 2) | (1 << 3);
 467                /* set vmid to 300k for standby */
 468                ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x2);
 469                break;
 470        case SND_SOC_BIAS_OFF:
 471                ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg);
 472                break;
 473        }
 474
 475        return ret;
 476}
 477
 478struct pll_ {
 479        unsigned int pre_scale:2;
 480        unsigned int n:4;
 481        unsigned int k;
 482};
 483
 484static struct pll_ pll_div;
 485
 486/* The size in bits of the pll divide multiplied by 10
 487 * to allow rounding later */
 488#define FIXED_PLL_SIZE ((1 << 24) * 10)
 489static void pll_factors(unsigned int target, unsigned int source)
 490{
 491        unsigned long long Kpart;
 492        unsigned int K, Ndiv, Nmod;
 493        /* The left shift ist to avoid accuracy loss when right shifting */
 494        Ndiv = target / source;
 495
 496        if (Ndiv > 12) {
 497                source <<= 1;
 498                /* Multiply by 2 */
 499                pll_div.pre_scale = 0;
 500                Ndiv = target / source;
 501        } else if (Ndiv < 3) {
 502                source >>= 2;
 503                /* Divide by 4 */
 504                pll_div.pre_scale = 3;
 505                Ndiv = target / source;
 506        } else if (Ndiv < 6) {
 507                source >>= 1;
 508                /* divide by 2 */
 509                pll_div.pre_scale = 2;
 510                Ndiv = target / source;
 511        } else
 512                pll_div.pre_scale = 1;
 513
 514        if ((Ndiv < 6) || (Ndiv > 12))
 515                printk(KERN_WARNING
 516                        "WM8940 N value %d outwith recommended range!d\n",
 517                        Ndiv);
 518
 519        pll_div.n = Ndiv;
 520        Nmod = target % source;
 521        Kpart = FIXED_PLL_SIZE * (long long)Nmod;
 522
 523        do_div(Kpart, source);
 524
 525        K = Kpart & 0xFFFFFFFF;
 526
 527        /* Check if we need to round */
 528        if ((K % 10) >= 5)
 529                K += 5;
 530
 531        /* Move down to proper range now rounding is done */
 532        K /= 10;
 533
 534        pll_div.k = K;
 535}
 536
 537/* Untested at the moment */
 538static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
 539                int source, unsigned int freq_in, unsigned int freq_out)
 540{
 541        struct snd_soc_codec *codec = codec_dai->codec;
 542        u16 reg;
 543
 544        /* Turn off PLL */
 545        reg = snd_soc_read(codec, WM8940_POWER1);
 546        snd_soc_write(codec, WM8940_POWER1, reg & 0x1df);
 547
 548        if (freq_in == 0 || freq_out == 0) {
 549                /* Clock CODEC directly from MCLK */
 550                reg = snd_soc_read(codec, WM8940_CLOCK);
 551                snd_soc_write(codec, WM8940_CLOCK, reg & 0x0ff);
 552                /* Pll power down */
 553                snd_soc_write(codec, WM8940_PLLN, (1 << 7));
 554                return 0;
 555        }
 556
 557        /* Pll is followed by a frequency divide by 4 */
 558        pll_factors(freq_out*4, freq_in);
 559        if (pll_div.k)
 560                snd_soc_write(codec, WM8940_PLLN,
 561                             (pll_div.pre_scale << 4) | pll_div.n | (1 << 6));
 562        else /* No factional component */
 563                snd_soc_write(codec, WM8940_PLLN,
 564                             (pll_div.pre_scale << 4) | pll_div.n);
 565        snd_soc_write(codec, WM8940_PLLK1, pll_div.k >> 18);
 566        snd_soc_write(codec, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff);
 567        snd_soc_write(codec, WM8940_PLLK3, pll_div.k & 0x1ff);
 568        /* Enable the PLL */
 569        reg = snd_soc_read(codec, WM8940_POWER1);
 570        snd_soc_write(codec, WM8940_POWER1, reg | 0x020);
 571
 572        /* Run CODEC from PLL instead of MCLK */
 573        reg = snd_soc_read(codec, WM8940_CLOCK);
 574        snd_soc_write(codec, WM8940_CLOCK, reg | 0x100);
 575
 576        return 0;
 577}
 578
 579static int wm8940_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 580                                 int clk_id, unsigned int freq, int dir)
 581{
 582        struct snd_soc_codec *codec = codec_dai->codec;
 583        struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
 584
 585        switch (freq) {
 586        case 11289600:
 587        case 12000000:
 588        case 12288000:
 589        case 16934400:
 590        case 18432000:
 591                wm8940->sysclk = freq;
 592                return 0;
 593        }
 594        return -EINVAL;
 595}
 596
 597static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
 598                                 int div_id, int div)
 599{
 600        struct snd_soc_codec *codec = codec_dai->codec;
 601        u16 reg;
 602        int ret = 0;
 603
 604        switch (div_id) {
 605        case WM8940_BCLKDIV:
 606                reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFFEF3;
 607                ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 2));
 608                break;
 609        case WM8940_MCLKDIV:
 610                reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFF1F;
 611                ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 5));
 612                break;
 613        case WM8940_OPCLKDIV:
 614                reg = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFCF;
 615                ret = snd_soc_write(codec, WM8940_ADDCNTRL, reg | (div << 4));
 616                break;
 617        }
 618        return ret;
 619}
 620
 621#define WM8940_RATES SNDRV_PCM_RATE_8000_48000
 622
 623#define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 |                           \
 624                        SNDRV_PCM_FMTBIT_S16_LE |                       \
 625                        SNDRV_PCM_FMTBIT_S20_3LE |                      \
 626                        SNDRV_PCM_FMTBIT_S24_LE |                       \
 627                        SNDRV_PCM_FMTBIT_S32_LE)
 628
 629static struct snd_soc_dai_ops wm8940_dai_ops = {
 630        .hw_params = wm8940_i2s_hw_params,
 631        .set_sysclk = wm8940_set_dai_sysclk,
 632        .digital_mute = wm8940_mute,
 633        .set_fmt = wm8940_set_dai_fmt,
 634        .set_clkdiv = wm8940_set_dai_clkdiv,
 635        .set_pll = wm8940_set_dai_pll,
 636};
 637
 638static struct snd_soc_dai_driver wm8940_dai = {
 639        .name = "wm8940-hifi",
 640        .playback = {
 641                .stream_name = "Playback",
 642                .channels_min = 1,
 643                .channels_max = 2,
 644                .rates = WM8940_RATES,
 645                .formats = WM8940_FORMATS,
 646        },
 647        .capture = {
 648                .stream_name = "Capture",
 649                .channels_min = 1,
 650                .channels_max = 2,
 651                .rates = WM8940_RATES,
 652                .formats = WM8940_FORMATS,
 653        },
 654        .ops = &wm8940_dai_ops,
 655        .symmetric_rates = 1,
 656};
 657
 658static int wm8940_suspend(struct snd_soc_codec *codec, pm_message_t state)
 659{
 660        return wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF);
 661}
 662
 663static int wm8940_resume(struct snd_soc_codec *codec)
 664{
 665        int i;
 666        int ret;
 667        u8 data[3];
 668        u16 *cache = codec->reg_cache;
 669
 670        /* Sync reg_cache with the hardware
 671         * Could use auto incremented writes to speed this up
 672         */
 673        for (i = 0; i < ARRAY_SIZE(wm8940_reg_defaults); i++) {
 674                data[0] = i;
 675                data[1] = (cache[i] & 0xFF00) >> 8;
 676                data[2] = cache[i] & 0x00FF;
 677                ret = codec->hw_write(codec->control_data, data, 3);
 678                if (ret < 0)
 679                        goto error_ret;
 680                else if (ret != 3) {
 681                        ret = -EIO;
 682                        goto error_ret;
 683                }
 684        }
 685        ret = wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 686        if (ret)
 687                goto error_ret;
 688
 689error_ret:
 690        return ret;
 691}
 692
 693static int wm8940_probe(struct snd_soc_codec *codec)
 694{
 695        struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
 696        struct wm8940_setup_data *pdata = codec->dev->platform_data;
 697        int ret;
 698        u16 reg;
 699
 700        codec->control_data = wm8940->control_data;
 701        ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8940->control_type);
 702        if (ret < 0) {
 703                dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
 704                return ret;
 705        }
 706
 707        ret = wm8940_reset(codec);
 708        if (ret < 0) {
 709                dev_err(codec->dev, "Failed to issue reset\n");
 710                return ret;
 711        }
 712
 713        wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 714
 715        ret = snd_soc_write(codec, WM8940_POWER1, 0x180);
 716        if (ret < 0)
 717                return ret;
 718
 719        if (!pdata)
 720                dev_warn(codec->dev, "No platform data supplied\n");
 721        else {
 722                reg = snd_soc_read(codec, WM8940_OUTPUTCTL);
 723                ret = snd_soc_write(codec, WM8940_OUTPUTCTL, reg | pdata->vroi);
 724                if (ret < 0)
 725                        return ret;
 726        }
 727
 728        ret = snd_soc_add_controls(codec, wm8940_snd_controls,
 729                             ARRAY_SIZE(wm8940_snd_controls));
 730        if (ret)
 731                return ret;
 732        ret = wm8940_add_widgets(codec);
 733        if (ret)
 734                return ret;
 735
 736        return ret;
 737}
 738
 739static int wm8940_remove(struct snd_soc_codec *codec)
 740{
 741        wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF);
 742        return 0;
 743}
 744
 745static struct snd_soc_codec_driver soc_codec_dev_wm8940 = {
 746        .probe =        wm8940_probe,
 747        .remove =       wm8940_remove,
 748        .suspend =      wm8940_suspend,
 749        .resume =       wm8940_resume,
 750        .set_bias_level = wm8940_set_bias_level,
 751        .reg_cache_size = ARRAY_SIZE(wm8940_reg_defaults),
 752        .reg_word_size = sizeof(u16),
 753        .reg_cache_default = wm8940_reg_defaults,
 754};
 755
 756#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 757static __devinit int wm8940_i2c_probe(struct i2c_client *i2c,
 758                                      const struct i2c_device_id *id)
 759{
 760        struct wm8940_priv *wm8940;
 761        int ret;
 762
 763        wm8940 = kzalloc(sizeof(struct wm8940_priv), GFP_KERNEL);
 764        if (wm8940 == NULL)
 765                return -ENOMEM;
 766
 767        i2c_set_clientdata(i2c, wm8940);
 768        wm8940->control_data = i2c;
 769        wm8940->control_type = SND_SOC_I2C;
 770
 771        ret = snd_soc_register_codec(&i2c->dev,
 772                        &soc_codec_dev_wm8940, &wm8940_dai, 1);
 773        if (ret < 0)
 774                kfree(wm8940);
 775        return ret;
 776}
 777
 778static __devexit int wm8940_i2c_remove(struct i2c_client *client)
 779{
 780        snd_soc_unregister_codec(&client->dev);
 781        kfree(i2c_get_clientdata(client));
 782        return 0;
 783}
 784
 785static const struct i2c_device_id wm8940_i2c_id[] = {
 786        { "wm8940", 0 },
 787        { }
 788};
 789MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id);
 790
 791static struct i2c_driver wm8940_i2c_driver = {
 792        .driver = {
 793                .name = "wm8940-codec",
 794                .owner = THIS_MODULE,
 795        },
 796        .probe =    wm8940_i2c_probe,
 797        .remove =   __devexit_p(wm8940_i2c_remove),
 798        .id_table = wm8940_i2c_id,
 799};
 800#endif
 801
 802static int __init wm8940_modinit(void)
 803{
 804        int ret = 0;
 805#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 806        ret = i2c_add_driver(&wm8940_i2c_driver);
 807        if (ret != 0) {
 808                printk(KERN_ERR "Failed to register wm8940 I2C driver: %d\n",
 809                       ret);
 810        }
 811#endif
 812        return ret;
 813}
 814module_init(wm8940_modinit);
 815
 816static void __exit wm8940_exit(void)
 817{
 818#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 819        i2c_del_driver(&wm8940_i2c_driver);
 820#endif
 821}
 822module_exit(wm8940_exit);
 823
 824MODULE_DESCRIPTION("ASoC WM8940 driver");
 825MODULE_AUTHOR("Jonathan Cameron");
 826MODULE_LICENSE("GPL");
 827