aboutsummaryrefslogtreecommitdiff
path: root/sound/soc/codecs/wm8776.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/wm8776.c')
-rw-r--r--sound/soc/codecs/wm8776.c216
1 files changed, 128 insertions, 88 deletions
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index 04182c464e3..70952ceb278 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -1,7 +1,7 @@
/*
* wm8776.c -- WM8776 ALSA SoC Audio driver
*
- * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2009-12 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
@@ -18,34 +18,66 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "wm8776.h"
+enum wm8776_chip_type {
+ WM8775 = 1,
+ WM8776,
+};
+
/* codec private data */
struct wm8776_priv {
- enum snd_soc_control_type control_type;
- u16 reg_cache[WM8776_CACHEREGNUM];
+ struct regmap *regmap;
int sysclk[2];
};
-static const u16 wm8776_reg[WM8776_CACHEREGNUM] = {
- 0x79, 0x79, 0x79, 0xff, 0xff, /* 4 */
- 0xff, 0x00, 0x90, 0x00, 0x00, /* 9 */
- 0x22, 0x22, 0x22, 0x08, 0xcf, /* 14 */
- 0xcf, 0x7b, 0x00, 0x32, 0x00, /* 19 */
- 0xa6, 0x01, 0x01
+static const struct reg_default wm8776_reg_defaults[] = {
+ { 0, 0x79 },
+ { 1, 0x79 },
+ { 2, 0x79 },
+ { 3, 0xff },
+ { 4, 0xff },
+ { 5, 0xff },
+ { 6, 0x00 },
+ { 7, 0x90 },
+ { 8, 0x00 },
+ { 9, 0x00 },
+ { 10, 0x22 },
+ { 11, 0x22 },
+ { 12, 0x22 },
+ { 13, 0x08 },
+ { 14, 0xcf },
+ { 15, 0xcf },
+ { 16, 0x7b },
+ { 17, 0x00 },
+ { 18, 0x32 },
+ { 19, 0x00 },
+ { 20, 0xa6 },
+ { 21, 0x01 },
+ { 22, 0x01 },
};
+static bool wm8776_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8776_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int wm8776_reset(struct snd_soc_codec *codec)
{
return snd_soc_write(codec, WM8776_RESET, 0);
@@ -217,8 +249,6 @@ static int wm8776_hw_params(struct snd_pcm_substream *substream,
int ratio_shift, master;
int i;
- iface = 0;
-
switch (dai->driver->id) {
case WM8776_DAI_DAC:
iface_reg = WM8776_DACIFCTRL;
@@ -234,20 +264,24 @@ static int wm8776_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
-
/* Set word length */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ iface = 0;
break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- iface |= 0x10;
+ case 20:
+ iface = 0x10;
break;
- case SNDRV_PCM_FORMAT_S24_LE:
- iface |= 0x20;
+ case 24:
+ iface = 0x20;
break;
- case SNDRV_PCM_FORMAT_S32_LE:
- iface |= 0x30;
+ case 32:
+ iface = 0x30;
break;
+ default:
+ dev_err(codec->dev, "Unsupported sample size: %i\n",
+ snd_pcm_format_width(params_format(params)));
+ return -EINVAL;
}
/* Only need to set MCLK/LRCLK ratio if we're master */
@@ -291,7 +325,8 @@ static int wm8776_set_sysclk(struct snd_soc_dai *dai,
struct snd_soc_codec *codec = dai->codec;
struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec);
- BUG_ON(dai->driver->id >= ARRAY_SIZE(wm8776->sysclk));
+ if (WARN_ON(dai->driver->id >= ARRAY_SIZE(wm8776->sysclk)))
+ return -EINVAL;
wm8776->sysclk[dai->driver->id] = freq;
@@ -301,13 +336,17 @@ static int wm8776_set_sysclk(struct snd_soc_dai *dai,
static int wm8776_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec);
+
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ regcache_sync(wm8776->regmap);
+
/* Disable the global powerdown; DAPM does the rest */
snd_soc_update_bits(codec, WM8776_PWRDOWN, 1, 0);
}
@@ -318,26 +357,21 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->bias_level = level;
+ codec->dapm.bias_level = level;
return 0;
}
-#define WM8776_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
- SNDRV_PCM_RATE_96000)
-
-
#define WM8776_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-static struct snd_soc_dai_ops wm8776_dac_ops = {
+static const struct snd_soc_dai_ops wm8776_dac_ops = {
.digital_mute = wm8776_mute,
.hw_params = wm8776_hw_params,
.set_fmt = wm8776_set_fmt,
.set_sysclk = wm8776_set_sysclk,
};
-static struct snd_soc_dai_ops wm8776_adc_ops = {
+static const struct snd_soc_dai_ops wm8776_adc_ops = {
.hw_params = wm8776_hw_params,
.set_fmt = wm8776_set_fmt,
.set_sysclk = wm8776_set_sysclk,
@@ -351,7 +385,9 @@ static struct snd_soc_dai_driver wm8776_dai[] = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
- .rates = WM8776_RATES,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 32000,
+ .rate_max = 192000,
.formats = WM8776_FORMATS,
},
.ops = &wm8776_dac_ops,
@@ -363,7 +399,9 @@ static struct snd_soc_dai_driver wm8776_dai[] = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
- .rates = WM8776_RATES,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 32000,
+ .rate_max = 96000,
.formats = WM8776_FORMATS,
},
.ops = &wm8776_adc_ops,
@@ -371,7 +409,7 @@ static struct snd_soc_dai_driver wm8776_dai[] = {
};
#ifdef CONFIG_PM
-static int wm8776_suspend(struct snd_soc_codec *codec, pm_message_t state)
+static int wm8776_suspend(struct snd_soc_codec *codec)
{
wm8776_set_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -380,21 +418,7 @@ static int wm8776_suspend(struct snd_soc_codec *codec, pm_message_t state)
static int wm8776_resume(struct snd_soc_codec *codec)
{
- int i;
- u8 data[2];
- u16 *cache = codec->reg_cache;
-
- /* Sync reg_cache with the hardware */
- for (i = 0; i < ARRAY_SIZE(wm8776_reg); i++) {
- if (cache[i] == wm8776_reg[i])
- continue;
- data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
- data[1] = cache[i] & 0x00ff;
- codec->hw_write(codec->control_data, data, 2);
- }
-
wm8776_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
return 0;
}
#else
@@ -404,15 +428,8 @@ static int wm8776_resume(struct snd_soc_codec *codec)
static int wm8776_probe(struct snd_soc_codec *codec)
{
- struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
- ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8776->control_type);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
- return ret;
- }
-
ret = wm8776_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
@@ -426,12 +443,6 @@ static int wm8776_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8776_HPRVOL, 0x100, 0x100);
snd_soc_update_bits(codec, WM8776_DACRVOL, 0x100, 0x100);
- snd_soc_add_controls(codec, wm8776_snd_controls,
- ARRAY_SIZE(wm8776_snd_controls));
- snd_soc_dapm_new_controls(codec, wm8776_dapm_widgets,
- ARRAY_SIZE(wm8776_dapm_widgets));
- snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes));
-
return ret;
}
@@ -448,89 +459,118 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8776 = {
.suspend = wm8776_suspend,
.resume = wm8776_resume,
.set_bias_level = wm8776_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(wm8776_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8776_reg,
+
+ .controls = wm8776_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8776_snd_controls),
+ .dapm_widgets = wm8776_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8776_dapm_widgets),
+ .dapm_routes = routes,
+ .num_dapm_routes = ARRAY_SIZE(routes),
+};
+
+static const struct of_device_id wm8776_of_match[] = {
+ { .compatible = "wlf,wm8776", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8776_of_match);
+
+static const struct regmap_config wm8776_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8776_RESET,
+
+ .reg_defaults = wm8776_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8776_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8776_volatile,
};
#if defined(CONFIG_SPI_MASTER)
-static int __devinit wm8776_spi_probe(struct spi_device *spi)
+static int wm8776_spi_probe(struct spi_device *spi)
{
struct wm8776_priv *wm8776;
int ret;
- wm8776 = kzalloc(sizeof(struct wm8776_priv), GFP_KERNEL);
+ wm8776 = devm_kzalloc(&spi->dev, sizeof(struct wm8776_priv),
+ GFP_KERNEL);
if (wm8776 == NULL)
return -ENOMEM;
- wm8776->control_type = SND_SOC_SPI;
+ wm8776->regmap = devm_regmap_init_spi(spi, &wm8776_regmap);
+ if (IS_ERR(wm8776->regmap))
+ return PTR_ERR(wm8776->regmap);
+
spi_set_drvdata(spi, wm8776);
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai));
- if (ret < 0)
- kfree(wm8776);
+
return ret;
}
-static int __devexit wm8776_spi_remove(struct spi_device *spi)
+static int wm8776_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
- kfree(spi_get_drvdata(spi));
return 0;
}
static struct spi_driver wm8776_spi_driver = {
.driver = {
- .name = "wm8776-codec",
+ .name = "wm8776",
.owner = THIS_MODULE,
+ .of_match_table = wm8776_of_match,
},
.probe = wm8776_spi_probe,
- .remove = __devexit_p(wm8776_spi_remove),
+ .remove = wm8776_spi_remove,
};
#endif /* CONFIG_SPI_MASTER */
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-static __devinit int wm8776_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+#if IS_ENABLED(CONFIG_I2C)
+static int wm8776_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
{
struct wm8776_priv *wm8776;
int ret;
- wm8776 = kzalloc(sizeof(struct wm8776_priv), GFP_KERNEL);
+ wm8776 = devm_kzalloc(&i2c->dev, sizeof(struct wm8776_priv),
+ GFP_KERNEL);
if (wm8776 == NULL)
return -ENOMEM;
+ wm8776->regmap = devm_regmap_init_i2c(i2c, &wm8776_regmap);
+ if (IS_ERR(wm8776->regmap))
+ return PTR_ERR(wm8776->regmap);
+
i2c_set_clientdata(i2c, wm8776);
- wm8776->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai));
- if (ret < 0)
- kfree(wm8776);
+
return ret;
}
-static __devexit int wm8776_i2c_remove(struct i2c_client *client)
+static int wm8776_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
return 0;
}
static const struct i2c_device_id wm8776_i2c_id[] = {
- { "wm8776", 0 },
+ { "wm8775", WM8775 },
+ { "wm8776", WM8776 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id);
static struct i2c_driver wm8776_i2c_driver = {
.driver = {
- .name = "wm8776-codec",
+ .name = "wm8776",
.owner = THIS_MODULE,
+ .of_match_table = wm8776_of_match,
},
.probe = wm8776_i2c_probe,
- .remove = __devexit_p(wm8776_i2c_remove),
+ .remove = wm8776_i2c_remove,
.id_table = wm8776_i2c_id,
};
#endif
@@ -538,7 +578,7 @@ static struct i2c_driver wm8776_i2c_driver = {
static int __init wm8776_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8776_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register wm8776 I2C driver: %d\n",
@@ -558,7 +598,7 @@ module_init(wm8776_modinit);
static void __exit wm8776_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8776_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)