Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * wm8728.c  --  WM8728 ALSA SoC Audio driver
0004  *
0005  * Copyright 2008 Wolfson Microelectronics plc
0006  *
0007  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/moduleparam.h>
0012 #include <linux/init.h>
0013 #include <linux/delay.h>
0014 #include <linux/pm.h>
0015 #include <linux/i2c.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/regmap.h>
0018 #include <linux/spi/spi.h>
0019 #include <linux/slab.h>
0020 #include <linux/of_device.h>
0021 #include <sound/core.h>
0022 #include <sound/pcm.h>
0023 #include <sound/pcm_params.h>
0024 #include <sound/soc.h>
0025 #include <sound/initval.h>
0026 #include <sound/tlv.h>
0027 
0028 #include "wm8728.h"
0029 
0030 /*
0031  * We can't read the WM8728 register space so we cache them instead.
0032  * Note that the defaults here aren't the physical defaults, we latch
0033  * the volume update bits, mute the output and enable infinite zero
0034  * detect.
0035  */
0036 static const struct reg_default wm8728_reg_defaults[] = {
0037     { 0, 0x1ff },
0038     { 1, 0x1ff },
0039     { 2, 0x001 },
0040     { 3, 0x100 },
0041 };
0042 
0043 /* codec private data */
0044 struct wm8728_priv {
0045     struct regmap *regmap;
0046 };
0047 
0048 static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1);
0049 
0050 static const struct snd_kcontrol_new wm8728_snd_controls[] = {
0051 
0052 SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8728_DACLVOL, WM8728_DACRVOL,
0053          0, 255, 0, wm8728_tlv),
0054 
0055 SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0),
0056 };
0057 
0058 /*
0059  * DAPM controls.
0060  */
0061 static const struct snd_soc_dapm_widget wm8728_dapm_widgets[] = {
0062 SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0),
0063 SND_SOC_DAPM_OUTPUT("VOUTL"),
0064 SND_SOC_DAPM_OUTPUT("VOUTR"),
0065 };
0066 
0067 static const struct snd_soc_dapm_route wm8728_intercon[] = {
0068     {"VOUTL", NULL, "DAC"},
0069     {"VOUTR", NULL, "DAC"},
0070 };
0071 
0072 static int wm8728_mute(struct snd_soc_dai *dai, int mute, int direction)
0073 {
0074     struct snd_soc_component *component = dai->component;
0075     u16 mute_reg = snd_soc_component_read(component, WM8728_DACCTL);
0076 
0077     if (mute)
0078         snd_soc_component_write(component, WM8728_DACCTL, mute_reg | 1);
0079     else
0080         snd_soc_component_write(component, WM8728_DACCTL, mute_reg & ~1);
0081 
0082     return 0;
0083 }
0084 
0085 static int wm8728_hw_params(struct snd_pcm_substream *substream,
0086     struct snd_pcm_hw_params *params,
0087     struct snd_soc_dai *dai)
0088 {
0089     struct snd_soc_component *component = dai->component;
0090     u16 dac = snd_soc_component_read(component, WM8728_DACCTL);
0091 
0092     dac &= ~0x18;
0093 
0094     switch (params_width(params)) {
0095     case 16:
0096         break;
0097     case 20:
0098         dac |= 0x10;
0099         break;
0100     case 24:
0101         dac |= 0x08;
0102         break;
0103     default:
0104         return -EINVAL;
0105     }
0106 
0107     snd_soc_component_write(component, WM8728_DACCTL, dac);
0108 
0109     return 0;
0110 }
0111 
0112 static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
0113         unsigned int fmt)
0114 {
0115     struct snd_soc_component *component = codec_dai->component;
0116     u16 iface = snd_soc_component_read(component, WM8728_IFCTL);
0117 
0118     /* Currently only I2S is supported by the driver, though the
0119      * hardware is more flexible.
0120      */
0121     switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
0122     case SND_SOC_DAIFMT_I2S:
0123         iface |= 1;
0124         break;
0125     default:
0126         return -EINVAL;
0127     }
0128 
0129     /* The hardware only support full slave mode */
0130     switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
0131     case SND_SOC_DAIFMT_CBS_CFS:
0132         break;
0133     default:
0134         return -EINVAL;
0135     }
0136 
0137     switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
0138     case SND_SOC_DAIFMT_NB_NF:
0139         iface &= ~0x22;
0140         break;
0141     case SND_SOC_DAIFMT_IB_NF:
0142         iface |=  0x20;
0143         iface &= ~0x02;
0144         break;
0145     case SND_SOC_DAIFMT_NB_IF:
0146         iface |= 0x02;
0147         iface &= ~0x20;
0148         break;
0149     case SND_SOC_DAIFMT_IB_IF:
0150         iface |= 0x22;
0151         break;
0152     default:
0153         return -EINVAL;
0154     }
0155 
0156     snd_soc_component_write(component, WM8728_IFCTL, iface);
0157     return 0;
0158 }
0159 
0160 static int wm8728_set_bias_level(struct snd_soc_component *component,
0161                  enum snd_soc_bias_level level)
0162 {
0163     struct wm8728_priv *wm8728 = snd_soc_component_get_drvdata(component);
0164     u16 reg;
0165 
0166     switch (level) {
0167     case SND_SOC_BIAS_ON:
0168     case SND_SOC_BIAS_PREPARE:
0169     case SND_SOC_BIAS_STANDBY:
0170         if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
0171             /* Power everything up... */
0172             reg = snd_soc_component_read(component, WM8728_DACCTL);
0173             snd_soc_component_write(component, WM8728_DACCTL, reg & ~0x4);
0174 
0175             /* ..then sync in the register cache. */
0176             regcache_sync(wm8728->regmap);
0177         }
0178         break;
0179 
0180     case SND_SOC_BIAS_OFF:
0181         reg = snd_soc_component_read(component, WM8728_DACCTL);
0182         snd_soc_component_write(component, WM8728_DACCTL, reg | 0x4);
0183         break;
0184     }
0185     return 0;
0186 }
0187 
0188 #define WM8728_RATES (SNDRV_PCM_RATE_8000_192000)
0189 
0190 #define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
0191     SNDRV_PCM_FMTBIT_S24_LE)
0192 
0193 static const struct snd_soc_dai_ops wm8728_dai_ops = {
0194     .hw_params  = wm8728_hw_params,
0195     .mute_stream    = wm8728_mute,
0196     .set_fmt    = wm8728_set_dai_fmt,
0197     .no_capture_mute = 1,
0198 };
0199 
0200 static struct snd_soc_dai_driver wm8728_dai = {
0201     .name = "wm8728-hifi",
0202     .playback = {
0203         .stream_name = "Playback",
0204         .channels_min = 2,
0205         .channels_max = 2,
0206         .rates = WM8728_RATES,
0207         .formats = WM8728_FORMATS,
0208     },
0209     .ops = &wm8728_dai_ops,
0210 };
0211 
0212 static const struct snd_soc_component_driver soc_component_dev_wm8728 = {
0213     .set_bias_level     = wm8728_set_bias_level,
0214     .controls       = wm8728_snd_controls,
0215     .num_controls       = ARRAY_SIZE(wm8728_snd_controls),
0216     .dapm_widgets       = wm8728_dapm_widgets,
0217     .num_dapm_widgets   = ARRAY_SIZE(wm8728_dapm_widgets),
0218     .dapm_routes        = wm8728_intercon,
0219     .num_dapm_routes    = ARRAY_SIZE(wm8728_intercon),
0220     .suspend_bias_off   = 1,
0221     .idle_bias_on       = 1,
0222     .use_pmdown_time    = 1,
0223     .endianness     = 1,
0224 };
0225 
0226 static const struct of_device_id wm8728_of_match[] = {
0227     { .compatible = "wlf,wm8728", },
0228     { }
0229 };
0230 MODULE_DEVICE_TABLE(of, wm8728_of_match);
0231 
0232 static const struct regmap_config wm8728_regmap = {
0233     .reg_bits = 7,
0234     .val_bits = 9,
0235     .max_register = WM8728_IFCTL,
0236 
0237     .reg_defaults = wm8728_reg_defaults,
0238     .num_reg_defaults = ARRAY_SIZE(wm8728_reg_defaults),
0239     .cache_type = REGCACHE_RBTREE,
0240 };
0241 
0242 #if defined(CONFIG_SPI_MASTER)
0243 static int wm8728_spi_probe(struct spi_device *spi)
0244 {
0245     struct wm8728_priv *wm8728;
0246     int ret;
0247 
0248     wm8728 = devm_kzalloc(&spi->dev, sizeof(struct wm8728_priv),
0249                   GFP_KERNEL);
0250     if (wm8728 == NULL)
0251         return -ENOMEM;
0252 
0253     wm8728->regmap = devm_regmap_init_spi(spi, &wm8728_regmap);
0254     if (IS_ERR(wm8728->regmap))
0255         return PTR_ERR(wm8728->regmap);
0256 
0257     spi_set_drvdata(spi, wm8728);
0258 
0259     ret = devm_snd_soc_register_component(&spi->dev,
0260             &soc_component_dev_wm8728, &wm8728_dai, 1);
0261 
0262     return ret;
0263 }
0264 
0265 static struct spi_driver wm8728_spi_driver = {
0266     .driver = {
0267         .name   = "wm8728",
0268         .of_match_table = wm8728_of_match,
0269     },
0270     .probe      = wm8728_spi_probe,
0271 };
0272 #endif /* CONFIG_SPI_MASTER */
0273 
0274 #if IS_ENABLED(CONFIG_I2C)
0275 static int wm8728_i2c_probe(struct i2c_client *i2c)
0276 {
0277     struct wm8728_priv *wm8728;
0278     int ret;
0279 
0280     wm8728 = devm_kzalloc(&i2c->dev, sizeof(struct wm8728_priv),
0281                   GFP_KERNEL);
0282     if (wm8728 == NULL)
0283         return -ENOMEM;
0284 
0285     wm8728->regmap = devm_regmap_init_i2c(i2c, &wm8728_regmap);
0286     if (IS_ERR(wm8728->regmap))
0287         return PTR_ERR(wm8728->regmap);
0288 
0289     i2c_set_clientdata(i2c, wm8728);
0290 
0291     ret =  devm_snd_soc_register_component(&i2c->dev,
0292             &soc_component_dev_wm8728, &wm8728_dai, 1);
0293 
0294     return ret;
0295 }
0296 
0297 static const struct i2c_device_id wm8728_i2c_id[] = {
0298     { "wm8728", 0 },
0299     { }
0300 };
0301 MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
0302 
0303 static struct i2c_driver wm8728_i2c_driver = {
0304     .driver = {
0305         .name = "wm8728",
0306         .of_match_table = wm8728_of_match,
0307     },
0308     .probe_new = wm8728_i2c_probe,
0309     .id_table = wm8728_i2c_id,
0310 };
0311 #endif
0312 
0313 static int __init wm8728_modinit(void)
0314 {
0315     int ret = 0;
0316 #if IS_ENABLED(CONFIG_I2C)
0317     ret = i2c_add_driver(&wm8728_i2c_driver);
0318     if (ret != 0) {
0319         printk(KERN_ERR "Failed to register wm8728 I2C driver: %d\n",
0320                ret);
0321     }
0322 #endif
0323 #if defined(CONFIG_SPI_MASTER)
0324     ret = spi_register_driver(&wm8728_spi_driver);
0325     if (ret != 0) {
0326         printk(KERN_ERR "Failed to register wm8728 SPI driver: %d\n",
0327                ret);
0328     }
0329 #endif
0330     return ret;
0331 }
0332 module_init(wm8728_modinit);
0333 
0334 static void __exit wm8728_exit(void)
0335 {
0336 #if IS_ENABLED(CONFIG_I2C)
0337     i2c_del_driver(&wm8728_i2c_driver);
0338 #endif
0339 #if defined(CONFIG_SPI_MASTER)
0340     spi_unregister_driver(&wm8728_spi_driver);
0341 #endif
0342 }
0343 module_exit(wm8728_exit);
0344 
0345 MODULE_DESCRIPTION("ASoC WM8728 driver");
0346 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
0347 MODULE_LICENSE("GPL");