summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/wm8978.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-03-23 00:00:13 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-03-23 00:00:13 +0400
commitb2094ef840697bc8ca5d17a83b7e30fad5f1e9fa (patch)
tree64e5f7253b6a85b6d5d36f95c0d3c67c1798918d /sound/soc/codecs/wm8978.c
parent424a6f6ef990b7e9f56f6627bfc6c46b493faeb4 (diff)
parent6681bc0deba495fad0d6fb349e40524abd1b1732 (diff)
downloadlinux-b2094ef840697bc8ca5d17a83b7e30fad5f1e9fa.tar.xz
Merge tag 'sound-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull updates of sound stuff from Takashi Iwai: "Here is the first big update chunk of sound stuff for 3.4-rc1. In the common sound infrastructure, there are a few changes for dynamic PCM support (used in ASoC) and a few clean-ups. Majority of changes are found, as usual, in HD-audio and ASoC. Some highlights of HD-audio changes: - All the long-standing static quirk codes for Realtek codec were finally removed by fixing and extending the Realtek auto-parser. - The mute-LED control is standardized over all HD-audio codec drivers using the extended vmaster hook. - The vmaster slave mixer elements are initialized to 0dB as default so that the user won't be annoyed by the silent output after updates, e.g. due to the additions of new elements. - Other many fix-ups for the misc HD-audio devices. In the ASoC side, this is a very active release, including a quite a few framework enhancements. Some highlights: - Support for widgets not associated with a CODEC, an important part of the dynamic PCM framework. - A library factoring out the common code shared by dmaengine based DMA drivers contributed by Lars-Peter Clausen. This will save a lot of code and make it much easier to deploy enhancements to dmaengine. - Support for binary controls, used for providing runtime configuration of algorithm coefficients. - A new DAPM widget type for regulator supplies allowing drivers for devices that can power down unused supplies while active to do without any per-driver code. - DAPM widgets for DAIs, initially giving a speed boost for playback startup and shutdown and also the basis for CODEC<->CODEC DAI link support. - Support for specifying the number of significant bits on audio interfaces, useful for allowing applications to know how much effort to put into generating data for a larger sample format. - Conversion of the FSI driver used on some SH processors to DMAEngine. - Conversion of EP93xx drivers to DMAEngine. - New CODEC drivers for Maxim MAX9768 and Wolfson Microelectronics WM2200. - Move audmux driver from arc/arm to sound/soc - McBSP move from arch/ to sound/ and updates Also, a few small updates and fixes for other drivers like au88x0, ymfpci, USB 6fire, USB usx2yaudio are included." * tag 'sound-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (446 commits) ASoC: wm8994: Provide VMID mode control and fix default sequence ASoC: wm8994: Add missing break in resume ASoC: wm_hubs: Don't actively manage LINEOUT_VMID_BUF ASoC: pxa-ssp: atomically set stream active masks ASoC: fsl: p1022ds: tell the WM8776 codec driver that it's the master ASoC: Samsung: Added to support mono recording ALSA: hda - Fix build with CONFIG_PM=n ALSA: au88x0 - Avoid possible Oops at unbinding ALSA: usb-audio - Fix build error by consitification of rate list ASoC: core: Fix obscure leak of runtime array ALSA: pcm - Avoid GFP_ATOMIC in snd_pcm_link() ALSA: pcm: Constify the list in snd_pcm_hw_constraint_list ASoC: wm8996: Add 44.1kHz support ALSA: hda - Fix build of patch_sigmatel.c without CONFIG_SND_HDA_POWER_SAVE ASoC: mx27vis-aic32x4: Convert it to platform driver ALSA: hda - fix printing of high HDMI sample rates ALSA: ymfpci - Fix legacy registers on S3/S4 resume ALSA: control - Fixe a trailing white space error ALSA: hda - Add expose_enum_ctl flag to snd_hda_add_vmaster_hook() ALSA: hda - Add "Mute-LED Mode" enum control ...
Diffstat (limited to 'sound/soc/codecs/wm8978.c')
-rw-r--r--sound/soc/codecs/wm8978.c185
1 files changed, 121 insertions, 64 deletions
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index 85d514d63a4c..72d5fdcd3cc2 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -18,6 +18,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -29,28 +30,74 @@
#include "wm8978.h"
-/* wm8978 register cache. Note that register 0 is not included in the cache. */
-static const u16 wm8978_reg[WM8978_CACHEREGNUM] = {
- 0x0000, 0x0000, 0x0000, 0x0000, /* 0x00...0x03 */
- 0x0050, 0x0000, 0x0140, 0x0000, /* 0x04...0x07 */
- 0x0000, 0x0000, 0x0000, 0x00ff, /* 0x08...0x0b */
- 0x00ff, 0x0000, 0x0100, 0x00ff, /* 0x0c...0x0f */
- 0x00ff, 0x0000, 0x012c, 0x002c, /* 0x10...0x13 */
- 0x002c, 0x002c, 0x002c, 0x0000, /* 0x14...0x17 */
- 0x0032, 0x0000, 0x0000, 0x0000, /* 0x18...0x1b */
- 0x0000, 0x0000, 0x0000, 0x0000, /* 0x1c...0x1f */
- 0x0038, 0x000b, 0x0032, 0x0000, /* 0x20...0x23 */
- 0x0008, 0x000c, 0x0093, 0x00e9, /* 0x24...0x27 */
- 0x0000, 0x0000, 0x0000, 0x0000, /* 0x28...0x2b */
- 0x0033, 0x0010, 0x0010, 0x0100, /* 0x2c...0x2f */
- 0x0100, 0x0002, 0x0001, 0x0001, /* 0x30...0x33 */
- 0x0039, 0x0039, 0x0039, 0x0039, /* 0x34...0x37 */
- 0x0001, 0x0001, /* 0x38...0x3b */
+static const struct reg_default wm8978_reg_defaults[] = {
+ { 1, 0x0000 },
+ { 2, 0x0000 },
+ { 3, 0x0000 },
+ { 4, 0x0050 },
+ { 5, 0x0000 },
+ { 6, 0x0140 },
+ { 7, 0x0000 },
+ { 8, 0x0000 },
+ { 9, 0x0000 },
+ { 10, 0x0000 },
+ { 11, 0x00ff },
+ { 12, 0x00ff },
+ { 13, 0x0000 },
+ { 14, 0x0100 },
+ { 15, 0x00ff },
+ { 16, 0x00ff },
+ { 17, 0x0000 },
+ { 18, 0x012c },
+ { 19, 0x002c },
+ { 20, 0x002c },
+ { 21, 0x002c },
+ { 22, 0x002c },
+ { 23, 0x0000 },
+ { 24, 0x0032 },
+ { 25, 0x0000 },
+ { 26, 0x0000 },
+ { 27, 0x0000 },
+ { 28, 0x0000 },
+ { 29, 0x0000 },
+ { 30, 0x0000 },
+ { 31, 0x0000 },
+ { 32, 0x0038 },
+ { 33, 0x000b },
+ { 34, 0x0032 },
+ { 35, 0x0000 },
+ { 36, 0x0008 },
+ { 37, 0x000c },
+ { 38, 0x0093 },
+ { 39, 0x00e9 },
+ { 40, 0x0000 },
+ { 41, 0x0000 },
+ { 42, 0x0000 },
+ { 43, 0x0000 },
+ { 44, 0x0033 },
+ { 45, 0x0010 },
+ { 46, 0x0010 },
+ { 47, 0x0100 },
+ { 48, 0x0100 },
+ { 49, 0x0002 },
+ { 50, 0x0001 },
+ { 51, 0x0001 },
+ { 52, 0x0039 },
+ { 53, 0x0039 },
+ { 54, 0x0039 },
+ { 55, 0x0039 },
+ { 56, 0x0001 },
+ { 57, 0x0001 },
};
+static bool wm8978_volatile(struct device *dev, unsigned int reg)
+{
+ return reg == WM8978_RESET;
+}
+
/* codec private data */
struct wm8978_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
unsigned int f_pllout;
unsigned int f_mclk;
unsigned int f_256fs;
@@ -303,7 +350,7 @@ static const struct snd_soc_dapm_widget wm8978_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("RSPK"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route wm8978_dapm_routes[] = {
/* Output mixer */
{"Right Output Mixer", "PCM Playback Switch", "Right DAC"},
{"Right Output Mixer", "Aux Playback Switch", "RAUX"},
@@ -352,18 +399,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Left Input Mixer", "MicP Switch", "LMICP"},
};
-static int wm8978_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8978_dapm_widgets,
- ARRAY_SIZE(wm8978_dapm_widgets));
- /* set up the WM8978 audio map */
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
/* PLL divisors */
struct wm8978_pll_div {
u32 k;
@@ -894,26 +929,23 @@ static struct snd_soc_dai_driver wm8978_dai = {
static int wm8978_suspend(struct snd_soc_codec *codec)
{
+ struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+
wm8978_set_bias_level(codec, SND_SOC_BIAS_OFF);
/* Also switch PLL off */
snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, 0);
+ regcache_mark_dirty(wm8978->regmap);
+
return 0;
}
static int wm8978_resume(struct snd_soc_codec *codec)
{
struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
- int i;
- u16 *cache = codec->reg_cache;
/* Sync reg_cache with the hardware */
- for (i = 0; i < ARRAY_SIZE(wm8978_reg); i++) {
- if (i == WM8978_RESET)
- continue;
- if (cache[i] != wm8978_reg[i])
- snd_soc_write(codec, i, cache[i]);
- }
+ regcache_sync(wm8978->regmap);
wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -953,7 +985,8 @@ static int wm8978_probe(struct snd_soc_codec *codec)
* default hardware setting
*/
wm8978->sysclk = WM8978_PLL;
- ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
+ codec->control_data = wm8978->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
@@ -967,19 +1000,8 @@ static int wm8978_probe(struct snd_soc_codec *codec)
for (i = 0; i < ARRAY_SIZE(update_reg); i++)
snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100);
- /* Reset the codec */
- ret = snd_soc_write(codec, WM8978_RESET, 0);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
- return ret;
- }
-
wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- snd_soc_add_controls(codec, wm8978_snd_controls,
- ARRAY_SIZE(wm8978_snd_controls));
- wm8978_add_widgets(codec);
-
return 0;
}
@@ -996,35 +1018,75 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8978 = {
.suspend = wm8978_suspend,
.resume = wm8978_resume,
.set_bias_level = wm8978_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(wm8978_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8978_reg,
+
+ .controls = wm8978_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8978_snd_controls),
+ .dapm_widgets = wm8978_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8978_dapm_widgets),
+ .dapm_routes = wm8978_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8978_dapm_routes),
+};
+
+static const struct regmap_config wm8978_regmap_config = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = WM8978_MAX_REGISTER,
+ .volatile_reg = wm8978_volatile,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm8978_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8978_reg_defaults),
};
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static __devinit int wm8978_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8978_priv *wm8978;
int ret;
- wm8978 = kzalloc(sizeof(struct wm8978_priv), GFP_KERNEL);
+ wm8978 = devm_kzalloc(&i2c->dev, sizeof(struct wm8978_priv),
+ GFP_KERNEL);
if (wm8978 == NULL)
return -ENOMEM;
+ wm8978->regmap = regmap_init_i2c(i2c, &wm8978_regmap_config);
+ if (IS_ERR(wm8978->regmap)) {
+ ret = PTR_ERR(wm8978->regmap);
+ dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
i2c_set_clientdata(i2c, wm8978);
+ /* Reset the codec */
+ ret = regmap_write(wm8978->regmap, WM8978_RESET, 0);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
+ goto err;
+ }
+
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8978, &wm8978_dai, 1);
- if (ret < 0)
- kfree(wm8978);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regmap_exit(wm8978->regmap);
return ret;
}
static __devexit int wm8978_i2c_remove(struct i2c_client *client)
{
+ struct wm8978_priv *wm8978 = i2c_get_clientdata(client);
+
snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
+ regmap_exit(wm8978->regmap);
+
return 0;
}
@@ -1043,27 +1105,22 @@ static struct i2c_driver wm8978_i2c_driver = {
.remove = __devexit_p(wm8978_i2c_remove),
.id_table = wm8978_i2c_id,
};
-#endif
static int __init wm8978_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&wm8978_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8978 I2C driver: %d\n",
ret);
}
-#endif
return ret;
}
module_init(wm8978_modinit);
static void __exit wm8978_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&wm8978_i2c_driver);
-#endif
}
module_exit(wm8978_exit);