linux/sound/soc/codecs/wm8711.c
<<
>>
Prefs
   1/*
   2 * wm8711.c  --  WM8711 ALSA SoC Audio driver
   3 *
   4 * Copyright 2006 Wolfson Microelectronics
   5 *
   6 * Author: Mike Arthur <Mike.Arthur@wolfsonmicro.com>
   7 *
   8 * Based on wm8731.c by Richard Purdie
   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
  15#include <linux/module.h>
  16#include <linux/moduleparam.h>
  17#include <linux/init.h>
  18#include <linux/delay.h>
  19#include <linux/pm.h>
  20#include <linux/i2c.h>
  21#include <linux/platform_device.h>
  22#include <linux/spi/spi.h>
  23#include <linux/slab.h>
  24#include <linux/of_device.h>
  25#include <sound/core.h>
  26#include <sound/pcm.h>
  27#include <sound/pcm_params.h>
  28#include <sound/soc.h>
  29#include <sound/tlv.h>
  30#include <sound/initval.h>
  31
  32#include "wm8711.h"
  33
  34/* codec private data */
  35struct wm8711_priv {
  36        enum snd_soc_control_type bus_type;
  37        unsigned int sysclk;
  38};
  39
  40/*
  41 * wm8711 register cache
  42 * We can't read the WM8711 register space when we are
  43 * using 2 wire for device control, so we cache them instead.
  44 * There is no point in caching the reset register
  45 */
  46static const u16 wm8711_reg[WM8711_CACHEREGNUM] = {
  47        0x0079, 0x0079, 0x000a, 0x0008,
  48        0x009f, 0x000a, 0x0000, 0x0000
  49};
  50
  51#define wm8711_reset(c) snd_soc_write(c, WM8711_RESET, 0)
  52
  53static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
  54
  55static const struct snd_kcontrol_new wm8711_snd_controls[] = {
  56
  57SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V,
  58                 0, 127, 0, out_tlv),
  59SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V,
  60        7, 1, 0),
  61
  62};
  63
  64/* Output Mixer */
  65static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = {
  66SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0),
  67SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0),
  68};
  69
  70static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = {
  71SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1,
  72        &wm8711_output_mixer_controls[0],
  73        ARRAY_SIZE(wm8711_output_mixer_controls)),
  74SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1),
  75SND_SOC_DAPM_OUTPUT("LOUT"),
  76SND_SOC_DAPM_OUTPUT("LHPOUT"),
  77SND_SOC_DAPM_OUTPUT("ROUT"),
  78SND_SOC_DAPM_OUTPUT("RHPOUT"),
  79};
  80
  81static const struct snd_soc_dapm_route wm8711_intercon[] = {
  82        /* output mixer */
  83        {"Output Mixer", "Line Bypass Switch", "Line Input"},
  84        {"Output Mixer", "HiFi Playback Switch", "DAC"},
  85
  86        /* outputs */
  87        {"RHPOUT", NULL, "Output Mixer"},
  88        {"ROUT", NULL, "Output Mixer"},
  89        {"LHPOUT", NULL, "Output Mixer"},
  90        {"LOUT", NULL, "Output Mixer"},
  91};
  92
  93struct _coeff_div {
  94        u32 mclk;
  95        u32 rate;
  96        u16 fs;
  97        u8 sr:4;
  98        u8 bosr:1;
  99        u8 usb:1;
 100};
 101
 102/* codec mclk clock divider coefficients */
 103static const struct _coeff_div coeff_div[] = {
 104        /* 48k */
 105        {12288000, 48000, 256, 0x0, 0x0, 0x0},
 106        {18432000, 48000, 384, 0x0, 0x1, 0x0},
 107        {12000000, 48000, 250, 0x0, 0x0, 0x1},
 108
 109        /* 32k */
 110        {12288000, 32000, 384, 0x6, 0x0, 0x0},
 111        {18432000, 32000, 576, 0x6, 0x1, 0x0},
 112        {12000000, 32000, 375, 0x6, 0x0, 0x1},
 113
 114        /* 8k */
 115        {12288000, 8000, 1536, 0x3, 0x0, 0x0},
 116        {18432000, 8000, 2304, 0x3, 0x1, 0x0},
 117        {11289600, 8000, 1408, 0xb, 0x0, 0x0},
 118        {16934400, 8000, 2112, 0xb, 0x1, 0x0},
 119        {12000000, 8000, 1500, 0x3, 0x0, 0x1},
 120
 121        /* 96k */
 122        {12288000, 96000, 128, 0x7, 0x0, 0x0},
 123        {18432000, 96000, 192, 0x7, 0x1, 0x0},
 124        {12000000, 96000, 125, 0x7, 0x0, 0x1},
 125
 126        /* 44.1k */
 127        {11289600, 44100, 256, 0x8, 0x0, 0x0},
 128        {16934400, 44100, 384, 0x8, 0x1, 0x0},
 129        {12000000, 44100, 272, 0x8, 0x1, 0x1},
 130
 131        /* 88.2k */
 132        {11289600, 88200, 128, 0xf, 0x0, 0x0},
 133        {16934400, 88200, 192, 0xf, 0x1, 0x0},
 134        {12000000, 88200, 136, 0xf, 0x1, 0x1},
 135};
 136
 137static inline int get_coeff(int mclk, int rate)
 138{
 139        int i;
 140
 141        for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
 142                if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
 143                        return i;
 144        }
 145        return 0;
 146}
 147
 148static int wm8711_hw_params(struct snd_pcm_substream *substream,
 149        struct snd_pcm_hw_params *params,
 150        struct snd_soc_dai *dai)
 151{
 152        struct snd_soc_codec *codec = dai->codec;
 153        struct wm8711_priv *wm8711 =  snd_soc_codec_get_drvdata(codec);
 154        u16 iface = snd_soc_read(codec, WM8711_IFACE) & 0xfff3;
 155        int i = get_coeff(wm8711->sysclk, params_rate(params));
 156        u16 srate = (coeff_div[i].sr << 2) |
 157                (coeff_div[i].bosr << 1) | coeff_div[i].usb;
 158
 159        snd_soc_write(codec, WM8711_SRATE, srate);
 160
 161        /* bit size */
 162        switch (params_format(params)) {
 163        case SNDRV_PCM_FORMAT_S16_LE:
 164                break;
 165        case SNDRV_PCM_FORMAT_S20_3LE:
 166                iface |= 0x0004;
 167                break;
 168        case SNDRV_PCM_FORMAT_S24_LE:
 169                iface |= 0x0008;
 170                break;
 171        }
 172
 173        snd_soc_write(codec, WM8711_IFACE, iface);
 174        return 0;
 175}
 176
 177static int wm8711_pcm_prepare(struct snd_pcm_substream *substream,
 178                              struct snd_soc_dai *dai)
 179{
 180        struct snd_soc_codec *codec = dai->codec;
 181
 182        /* set active */
 183        snd_soc_write(codec, WM8711_ACTIVE, 0x0001);
 184
 185        return 0;
 186}
 187
 188static void wm8711_shutdown(struct snd_pcm_substream *substream,
 189                            struct snd_soc_dai *dai)
 190{
 191        struct snd_soc_codec *codec = dai->codec;
 192
 193        /* deactivate */
 194        if (!codec->active) {
 195                udelay(50);
 196                snd_soc_write(codec, WM8711_ACTIVE, 0x0);
 197        }
 198}
 199
 200static int wm8711_mute(struct snd_soc_dai *dai, int mute)
 201{
 202        struct snd_soc_codec *codec = dai->codec;
 203        u16 mute_reg = snd_soc_read(codec, WM8711_APDIGI) & 0xfff7;
 204
 205        if (mute)
 206                snd_soc_write(codec, WM8711_APDIGI, mute_reg | 0x8);
 207        else
 208                snd_soc_write(codec, WM8711_APDIGI, mute_reg);
 209
 210        return 0;
 211}
 212
 213static int wm8711_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 214                int clk_id, unsigned int freq, int dir)
 215{
 216        struct snd_soc_codec *codec = codec_dai->codec;
 217        struct wm8711_priv *wm8711 =  snd_soc_codec_get_drvdata(codec);
 218
 219        switch (freq) {
 220        case 11289600:
 221        case 12000000:
 222        case 12288000:
 223        case 16934400:
 224        case 18432000:
 225                wm8711->sysclk = freq;
 226                return 0;
 227        }
 228        return -EINVAL;
 229}
 230
 231static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai,
 232                unsigned int fmt)
 233{
 234        struct snd_soc_codec *codec = codec_dai->codec;
 235        u16 iface = snd_soc_read(codec, WM8711_IFACE) & 0x000c;
 236
 237        /* set master/slave audio interface */
 238        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 239        case SND_SOC_DAIFMT_CBM_CFM:
 240                iface |= 0x0040;
 241                break;
 242        case SND_SOC_DAIFMT_CBS_CFS:
 243                break;
 244        default:
 245                return -EINVAL;
 246        }
 247
 248        /* interface format */
 249        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 250        case SND_SOC_DAIFMT_I2S:
 251                iface |= 0x0002;
 252                break;
 253        case SND_SOC_DAIFMT_RIGHT_J:
 254                break;
 255        case SND_SOC_DAIFMT_LEFT_J:
 256                iface |= 0x0001;
 257                break;
 258        case SND_SOC_DAIFMT_DSP_A:
 259                iface |= 0x0003;
 260                break;
 261        case SND_SOC_DAIFMT_DSP_B:
 262                iface |= 0x0013;
 263                break;
 264        default:
 265                return -EINVAL;
 266        }
 267
 268        /* clock inversion */
 269        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 270        case SND_SOC_DAIFMT_NB_NF:
 271                break;
 272        case SND_SOC_DAIFMT_IB_IF:
 273                iface |= 0x0090;
 274                break;
 275        case SND_SOC_DAIFMT_IB_NF:
 276                iface |= 0x0080;
 277                break;
 278        case SND_SOC_DAIFMT_NB_IF:
 279                iface |= 0x0010;
 280                break;
 281        default:
 282                return -EINVAL;
 283        }
 284
 285        /* set iface */
 286        snd_soc_write(codec, WM8711_IFACE, iface);
 287        return 0;
 288}
 289
 290static int wm8711_set_bias_level(struct snd_soc_codec *codec,
 291        enum snd_soc_bias_level level)
 292{
 293        u16 reg = snd_soc_read(codec, WM8711_PWR) & 0xff7f;
 294
 295        switch (level) {
 296        case SND_SOC_BIAS_ON:
 297                snd_soc_write(codec, WM8711_PWR, reg);
 298                break;
 299        case SND_SOC_BIAS_PREPARE:
 300                break;
 301        case SND_SOC_BIAS_STANDBY:
 302                if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
 303                        snd_soc_cache_sync(codec);
 304
 305                snd_soc_write(codec, WM8711_PWR, reg | 0x0040);
 306                break;
 307        case SND_SOC_BIAS_OFF:
 308                snd_soc_write(codec, WM8711_ACTIVE, 0x0);
 309                snd_soc_write(codec, WM8711_PWR, 0xffff);
 310                break;
 311        }
 312        codec->dapm.bias_level = level;
 313        return 0;
 314}
 315
 316#define WM8711_RATES SNDRV_PCM_RATE_8000_96000
 317
 318#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
 319        SNDRV_PCM_FMTBIT_S24_LE)
 320
 321static struct snd_soc_dai_ops wm8711_ops = {
 322        .prepare = wm8711_pcm_prepare,
 323        .hw_params = wm8711_hw_params,
 324        .shutdown = wm8711_shutdown,
 325        .digital_mute = wm8711_mute,
 326        .set_sysclk = wm8711_set_dai_sysclk,
 327        .set_fmt = wm8711_set_dai_fmt,
 328};
 329
 330static struct snd_soc_dai_driver wm8711_dai = {
 331        .name = "wm8711-hifi",
 332        .playback = {
 333                .stream_name = "Playback",
 334                .channels_min = 1,
 335                .channels_max = 2,
 336                .rates = WM8711_RATES,
 337                .formats = WM8711_FORMATS,
 338        },
 339        .ops = &wm8711_ops,
 340};
 341
 342static int wm8711_suspend(struct snd_soc_codec *codec, pm_message_t state)
 343{
 344        snd_soc_write(codec, WM8711_ACTIVE, 0x0);
 345        wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF);
 346        return 0;
 347}
 348
 349static int wm8711_resume(struct snd_soc_codec *codec)
 350{
 351        wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 352        return 0;
 353}
 354
 355static int wm8711_probe(struct snd_soc_codec *codec)
 356{
 357        struct wm8711_priv *wm8711 = snd_soc_codec_get_drvdata(codec);
 358        int ret;
 359
 360        ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8711->bus_type);
 361        if (ret < 0) {
 362                dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
 363                return ret;
 364        }
 365
 366        ret = wm8711_reset(codec);
 367        if (ret < 0) {
 368                dev_err(codec->dev, "Failed to issue reset\n");
 369                return ret;
 370        }
 371
 372        wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 373
 374        /* Latch the update bits */
 375        snd_soc_update_bits(codec, WM8711_LOUT1V, 0x0100, 0x0100);
 376        snd_soc_update_bits(codec, WM8711_ROUT1V, 0x0100, 0x0100);
 377
 378        snd_soc_add_controls(codec, wm8711_snd_controls,
 379                             ARRAY_SIZE(wm8711_snd_controls));
 380
 381        return ret;
 382
 383}
 384
 385/* power down chip */
 386static int wm8711_remove(struct snd_soc_codec *codec)
 387{
 388        wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF);
 389        return 0;
 390}
 391
 392static struct snd_soc_codec_driver soc_codec_dev_wm8711 = {
 393        .probe =        wm8711_probe,
 394        .remove =       wm8711_remove,
 395        .suspend =      wm8711_suspend,
 396        .resume =       wm8711_resume,
 397        .set_bias_level = wm8711_set_bias_level,
 398        .reg_cache_size = ARRAY_SIZE(wm8711_reg),
 399        .reg_word_size = sizeof(u16),
 400        .reg_cache_default = wm8711_reg,
 401        .dapm_widgets = wm8711_dapm_widgets,
 402        .num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets),
 403        .dapm_routes = wm8711_intercon,
 404        .num_dapm_routes = ARRAY_SIZE(wm8711_intercon),
 405};
 406
 407static const struct of_device_id wm8711_of_match[] = {
 408        { .compatible = "wlf,wm8711", },
 409        { }
 410};
 411MODULE_DEVICE_TABLE(of, wm8711_of_match);
 412
 413#if defined(CONFIG_SPI_MASTER)
 414static int __devinit wm8711_spi_probe(struct spi_device *spi)
 415{
 416        struct wm8711_priv *wm8711;
 417        int ret;
 418
 419        wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
 420        if (wm8711 == NULL)
 421                return -ENOMEM;
 422
 423        spi_set_drvdata(spi, wm8711);
 424        wm8711->bus_type = SND_SOC_SPI;
 425
 426        ret = snd_soc_register_codec(&spi->dev,
 427                        &soc_codec_dev_wm8711, &wm8711_dai, 1);
 428        if (ret < 0)
 429                kfree(wm8711);
 430        return ret;
 431}
 432
 433static int __devexit wm8711_spi_remove(struct spi_device *spi)
 434{
 435        snd_soc_unregister_codec(&spi->dev);
 436        kfree(spi_get_drvdata(spi));
 437        return 0;
 438}
 439
 440static struct spi_driver wm8711_spi_driver = {
 441        .driver = {
 442                .name   = "wm8711",
 443                .owner  = THIS_MODULE,
 444                .of_match_table = wm8711_of_match,
 445        },
 446        .probe          = wm8711_spi_probe,
 447        .remove         = __devexit_p(wm8711_spi_remove),
 448};
 449#endif /* CONFIG_SPI_MASTER */
 450
 451#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 452static __devinit int wm8711_i2c_probe(struct i2c_client *client,
 453                                      const struct i2c_device_id *id)
 454{
 455        struct wm8711_priv *wm8711;
 456        int ret;
 457
 458        wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
 459        if (wm8711 == NULL)
 460                return -ENOMEM;
 461
 462        i2c_set_clientdata(client, wm8711);
 463        wm8711->bus_type = SND_SOC_I2C;
 464
 465        ret =  snd_soc_register_codec(&client->dev,
 466                        &soc_codec_dev_wm8711, &wm8711_dai, 1);
 467        if (ret < 0)
 468                kfree(wm8711);
 469        return ret;
 470}
 471
 472static __devexit int wm8711_i2c_remove(struct i2c_client *client)
 473{
 474        snd_soc_unregister_codec(&client->dev);
 475        kfree(i2c_get_clientdata(client));
 476        return 0;
 477}
 478
 479static const struct i2c_device_id wm8711_i2c_id[] = {
 480        { "wm8711", 0 },
 481        { }
 482};
 483MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
 484
 485static struct i2c_driver wm8711_i2c_driver = {
 486        .driver = {
 487                .name = "wm8711",
 488                .owner = THIS_MODULE,
 489                .of_match_table = wm8711_of_match,
 490        },
 491        .probe =    wm8711_i2c_probe,
 492        .remove =   __devexit_p(wm8711_i2c_remove),
 493        .id_table = wm8711_i2c_id,
 494};
 495#endif
 496
 497static int __init wm8711_modinit(void)
 498{
 499        int ret;
 500#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 501        ret = i2c_add_driver(&wm8711_i2c_driver);
 502        if (ret != 0) {
 503                printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n",
 504                       ret);
 505        }
 506#endif
 507#if defined(CONFIG_SPI_MASTER)
 508        ret = spi_register_driver(&wm8711_spi_driver);
 509        if (ret != 0) {
 510                printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n",
 511                       ret);
 512        }
 513#endif
 514        return 0;
 515}
 516module_init(wm8711_modinit);
 517
 518static void __exit wm8711_exit(void)
 519{
 520#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 521        i2c_del_driver(&wm8711_i2c_driver);
 522#endif
 523#if defined(CONFIG_SPI_MASTER)
 524        spi_unregister_driver(&wm8711_spi_driver);
 525#endif
 526}
 527module_exit(wm8711_exit);
 528
 529MODULE_DESCRIPTION("ASoC WM8711 driver");
 530MODULE_AUTHOR("Mike Arthur");
 531MODULE_LICENSE("GPL");
 532