Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * wm8711.c  --  WM8711 ALSA SoC Audio driver
0004  *
0005  * Copyright 2006 Wolfson Microelectronics
0006  *
0007  * Author: Mike Arthur <Mike.Arthur@wolfsonmicro.com>
0008  *
0009  * Based on wm8731.c by Richard Purdie
0010  */
0011 
0012 #include <linux/module.h>
0013 #include <linux/moduleparam.h>
0014 #include <linux/init.h>
0015 #include <linux/delay.h>
0016 #include <linux/pm.h>
0017 #include <linux/i2c.h>
0018 #include <linux/regmap.h>
0019 #include <linux/spi/spi.h>
0020 #include <linux/slab.h>
0021 #include <linux/of_device.h>
0022 #include <sound/core.h>
0023 #include <sound/pcm.h>
0024 #include <sound/pcm_params.h>
0025 #include <sound/soc.h>
0026 #include <sound/tlv.h>
0027 #include <sound/initval.h>
0028 
0029 #include "wm8711.h"
0030 
0031 /* codec private data */
0032 struct wm8711_priv {
0033     struct regmap *regmap;
0034     unsigned int sysclk;
0035 };
0036 
0037 /*
0038  * wm8711 register cache
0039  * We can't read the WM8711 register space when we are
0040  * using 2 wire for device control, so we cache them instead.
0041  * There is no point in caching the reset register
0042  */
0043 static const struct reg_default wm8711_reg_defaults[] = {
0044     { 0, 0x0079 }, { 1, 0x0079 }, { 2, 0x000a }, { 3, 0x0008 },
0045     { 4, 0x009f }, { 5, 0x000a }, { 6, 0x0000 }, { 7, 0x0000 },
0046 };
0047 
0048 static bool wm8711_volatile(struct device *dev, unsigned int reg)
0049 {
0050     switch (reg) {
0051     case WM8711_RESET:
0052         return true;
0053     default:
0054         return false;
0055     }
0056 }
0057 
0058 #define wm8711_reset(c) snd_soc_component_write(c, WM8711_RESET, 0)
0059 
0060 static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
0061 
0062 static const struct snd_kcontrol_new wm8711_snd_controls[] = {
0063 
0064 SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V,
0065          0, 127, 0, out_tlv),
0066 SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V,
0067     7, 1, 0),
0068 
0069 };
0070 
0071 /* Output Mixer */
0072 static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = {
0073 SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0),
0074 SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0),
0075 };
0076 
0077 static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = {
0078 SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1,
0079     &wm8711_output_mixer_controls[0],
0080     ARRAY_SIZE(wm8711_output_mixer_controls)),
0081 SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1),
0082 SND_SOC_DAPM_OUTPUT("LOUT"),
0083 SND_SOC_DAPM_OUTPUT("LHPOUT"),
0084 SND_SOC_DAPM_OUTPUT("ROUT"),
0085 SND_SOC_DAPM_OUTPUT("RHPOUT"),
0086 };
0087 
0088 static const struct snd_soc_dapm_route wm8711_intercon[] = {
0089     /* output mixer */
0090     {"Output Mixer", "Line Bypass Switch", "Line Input"},
0091     {"Output Mixer", "HiFi Playback Switch", "DAC"},
0092 
0093     /* outputs */
0094     {"RHPOUT", NULL, "Output Mixer"},
0095     {"ROUT", NULL, "Output Mixer"},
0096     {"LHPOUT", NULL, "Output Mixer"},
0097     {"LOUT", NULL, "Output Mixer"},
0098 };
0099 
0100 struct _coeff_div {
0101     u32 mclk;
0102     u32 rate;
0103     u16 fs;
0104     u8 sr:4;
0105     u8 bosr:1;
0106     u8 usb:1;
0107 };
0108 
0109 /* codec mclk clock divider coefficients */
0110 static const struct _coeff_div coeff_div[] = {
0111     /* 48k */
0112     {12288000, 48000, 256, 0x0, 0x0, 0x0},
0113     {18432000, 48000, 384, 0x0, 0x1, 0x0},
0114     {12000000, 48000, 250, 0x0, 0x0, 0x1},
0115 
0116     /* 32k */
0117     {12288000, 32000, 384, 0x6, 0x0, 0x0},
0118     {18432000, 32000, 576, 0x6, 0x1, 0x0},
0119     {12000000, 32000, 375, 0x6, 0x0, 0x1},
0120 
0121     /* 8k */
0122     {12288000, 8000, 1536, 0x3, 0x0, 0x0},
0123     {18432000, 8000, 2304, 0x3, 0x1, 0x0},
0124     {11289600, 8000, 1408, 0xb, 0x0, 0x0},
0125     {16934400, 8000, 2112, 0xb, 0x1, 0x0},
0126     {12000000, 8000, 1500, 0x3, 0x0, 0x1},
0127 
0128     /* 96k */
0129     {12288000, 96000, 128, 0x7, 0x0, 0x0},
0130     {18432000, 96000, 192, 0x7, 0x1, 0x0},
0131     {12000000, 96000, 125, 0x7, 0x0, 0x1},
0132 
0133     /* 44.1k */
0134     {11289600, 44100, 256, 0x8, 0x0, 0x0},
0135     {16934400, 44100, 384, 0x8, 0x1, 0x0},
0136     {12000000, 44100, 272, 0x8, 0x1, 0x1},
0137 
0138     /* 88.2k */
0139     {11289600, 88200, 128, 0xf, 0x0, 0x0},
0140     {16934400, 88200, 192, 0xf, 0x1, 0x0},
0141     {12000000, 88200, 136, 0xf, 0x1, 0x1},
0142 };
0143 
0144 static inline int get_coeff(int mclk, int rate)
0145 {
0146     int i;
0147 
0148     for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
0149         if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
0150             return i;
0151     }
0152     return 0;
0153 }
0154 
0155 static int wm8711_hw_params(struct snd_pcm_substream *substream,
0156     struct snd_pcm_hw_params *params,
0157     struct snd_soc_dai *dai)
0158 {
0159     struct snd_soc_component *component = dai->component;
0160     struct wm8711_priv *wm8711 =  snd_soc_component_get_drvdata(component);
0161     u16 iface = snd_soc_component_read(component, WM8711_IFACE) & 0xfff3;
0162     int i = get_coeff(wm8711->sysclk, params_rate(params));
0163     u16 srate = (coeff_div[i].sr << 2) |
0164         (coeff_div[i].bosr << 1) | coeff_div[i].usb;
0165 
0166     snd_soc_component_write(component, WM8711_SRATE, srate);
0167 
0168     /* bit size */
0169     switch (params_width(params)) {
0170     case 16:
0171         break;
0172     case 20:
0173         iface |= 0x0004;
0174         break;
0175     case 24:
0176         iface |= 0x0008;
0177         break;
0178     }
0179 
0180     snd_soc_component_write(component, WM8711_IFACE, iface);
0181     return 0;
0182 }
0183 
0184 static int wm8711_pcm_prepare(struct snd_pcm_substream *substream,
0185                   struct snd_soc_dai *dai)
0186 {
0187     struct snd_soc_component *component = dai->component;
0188 
0189     /* set active */
0190     snd_soc_component_write(component, WM8711_ACTIVE, 0x0001);
0191 
0192     return 0;
0193 }
0194 
0195 static void wm8711_shutdown(struct snd_pcm_substream *substream,
0196                 struct snd_soc_dai *dai)
0197 {
0198     struct snd_soc_component *component = dai->component;
0199 
0200     /* deactivate */
0201     if (!snd_soc_component_active(component)) {
0202         udelay(50);
0203         snd_soc_component_write(component, WM8711_ACTIVE, 0x0);
0204     }
0205 }
0206 
0207 static int wm8711_mute(struct snd_soc_dai *dai, int mute, int direction)
0208 {
0209     struct snd_soc_component *component = dai->component;
0210     u16 mute_reg = snd_soc_component_read(component, WM8711_APDIGI) & 0xfff7;
0211 
0212     if (mute)
0213         snd_soc_component_write(component, WM8711_APDIGI, mute_reg | 0x8);
0214     else
0215         snd_soc_component_write(component, WM8711_APDIGI, mute_reg);
0216 
0217     return 0;
0218 }
0219 
0220 static int wm8711_set_dai_sysclk(struct snd_soc_dai *codec_dai,
0221         int clk_id, unsigned int freq, int dir)
0222 {
0223     struct snd_soc_component *component = codec_dai->component;
0224     struct wm8711_priv *wm8711 =  snd_soc_component_get_drvdata(component);
0225 
0226     switch (freq) {
0227     case 11289600:
0228     case 12000000:
0229     case 12288000:
0230     case 16934400:
0231     case 18432000:
0232         wm8711->sysclk = freq;
0233         return 0;
0234     }
0235     return -EINVAL;
0236 }
0237 
0238 static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai,
0239         unsigned int fmt)
0240 {
0241     struct snd_soc_component *component = codec_dai->component;
0242     u16 iface = snd_soc_component_read(component, WM8711_IFACE) & 0x000c;
0243 
0244     /* set master/slave audio interface */
0245     switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
0246     case SND_SOC_DAIFMT_CBM_CFM:
0247         iface |= 0x0040;
0248         break;
0249     case SND_SOC_DAIFMT_CBS_CFS:
0250         break;
0251     default:
0252         return -EINVAL;
0253     }
0254 
0255     /* interface format */
0256     switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
0257     case SND_SOC_DAIFMT_I2S:
0258         iface |= 0x0002;
0259         break;
0260     case SND_SOC_DAIFMT_RIGHT_J:
0261         break;
0262     case SND_SOC_DAIFMT_LEFT_J:
0263         iface |= 0x0001;
0264         break;
0265     case SND_SOC_DAIFMT_DSP_A:
0266         iface |= 0x0003;
0267         break;
0268     case SND_SOC_DAIFMT_DSP_B:
0269         iface |= 0x0013;
0270         break;
0271     default:
0272         return -EINVAL;
0273     }
0274 
0275     /* clock inversion */
0276     switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
0277     case SND_SOC_DAIFMT_NB_NF:
0278         break;
0279     case SND_SOC_DAIFMT_IB_IF:
0280         iface |= 0x0090;
0281         break;
0282     case SND_SOC_DAIFMT_IB_NF:
0283         iface |= 0x0080;
0284         break;
0285     case SND_SOC_DAIFMT_NB_IF:
0286         iface |= 0x0010;
0287         break;
0288     default:
0289         return -EINVAL;
0290     }
0291 
0292     /* set iface */
0293     snd_soc_component_write(component, WM8711_IFACE, iface);
0294     return 0;
0295 }
0296 
0297 static int wm8711_set_bias_level(struct snd_soc_component *component,
0298     enum snd_soc_bias_level level)
0299 {
0300     struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component);
0301     u16 reg = snd_soc_component_read(component, WM8711_PWR) & 0xff7f;
0302 
0303     switch (level) {
0304     case SND_SOC_BIAS_ON:
0305         snd_soc_component_write(component, WM8711_PWR, reg);
0306         break;
0307     case SND_SOC_BIAS_PREPARE:
0308         break;
0309     case SND_SOC_BIAS_STANDBY:
0310         if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
0311             regcache_sync(wm8711->regmap);
0312 
0313         snd_soc_component_write(component, WM8711_PWR, reg | 0x0040);
0314         break;
0315     case SND_SOC_BIAS_OFF:
0316         snd_soc_component_write(component, WM8711_ACTIVE, 0x0);
0317         snd_soc_component_write(component, WM8711_PWR, 0xffff);
0318         break;
0319     }
0320     return 0;
0321 }
0322 
0323 #define WM8711_RATES SNDRV_PCM_RATE_8000_96000
0324 
0325 #define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
0326     SNDRV_PCM_FMTBIT_S24_LE)
0327 
0328 static const struct snd_soc_dai_ops wm8711_ops = {
0329     .prepare = wm8711_pcm_prepare,
0330     .hw_params = wm8711_hw_params,
0331     .shutdown = wm8711_shutdown,
0332     .mute_stream = wm8711_mute,
0333     .set_sysclk = wm8711_set_dai_sysclk,
0334     .set_fmt = wm8711_set_dai_fmt,
0335     .no_capture_mute = 1,
0336 };
0337 
0338 static struct snd_soc_dai_driver wm8711_dai = {
0339     .name = "wm8711-hifi",
0340     .playback = {
0341         .stream_name = "Playback",
0342         .channels_min = 1,
0343         .channels_max = 2,
0344         .rates = WM8711_RATES,
0345         .formats = WM8711_FORMATS,
0346     },
0347     .ops = &wm8711_ops,
0348 };
0349 
0350 static int wm8711_probe(struct snd_soc_component *component)
0351 {
0352     int ret;
0353 
0354     ret = wm8711_reset(component);
0355     if (ret < 0) {
0356         dev_err(component->dev, "Failed to issue reset\n");
0357         return ret;
0358     }
0359 
0360     /* Latch the update bits */
0361     snd_soc_component_update_bits(component, WM8711_LOUT1V, 0x0100, 0x0100);
0362     snd_soc_component_update_bits(component, WM8711_ROUT1V, 0x0100, 0x0100);
0363 
0364     return ret;
0365 
0366 }
0367 
0368 static const struct snd_soc_component_driver soc_component_dev_wm8711 = {
0369     .probe          = wm8711_probe,
0370     .set_bias_level     = wm8711_set_bias_level,
0371     .controls       = wm8711_snd_controls,
0372     .num_controls       = ARRAY_SIZE(wm8711_snd_controls),
0373     .dapm_widgets       = wm8711_dapm_widgets,
0374     .num_dapm_widgets   = ARRAY_SIZE(wm8711_dapm_widgets),
0375     .dapm_routes        = wm8711_intercon,
0376     .num_dapm_routes    = ARRAY_SIZE(wm8711_intercon),
0377     .suspend_bias_off   = 1,
0378     .idle_bias_on       = 1,
0379     .use_pmdown_time    = 1,
0380     .endianness     = 1,
0381 };
0382 
0383 static const struct of_device_id wm8711_of_match[] = {
0384     { .compatible = "wlf,wm8711", },
0385     { }
0386 };
0387 MODULE_DEVICE_TABLE(of, wm8711_of_match);
0388 
0389 static const struct regmap_config wm8711_regmap = {
0390     .reg_bits = 7,
0391     .val_bits = 9,
0392     .max_register = WM8711_RESET,
0393 
0394     .reg_defaults = wm8711_reg_defaults,
0395     .num_reg_defaults = ARRAY_SIZE(wm8711_reg_defaults),
0396     .cache_type = REGCACHE_RBTREE,
0397 
0398     .volatile_reg = wm8711_volatile,
0399 };
0400 
0401 #if defined(CONFIG_SPI_MASTER)
0402 static int wm8711_spi_probe(struct spi_device *spi)
0403 {
0404     struct wm8711_priv *wm8711;
0405     int ret;
0406 
0407     wm8711 = devm_kzalloc(&spi->dev, sizeof(struct wm8711_priv),
0408                   GFP_KERNEL);
0409     if (wm8711 == NULL)
0410         return -ENOMEM;
0411 
0412     wm8711->regmap = devm_regmap_init_spi(spi, &wm8711_regmap);
0413     if (IS_ERR(wm8711->regmap))
0414         return PTR_ERR(wm8711->regmap);
0415 
0416     spi_set_drvdata(spi, wm8711);
0417 
0418     ret = devm_snd_soc_register_component(&spi->dev,
0419             &soc_component_dev_wm8711, &wm8711_dai, 1);
0420 
0421     return ret;
0422 }
0423 
0424 static struct spi_driver wm8711_spi_driver = {
0425     .driver = {
0426         .name   = "wm8711",
0427         .of_match_table = wm8711_of_match,
0428     },
0429     .probe      = wm8711_spi_probe,
0430 };
0431 #endif /* CONFIG_SPI_MASTER */
0432 
0433 #if IS_ENABLED(CONFIG_I2C)
0434 static int wm8711_i2c_probe(struct i2c_client *client)
0435 {
0436     struct wm8711_priv *wm8711;
0437     int ret;
0438 
0439     wm8711 = devm_kzalloc(&client->dev, sizeof(struct wm8711_priv),
0440                   GFP_KERNEL);
0441     if (wm8711 == NULL)
0442         return -ENOMEM;
0443 
0444     wm8711->regmap = devm_regmap_init_i2c(client, &wm8711_regmap);
0445     if (IS_ERR(wm8711->regmap))
0446         return PTR_ERR(wm8711->regmap);
0447 
0448     i2c_set_clientdata(client, wm8711);
0449 
0450     ret = devm_snd_soc_register_component(&client->dev,
0451             &soc_component_dev_wm8711, &wm8711_dai, 1);
0452 
0453     return ret;
0454 }
0455 
0456 static const struct i2c_device_id wm8711_i2c_id[] = {
0457     { "wm8711", 0 },
0458     { }
0459 };
0460 MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
0461 
0462 static struct i2c_driver wm8711_i2c_driver = {
0463     .driver = {
0464         .name = "wm8711",
0465         .of_match_table = wm8711_of_match,
0466     },
0467     .probe_new = wm8711_i2c_probe,
0468     .id_table = wm8711_i2c_id,
0469 };
0470 #endif
0471 
0472 static int __init wm8711_modinit(void)
0473 {
0474     int ret;
0475 #if IS_ENABLED(CONFIG_I2C)
0476     ret = i2c_add_driver(&wm8711_i2c_driver);
0477     if (ret != 0) {
0478         printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n",
0479                ret);
0480     }
0481 #endif
0482 #if defined(CONFIG_SPI_MASTER)
0483     ret = spi_register_driver(&wm8711_spi_driver);
0484     if (ret != 0) {
0485         printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n",
0486                ret);
0487     }
0488 #endif
0489     return 0;
0490 }
0491 module_init(wm8711_modinit);
0492 
0493 static void __exit wm8711_exit(void)
0494 {
0495 #if IS_ENABLED(CONFIG_I2C)
0496     i2c_del_driver(&wm8711_i2c_driver);
0497 #endif
0498 #if defined(CONFIG_SPI_MASTER)
0499     spi_unregister_driver(&wm8711_spi_driver);
0500 #endif
0501 }
0502 module_exit(wm8711_exit);
0503 
0504 MODULE_DESCRIPTION("ASoC WM8711 driver");
0505 MODULE_AUTHOR("Mike Arthur");
0506 MODULE_LICENSE("GPL");