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