Commit 62b1653e authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'for-2.6.32' of...

Merge branch 'for-2.6.32' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6 into topic/asoc
parents 10121a12 d5fc3b5f
...@@ -279,6 +279,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec, ...@@ -279,6 +279,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
/* dapm events */ /* dapm events */
int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream, int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
int event); int event);
void snd_soc_dapm_shutdown(struct snd_soc_device *socdev);
/* dapm sys fs - used by the core */ /* dapm sys fs - used by the core */
int snd_soc_dapm_sys_add(struct device *dev); int snd_soc_dapm_sys_add(struct device *dev);
......
...@@ -192,6 +192,11 @@ void snd_soc_unregister_platform(struct snd_soc_platform *platform); ...@@ -192,6 +192,11 @@ void snd_soc_unregister_platform(struct snd_soc_platform *platform);
int snd_soc_register_codec(struct snd_soc_codec *codec); int snd_soc_register_codec(struct snd_soc_codec *codec);
void snd_soc_unregister_codec(struct snd_soc_codec *codec); void snd_soc_unregister_codec(struct snd_soc_codec *codec);
#ifdef CONFIG_PM
int snd_soc_suspend_device(struct device *dev);
int snd_soc_resume_device(struct device *dev);
#endif
/* pcm <-> DAI connect */ /* pcm <-> DAI connect */
void snd_soc_free_pcms(struct snd_soc_device *socdev); void snd_soc_free_pcms(struct snd_soc_device *socdev);
int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid); int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
...@@ -216,9 +221,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, ...@@ -216,9 +221,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
/* codec register bit access */ /* codec register bit access */
int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
unsigned short mask, unsigned short value); unsigned int mask, unsigned int value);
int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
unsigned short mask, unsigned short value); unsigned int mask, unsigned int value);
int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
struct snd_ac97_bus_ops *ops, int num); struct snd_ac97_bus_ops *ops, int num);
...@@ -369,8 +374,6 @@ struct snd_soc_codec { ...@@ -369,8 +374,6 @@ struct snd_soc_codec {
enum snd_soc_bias_level bias_level; enum snd_soc_bias_level bias_level;
enum snd_soc_bias_level suspend_bias_level; enum snd_soc_bias_level suspend_bias_level;
struct delayed_work delayed_work; struct delayed_work delayed_work;
struct list_head up_list;
struct list_head down_list;
/* codec DAI's */ /* codec DAI's */
struct snd_soc_dai *dai; struct snd_soc_dai *dai;
......
/*
* UDA1380 ALSA SoC Codec driver
*
* Copyright 2009 Philipp Zabel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __UDA1380_H
#define __UDA1380_H
struct uda1380_platform_data {
int gpio_power;
int gpio_reset;
int dac_clk;
#define UDA1380_DAC_CLK_SYSCLK 0
#define UDA1380_DAC_CLK_WSPLL 1
};
#endif /* __UDA1380_H */
...@@ -203,23 +203,23 @@ static struct snd_soc_device bf5xx_ad73311_snd_devdata = { ...@@ -203,23 +203,23 @@ static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
.codec_dev = &soc_codec_dev_ad73311, .codec_dev = &soc_codec_dev_ad73311,
}; };
static struct platform_device *bf52x_ad73311_snd_device; static struct platform_device *bf5xx_ad73311_snd_device;
static int __init bf5xx_ad73311_init(void) static int __init bf5xx_ad73311_init(void)
{ {
int ret; int ret;
pr_debug("%s enter\n", __func__); pr_debug("%s enter\n", __func__);
bf52x_ad73311_snd_device = platform_device_alloc("soc-audio", -1); bf5xx_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
if (!bf52x_ad73311_snd_device) if (!bf5xx_ad73311_snd_device)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(bf52x_ad73311_snd_device, &bf5xx_ad73311_snd_devdata); platform_set_drvdata(bf5xx_ad73311_snd_device, &bf5xx_ad73311_snd_devdata);
bf5xx_ad73311_snd_devdata.dev = &bf52x_ad73311_snd_device->dev; bf5xx_ad73311_snd_devdata.dev = &bf5xx_ad73311_snd_device->dev;
ret = platform_device_add(bf52x_ad73311_snd_device); ret = platform_device_add(bf5xx_ad73311_snd_device);
if (ret) if (ret)
platform_device_put(bf52x_ad73311_snd_device); platform_device_put(bf5xx_ad73311_snd_device);
return ret; return ret;
} }
...@@ -227,7 +227,7 @@ static int __init bf5xx_ad73311_init(void) ...@@ -227,7 +227,7 @@ static int __init bf5xx_ad73311_init(void)
static void __exit bf5xx_ad73311_exit(void) static void __exit bf5xx_ad73311_exit(void)
{ {
pr_debug("%s enter\n", __func__); pr_debug("%s enter\n", __func__);
platform_device_unregister(bf52x_ad73311_snd_device); platform_device_unregister(bf5xx_ad73311_snd_device);
} }
module_init(bf5xx_ad73311_init); module_init(bf5xx_ad73311_init);
......
...@@ -148,24 +148,24 @@ static struct snd_soc_device bf5xx_ssm2602_snd_devdata = { ...@@ -148,24 +148,24 @@ static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
.codec_data = &bf5xx_ssm2602_setup, .codec_data = &bf5xx_ssm2602_setup,
}; };
static struct platform_device *bf52x_ssm2602_snd_device; static struct platform_device *bf5xx_ssm2602_snd_device;
static int __init bf5xx_ssm2602_init(void) static int __init bf5xx_ssm2602_init(void)
{ {
int ret; int ret;
pr_debug("%s enter\n", __func__); pr_debug("%s enter\n", __func__);
bf52x_ssm2602_snd_device = platform_device_alloc("soc-audio", -1); bf5xx_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
if (!bf52x_ssm2602_snd_device) if (!bf5xx_ssm2602_snd_device)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(bf52x_ssm2602_snd_device, platform_set_drvdata(bf5xx_ssm2602_snd_device,
&bf5xx_ssm2602_snd_devdata); &bf5xx_ssm2602_snd_devdata);
bf5xx_ssm2602_snd_devdata.dev = &bf52x_ssm2602_snd_device->dev; bf5xx_ssm2602_snd_devdata.dev = &bf5xx_ssm2602_snd_device->dev;
ret = platform_device_add(bf52x_ssm2602_snd_device); ret = platform_device_add(bf5xx_ssm2602_snd_device);
if (ret) if (ret)
platform_device_put(bf52x_ssm2602_snd_device); platform_device_put(bf5xx_ssm2602_snd_device);
return ret; return ret;
} }
...@@ -173,7 +173,7 @@ static int __init bf5xx_ssm2602_init(void) ...@@ -173,7 +173,7 @@ static int __init bf5xx_ssm2602_init(void)
static void __exit bf5xx_ssm2602_exit(void) static void __exit bf5xx_ssm2602_exit(void)
{ {
pr_debug("%s enter\n", __func__); pr_debug("%s enter\n", __func__);
platform_device_unregister(bf52x_ssm2602_snd_device); platform_device_unregister(bf5xx_ssm2602_snd_device);
} }
module_init(bf5xx_ssm2602_init); module_init(bf5xx_ssm2602_init);
......
...@@ -39,6 +39,7 @@ config SND_SOC_ALL_CODECS ...@@ -39,6 +39,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8903 if I2C select SND_SOC_WM8903 if I2C
select SND_SOC_WM8940 if I2C select SND_SOC_WM8940 if I2C
select SND_SOC_WM8960 if I2C select SND_SOC_WM8960 if I2C
select SND_SOC_WM8961 if I2C
select SND_SOC_WM8971 if I2C select SND_SOC_WM8971 if I2C
select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8990 if I2C select SND_SOC_WM8990 if I2C
...@@ -156,6 +157,9 @@ config SND_SOC_WM8940 ...@@ -156,6 +157,9 @@ config SND_SOC_WM8940
config SND_SOC_WM8960 config SND_SOC_WM8960
tristate tristate
config SND_SOC_WM8961
tristate
config SND_SOC_WM8971 config SND_SOC_WM8971
tristate tristate
......
...@@ -27,6 +27,7 @@ snd-soc-wm8900-objs := wm8900.o ...@@ -27,6 +27,7 @@ snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8940-objs := wm8940.o snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8960-objs := wm8960.o snd-soc-wm8960-objs := wm8960.o
snd-soc-wm8961-objs := wm8961.o
snd-soc-wm8971-objs := wm8971.o snd-soc-wm8971-objs := wm8971.o
snd-soc-wm8988-objs := wm8988.o snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o snd-soc-wm8990-objs := wm8990.o
...@@ -65,6 +66,7 @@ obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o ...@@ -65,6 +66,7 @@ obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o
obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
obj-$(CONFIG_SND_SOC_WM8961) += snd-soc-wm8961.o
obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o
......
...@@ -712,7 +712,19 @@ static int bypass_event(struct snd_soc_dapm_widget *w, ...@@ -712,7 +712,19 @@ static int bypass_event(struct snd_soc_dapm_widget *w,
reg = twl4030_read_reg_cache(w->codec, m->reg); reg = twl4030_read_reg_cache(w->codec, m->reg);
if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) { /*
* bypass_state[0:3] - analog HiFi bypass
* bypass_state[4] - analog voice bypass
* bypass_state[5] - digital voice bypass
* bypass_state[6:7] - digital HiFi bypass
*/
if (m->reg == TWL4030_REG_VSTPGA) {
/* Voice digital bypass */
if (reg)
twl4030->bypass_state |= (1 << 5);
else
twl4030->bypass_state &= ~(1 << 5);
} else if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
/* Analog bypass */ /* Analog bypass */
if (reg & (1 << m->shift)) if (reg & (1 << m->shift))
twl4030->bypass_state |= twl4030->bypass_state |=
...@@ -726,12 +738,6 @@ static int bypass_event(struct snd_soc_dapm_widget *w, ...@@ -726,12 +738,6 @@ static int bypass_event(struct snd_soc_dapm_widget *w,
twl4030->bypass_state |= (1 << 4); twl4030->bypass_state |= (1 << 4);
else else
twl4030->bypass_state &= ~(1 << 4); twl4030->bypass_state &= ~(1 << 4);
} else if (m->reg == TWL4030_REG_VSTPGA) {
/* Voice digital bypass */
if (reg)
twl4030->bypass_state |= (1 << 5);
else
twl4030->bypass_state &= ~(1 << 5);
} else { } else {
/* Digital bypass */ /* Digital bypass */
if (reg & (0x7 << m->shift)) if (reg & (0x7 << m->shift))
...@@ -924,7 +930,7 @@ static const struct soc_enum twl4030_op_modes_enum = ...@@ -924,7 +930,7 @@ static const struct soc_enum twl4030_op_modes_enum =
ARRAY_SIZE(twl4030_op_modes_texts), ARRAY_SIZE(twl4030_op_modes_texts),
twl4030_op_modes_texts); twl4030_op_modes_texts);
int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol, static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
...@@ -1005,6 +1011,16 @@ static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0); ...@@ -1005,6 +1011,16 @@ static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0);
*/ */
static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0); static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
/* AVADC clock priority */
static const char *twl4030_avadc_clk_priority_texts[] = {
"Voice high priority", "HiFi high priority"
};
static const struct soc_enum twl4030_avadc_clk_priority_enum =
SOC_ENUM_SINGLE(TWL4030_REG_AVADC_CTL, 2,
ARRAY_SIZE(twl4030_avadc_clk_priority_texts),
twl4030_avadc_clk_priority_texts);
static const char *twl4030_rampdelay_texts[] = { static const char *twl4030_rampdelay_texts[] = {
"27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms", "27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms",
"437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms", "437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms",
...@@ -1106,6 +1122,8 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { ...@@ -1106,6 +1122,8 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = {
SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN, SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN,
0, 3, 5, 0, input_gain_tlv), 0, 3, 5, 0, input_gain_tlv),
SOC_ENUM("AVADC Clock Priority", twl4030_avadc_clk_priority_enum),
SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum), SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum),
SOC_ENUM("Vibra H-bridge mode", twl4030_vibradirmode_enum), SOC_ENUM("Vibra H-bridge mode", twl4030_vibradirmode_enum),
...@@ -1609,8 +1627,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, ...@@ -1609,8 +1627,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
/* If the substream has 4 channel, do the necessary setup */ /* If the substream has 4 channel, do the necessary setup */
if (params_channels(params) == 4) { if (params_channels(params) == 4) {
u8 format, mode;
format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
...@@ -1948,7 +1964,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, ...@@ -1948,7 +1964,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* set master/slave audio interface */ /* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFM: case SND_SOC_DAIFMT_CBM_CFM:
format &= ~(TWL4030_VIF_SLAVE_EN); format &= ~(TWL4030_VIF_SLAVE_EN);
break; break;
case SND_SOC_DAIFMT_CBS_CFS: case SND_SOC_DAIFMT_CBS_CFS:
......
...@@ -5,9 +5,7 @@ ...@@ -5,9 +5,7 @@
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
* *
* Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com> * Copyright (c) 2007-2009 Philipp Zabel <philipp.zabel@gmail.com>
* Improved support for DAPM and audio routing/mixing capabilities,
* added TLV support.
* *
* Modified by Richard Purdie <richard@openedhand.com> to fit into SoC * Modified by Richard Purdie <richard@openedhand.com> to fit into SoC
* codec model. * codec model.
...@@ -19,26 +17,32 @@ ...@@ -19,26 +17,32 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/string.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/ioctl.h> #include <linux/gpio.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/info.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/soc-dapm.h> #include <sound/soc-dapm.h>
#include <sound/tlv.h> #include <sound/tlv.h>
#include <sound/uda1380.h>
#include "uda1380.h" #include "uda1380.h"
static struct work_struct uda1380_work;
static struct snd_soc_codec *uda1380_codec; static struct snd_soc_codec *uda1380_codec;
/* codec private data */
struct uda1380_priv {
struct snd_soc_codec codec;
u16 reg_cache[UDA1380_CACHEREGNUM];
unsigned int dac_clk;
struct work_struct work;
};
/* /*
* uda1380 register cache * uda1380 register cache
*/ */
...@@ -473,6 +477,7 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -473,6 +477,7 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec; struct snd_soc_codec *codec = socdev->card->codec;
struct uda1380_priv *uda1380 = codec->private_data;
int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER); int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
switch (cmd) { switch (cmd) {
...@@ -480,13 +485,13 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -480,13 +485,13 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
uda1380_write_reg_cache(codec, UDA1380_MIXER, uda1380_write_reg_cache(codec, UDA1380_MIXER,
mixer & ~R14_SILENCE); mixer & ~R14_SILENCE);
schedule_work(&uda1380_work); schedule_work(&uda1380->work);
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
uda1380_write_reg_cache(codec, UDA1380_MIXER, uda1380_write_reg_cache(codec, UDA1380_MIXER,
mixer | R14_SILENCE); mixer | R14_SILENCE);
schedule_work(&uda1380_work); schedule_work(&uda1380->work);
break; break;
} }
return 0; return 0;
...@@ -670,44 +675,33 @@ static int uda1380_resume(struct platform_device *pdev) ...@@ -670,44 +675,33 @@ static int uda1380_resume(struct platform_device *pdev)
return 0; return 0;
} }
/* static int uda1380_probe(struct platform_device *pdev)
* initialise the UDA1380 driver
* register mixer and dsp interfaces with the kernel
*/
static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
{ {
struct snd_soc_codec *codec = socdev->card->codec; struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
struct uda1380_platform_data *pdata;
int ret = 0; int ret = 0;
codec->name = "UDA1380"; if (uda1380_codec == NULL) {
codec->owner = THIS_MODULE; dev_err(&pdev->dev, "Codec device not registered\n");
codec->read = uda1380_read_reg_cache; return -ENODEV;
codec->write = uda1380_write; }
codec->set_bias_level = uda1380_set_bias_level;
codec->dai = uda1380_dai;
codec->num_dai = ARRAY_SIZE(uda1380_dai);
codec->reg_cache = kmemdup(uda1380_reg, sizeof(uda1380_reg),
GFP_KERNEL);
if (codec->reg_cache == NULL)
return -ENOMEM;
codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
codec->reg_cache_step = 1;
uda1380_reset(codec);
uda1380_codec = codec; socdev->card->codec = uda1380_codec;
INIT_WORK(&uda1380_work, uda1380_flush_work); codec = uda1380_codec;
pdata = codec->dev->platform_data;
/* register pcms */ /* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) { if (ret < 0) {
pr_err("uda1380: failed to create pcms\n"); dev_err(codec->dev, "failed to create pcms: %d\n", ret);
goto pcm_err; goto pcm_err;
} }
/* power on device */ /* power on device */
uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY); uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* set clock input */ /* set clock input */
switch (dac_clk) { switch (pdata->dac_clk) {
case UDA1380_DAC_CLK_SYSCLK: case UDA1380_DAC_CLK_SYSCLK:
uda1380_write(codec, UDA1380_CLK, 0); uda1380_write(codec, UDA1380_CLK, 0);
break; break;
...@@ -716,13 +710,12 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk) ...@@ -716,13 +710,12 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
break; break;
} }
/* uda1380 init */
snd_soc_add_controls(codec, uda1380_snd_controls, snd_soc_add_controls(codec, uda1380_snd_controls,
ARRAY_SIZE(uda1380_snd_controls)); ARRAY_SIZE(uda1380_snd_controls));
uda1380_add_widgets(codec); uda1380_add_widgets(codec);
ret = snd_soc_init_card(socdev); ret = snd_soc_init_card(socdev);
if (ret < 0) { if (ret < 0) {
pr_err("uda1380: failed to register card\n"); dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err; goto card_err;
} }
...@@ -732,165 +725,201 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk) ...@@ -732,165 +725,201 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
snd_soc_free_pcms(socdev); snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev); snd_soc_dapm_free(socdev);
pcm_err: pcm_err:
kfree(codec->reg_cache);
return ret; return ret;
} }
static struct snd_soc_device *uda1380_socdev; /* power down chip */
static int uda1380_remove(struct platform_device *pdev)
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static int uda1380_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{ {
struct snd_soc_device *socdev = uda1380_socdev; struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct uda1380_setup_data *setup = socdev->codec_data;
struct snd_soc_codec *codec = socdev->card->codec; struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
codec->control_data = i2c;
ret = uda1380_init(socdev, setup->dac_clk); if (codec->control_data)
if (ret < 0) uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
pr_err("uda1380: failed to initialise UDA1380\n");
return ret; snd_soc_free_pcms(socdev);
} snd_soc_dapm_free(socdev);
static int uda1380_i2c_remove(struct i2c_client *client)
{
struct snd_soc_codec *codec = i2c_get_clientdata(client);
kfree(codec->reg_cache);
return 0; return 0;
} }
static const struct i2c_device_id uda1380_i2c_id[] = { struct snd_soc_codec_device soc_codec_dev_uda1380 = {
{ "uda1380", 0 }, .probe = uda1380_probe,
{ } .remove = uda1380_remove,
}; .suspend = uda1380_suspend,
MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id); .resume = uda1380_resume,
static struct i2c_driver uda1380_i2c_driver = {
.driver = {
.name = "UDA1380 I2C Codec",
.owner = THIS_MODULE,
},
.probe = uda1380_i2c_probe,
.remove = uda1380_i2c_remove,
.id_table = uda1380_i2c_id,
}; };
EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
static int uda1380_add_i2c_device(struct platform_device *pdev, static int uda1380_register(struct uda1380_priv *uda1380)
const struct uda1380_setup_data *setup)
{ {
struct i2c_board_info info; int ret, i;
struct i2c_adapter *adapter; struct snd_soc_codec *codec = &uda1380->codec;
struct i2c_client *client; struct uda1380_platform_data *pdata = codec->dev->platform_data;
int ret;
ret = i2c_add_driver(&uda1380_i2c_driver); if (uda1380_codec) {
if (ret != 0) { dev_err(codec->dev, "Another UDA1380 is registered\n");
dev_err(&pdev->dev, "can't add i2c driver\n"); return -EINVAL;
return ret; }
if (!pdata || !pdata->gpio_power || !pdata->gpio_reset)
return -EINVAL;
ret = gpio_request(pdata->gpio_power, "uda1380 power");
if (ret)
goto err_out;
ret = gpio_request(pdata->gpio_reset, "uda1380 reset");
if (ret)
goto err_gpio;
gpio_direction_output(pdata->gpio_power, 1);
/* we may need to have the clock running here - pH5 */
gpio_direction_output(pdata->gpio_reset, 1);
udelay(5);
gpio_set_value(pdata->gpio_reset, 0);
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->private_data = uda1380;
codec->name = "UDA1380";
codec->owner = THIS_MODULE;
codec->read = uda1380_read_reg_cache;
codec->write = uda1380_write;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = uda1380_set_bias_level;
codec->dai = uda1380_dai;
codec->num_dai = ARRAY_SIZE(uda1380_dai);
codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
codec->reg_cache = &uda1380->reg_cache;
codec->reg_cache_step = 1;
memcpy(codec->reg_cache, uda1380_reg, sizeof(uda1380_reg));
ret = uda1380_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset\n");
goto err_reset;
} }
memset(&info, 0, sizeof(struct i2c_board_info)); INIT_WORK(&uda1380->work, uda1380_flush_work);
info.addr = setup->i2c_address;
strlcpy(info.type, "uda1380", I2C_NAME_SIZE); for (i = 0; i < ARRAY_SIZE(uda1380_dai); i++)
uda1380_dai[i].dev = codec->dev;
adapter = i2c_get_adapter(setup->i2c_bus); uda1380_codec = codec;
if (!adapter) {
dev_err(&pdev->dev, "can't get i2c adapter %d\n", ret = snd_soc_register_codec(codec);
setup->i2c_bus); if (ret != 0) {
goto err_driver; dev_err(codec->dev, "Failed to register codec: %d\n", ret);
goto err_reset;
} }
client = i2c_new_device(adapter, &info); ret = snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
i2c_put_adapter(adapter); if (ret != 0) {
if (!client) { dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", goto err_dai;
(unsigned int)info.addr);
goto err_driver;
} }
return 0; return 0;
err_driver: err_dai:
i2c_del_driver(&uda1380_i2c_driver); snd_soc_unregister_codec(codec);
return -ENODEV; err_reset:
gpio_set_value(pdata->gpio_power, 0);
gpio_free(pdata->gpio_reset);
err_gpio:
gpio_free(pdata->gpio_power);
err_out:
return ret;
} }
#endif
static int uda1380_probe(struct platform_device *pdev) static void uda1380_unregister(struct uda1380_priv *uda1380)
{ {
struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = &uda1380->codec;
struct uda1380_setup_data *setup; struct uda1380_platform_data *pdata = codec->dev->platform_data;
snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
snd_soc_unregister_codec(&uda1380->codec);
gpio_set_value(pdata->gpio_power, 0);
gpio_free(pdata->gpio_reset);
gpio_free(pdata->gpio_power);
kfree(uda1380);
uda1380_codec = NULL;
}
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static __devinit int uda1380_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct uda1380_priv *uda1380;
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
int ret; int ret;
setup = socdev->codec_data; uda1380 = kzalloc(sizeof(struct uda1380_priv), GFP_KERNEL);
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); if (uda1380 == NULL)
if (codec == NULL)
return -ENOMEM; return -ENOMEM;
socdev->card->codec = codec; codec = &uda1380->codec;
mutex_init(&codec->mutex); codec->hw_write = (hw_write_t)i2c_master_send;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
uda1380_socdev = socdev; i2c_set_clientdata(i2c, uda1380);
ret = -ENODEV; codec->control_data = i2c;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) codec->dev = &i2c->dev;
if (setup->i2c_address) {
codec->hw_write = (hw_write_t)i2c_master_send;
ret = uda1380_add_i2c_device(pdev, setup);
}
#endif
ret = uda1380_register(uda1380);
if (ret != 0) if (ret != 0)
kfree(codec); kfree(uda1380);
return ret; return ret;
} }
/* power down chip */ static int __devexit uda1380_i2c_remove(struct i2c_client *i2c)
static int uda1380_remove(struct platform_device *pdev)
{ {
struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct uda1380_priv *uda1380 = i2c_get_clientdata(i2c);
struct snd_soc_codec *codec = socdev->card->codec; uda1380_unregister(uda1380);
if (codec->control_data)
uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_unregister_device(codec->control_data);
i2c_del_driver(&uda1380_i2c_driver);
#endif
kfree(codec);
return 0; return 0;
} }
struct snd_soc_codec_device soc_codec_dev_uda1380 = { static const struct i2c_device_id uda1380_i2c_id[] = {
.probe = uda1380_probe, { "uda1380", 0 },
.remove = uda1380_remove, { }
.suspend = uda1380_suspend,
.resume = uda1380_resume,
}; };
EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380); MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
static struct i2c_driver uda1380_i2c_driver = {
.driver = {
.name = "UDA1380 I2C Codec",
.owner = THIS_MODULE,
},
.probe = uda1380_i2c_probe,
.remove = __devexit_p(uda1380_i2c_remove),
.id_table = uda1380_i2c_id,
};
#endif
static int __init uda1380_modinit(void) static int __init uda1380_modinit(void)
{ {
return snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai)); int ret;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&uda1380_i2c_driver);
if (ret != 0)
pr_err("Failed to register UDA1380 I2C driver: %d\n", ret);
#endif
return 0;
} }
module_init(uda1380_modinit); module_init(uda1380_modinit);
static void __exit uda1380_exit(void) static void __exit uda1380_exit(void)
{ {
snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai)); #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&uda1380_i2c_driver);
#endif
} }
module_exit(uda1380_exit); module_exit(uda1380_exit);
......
...@@ -72,14 +72,6 @@ ...@@ -72,14 +72,6 @@
#define R22_SKIP_DCFIL 0x0002 #define R22_SKIP_DCFIL 0x0002
#define R23_AGC_EN 0x0001 #define R23_AGC_EN 0x0001
struct uda1380_setup_data {
int i2c_bus;
unsigned short i2c_address;
int dac_clk;
#define UDA1380_DAC_CLK_SYSCLK 0
#define UDA1380_DAC_CLK_WSPLL 1
};
#define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */ #define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */
#define UDA1380_DAI_PLAYBACK 1 /* playback DAI */ #define UDA1380_DAI_PLAYBACK 1 /* playback DAI */
#define UDA1380_DAI_CAPTURE 2 /* capture DAI */ #define UDA1380_DAI_CAPTURE 2 /* capture DAI */
......
...@@ -406,7 +406,6 @@ static const char *wm8350_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" }; ...@@ -406,7 +406,6 @@ static const char *wm8350_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" }; static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" };
static const char *wm8350_dacmutem[] = { "Normal", "Soft" }; static const char *wm8350_dacmutem[] = { "Normal", "Soft" };
static const char *wm8350_dacmutes[] = { "Fast", "Slow" }; static const char *wm8350_dacmutes[] = { "Fast", "Slow" };
static const char *wm8350_dacfilter[] = { "Normal", "Sloping" };
static const char *wm8350_adcfilter[] = { "None", "High Pass" }; static const char *wm8350_adcfilter[] = { "None", "High Pass" };
static const char *wm8350_adchp[] = { "44.1kHz", "8kHz", "16kHz", "32kHz" }; static const char *wm8350_adchp[] = { "44.1kHz", "8kHz", "16kHz", "32kHz" };
static const char *wm8350_lr[] = { "Left", "Right" }; static const char *wm8350_lr[] = { "Left", "Right" };
...@@ -416,7 +415,6 @@ static const struct soc_enum wm8350_enum[] = { ...@@ -416,7 +415,6 @@ static const struct soc_enum wm8350_enum[] = {
SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 0, 4, wm8350_pol), SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 0, 4, wm8350_pol),
SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 14, 2, wm8350_dacmutem), SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 14, 2, wm8350_dacmutem),
SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 13, 2, wm8350_dacmutes), SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 13, 2, wm8350_dacmutes),
SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 12, 2, wm8350_dacfilter),
SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 15, 2, wm8350_adcfilter), SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 15, 2, wm8350_adcfilter),
SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 8, 4, wm8350_adchp), SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 8, 4, wm8350_adchp),
SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 0, 4, wm8350_pol), SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 0, 4, wm8350_pol),
...@@ -444,10 +442,9 @@ static const struct snd_kcontrol_new wm8350_snd_controls[] = { ...@@ -444,10 +442,9 @@ static const struct snd_kcontrol_new wm8350_snd_controls[] = {
0, 255, 0, dac_pcm_tlv), 0, 255, 0, dac_pcm_tlv),
SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]), SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]),
SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]), SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]),
SOC_ENUM("Playback PCM Filter", wm8350_enum[4]), SOC_ENUM("Capture PCM Filter", wm8350_enum[4]),
SOC_ENUM("Capture PCM Filter", wm8350_enum[5]), SOC_ENUM("Capture PCM HP Filter", wm8350_enum[5]),
SOC_ENUM("Capture PCM HP Filter", wm8350_enum[6]), SOC_ENUM("Capture ADC Inversion", wm8350_enum[6]),
SOC_ENUM("Capture ADC Inversion", wm8350_enum[7]),
SOC_WM8350_DOUBLE_R_TLV("Capture PCM Volume", SOC_WM8350_DOUBLE_R_TLV("Capture PCM Volume",
WM8350_ADC_DIGITAL_VOLUME_L, WM8350_ADC_DIGITAL_VOLUME_L,
WM8350_ADC_DIGITAL_VOLUME_R, WM8350_ADC_DIGITAL_VOLUME_R,
...@@ -993,6 +990,7 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream, ...@@ -993,6 +990,7 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai) struct snd_soc_dai *codec_dai)
{ {
struct snd_soc_codec *codec = codec_dai->codec; struct snd_soc_codec *codec = codec_dai->codec;
struct wm8350 *wm8350 = codec->control_data;
u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) & u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
~WM8350_AIF_WL_MASK; ~WM8350_AIF_WL_MASK;
...@@ -1012,6 +1010,19 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream, ...@@ -1012,6 +1010,19 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
} }
wm8350_codec_write(codec, WM8350_AI_FORMATING, iface); wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
/* The sloping stopband filter is recommended for use with
* lower sample rates to improve performance.
*/
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (params_rate(params) < 24000)
wm8350_set_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
WM8350_DAC_SB_FILT);
else
wm8350_clear_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
WM8350_DAC_SB_FILT);
}
return 0; return 0;
} }
...@@ -1660,6 +1671,21 @@ static int __devexit wm8350_codec_remove(struct platform_device *pdev) ...@@ -1660,6 +1671,21 @@ static int __devexit wm8350_codec_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm8350_codec_suspend(struct platform_device *pdev, pm_message_t m)
{
return snd_soc_suspend_device(&pdev->dev);
}
static int wm8350_codec_resume(struct platform_device *pdev)
{
return snd_soc_resume_device(&pdev->dev);
}
#else
#define wm8350_codec_suspend NULL
#define wm8350_codec_resume NULL
#endif
static struct platform_driver wm8350_codec_driver = { static struct platform_driver wm8350_codec_driver = {
.driver = { .driver = {
.name = "wm8350-codec", .name = "wm8350-codec",
...@@ -1667,6 +1693,8 @@ static struct platform_driver wm8350_codec_driver = { ...@@ -1667,6 +1693,8 @@ static struct platform_driver wm8350_codec_driver = {
}, },
.probe = wm8350_codec_probe, .probe = wm8350_codec_probe,
.remove = __devexit_p(wm8350_codec_remove), .remove = __devexit_p(wm8350_codec_remove),
.suspend = wm8350_codec_suspend,
.resume = wm8350_codec_resume,
}; };
static __init int wm8350_init(void) static __init int wm8350_init(void)
......
...@@ -1553,6 +1553,21 @@ static int __exit wm8400_codec_remove(struct platform_device *dev) ...@@ -1553,6 +1553,21 @@ static int __exit wm8400_codec_remove(struct platform_device *dev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm8400_pdev_suspend(struct platform_device *pdev, pm_message_t msg)
{
return snd_soc_suspend_device(&pdev->dev);
}
static int wm8400_pdev_resume(struct platform_device *pdev)
{
return snd_soc_resume_device(&pdev->dev);
}
#else
#define wm8400_pdev_suspend NULL
#define wm8400_pdev_resume NULL
#endif
static struct platform_driver wm8400_codec_driver = { static struct platform_driver wm8400_codec_driver = {
.driver = { .driver = {
.name = "wm8400-codec", .name = "wm8400-codec",
...@@ -1560,6 +1575,8 @@ static struct platform_driver wm8400_codec_driver = { ...@@ -1560,6 +1575,8 @@ static struct platform_driver wm8400_codec_driver = {
}, },
.probe = wm8400_codec_probe, .probe = wm8400_codec_probe,
.remove = __exit_p(wm8400_codec_remove), .remove = __exit_p(wm8400_codec_remove),
.suspend = wm8400_pdev_suspend,
.resume = wm8400_pdev_resume,
}; };
static int __init wm8400_codec_init(void) static int __init wm8400_codec_init(void)
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
...@@ -187,15 +189,22 @@ struct pll_state { ...@@ -187,15 +189,22 @@ struct pll_state {
unsigned int out; unsigned int out;
}; };
#define WM8580_NUM_SUPPLIES 3
static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = {
"AVDD",
"DVDD",
"PVDD",
};
/* codec private data */ /* codec private data */
struct wm8580_priv { struct wm8580_priv {
struct snd_soc_codec codec; struct snd_soc_codec codec;
struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES];
u16 reg_cache[WM8580_MAX_REGISTER + 1]; u16 reg_cache[WM8580_MAX_REGISTER + 1];
struct pll_state a; struct pll_state a;
struct pll_state b; struct pll_state b;
}; };
/* /*
* read wm8580 register cache * read wm8580 register cache
*/ */
...@@ -922,11 +931,28 @@ static int wm8580_register(struct wm8580_priv *wm8580) ...@@ -922,11 +931,28 @@ static int wm8580_register(struct wm8580_priv *wm8580)
memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg)); memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg));
for (i = 0; i < ARRAY_SIZE(wm8580->supplies); i++)
wm8580->supplies[i].supply = wm8580_supply_names[i];
ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8580->supplies),
wm8580->supplies);
if (ret != 0) {
dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
goto err;
}
ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies),
wm8580->supplies);
if (ret != 0) {
dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
goto err_regulator_get;
}
/* Get the codec into a known state */ /* Get the codec into a known state */
ret = wm8580_write(codec, WM8580_RESET, 0); ret = wm8580_write(codec, WM8580_RESET, 0);
if (ret != 0) { if (ret != 0) {
dev_err(codec->dev, "Failed to reset codec: %d\n", ret); dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
goto err; goto err_regulator_enable;
} }
for (i = 0; i < ARRAY_SIZE(wm8580_dai); i++) for (i = 0; i < ARRAY_SIZE(wm8580_dai); i++)
...@@ -939,7 +965,7 @@ static int wm8580_register(struct wm8580_priv *wm8580) ...@@ -939,7 +965,7 @@ static int wm8580_register(struct wm8580_priv *wm8580)
ret = snd_soc_register_codec(codec); ret = snd_soc_register_codec(codec);
if (ret != 0) { if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret); dev_err(codec->dev, "Failed to register codec: %d\n", ret);
goto err; goto err_regulator_enable;
} }
ret = snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai)); ret = snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
...@@ -952,6 +978,10 @@ static int wm8580_register(struct wm8580_priv *wm8580) ...@@ -952,6 +978,10 @@ static int wm8580_register(struct wm8580_priv *wm8580)
err_codec: err_codec:
snd_soc_unregister_codec(codec); snd_soc_unregister_codec(codec);
err_regulator_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
err_regulator_get:
regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
err: err:
kfree(wm8580); kfree(wm8580);
return ret; return ret;
...@@ -962,6 +992,8 @@ static void wm8580_unregister(struct wm8580_priv *wm8580) ...@@ -962,6 +992,8 @@ static void wm8580_unregister(struct wm8580_priv *wm8580)
wm8580_set_bias_level(&wm8580->codec, SND_SOC_BIAS_OFF); wm8580_set_bias_level(&wm8580->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai)); snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
snd_soc_unregister_codec(&wm8580->codec); snd_soc_unregister_codec(&wm8580->codec);
regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
kfree(wm8580); kfree(wm8580);
wm8580_codec = NULL; wm8580_codec = NULL;
} }
...@@ -995,6 +1027,21 @@ static int wm8580_i2c_remove(struct i2c_client *client) ...@@ -995,6 +1027,21 @@ static int wm8580_i2c_remove(struct i2c_client *client)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm8580_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8580_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8580_i2c_suspend NULL
#define wm8580_i2c_resume NULL
#endif
static const struct i2c_device_id wm8580_i2c_id[] = { static const struct i2c_device_id wm8580_i2c_id[] = {
{ "wm8580", 0 }, { "wm8580", 0 },
{ } { }
...@@ -1008,6 +1055,8 @@ static struct i2c_driver wm8580_i2c_driver = { ...@@ -1008,6 +1055,8 @@ static struct i2c_driver wm8580_i2c_driver = {
}, },
.probe = wm8580_i2c_probe, .probe = wm8580_i2c_probe,
.remove = wm8580_i2c_remove, .remove = wm8580_i2c_remove,
.suspend = wm8580_i2c_suspend,
.resume = wm8580_i2c_resume,
.id_table = wm8580_i2c_id, .id_table = wm8580_i2c_id,
}; };
#endif #endif
......
...@@ -460,6 +460,7 @@ struct snd_soc_dai wm8731_dai = { ...@@ -460,6 +460,7 @@ struct snd_soc_dai wm8731_dai = {
}; };
EXPORT_SYMBOL_GPL(wm8731_dai); EXPORT_SYMBOL_GPL(wm8731_dai);
#ifdef CONFIG_PM
static int wm8731_suspend(struct platform_device *pdev, pm_message_t state) static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
{ {
struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_device *socdev = platform_get_drvdata(pdev);
...@@ -488,6 +489,10 @@ static int wm8731_resume(struct platform_device *pdev) ...@@ -488,6 +489,10 @@ static int wm8731_resume(struct platform_device *pdev)
wm8731_set_bias_level(codec, codec->suspend_bias_level); wm8731_set_bias_level(codec, codec->suspend_bias_level);
return 0; return 0;
} }
#else
#define wm8731_suspend NULL
#define wm8731_resume NULL
#endif
static int wm8731_probe(struct platform_device *pdev) static int wm8731_probe(struct platform_device *pdev)
{ {
...@@ -680,6 +685,21 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi) ...@@ -680,6 +685,21 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm8731_spi_suspend(struct spi_device *spi, pm_message_t msg)
{
return snd_soc_suspend_device(&spi->dev);
}
static int wm8731_spi_resume(struct spi_device *spi)
{
return snd_soc_resume_device(&spi->dev);
}
#else
#define wm8731_spi_suspend NULL
#define wm8731_spi_resume NULL
#endif
static struct spi_driver wm8731_spi_driver = { static struct spi_driver wm8731_spi_driver = {
.driver = { .driver = {
.name = "wm8731", .name = "wm8731",
...@@ -687,6 +707,8 @@ static struct spi_driver wm8731_spi_driver = { ...@@ -687,6 +707,8 @@ static struct spi_driver wm8731_spi_driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = wm8731_spi_probe, .probe = wm8731_spi_probe,
.suspend = wm8731_spi_suspend,
.resume = wm8731_spi_resume,
.remove = __devexit_p(wm8731_spi_remove), .remove = __devexit_p(wm8731_spi_remove),
}; };
#endif /* CONFIG_SPI_MASTER */ #endif /* CONFIG_SPI_MASTER */
...@@ -720,6 +742,21 @@ static __devexit int wm8731_i2c_remove(struct i2c_client *client) ...@@ -720,6 +742,21 @@ static __devexit int wm8731_i2c_remove(struct i2c_client *client)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm8731_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
{
return snd_soc_suspend_device(&i2c->dev);
}
static int wm8731_i2c_resume(struct i2c_client *i2c)
{
return snd_soc_resume_device(&i2c->dev);
}
#else
#define wm8731_i2c_suspend NULL
#define wm8731_i2c_resume NULL
#endif
static const struct i2c_device_id wm8731_i2c_id[] = { static const struct i2c_device_id wm8731_i2c_id[] = {
{ "wm8731", 0 }, { "wm8731", 0 },
{ } { }
...@@ -733,6 +770,8 @@ static struct i2c_driver wm8731_i2c_driver = { ...@@ -733,6 +770,8 @@ static struct i2c_driver wm8731_i2c_driver = {
}, },
.probe = wm8731_i2c_probe, .probe = wm8731_i2c_probe,
.remove = __devexit_p(wm8731_i2c_remove), .remove = __devexit_p(wm8731_i2c_remove),
.suspend = wm8731_i2c_suspend,
.resume = wm8731_i2c_resume,
.id_table = wm8731_i2c_id, .id_table = wm8731_i2c_id,
}; };
#endif #endif
......
...@@ -1766,6 +1766,21 @@ static int wm8753_i2c_remove(struct i2c_client *client) ...@@ -1766,6 +1766,21 @@ static int wm8753_i2c_remove(struct i2c_client *client)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm8753_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8753_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8753_i2c_suspend NULL
#define wm8753_i2c_resume NULL
#endif
static const struct i2c_device_id wm8753_i2c_id[] = { static const struct i2c_device_id wm8753_i2c_id[] = {
{ "wm8753", 0 }, { "wm8753", 0 },
{ } { }
...@@ -1779,6 +1794,8 @@ static struct i2c_driver wm8753_i2c_driver = { ...@@ -1779,6 +1794,8 @@ static struct i2c_driver wm8753_i2c_driver = {
}, },
.probe = wm8753_i2c_probe, .probe = wm8753_i2c_probe,
.remove = wm8753_i2c_remove, .remove = wm8753_i2c_remove,
.suspend = wm8753_i2c_suspend,
.resume = wm8753_i2c_resume,
.id_table = wm8753_i2c_id, .id_table = wm8753_i2c_id,
}; };
#endif #endif
...@@ -1834,6 +1851,22 @@ static int __devexit wm8753_spi_remove(struct spi_device *spi) ...@@ -1834,6 +1851,22 @@ static int __devexit wm8753_spi_remove(struct spi_device *spi)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm8753_spi_suspend(struct spi_device *spi, pm_message_t msg)
{
return snd_soc_suspend_device(&spi->dev);
}
static int wm8753_spi_resume(struct spi_device *spi)
{
return snd_soc_resume_device(&spi->dev);
}
#else
#define wm8753_spi_suspend NULL
#define wm8753_spi_resume NULL
#endif
static struct spi_driver wm8753_spi_driver = { static struct spi_driver wm8753_spi_driver = {
.driver = { .driver = {
.name = "wm8753", .name = "wm8753",
...@@ -1842,6 +1875,8 @@ static struct spi_driver wm8753_spi_driver = { ...@@ -1842,6 +1875,8 @@ static struct spi_driver wm8753_spi_driver = {
}, },
.probe = wm8753_spi_probe, .probe = wm8753_spi_probe,
.remove = __devexit_p(wm8753_spi_remove), .remove = __devexit_p(wm8753_spi_remove),
.suspend = wm8753_spi_suspend,
.resume = wm8753_spi_resume,
}; };
#endif #endif
......
...@@ -116,6 +116,7 @@ ...@@ -116,6 +116,7 @@
#define WM8900_REG_CLOCKING2_DAC_CLKDIV 0x1c #define WM8900_REG_CLOCKING2_DAC_CLKDIV 0x1c
#define WM8900_REG_DACCTRL_MUTE 0x004 #define WM8900_REG_DACCTRL_MUTE 0x004
#define WM8900_REG_DACCTRL_DAC_SB_FILT 0x100
#define WM8900_REG_DACCTRL_AIF_LRCLKRATE 0x400 #define WM8900_REG_DACCTRL_AIF_LRCLKRATE 0x400
#define WM8900_REG_AUDIO3_ADCLRC_DIR 0x0800 #define WM8900_REG_AUDIO3_ADCLRC_DIR 0x0800
...@@ -439,7 +440,6 @@ SOC_SINGLE("DAC Soft Mute Switch", WM8900_REG_DACCTRL, 6, 1, 1), ...@@ -439,7 +440,6 @@ SOC_SINGLE("DAC Soft Mute Switch", WM8900_REG_DACCTRL, 6, 1, 1),
SOC_ENUM("DAC Mute Rate", dac_mute_rate), SOC_ENUM("DAC Mute Rate", dac_mute_rate),
SOC_SINGLE("DAC Mono Switch", WM8900_REG_DACCTRL, 9, 1, 0), SOC_SINGLE("DAC Mono Switch", WM8900_REG_DACCTRL, 9, 1, 0),
SOC_ENUM("DAC Deemphasis", dac_deemphasis), SOC_ENUM("DAC Deemphasis", dac_deemphasis),
SOC_SINGLE("DAC Sloping Stopband Filter Switch", WM8900_REG_DACCTRL, 8, 1, 0),
SOC_SINGLE("DAC Sigma-Delta Modulator Clock Switch", WM8900_REG_DACCTRL, SOC_SINGLE("DAC Sigma-Delta Modulator Clock Switch", WM8900_REG_DACCTRL,
12, 1, 0), 12, 1, 0),
...@@ -743,6 +743,17 @@ static int wm8900_hw_params(struct snd_pcm_substream *substream, ...@@ -743,6 +743,17 @@ static int wm8900_hw_params(struct snd_pcm_substream *substream,
wm8900_write(codec, WM8900_REG_AUDIO1, reg); wm8900_write(codec, WM8900_REG_AUDIO1, reg);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
reg = wm8900_read(codec, WM8900_REG_DACCTRL);
if (params_rate(params) <= 24000)
reg |= WM8900_REG_DACCTRL_DAC_SB_FILT;
else
reg &= ~WM8900_REG_DACCTRL_DAC_SB_FILT;
wm8900_write(codec, WM8900_REG_DACCTRL, reg);
}
return 0; return 0;
} }
...@@ -1388,6 +1399,21 @@ static __devexit int wm8900_i2c_remove(struct i2c_client *client) ...@@ -1388,6 +1399,21 @@ static __devexit int wm8900_i2c_remove(struct i2c_client *client)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm8900_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8900_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8900_i2c_suspend NULL
#define wm8900_i2c_resume NULL
#endif
static const struct i2c_device_id wm8900_i2c_id[] = { static const struct i2c_device_id wm8900_i2c_id[] = {
{ "wm8900", 0 }, { "wm8900", 0 },
{ } { }
...@@ -1401,6 +1427,8 @@ static struct i2c_driver wm8900_i2c_driver = { ...@@ -1401,6 +1427,8 @@ static struct i2c_driver wm8900_i2c_driver = {
}, },
.probe = wm8900_i2c_probe, .probe = wm8900_i2c_probe,
.remove = __devexit_p(wm8900_i2c_remove), .remove = __devexit_p(wm8900_i2c_remove),
.suspend = wm8900_i2c_suspend,
.resume = wm8900_i2c_resume,
.id_table = wm8900_i2c_id, .id_table = wm8900_i2c_id,
}; };
......
...@@ -715,8 +715,6 @@ SOC_ENUM("DAC Soft Mute Rate", soft_mute), ...@@ -715,8 +715,6 @@ SOC_ENUM("DAC Soft Mute Rate", soft_mute),
SOC_ENUM("DAC Mute Mode", mute_mode), SOC_ENUM("DAC Mute Mode", mute_mode),
SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0), SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0),
SOC_ENUM("DAC De-emphasis", dac_deemphasis), SOC_ENUM("DAC De-emphasis", dac_deemphasis),
SOC_SINGLE("DAC Sloping Stopband Filter Switch",
WM8903_DAC_DIGITAL_1, 11, 1, 0),
SOC_ENUM("DAC Companding Mode", dac_companding), SOC_ENUM("DAC Companding Mode", dac_companding),
SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0), SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0),
...@@ -1373,12 +1371,19 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, ...@@ -1373,12 +1371,19 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
u16 aif3 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_3); u16 aif3 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_3);
u16 clock0 = wm8903_read(codec, WM8903_CLOCK_RATES_0); u16 clock0 = wm8903_read(codec, WM8903_CLOCK_RATES_0);
u16 clock1 = wm8903_read(codec, WM8903_CLOCK_RATES_1); u16 clock1 = wm8903_read(codec, WM8903_CLOCK_RATES_1);
u16 dac_digital1 = wm8903_read(codec, WM8903_DAC_DIGITAL_1);
if (substream == wm8903->slave_substream) { if (substream == wm8903->slave_substream) {
dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n"); dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n");
return 0; return 0;
} }
/* Enable sloping stopband filter for low sample rates */
if (fs <= 24000)
dac_digital1 |= WM8903_DAC_SB_FILT;
else
dac_digital1 &= ~WM8903_DAC_SB_FILT;
/* Configure sample rate logic for DSP - choose nearest rate */ /* Configure sample rate logic for DSP - choose nearest rate */
dsp_config = 0; dsp_config = 0;
best_val = abs(sample_rates[dsp_config].rate - fs); best_val = abs(sample_rates[dsp_config].rate - fs);
...@@ -1503,6 +1508,7 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, ...@@ -1503,6 +1508,7 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1); wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
wm8903_write(codec, WM8903_AUDIO_INTERFACE_2, aif2); wm8903_write(codec, WM8903_AUDIO_INTERFACE_2, aif2);
wm8903_write(codec, WM8903_AUDIO_INTERFACE_3, aif3); wm8903_write(codec, WM8903_AUDIO_INTERFACE_3, aif3);
wm8903_write(codec, WM8903_DAC_DIGITAL_1, dac_digital1);
return 0; return 0;
} }
...@@ -1721,6 +1727,21 @@ static __devexit int wm8903_i2c_remove(struct i2c_client *client) ...@@ -1721,6 +1727,21 @@ static __devexit int wm8903_i2c_remove(struct i2c_client *client)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm8903_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8903_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8903_i2c_suspend NULL
#define wm8903_i2c_resume NULL
#endif
/* i2c codec control layer */ /* i2c codec control layer */
static const struct i2c_device_id wm8903_i2c_id[] = { static const struct i2c_device_id wm8903_i2c_id[] = {
{ "wm8903", 0 }, { "wm8903", 0 },
...@@ -1735,6 +1756,8 @@ static struct i2c_driver wm8903_i2c_driver = { ...@@ -1735,6 +1756,8 @@ static struct i2c_driver wm8903_i2c_driver = {
}, },
.probe = wm8903_i2c_probe, .probe = wm8903_i2c_probe,
.remove = __devexit_p(wm8903_i2c_remove), .remove = __devexit_p(wm8903_i2c_remove),
.suspend = wm8903_i2c_suspend,
.resume = wm8903_i2c_resume,
.id_table = wm8903_i2c_id, .id_table = wm8903_i2c_id,
}; };
......
...@@ -916,6 +916,21 @@ static int __devexit wm8940_i2c_remove(struct i2c_client *client) ...@@ -916,6 +916,21 @@ static int __devexit wm8940_i2c_remove(struct i2c_client *client)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm8940_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8940_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8940_i2c_suspend NULL
#define wm8940_i2c_resume NULL
#endif
static const struct i2c_device_id wm8940_i2c_id[] = { static const struct i2c_device_id wm8940_i2c_id[] = {
{ "wm8940", 0 }, { "wm8940", 0 },
{ } { }
...@@ -929,6 +944,8 @@ static struct i2c_driver wm8940_i2c_driver = { ...@@ -929,6 +944,8 @@ static struct i2c_driver wm8940_i2c_driver = {
}, },
.probe = wm8940_i2c_probe, .probe = wm8940_i2c_probe,
.remove = __devexit_p(wm8940_i2c_remove), .remove = __devexit_p(wm8940_i2c_remove),
.suspend = wm8940_i2c_suspend,
.resume = wm8940_i2c_resume,
.id_table = wm8940_i2c_id, .id_table = wm8940_i2c_id,
}; };
......
...@@ -927,6 +927,21 @@ static __devexit int wm8960_i2c_remove(struct i2c_client *client) ...@@ -927,6 +927,21 @@ static __devexit int wm8960_i2c_remove(struct i2c_client *client)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm8960_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8960_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8960_i2c_suspend NULL
#define wm8960_i2c_resume NULL
#endif
static const struct i2c_device_id wm8960_i2c_id[] = { static const struct i2c_device_id wm8960_i2c_id[] = {
{ "wm8960", 0 }, { "wm8960", 0 },
{ } { }
...@@ -940,6 +955,8 @@ static struct i2c_driver wm8960_i2c_driver = { ...@@ -940,6 +955,8 @@ static struct i2c_driver wm8960_i2c_driver = {
}, },
.probe = wm8960_i2c_probe, .probe = wm8960_i2c_probe,
.remove = __devexit_p(wm8960_i2c_remove), .remove = __devexit_p(wm8960_i2c_remove),
.suspend = wm8960_i2c_suspend,
.resume = wm8960_i2c_resume,
.id_table = wm8960_i2c_id, .id_table = wm8960_i2c_id,
}; };
......
/*
* wm8961.c -- WM8961 ALSA SoC Audio driver
*
* Author: Mark Brown
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Currently unimplemented features:
* - ALC
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.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 "wm8961.h"
#define WM8961_MAX_REGISTER 0xFC
static u16 wm8961_reg_defaults[] = {
0x009F, /* R0 - Left Input volume */
0x009F, /* R1 - Right Input volume */
0x0000, /* R2 - LOUT1 volume */
0x0000, /* R3 - ROUT1 volume */
0x0020, /* R4 - Clocking1 */
0x0008, /* R5 - ADC & DAC Control 1 */
0x0000, /* R6 - ADC & DAC Control 2 */
0x000A, /* R7 - Audio Interface 0 */
0x01F4, /* R8 - Clocking2 */
0x0000, /* R9 - Audio Interface 1 */
0x00FF, /* R10 - Left DAC volume */
0x00FF, /* R11 - Right DAC volume */
0x0000, /* R12 */
0x0000, /* R13 */
0x0040, /* R14 - Audio Interface 2 */
0x0000, /* R15 - Software Reset */
0x0000, /* R16 */
0x007B, /* R17 - ALC1 */
0x0000, /* R18 - ALC2 */
0x0032, /* R19 - ALC3 */
0x0000, /* R20 - Noise Gate */
0x00C0, /* R21 - Left ADC volume */
0x00C0, /* R22 - Right ADC volume */
0x0120, /* R23 - Additional control(1) */
0x0000, /* R24 - Additional control(2) */
0x0000, /* R25 - Pwr Mgmt (1) */
0x0000, /* R26 - Pwr Mgmt (2) */
0x0000, /* R27 - Additional Control (3) */
0x0000, /* R28 - Anti-pop */
0x0000, /* R29 */
0x005F, /* R30 - Clocking 3 */
0x0000, /* R31 */
0x0000, /* R32 - ADCL signal path */
0x0000, /* R33 - ADCR signal path */
0x0000, /* R34 */
0x0000, /* R35 */
0x0000, /* R36 */
0x0000, /* R37 */
0x0000, /* R38 */
0x0000, /* R39 */
0x0000, /* R40 - LOUT2 volume */
0x0000, /* R41 - ROUT2 volume */
0x0000, /* R42 */
0x0000, /* R43 */
0x0000, /* R44 */
0x0000, /* R45 */
0x0000, /* R46 */
0x0000, /* R47 - Pwr Mgmt (3) */
0x0023, /* R48 - Additional Control (4) */
0x0000, /* R49 - Class D Control 1 */
0x0000, /* R50 */
0x0003, /* R51 - Class D Control 2 */
0x0000, /* R52 */
0x0000, /* R53 */
0x0000, /* R54 */
0x0000, /* R55 */
0x0106, /* R56 - Clocking 4 */
0x0000, /* R57 - DSP Sidetone 0 */
0x0000, /* R58 - DSP Sidetone 1 */
0x0000, /* R59 */
0x0000, /* R60 - DC Servo 0 */
0x0000, /* R61 - DC Servo 1 */
0x0000, /* R62 */
0x015E, /* R63 - DC Servo 3 */
0x0010, /* R64 */
0x0010, /* R65 - DC Servo 5 */
0x0000, /* R66 */
0x0001, /* R67 */
0x0003, /* R68 - Analogue PGA Bias */
0x0000, /* R69 - Analogue HP 0 */
0x0060, /* R70 */
0x01FB, /* R71 - Analogue HP 2 */
0x0000, /* R72 - Charge Pump 1 */
0x0065, /* R73 */
0x005F, /* R74 */
0x0059, /* R75 */
0x006B, /* R76 */
0x0038, /* R77 */
0x000C, /* R78 */
0x000A, /* R79 */
0x006B, /* R80 */
0x0000, /* R81 */
0x0000, /* R82 - Charge Pump B */
0x0087, /* R83 */
0x0000, /* R84 */
0x005C, /* R85 */
0x0000, /* R86 */
0x0000, /* R87 - Write Sequencer 1 */
0x0000, /* R88 - Write Sequencer 2 */
0x0000, /* R89 - Write Sequencer 3 */
0x0000, /* R90 - Write Sequencer 4 */
0x0000, /* R91 - Write Sequencer 5 */
0x0000, /* R92 - Write Sequencer 6 */
0x0000, /* R93 - Write Sequencer 7 */
0x0000, /* R94 */
0x0000, /* R95 */
0x0000, /* R96 */
0x0000, /* R97 */
0x0000, /* R98 */
0x0000, /* R99 */
0x0000, /* R100 */
0x0000, /* R101 */
0x0000, /* R102 */
0x0000, /* R103 */
0x0000, /* R104 */
0x0000, /* R105 */
0x0000, /* R106 */
0x0000, /* R107 */
0x0000, /* R108 */
0x0000, /* R109 */
0x0000, /* R110 */
0x0000, /* R111 */
0x0000, /* R112 */
0x0000, /* R113 */
0x0000, /* R114 */
0x0000, /* R115 */
0x0000, /* R116 */
0x0000, /* R117 */
0x0000, /* R118 */
0x0000, /* R119 */
0x0000, /* R120 */
0x0000, /* R121 */
0x0000, /* R122 */
0x0000, /* R123 */
0x0000, /* R124 */
0x0000, /* R125 */
0x0000, /* R126 */
0x0000, /* R127 */
0x0000, /* R128 */
0x0000, /* R129 */
0x0000, /* R130 */
0x0000, /* R131 */
0x0000, /* R132 */
0x0000, /* R133 */
0x0000, /* R134 */
0x0000, /* R135 */
0x0000, /* R136 */
0x0000, /* R137 */
0x0000, /* R138 */
0x0000, /* R139 */
0x0000, /* R140 */
0x0000, /* R141 */
0x0000, /* R142 */
0x0000, /* R143 */
0x0000, /* R144 */
0x0000, /* R145 */
0x0000, /* R146 */
0x0000, /* R147 */
0x0000, /* R148 */
0x0000, /* R149 */
0x0000, /* R150 */
0x0000, /* R151 */
0x0000, /* R152 */
0x0000, /* R153 */
0x0000, /* R154 */
0x0000, /* R155 */
0x0000, /* R156 */
0x0000, /* R157 */
0x0000, /* R158 */
0x0000, /* R159 */
0x0000, /* R160 */
0x0000, /* R161 */
0x0000, /* R162 */
0x0000, /* R163 */
0x0000, /* R164 */
0x0000, /* R165 */
0x0000, /* R166 */
0x0000, /* R167 */
0x0000, /* R168 */
0x0000, /* R169 */
0x0000, /* R170 */
0x0000, /* R171 */
0x0000, /* R172 */
0x0000, /* R173 */
0x0000, /* R174 */
0x0000, /* R175 */
0x0000, /* R176 */
0x0000, /* R177 */
0x0000, /* R178 */
0x0000, /* R179 */
0x0000, /* R180 */
0x0000, /* R181 */
0x0000, /* R182 */
0x0000, /* R183 */
0x0000, /* R184 */
0x0000, /* R185 */
0x0000, /* R186 */
0x0000, /* R187 */
0x0000, /* R188 */
0x0000, /* R189 */
0x0000, /* R190 */
0x0000, /* R191 */
0x0000, /* R192 */
0x0000, /* R193 */
0x0000, /* R194 */
0x0000, /* R195 */
0x0030, /* R196 */
0x0006, /* R197 */
0x0000, /* R198 */
0x0060, /* R199 */
0x0000, /* R200 */
0x003F, /* R201 */
0x0000, /* R202 */
0x0000, /* R203 */
0x0000, /* R204 */
0x0001, /* R205 */
0x0000, /* R206 */
0x0181, /* R207 */
0x0005, /* R208 */
0x0008, /* R209 */
0x0008, /* R210 */
0x0000, /* R211 */
0x013B, /* R212 */
0x0000, /* R213 */
0x0000, /* R214 */
0x0000, /* R215 */
0x0000, /* R216 */
0x0070, /* R217 */
0x0000, /* R218 */
0x0000, /* R219 */
0x0000, /* R220 */
0x0000, /* R221 */
0x0000, /* R222 */
0x0003, /* R223 */
0x0000, /* R224 */
0x0000, /* R225 */
0x0001, /* R226 */
0x0008, /* R227 */
0x0000, /* R228 */
0x0000, /* R229 */
0x0000, /* R230 */
0x0000, /* R231 */
0x0004, /* R232 */
0x0000, /* R233 */
0x0000, /* R234 */
0x0000, /* R235 */
0x0000, /* R236 */
0x0000, /* R237 */
0x0080, /* R238 */
0x0000, /* R239 */
0x0000, /* R240 */
0x0000, /* R241 */
0x0000, /* R242 */
0x0000, /* R243 */
0x0000, /* R244 */
0x0052, /* R245 */
0x0110, /* R246 */
0x0040, /* R247 */
0x0000, /* R248 */
0x0030, /* R249 */
0x0000, /* R250 */
0x0000, /* R251 */
0x0001, /* R252 - General test 1 */
};
struct wm8961_priv {
struct snd_soc_codec codec;
int sysclk;
u16 reg_cache[WM8961_MAX_REGISTER];
};
static int wm8961_reg_is_volatile(int reg)
{
switch (reg) {
case WM8961_WRITE_SEQUENCER_7:
case WM8961_DC_SERVO_1:
return 1;
default:
return 0;
}
}
static unsigned int wm8961_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u16 *cache = codec->reg_cache;
BUG_ON(reg > WM8961_MAX_REGISTER);
return cache[reg];
}
static unsigned int wm8961_read_hw(struct snd_soc_codec *codec, u8 reg)
{
struct i2c_msg xfer[2];
u16 data;
int ret;
struct i2c_client *client = codec->control_data;
BUG_ON(reg > WM8961_MAX_REGISTER);
/* Write register */
xfer[0].addr = client->addr;
xfer[0].flags = 0;
xfer[0].len = 1;
xfer[0].buf = &reg;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = 2;
xfer[1].buf = (u8 *)&data;
ret = i2c_transfer(client->adapter, xfer, 2);
if (ret != 2) {
dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
return 0;
}
return (data >> 8) | ((data & 0xff) << 8);
}
static unsigned int wm8961_read(struct snd_soc_codec *codec, unsigned int reg)
{
if (wm8961_reg_is_volatile(reg))
return wm8961_read_hw(codec, reg);
else
return wm8961_read_reg_cache(codec, reg);
}
static int wm8961_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u16 *cache = codec->reg_cache;
u8 data[3];
BUG_ON(reg > WM8961_MAX_REGISTER);
if (!wm8961_reg_is_volatile(reg))
cache[reg] = value;
data[0] = reg;
data[1] = value >> 8;
data[2] = value & 0x00ff;
if (codec->hw_write(codec->control_data, data, 3) == 3)
return 0;
else
return -EIO;
}
static int wm8961_reset(struct snd_soc_codec *codec)
{
return wm8961_write(codec, WM8961_SOFTWARE_RESET, 0);
}
/*
* The headphone output supports special anti-pop sequences giving
* silent power up and power down.
*/
static int wm8961_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
u16 hp_reg = wm8961_read(codec, WM8961_ANALOGUE_HP_0);
u16 cp_reg = wm8961_read(codec, WM8961_CHARGE_PUMP_1);
u16 pwr_reg = wm8961_read(codec, WM8961_PWR_MGMT_2);
u16 dcs_reg = wm8961_read(codec, WM8961_DC_SERVO_1);
int timeout = 500;
if (event & SND_SOC_DAPM_POST_PMU) {
/* Make sure the output is shorted */
hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
/* Enable the charge pump */
cp_reg |= WM8961_CP_ENA;
wm8961_write(codec, WM8961_CHARGE_PUMP_1, cp_reg);
mdelay(5);
/* Enable the PGA */
pwr_reg |= WM8961_LOUT1_PGA | WM8961_ROUT1_PGA;
wm8961_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
/* Enable the amplifier */
hp_reg |= WM8961_HPR_ENA | WM8961_HPL_ENA;
wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
/* Second stage enable */
hp_reg |= WM8961_HPR_ENA_DLY | WM8961_HPL_ENA_DLY;
wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
/* Enable the DC servo & trigger startup */
dcs_reg |=
WM8961_DCS_ENA_CHAN_HPR | WM8961_DCS_TRIG_STARTUP_HPR |
WM8961_DCS_ENA_CHAN_HPL | WM8961_DCS_TRIG_STARTUP_HPL;
dev_dbg(codec->dev, "Enabling DC servo\n");
wm8961_write(codec, WM8961_DC_SERVO_1, dcs_reg);
do {
msleep(1);
dcs_reg = wm8961_read(codec, WM8961_DC_SERVO_1);
} while (--timeout &&
dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
WM8961_DCS_TRIG_STARTUP_HPL));
if (dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
WM8961_DCS_TRIG_STARTUP_HPL))
dev_err(codec->dev, "DC servo timed out\n");
else
dev_dbg(codec->dev, "DC servo startup complete\n");
/* Enable the output stage */
hp_reg |= WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP;
wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
/* Remove the short on the output stage */
hp_reg |= WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT;
wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
}
if (event & SND_SOC_DAPM_PRE_PMD) {
/* Short the output */
hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
/* Disable the output stage */
hp_reg &= ~(WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP);
wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
/* Disable DC offset cancellation */
dcs_reg &= ~(WM8961_DCS_ENA_CHAN_HPR |
WM8961_DCS_ENA_CHAN_HPL);
wm8961_write(codec, WM8961_DC_SERVO_1, dcs_reg);
/* Finish up */
hp_reg &= ~(WM8961_HPR_ENA_DLY | WM8961_HPR_ENA |
WM8961_HPL_ENA_DLY | WM8961_HPL_ENA);
wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
/* Disable the PGA */
pwr_reg &= ~(WM8961_LOUT1_PGA | WM8961_ROUT1_PGA);
wm8961_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
/* Disable the charge pump */
dev_dbg(codec->dev, "Disabling charge pump\n");
wm8961_write(codec, WM8961_CHARGE_PUMP_1,
cp_reg & ~WM8961_CP_ENA);
}
return 0;
}
static int wm8961_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
u16 pwr_reg = wm8961_read(codec, WM8961_PWR_MGMT_2);
u16 spk_reg = wm8961_read(codec, WM8961_CLASS_D_CONTROL_1);
if (event & SND_SOC_DAPM_POST_PMU) {
/* Enable the PGA */
pwr_reg |= WM8961_SPKL_PGA | WM8961_SPKR_PGA;
wm8961_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
/* Enable the amplifier */
spk_reg |= WM8961_SPKL_ENA | WM8961_SPKR_ENA;
wm8961_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg);
}
if (event & SND_SOC_DAPM_PRE_PMD) {
/* Enable the amplifier */
spk_reg &= ~(WM8961_SPKL_ENA | WM8961_SPKR_ENA);
wm8961_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg);
/* Enable the PGA */
pwr_reg &= ~(WM8961_SPKL_PGA | WM8961_SPKR_PGA);
wm8961_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
}
return 0;
}
static const char *adc_hpf_text[] = {
"Hi-fi", "Voice 1", "Voice 2", "Voice 3",
};
static const struct soc_enum adc_hpf =
SOC_ENUM_SINGLE(WM8961_ADC_DAC_CONTROL_2, 7, 4, adc_hpf_text);
static const char *dac_deemph_text[] = {
"None", "32kHz", "44.1kHz", "48kHz",
};
static const struct soc_enum dac_deemph =
SOC_ENUM_SINGLE(WM8961_ADC_DAC_CONTROL_1, 1, 4, dac_deemph_text);
static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
static const DECLARE_TLV_DB_SCALE(hp_sec_tlv, -700, 100, 0);
static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1);
static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
static unsigned int boost_tlv[] = {
TLV_DB_RANGE_HEAD(4),
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(13, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(20, 0, 0),
3, 3, TLV_DB_SCALE_ITEM(29, 0, 0),
};
static const DECLARE_TLV_DB_SCALE(pga_tlv, -2325, 75, 0);
static const struct snd_kcontrol_new wm8961_snd_controls[] = {
SOC_DOUBLE_R_TLV("Headphone Volume", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME,
0, 127, 0, out_tlv),
SOC_DOUBLE_TLV("Headphone Secondary Volume", WM8961_ANALOGUE_HP_2,
6, 3, 7, 0, hp_sec_tlv),
SOC_DOUBLE_R("Headphone ZC Switch", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME,
7, 1, 0),
SOC_DOUBLE_R_TLV("Speaker Volume", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME,
0, 127, 0, out_tlv),
SOC_DOUBLE_R("Speaker ZC Switch", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME,
7, 1, 0),
SOC_SINGLE("Speaker AC Gain", WM8961_CLASS_D_CONTROL_2, 0, 7, 0),
SOC_SINGLE("DAC x128 OSR Switch", WM8961_ADC_DAC_CONTROL_2, 0, 1, 0),
SOC_ENUM("DAC Deemphasis", dac_deemph),
SOC_SINGLE("DAC Soft Mute Switch", WM8961_ADC_DAC_CONTROL_2, 3, 1, 0),
SOC_DOUBLE_R_TLV("Sidetone Volume", WM8961_DSP_SIDETONE_0,
WM8961_DSP_SIDETONE_1, 4, 12, 0, sidetone_tlv),
SOC_SINGLE("ADC High Pass Filter Switch", WM8961_ADC_DAC_CONTROL_1, 0, 1, 0),
SOC_ENUM("ADC High Pass Filter Mode", adc_hpf),
SOC_DOUBLE_R_TLV("Capture Volume",
WM8961_LEFT_ADC_VOLUME, WM8961_RIGHT_ADC_VOLUME,
1, 119, 0, adc_tlv),
SOC_DOUBLE_R_TLV("Capture Boost Volume",
WM8961_ADCL_SIGNAL_PATH, WM8961_ADCR_SIGNAL_PATH,
4, 3, 0, boost_tlv),
SOC_DOUBLE_R_TLV("Capture PGA Volume",
WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
0, 62, 0, pga_tlv),
SOC_DOUBLE_R("Capture PGA ZC Switch",
WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
6, 1, 1),
SOC_DOUBLE_R("Capture PGA Switch",
WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
7, 1, 1),
};
static const char *sidetone_text[] = {
"None", "Left", "Right"
};
static const struct soc_enum dacl_sidetone =
SOC_ENUM_SINGLE(WM8961_DSP_SIDETONE_0, 2, 3, sidetone_text);
static const struct soc_enum dacr_sidetone =
SOC_ENUM_SINGLE(WM8961_DSP_SIDETONE_1, 2, 3, sidetone_text);
static const struct snd_kcontrol_new dacl_mux =
SOC_DAPM_ENUM("DACL Sidetone", dacl_sidetone);
static const struct snd_kcontrol_new dacr_mux =
SOC_DAPM_ENUM("DACR Sidetone", dacr_sidetone);
static const struct snd_soc_dapm_widget wm8961_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("LINPUT"),
SND_SOC_DAPM_INPUT("RINPUT"),
SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8961_CLOCKING2, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("Left Input", WM8961_PWR_MGMT_1, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right Input", WM8961_PWR_MGMT_1, 4, 0, NULL, 0),
SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", WM8961_PWR_MGMT_1, 3, 0),
SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", WM8961_PWR_MGMT_1, 2, 0),
SND_SOC_DAPM_MICBIAS("MICBIAS", WM8961_PWR_MGMT_1, 1, 0),
SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &dacl_mux),
SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &dacr_mux),
SND_SOC_DAPM_DAC("DACL", "HiFi Playback", WM8961_PWR_MGMT_2, 8, 0),
SND_SOC_DAPM_DAC("DACR", "HiFi Playback", WM8961_PWR_MGMT_2, 7, 0),
/* Handle as a mono path for DCS */
SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM,
4, 0, NULL, 0, wm8961_hp_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_E("Speaker Output", SND_SOC_NOPM,
4, 0, NULL, 0, wm8961_spk_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_OUTPUT("HP_L"),
SND_SOC_DAPM_OUTPUT("HP_R"),
SND_SOC_DAPM_OUTPUT("SPK_LN"),
SND_SOC_DAPM_OUTPUT("SPK_LP"),
SND_SOC_DAPM_OUTPUT("SPK_RN"),
SND_SOC_DAPM_OUTPUT("SPK_RP"),
};
static const struct snd_soc_dapm_route audio_paths[] = {
{ "DACL", NULL, "CLK_DSP" },
{ "DACL", NULL, "DACL Sidetone" },
{ "DACR", NULL, "CLK_DSP" },
{ "DACR", NULL, "DACR Sidetone" },
{ "DACL Sidetone", "Left", "ADCL" },
{ "DACL Sidetone", "Right", "ADCR" },
{ "DACR Sidetone", "Left", "ADCL" },
{ "DACR Sidetone", "Right", "ADCR" },
{ "HP_L", NULL, "Headphone Output" },
{ "HP_R", NULL, "Headphone Output" },
{ "Headphone Output", NULL, "DACL" },
{ "Headphone Output", NULL, "DACR" },
{ "SPK_LN", NULL, "Speaker Output" },
{ "SPK_LP", NULL, "Speaker Output" },
{ "SPK_RN", NULL, "Speaker Output" },
{ "SPK_RP", NULL, "Speaker Output" },
{ "Speaker Output", NULL, "DACL" },
{ "Speaker Output", NULL, "DACR" },
{ "ADCL", NULL, "Left Input" },
{ "ADCL", NULL, "CLK_DSP" },
{ "ADCR", NULL, "Right Input" },
{ "ADCR", NULL, "CLK_DSP" },
{ "Left Input", NULL, "LINPUT" },
{ "Right Input", NULL, "RINPUT" },
};
/* Values for CLK_SYS_RATE */
static struct {
int ratio;
u16 val;
} wm8961_clk_sys_ratio[] = {
{ 64, 0 },
{ 128, 1 },
{ 192, 2 },
{ 256, 3 },
{ 384, 4 },
{ 512, 5 },
{ 768, 6 },
{ 1024, 7 },
{ 1408, 8 },
{ 1536, 9 },
};
/* Values for SAMPLE_RATE */
static struct {
int rate;
u16 val;
} wm8961_srate[] = {
{ 48000, 0 },
{ 44100, 0 },
{ 32000, 1 },
{ 22050, 2 },
{ 24000, 2 },
{ 16000, 3 },
{ 11250, 4 },
{ 12000, 4 },
{ 8000, 5 },
};
static int wm8961_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct wm8961_priv *wm8961 = codec->private_data;
int i, best, target, fs;
u16 reg;
fs = params_rate(params);
if (!wm8961->sysclk) {
dev_err(codec->dev, "MCLK has not been specified\n");
return -EINVAL;
}
/* Find the closest sample rate for the filters */
best = 0;
for (i = 0; i < ARRAY_SIZE(wm8961_srate); i++) {
if (abs(wm8961_srate[i].rate - fs) <
abs(wm8961_srate[best].rate - fs))
best = i;
}
reg = wm8961_read(codec, WM8961_ADDITIONAL_CONTROL_3);
reg &= ~WM8961_SAMPLE_RATE_MASK;
reg |= wm8961_srate[best].val;
wm8961_write(codec, WM8961_ADDITIONAL_CONTROL_3, reg);
dev_dbg(codec->dev, "Selected SRATE %dHz for %dHz\n",
wm8961_srate[best].rate, fs);
/* Select a CLK_SYS/fs ratio equal to or higher than required */
target = wm8961->sysclk / fs;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && target < 64) {
dev_err(codec->dev,
"SYSCLK must be at least 64*fs for DAC\n");
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && target < 256) {
dev_err(codec->dev,
"SYSCLK must be at least 256*fs for ADC\n");
return -EINVAL;
}
for (i = 0; i < ARRAY_SIZE(wm8961_clk_sys_ratio); i++) {
if (wm8961_clk_sys_ratio[i].ratio >= target)
break;
}
if (i == ARRAY_SIZE(wm8961_clk_sys_ratio)) {
dev_err(codec->dev, "Unable to generate CLK_SYS_RATE\n");
return -EINVAL;
}
dev_dbg(codec->dev, "Selected CLK_SYS_RATE of %d for %d/%d=%d\n",
wm8961_clk_sys_ratio[i].ratio, wm8961->sysclk, fs,
wm8961->sysclk / fs);
reg = wm8961_read(codec, WM8961_CLOCKING_4);
reg &= ~WM8961_CLK_SYS_RATE_MASK;
reg |= wm8961_clk_sys_ratio[i].val << WM8961_CLK_SYS_RATE_SHIFT;
wm8961_write(codec, WM8961_CLOCKING_4, reg);
reg = wm8961_read(codec, WM8961_AUDIO_INTERFACE_0);
reg &= ~WM8961_WL_MASK;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
break;
case SNDRV_PCM_FORMAT_S20_3LE:
reg |= 1 << WM8961_WL_SHIFT;
break;
case SNDRV_PCM_FORMAT_S24_LE:
reg |= 2 << WM8961_WL_SHIFT;
break;
case SNDRV_PCM_FORMAT_S32_LE:
reg |= 3 << WM8961_WL_SHIFT;
break;
default:
return -EINVAL;
}
wm8961_write(codec, WM8961_AUDIO_INTERFACE_0, reg);
/* Sloping stop-band filter is recommended for <= 24kHz */
reg = wm8961_read(codec, WM8961_ADC_DAC_CONTROL_2);
if (fs <= 24000)
reg |= WM8961_DACSLOPE;
else
reg &= WM8961_DACSLOPE;
wm8961_write(codec, WM8961_ADC_DAC_CONTROL_2, reg);
return 0;
}
static int wm8961_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq,
int dir)
{
struct snd_soc_codec *codec = dai->codec;
struct wm8961_priv *wm8961 = codec->private_data;
u16 reg = wm8961_read(codec, WM8961_CLOCKING1);
if (freq > 33000000) {
dev_err(codec->dev, "MCLK must be <33MHz\n");
return -EINVAL;
}
if (freq > 16500000) {
dev_dbg(codec->dev, "Using MCLK/2 for %dHz MCLK\n", freq);
reg |= WM8961_MCLKDIV;
freq /= 2;
} else {
dev_dbg(codec->dev, "Using MCLK/1 for %dHz MCLK\n", freq);
reg &= WM8961_MCLKDIV;
}
wm8961_write(codec, WM8961_CLOCKING1, reg);
wm8961->sysclk = freq;
return 0;
}
static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
u16 aif = wm8961_read(codec, WM8961_AUDIO_INTERFACE_0);
aif &= ~(WM8961_BCLKINV | WM8961_LRP |
WM8961_MS | WM8961_FORMAT_MASK);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
aif |= WM8961_MS;
break;
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
break;
case SND_SOC_DAIFMT_LEFT_J:
aif |= 1;
break;
case SND_SOC_DAIFMT_I2S:
aif |= 2;
break;
case SND_SOC_DAIFMT_DSP_B:
aif |= WM8961_LRP;
case SND_SOC_DAIFMT_DSP_A:
aif |= 3;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
case SND_SOC_DAIFMT_IB_NF:
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
aif |= WM8961_LRP;
break;
case SND_SOC_DAIFMT_IB_NF:
aif |= WM8961_BCLKINV;
break;
case SND_SOC_DAIFMT_IB_IF:
aif |= WM8961_BCLKINV | WM8961_LRP;
break;
default:
return -EINVAL;
}
return wm8961_write(codec, WM8961_AUDIO_INTERFACE_0, aif);
}
static int wm8961_set_tristate(struct snd_soc_dai *dai, int tristate)
{
struct snd_soc_codec *codec = dai->codec;
u16 reg = wm8961_read(codec, WM8961_ADDITIONAL_CONTROL_2);
if (tristate)
reg |= WM8961_TRIS;
else
reg &= ~WM8961_TRIS;
return wm8961_write(codec, WM8961_ADDITIONAL_CONTROL_2, reg);
}
static int wm8961_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u16 reg = wm8961_read(codec, WM8961_ADC_DAC_CONTROL_1);
if (mute)
reg |= WM8961_DACMU;
else
reg &= ~WM8961_DACMU;
msleep(17);
return wm8961_write(codec, WM8961_ADC_DAC_CONTROL_1, reg);
}
static int wm8961_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
{
struct snd_soc_codec *codec = dai->codec;
u16 reg;
switch (div_id) {
case WM8961_BCLK:
reg = wm8961_read(codec, WM8961_CLOCKING2);
reg &= ~WM8961_BCLKDIV_MASK;
reg |= div;
wm8961_write(codec, WM8961_CLOCKING2, reg);
break;
case WM8961_LRCLK:
reg = wm8961_read(codec, WM8961_AUDIO_INTERFACE_2);
reg &= ~WM8961_LRCLK_RATE_MASK;
reg |= div;
wm8961_write(codec, WM8961_AUDIO_INTERFACE_2, reg);
break;
default:
return -EINVAL;
}
return 0;
}
static int wm8961_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
u16 reg;
/* This is all slightly unusual since we have no bypass paths
* and the output amplifier structure means we can just slam
* the biases straight up rather than having to ramp them
* slowly.
*/
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
if (codec->bias_level == SND_SOC_BIAS_STANDBY) {
/* Enable bias generation */
reg = wm8961_read(codec, WM8961_ANTI_POP);
reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN;
wm8961_write(codec, WM8961_ANTI_POP, reg);
/* VMID=2*50k, VREF */
reg = wm8961_read(codec, WM8961_PWR_MGMT_1);
reg &= ~WM8961_VMIDSEL_MASK;
reg |= (1 << WM8961_VMIDSEL_SHIFT) | WM8961_VREF;
wm8961_write(codec, WM8961_PWR_MGMT_1, reg);
}
break;
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_PREPARE) {
/* VREF off */
reg = wm8961_read(codec, WM8961_PWR_MGMT_1);
reg &= ~WM8961_VREF;
wm8961_write(codec, WM8961_PWR_MGMT_1, reg);
/* Bias generation off */
reg = wm8961_read(codec, WM8961_ANTI_POP);
reg &= ~(WM8961_BUFIOEN | WM8961_BUFDCOPEN);
wm8961_write(codec, WM8961_ANTI_POP, reg);
/* VMID off */
reg = wm8961_read(codec, WM8961_PWR_MGMT_1);
reg &= ~WM8961_VMIDSEL_MASK;
wm8961_write(codec, WM8961_PWR_MGMT_1, reg);
}
break;
case SND_SOC_BIAS_OFF:
break;
}
codec->bias_level = level;
return 0;
}
#define WM8961_RATES SNDRV_PCM_RATE_8000_48000
#define WM8961_FORMATS \
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_ops wm8961_dai_ops = {
.hw_params = wm8961_hw_params,
.set_sysclk = wm8961_set_sysclk,
.set_fmt = wm8961_set_fmt,
.digital_mute = wm8961_digital_mute,
.set_tristate = wm8961_set_tristate,
.set_clkdiv = wm8961_set_clkdiv,
};
struct snd_soc_dai wm8961_dai = {
.name = "WM8961",
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM8961_RATES,
.formats = WM8961_FORMATS,},
.capture = {
.stream_name = "HiFi Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8961_RATES,
.formats = WM8961_FORMATS,},
.ops = &wm8961_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8961_dai);
static struct snd_soc_codec *wm8961_codec;
static int wm8961_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
if (wm8961_codec == NULL) {
dev_err(&pdev->dev, "Codec device not registered\n");
return -ENODEV;
}
socdev->card->codec = wm8961_codec;
codec = wm8961_codec;
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
dev_err(codec->dev, "failed to create pcms: %d\n", ret);
goto pcm_err;
}
snd_soc_add_controls(codec, wm8961_snd_controls,
ARRAY_SIZE(wm8961_snd_controls));
snd_soc_dapm_new_controls(codec, wm8961_dapm_widgets,
ARRAY_SIZE(wm8961_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
snd_soc_dapm_new_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
static int wm8961_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
return 0;
}
#ifdef CONFIG_PM
static int wm8961_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int wm8961_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
u16 *reg_cache = codec->reg_cache;
int i;
for (i = 0; i < codec->reg_cache_size; i++) {
if (i == WM8961_SOFTWARE_RESET)
continue;
wm8961_write(codec, i, reg_cache[i]);
}
wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
#else
#define wm8961_suspend NULL
#define wm8961_resume NULL
#endif
struct snd_soc_codec_device soc_codec_dev_wm8961 = {
.probe = wm8961_probe,
.remove = wm8961_remove,
.suspend = wm8961_suspend,
.resume = wm8961_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8961);
static int wm8961_register(struct wm8961_priv *wm8961)
{
struct snd_soc_codec *codec = &wm8961->codec;
int ret;
u16 reg;
if (wm8961_codec) {
dev_err(codec->dev, "Another WM8961 is registered\n");
ret = -EINVAL;
goto err;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->private_data = wm8961;
codec->name = "WM8961";
codec->owner = THIS_MODULE;
codec->read = wm8961_read;
codec->write = wm8961_write;
codec->dai = &wm8961_dai;
codec->num_dai = 1;
codec->reg_cache_size = ARRAY_SIZE(wm8961->reg_cache);
codec->reg_cache = &wm8961->reg_cache;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8961_set_bias_level;
memcpy(codec->reg_cache, wm8961_reg_defaults,
sizeof(wm8961_reg_defaults));
reg = wm8961_read_hw(codec, WM8961_SOFTWARE_RESET);
if (reg != 0x1801) {
dev_err(codec->dev, "Device is not a WM8961: ID=0x%x\n", reg);
ret = -EINVAL;
goto err;
}
reg = wm8961_read_hw(codec, WM8961_RIGHT_INPUT_VOLUME);
dev_info(codec->dev, "WM8961 family %d revision %c\n",
(reg & WM8961_DEVICE_ID_MASK) >> WM8961_DEVICE_ID_SHIFT,
((reg & WM8961_CHIP_REV_MASK) >> WM8961_CHIP_REV_SHIFT)
+ 'A');
ret = wm8961_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset\n");
return ret;
}
/* Enable class W */
reg = wm8961_read(codec, WM8961_CHARGE_PUMP_B);
reg |= WM8961_CP_DYN_PWR_MASK;
wm8961_write(codec, WM8961_CHARGE_PUMP_B, reg);
/* Latch volume update bits (right channel only, we always
* write both out) and default ZC on. */
reg = wm8961_read(codec, WM8961_ROUT1_VOLUME);
wm8961_write(codec, WM8961_ROUT1_VOLUME,
reg | WM8961_LO1ZC | WM8961_OUT1VU);
wm8961_write(codec, WM8961_LOUT1_VOLUME, reg | WM8961_LO1ZC);
reg = wm8961_read(codec, WM8961_ROUT2_VOLUME);
wm8961_write(codec, WM8961_ROUT2_VOLUME,
reg | WM8961_SPKRZC | WM8961_SPKVU);
wm8961_write(codec, WM8961_LOUT2_VOLUME, reg | WM8961_SPKLZC);
reg = wm8961_read(codec, WM8961_RIGHT_ADC_VOLUME);
wm8961_write(codec, WM8961_RIGHT_ADC_VOLUME, reg | WM8961_ADCVU);
reg = wm8961_read(codec, WM8961_RIGHT_INPUT_VOLUME);
wm8961_write(codec, WM8961_RIGHT_INPUT_VOLUME, reg | WM8961_IPVU);
/* Use soft mute by default */
reg = wm8961_read(codec, WM8961_ADC_DAC_CONTROL_2);
reg |= WM8961_DACSMM;
wm8961_write(codec, WM8961_ADC_DAC_CONTROL_2, reg);
/* Use automatic clocking mode by default; for now this is all
* we support.
*/
reg = wm8961_read(codec, WM8961_CLOCKING_3);
reg &= ~WM8961_MANUAL_MODE;
wm8961_write(codec, WM8961_CLOCKING_3, reg);
wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm8961_dai.dev = codec->dev;
wm8961_codec = codec;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
return ret;
}
ret = snd_soc_register_dai(&wm8961_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
snd_soc_unregister_codec(codec);
return ret;
}
return 0;
err:
kfree(wm8961);
return ret;
}
static void wm8961_unregister(struct wm8961_priv *wm8961)
{
wm8961_set_bias_level(&wm8961->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dai(&wm8961_dai);
snd_soc_unregister_codec(&wm8961->codec);
kfree(wm8961);
wm8961_codec = NULL;
}
static __devinit int wm8961_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8961_priv *wm8961;
struct snd_soc_codec *codec;
wm8961 = kzalloc(sizeof(struct wm8961_priv), GFP_KERNEL);
if (wm8961 == NULL)
return -ENOMEM;
codec = &wm8961->codec;
codec->hw_write = (hw_write_t)i2c_master_send;
i2c_set_clientdata(i2c, wm8961);
codec->control_data = i2c;
codec->dev = &i2c->dev;
return wm8961_register(wm8961);
}
static __devexit int wm8961_i2c_remove(struct i2c_client *client)
{
struct wm8961_priv *wm8961 = i2c_get_clientdata(client);
wm8961_unregister(wm8961);
return 0;
}
#ifdef CONFIG_PM
static int wm8961_i2c_suspend(struct i2c_client *client)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8961_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8961_i2c_suspend NULL
#define wm8961_i2c_resume NULL
#endif
static const struct i2c_device_id wm8961_i2c_id[] = {
{ "wm8961", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id);
static struct i2c_driver wm8961_i2c_driver = {
.driver = {
.name = "wm8961",
.owner = THIS_MODULE,
},
.probe = wm8961_i2c_probe,
.remove = __devexit_p(wm8961_i2c_remove),
.suspend = wm8961_i2c_suspend,
.resume = wm8961_i2c_resume,
.id_table = wm8961_i2c_id,
};
static int __init wm8961_modinit(void)
{
int ret;
ret = i2c_add_driver(&wm8961_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8961 I2C driver: %d\n",
ret);
}
return ret;
}
module_init(wm8961_modinit);
static void __exit wm8961_exit(void)
{
i2c_del_driver(&wm8961_i2c_driver);
}
module_exit(wm8961_exit);
MODULE_DESCRIPTION("ASoC WM8961 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
/*
* wm8961.h -- WM8961 Soc Audio driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _WM8961_H
#define _WM8961_H
#include <sound/soc.h>
extern struct snd_soc_codec_device soc_codec_dev_wm8961;
extern struct snd_soc_dai wm8961_dai;
#define WM8961_BCLK 1
#define WM8961_LRCLK 2
#define WM8961_BCLK_DIV_1 0
#define WM8961_BCLK_DIV_1_5 1
#define WM8961_BCLK_DIV_2 2
#define WM8961_BCLK_DIV_3 3
#define WM8961_BCLK_DIV_4 4
#define WM8961_BCLK_DIV_5_5 5
#define WM8961_BCLK_DIV_6 6
#define WM8961_BCLK_DIV_8 7
#define WM8961_BCLK_DIV_11 8
#define WM8961_BCLK_DIV_12 9
#define WM8961_BCLK_DIV_16 10
#define WM8961_BCLK_DIV_24 11
#define WM8961_BCLK_DIV_32 13
/*
* Register values.
*/
#define WM8961_LEFT_INPUT_VOLUME 0x00
#define WM8961_RIGHT_INPUT_VOLUME 0x01
#define WM8961_LOUT1_VOLUME 0x02
#define WM8961_ROUT1_VOLUME 0x03
#define WM8961_CLOCKING1 0x04
#define WM8961_ADC_DAC_CONTROL_1 0x05
#define WM8961_ADC_DAC_CONTROL_2 0x06
#define WM8961_AUDIO_INTERFACE_0 0x07
#define WM8961_CLOCKING2 0x08
#define WM8961_AUDIO_INTERFACE_1 0x09
#define WM8961_LEFT_DAC_VOLUME 0x0A
#define WM8961_RIGHT_DAC_VOLUME 0x0B
#define WM8961_AUDIO_INTERFACE_2 0x0E
#define WM8961_SOFTWARE_RESET 0x0F
#define WM8961_ALC1 0x11
#define WM8961_ALC2 0x12
#define WM8961_ALC3 0x13
#define WM8961_NOISE_GATE 0x14
#define WM8961_LEFT_ADC_VOLUME 0x15
#define WM8961_RIGHT_ADC_VOLUME 0x16
#define WM8961_ADDITIONAL_CONTROL_1 0x17
#define WM8961_ADDITIONAL_CONTROL_2 0x18
#define WM8961_PWR_MGMT_1 0x19
#define WM8961_PWR_MGMT_2 0x1A
#define WM8961_ADDITIONAL_CONTROL_3 0x1B
#define WM8961_ANTI_POP 0x1C
#define WM8961_CLOCKING_3 0x1E
#define WM8961_ADCL_SIGNAL_PATH 0x20
#define WM8961_ADCR_SIGNAL_PATH 0x21
#define WM8961_LOUT2_VOLUME 0x28
#define WM8961_ROUT2_VOLUME 0x29
#define WM8961_PWR_MGMT_3 0x2F
#define WM8961_ADDITIONAL_CONTROL_4 0x30
#define WM8961_CLASS_D_CONTROL_1 0x31
#define WM8961_CLASS_D_CONTROL_2 0x33
#define WM8961_CLOCKING_4 0x38
#define WM8961_DSP_SIDETONE_0 0x39
#define WM8961_DSP_SIDETONE_1 0x3A
#define WM8961_DC_SERVO_0 0x3C
#define WM8961_DC_SERVO_1 0x3D
#define WM8961_DC_SERVO_3 0x3F
#define WM8961_DC_SERVO_5 0x41
#define WM8961_ANALOGUE_PGA_BIAS 0x44
#define WM8961_ANALOGUE_HP_0 0x45
#define WM8961_ANALOGUE_HP_2 0x47
#define WM8961_CHARGE_PUMP_1 0x48
#define WM8961_CHARGE_PUMP_B 0x52
#define WM8961_WRITE_SEQUENCER_1 0x57
#define WM8961_WRITE_SEQUENCER_2 0x58
#define WM8961_WRITE_SEQUENCER_3 0x59
#define WM8961_WRITE_SEQUENCER_4 0x5A
#define WM8961_WRITE_SEQUENCER_5 0x5B
#define WM8961_WRITE_SEQUENCER_6 0x5C
#define WM8961_WRITE_SEQUENCER_7 0x5D
#define WM8961_GENERAL_TEST_1 0xFC
/*
* Field Definitions.
*/
/*
* R0 (0x00) - Left Input volume
*/
#define WM8961_IPVU 0x0100 /* IPVU */
#define WM8961_IPVU_MASK 0x0100 /* IPVU */
#define WM8961_IPVU_SHIFT 8 /* IPVU */
#define WM8961_IPVU_WIDTH 1 /* IPVU */
#define WM8961_LINMUTE 0x0080 /* LINMUTE */
#define WM8961_LINMUTE_MASK 0x0080 /* LINMUTE */
#define WM8961_LINMUTE_SHIFT 7 /* LINMUTE */
#define WM8961_LINMUTE_WIDTH 1 /* LINMUTE */
#define WM8961_LIZC 0x0040 /* LIZC */
#define WM8961_LIZC_MASK 0x0040 /* LIZC */
#define WM8961_LIZC_SHIFT 6 /* LIZC */
#define WM8961_LIZC_WIDTH 1 /* LIZC */
#define WM8961_LINVOL_MASK 0x003F /* LINVOL - [5:0] */
#define WM8961_LINVOL_SHIFT 0 /* LINVOL - [5:0] */
#define WM8961_LINVOL_WIDTH 6 /* LINVOL - [5:0] */
/*
* R1 (0x01) - Right Input volume
*/
#define WM8961_DEVICE_ID_MASK 0xF000 /* DEVICE_ID - [15:12] */
#define WM8961_DEVICE_ID_SHIFT 12 /* DEVICE_ID - [15:12] */
#define WM8961_DEVICE_ID_WIDTH 4 /* DEVICE_ID - [15:12] */
#define WM8961_CHIP_REV_MASK 0x0E00 /* CHIP_REV - [11:9] */
#define WM8961_CHIP_REV_SHIFT 9 /* CHIP_REV - [11:9] */
#define WM8961_CHIP_REV_WIDTH 3 /* CHIP_REV - [11:9] */
#define WM8961_IPVU 0x0100 /* IPVU */
#define WM8961_IPVU_MASK 0x0100 /* IPVU */
#define WM8961_IPVU_SHIFT 8 /* IPVU */
#define WM8961_IPVU_WIDTH 1 /* IPVU */
#define WM8961_RINMUTE 0x0080 /* RINMUTE */
#define WM8961_RINMUTE_MASK 0x0080 /* RINMUTE */
#define WM8961_RINMUTE_SHIFT 7 /* RINMUTE */
#define WM8961_RINMUTE_WIDTH 1 /* RINMUTE */
#define WM8961_RIZC 0x0040 /* RIZC */
#define WM8961_RIZC_MASK 0x0040 /* RIZC */
#define WM8961_RIZC_SHIFT 6 /* RIZC */
#define WM8961_RIZC_WIDTH 1 /* RIZC */
#define WM8961_RINVOL_MASK 0x003F /* RINVOL - [5:0] */
#define WM8961_RINVOL_SHIFT 0 /* RINVOL - [5:0] */
#define WM8961_RINVOL_WIDTH 6 /* RINVOL - [5:0] */
/*
* R2 (0x02) - LOUT1 volume
*/
#define WM8961_OUT1VU 0x0100 /* OUT1VU */
#define WM8961_OUT1VU_MASK 0x0100 /* OUT1VU */
#define WM8961_OUT1VU_SHIFT 8 /* OUT1VU */
#define WM8961_OUT1VU_WIDTH 1 /* OUT1VU */
#define WM8961_LO1ZC 0x0080 /* LO1ZC */
#define WM8961_LO1ZC_MASK 0x0080 /* LO1ZC */
#define WM8961_LO1ZC_SHIFT 7 /* LO1ZC */
#define WM8961_LO1ZC_WIDTH 1 /* LO1ZC */
#define WM8961_LOUT1VOL_MASK 0x007F /* LOUT1VOL - [6:0] */
#define WM8961_LOUT1VOL_SHIFT 0 /* LOUT1VOL - [6:0] */
#define WM8961_LOUT1VOL_WIDTH 7 /* LOUT1VOL - [6:0] */
/*
* R3 (0x03) - ROUT1 volume
*/
#define WM8961_OUT1VU 0x0100 /* OUT1VU */
#define WM8961_OUT1VU_MASK 0x0100 /* OUT1VU */
#define WM8961_OUT1VU_SHIFT 8 /* OUT1VU */
#define WM8961_OUT1VU_WIDTH 1 /* OUT1VU */
#define WM8961_RO1ZC 0x0080 /* RO1ZC */
#define WM8961_RO1ZC_MASK 0x0080 /* RO1ZC */
#define WM8961_RO1ZC_SHIFT 7 /* RO1ZC */
#define WM8961_RO1ZC_WIDTH 1 /* RO1ZC */
#define WM8961_ROUT1VOL_MASK 0x007F /* ROUT1VOL - [6:0] */
#define WM8961_ROUT1VOL_SHIFT 0 /* ROUT1VOL - [6:0] */
#define WM8961_ROUT1VOL_WIDTH 7 /* ROUT1VOL - [6:0] */
/*
* R4 (0x04) - Clocking1
*/
#define WM8961_ADCDIV_MASK 0x01C0 /* ADCDIV - [8:6] */
#define WM8961_ADCDIV_SHIFT 6 /* ADCDIV - [8:6] */
#define WM8961_ADCDIV_WIDTH 3 /* ADCDIV - [8:6] */
#define WM8961_DACDIV_MASK 0x0038 /* DACDIV - [5:3] */
#define WM8961_DACDIV_SHIFT 3 /* DACDIV - [5:3] */
#define WM8961_DACDIV_WIDTH 3 /* DACDIV - [5:3] */
#define WM8961_MCLKDIV 0x0004 /* MCLKDIV */
#define WM8961_MCLKDIV_MASK 0x0004 /* MCLKDIV */
#define WM8961_MCLKDIV_SHIFT 2 /* MCLKDIV */
#define WM8961_MCLKDIV_WIDTH 1 /* MCLKDIV */
/*
* R5 (0x05) - ADC & DAC Control 1
*/
#define WM8961_ADCPOL_MASK 0x0060 /* ADCPOL - [6:5] */
#define WM8961_ADCPOL_SHIFT 5 /* ADCPOL - [6:5] */
#define WM8961_ADCPOL_WIDTH 2 /* ADCPOL - [6:5] */
#define WM8961_DACMU 0x0008 /* DACMU */
#define WM8961_DACMU_MASK 0x0008 /* DACMU */
#define WM8961_DACMU_SHIFT 3 /* DACMU */
#define WM8961_DACMU_WIDTH 1 /* DACMU */
#define WM8961_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */
#define WM8961_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */
#define WM8961_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */
#define WM8961_ADCHPD 0x0001 /* ADCHPD */
#define WM8961_ADCHPD_MASK 0x0001 /* ADCHPD */
#define WM8961_ADCHPD_SHIFT 0 /* ADCHPD */
#define WM8961_ADCHPD_WIDTH 1 /* ADCHPD */
/*
* R6 (0x06) - ADC & DAC Control 2
*/
#define WM8961_ADC_HPF_CUT_MASK 0x0180 /* ADC_HPF_CUT - [8:7] */
#define WM8961_ADC_HPF_CUT_SHIFT 7 /* ADC_HPF_CUT - [8:7] */
#define WM8961_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [8:7] */
#define WM8961_DACPOL_MASK 0x0060 /* DACPOL - [6:5] */
#define WM8961_DACPOL_SHIFT 5 /* DACPOL - [6:5] */
#define WM8961_DACPOL_WIDTH 2 /* DACPOL - [6:5] */
#define WM8961_DACSMM 0x0008 /* DACSMM */
#define WM8961_DACSMM_MASK 0x0008 /* DACSMM */
#define WM8961_DACSMM_SHIFT 3 /* DACSMM */
#define WM8961_DACSMM_WIDTH 1 /* DACSMM */
#define WM8961_DACMR 0x0004 /* DACMR */
#define WM8961_DACMR_MASK 0x0004 /* DACMR */
#define WM8961_DACMR_SHIFT 2 /* DACMR */
#define WM8961_DACMR_WIDTH 1 /* DACMR */
#define WM8961_DACSLOPE 0x0002 /* DACSLOPE */
#define WM8961_DACSLOPE_MASK 0x0002 /* DACSLOPE */
#define WM8961_DACSLOPE_SHIFT 1 /* DACSLOPE */
#define WM8961_DACSLOPE_WIDTH 1 /* DACSLOPE */
#define WM8961_DAC_OSR128 0x0001 /* DAC_OSR128 */
#define WM8961_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */
#define WM8961_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */
#define WM8961_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */
/*
* R7 (0x07) - Audio Interface 0
*/
#define WM8961_ALRSWAP 0x0100 /* ALRSWAP */
#define WM8961_ALRSWAP_MASK 0x0100 /* ALRSWAP */
#define WM8961_ALRSWAP_SHIFT 8 /* ALRSWAP */
#define WM8961_ALRSWAP_WIDTH 1 /* ALRSWAP */
#define WM8961_BCLKINV 0x0080 /* BCLKINV */
#define WM8961_BCLKINV_MASK 0x0080 /* BCLKINV */
#define WM8961_BCLKINV_SHIFT 7 /* BCLKINV */
#define WM8961_BCLKINV_WIDTH 1 /* BCLKINV */
#define WM8961_MS 0x0040 /* MS */
#define WM8961_MS_MASK 0x0040 /* MS */
#define WM8961_MS_SHIFT 6 /* MS */
#define WM8961_MS_WIDTH 1 /* MS */
#define WM8961_DLRSWAP 0x0020 /* DLRSWAP */
#define WM8961_DLRSWAP_MASK 0x0020 /* DLRSWAP */
#define WM8961_DLRSWAP_SHIFT 5 /* DLRSWAP */
#define WM8961_DLRSWAP_WIDTH 1 /* DLRSWAP */
#define WM8961_LRP 0x0010 /* LRP */
#define WM8961_LRP_MASK 0x0010 /* LRP */
#define WM8961_LRP_SHIFT 4 /* LRP */
#define WM8961_LRP_WIDTH 1 /* LRP */
#define WM8961_WL_MASK 0x000C /* WL - [3:2] */
#define WM8961_WL_SHIFT 2 /* WL - [3:2] */
#define WM8961_WL_WIDTH 2 /* WL - [3:2] */
#define WM8961_FORMAT_MASK 0x0003 /* FORMAT - [1:0] */
#define WM8961_FORMAT_SHIFT 0 /* FORMAT - [1:0] */
#define WM8961_FORMAT_WIDTH 2 /* FORMAT - [1:0] */
/*
* R8 (0x08) - Clocking2
*/
#define WM8961_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */
#define WM8961_DCLKDIV_SHIFT 6 /* DCLKDIV - [8:6] */
#define WM8961_DCLKDIV_WIDTH 3 /* DCLKDIV - [8:6] */
#define WM8961_CLK_SYS_ENA 0x0020 /* CLK_SYS_ENA */
#define WM8961_CLK_SYS_ENA_MASK 0x0020 /* CLK_SYS_ENA */
#define WM8961_CLK_SYS_ENA_SHIFT 5 /* CLK_SYS_ENA */
#define WM8961_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */
#define WM8961_CLK_DSP_ENA 0x0010 /* CLK_DSP_ENA */
#define WM8961_CLK_DSP_ENA_MASK 0x0010 /* CLK_DSP_ENA */
#define WM8961_CLK_DSP_ENA_SHIFT 4 /* CLK_DSP_ENA */
#define WM8961_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */
#define WM8961_BCLKDIV_MASK 0x000F /* BCLKDIV - [3:0] */
#define WM8961_BCLKDIV_SHIFT 0 /* BCLKDIV - [3:0] */
#define WM8961_BCLKDIV_WIDTH 4 /* BCLKDIV - [3:0] */
/*
* R9 (0x09) - Audio Interface 1
*/
#define WM8961_DACCOMP_MASK 0x0018 /* DACCOMP - [4:3] */
#define WM8961_DACCOMP_SHIFT 3 /* DACCOMP - [4:3] */
#define WM8961_DACCOMP_WIDTH 2 /* DACCOMP - [4:3] */
#define WM8961_ADCCOMP_MASK 0x0006 /* ADCCOMP - [2:1] */
#define WM8961_ADCCOMP_SHIFT 1 /* ADCCOMP - [2:1] */
#define WM8961_ADCCOMP_WIDTH 2 /* ADCCOMP - [2:1] */
#define WM8961_LOOPBACK 0x0001 /* LOOPBACK */
#define WM8961_LOOPBACK_MASK 0x0001 /* LOOPBACK */
#define WM8961_LOOPBACK_SHIFT 0 /* LOOPBACK */
#define WM8961_LOOPBACK_WIDTH 1 /* LOOPBACK */
/*
* R10 (0x0A) - Left DAC volume
*/
#define WM8961_DACVU 0x0100 /* DACVU */
#define WM8961_DACVU_MASK 0x0100 /* DACVU */
#define WM8961_DACVU_SHIFT 8 /* DACVU */
#define WM8961_DACVU_WIDTH 1 /* DACVU */
#define WM8961_LDACVOL_MASK 0x00FF /* LDACVOL - [7:0] */
#define WM8961_LDACVOL_SHIFT 0 /* LDACVOL - [7:0] */
#define WM8961_LDACVOL_WIDTH 8 /* LDACVOL - [7:0] */
/*
* R11 (0x0B) - Right DAC volume
*/
#define WM8961_DACVU 0x0100 /* DACVU */
#define WM8961_DACVU_MASK 0x0100 /* DACVU */
#define WM8961_DACVU_SHIFT 8 /* DACVU */
#define WM8961_DACVU_WIDTH 1 /* DACVU */
#define WM8961_RDACVOL_MASK 0x00FF /* RDACVOL - [7:0] */
#define WM8961_RDACVOL_SHIFT 0 /* RDACVOL - [7:0] */
#define WM8961_RDACVOL_WIDTH 8 /* RDACVOL - [7:0] */
/*
* R14 (0x0E) - Audio Interface 2
*/
#define WM8961_LRCLK_RATE_MASK 0x01FF /* LRCLK_RATE - [8:0] */
#define WM8961_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [8:0] */
#define WM8961_LRCLK_RATE_WIDTH 9 /* LRCLK_RATE - [8:0] */
/*
* R15 (0x0F) - Software Reset
*/
#define WM8961_SW_RST_DEV_ID1_MASK 0xFFFF /* SW_RST_DEV_ID1 - [15:0] */
#define WM8961_SW_RST_DEV_ID1_SHIFT 0 /* SW_RST_DEV_ID1 - [15:0] */
#define WM8961_SW_RST_DEV_ID1_WIDTH 16 /* SW_RST_DEV_ID1 - [15:0] */
/*
* R17 (0x11) - ALC1
*/
#define WM8961_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */
#define WM8961_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */
#define WM8961_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */
#define WM8961_MAXGAIN_MASK 0x0070 /* MAXGAIN - [6:4] */
#define WM8961_MAXGAIN_SHIFT 4 /* MAXGAIN - [6:4] */
#define WM8961_MAXGAIN_WIDTH 3 /* MAXGAIN - [6:4] */
#define WM8961_ALCL_MASK 0x000F /* ALCL - [3:0] */
#define WM8961_ALCL_SHIFT 0 /* ALCL - [3:0] */
#define WM8961_ALCL_WIDTH 4 /* ALCL - [3:0] */
/*
* R18 (0x12) - ALC2
*/
#define WM8961_ALCZC 0x0080 /* ALCZC */
#define WM8961_ALCZC_MASK 0x0080 /* ALCZC */
#define WM8961_ALCZC_SHIFT 7 /* ALCZC */
#define WM8961_ALCZC_WIDTH 1 /* ALCZC */
#define WM8961_MINGAIN_MASK 0x0070 /* MINGAIN - [6:4] */
#define WM8961_MINGAIN_SHIFT 4 /* MINGAIN - [6:4] */
#define WM8961_MINGAIN_WIDTH 3 /* MINGAIN - [6:4] */
#define WM8961_HLD_MASK 0x000F /* HLD - [3:0] */
#define WM8961_HLD_SHIFT 0 /* HLD - [3:0] */
#define WM8961_HLD_WIDTH 4 /* HLD - [3:0] */
/*
* R19 (0x13) - ALC3
*/
#define WM8961_ALCMODE 0x0100 /* ALCMODE */
#define WM8961_ALCMODE_MASK 0x0100 /* ALCMODE */
#define WM8961_ALCMODE_SHIFT 8 /* ALCMODE */
#define WM8961_ALCMODE_WIDTH 1 /* ALCMODE */
#define WM8961_DCY_MASK 0x00F0 /* DCY - [7:4] */
#define WM8961_DCY_SHIFT 4 /* DCY - [7:4] */
#define WM8961_DCY_WIDTH 4 /* DCY - [7:4] */
#define WM8961_ATK_MASK 0x000F /* ATK - [3:0] */
#define WM8961_ATK_SHIFT 0 /* ATK - [3:0] */
#define WM8961_ATK_WIDTH 4 /* ATK - [3:0] */
/*
* R20 (0x14) - Noise Gate
*/
#define WM8961_NGTH_MASK 0x00F8 /* NGTH - [7:3] */
#define WM8961_NGTH_SHIFT 3 /* NGTH - [7:3] */
#define WM8961_NGTH_WIDTH 5 /* NGTH - [7:3] */
#define WM8961_NGG 0x0002 /* NGG */
#define WM8961_NGG_MASK 0x0002 /* NGG */
#define WM8961_NGG_SHIFT 1 /* NGG */
#define WM8961_NGG_WIDTH 1 /* NGG */
#define WM8961_NGAT 0x0001 /* NGAT */
#define WM8961_NGAT_MASK 0x0001 /* NGAT */
#define WM8961_NGAT_SHIFT 0 /* NGAT */
#define WM8961_NGAT_WIDTH 1 /* NGAT */
/*
* R21 (0x15) - Left ADC volume
*/
#define WM8961_ADCVU 0x0100 /* ADCVU */
#define WM8961_ADCVU_MASK 0x0100 /* ADCVU */
#define WM8961_ADCVU_SHIFT 8 /* ADCVU */
#define WM8961_ADCVU_WIDTH 1 /* ADCVU */
#define WM8961_LADCVOL_MASK 0x00FF /* LADCVOL - [7:0] */
#define WM8961_LADCVOL_SHIFT 0 /* LADCVOL - [7:0] */
#define WM8961_LADCVOL_WIDTH 8 /* LADCVOL - [7:0] */
/*
* R22 (0x16) - Right ADC volume
*/
#define WM8961_ADCVU 0x0100 /* ADCVU */
#define WM8961_ADCVU_MASK 0x0100 /* ADCVU */
#define WM8961_ADCVU_SHIFT 8 /* ADCVU */
#define WM8961_ADCVU_WIDTH 1 /* ADCVU */
#define WM8961_RADCVOL_MASK 0x00FF /* RADCVOL - [7:0] */
#define WM8961_RADCVOL_SHIFT 0 /* RADCVOL - [7:0] */
#define WM8961_RADCVOL_WIDTH 8 /* RADCVOL - [7:0] */
/*
* R23 (0x17) - Additional control(1)
*/
#define WM8961_TSDEN 0x0100 /* TSDEN */
#define WM8961_TSDEN_MASK 0x0100 /* TSDEN */
#define WM8961_TSDEN_SHIFT 8 /* TSDEN */
#define WM8961_TSDEN_WIDTH 1 /* TSDEN */
#define WM8961_DMONOMIX 0x0010 /* DMONOMIX */
#define WM8961_DMONOMIX_MASK 0x0010 /* DMONOMIX */
#define WM8961_DMONOMIX_SHIFT 4 /* DMONOMIX */
#define WM8961_DMONOMIX_WIDTH 1 /* DMONOMIX */
#define WM8961_TOEN 0x0001 /* TOEN */
#define WM8961_TOEN_MASK 0x0001 /* TOEN */
#define WM8961_TOEN_SHIFT 0 /* TOEN */
#define WM8961_TOEN_WIDTH 1 /* TOEN */
/*
* R24 (0x18) - Additional control(2)
*/
#define WM8961_TRIS 0x0008 /* TRIS */
#define WM8961_TRIS_MASK 0x0008 /* TRIS */
#define WM8961_TRIS_SHIFT 3 /* TRIS */
#define WM8961_TRIS_WIDTH 1 /* TRIS */
/*
* R25 (0x19) - Pwr Mgmt (1)
*/
#define WM8961_VMIDSEL_MASK 0x0180 /* VMIDSEL - [8:7] */
#define WM8961_VMIDSEL_SHIFT 7 /* VMIDSEL - [8:7] */
#define WM8961_VMIDSEL_WIDTH 2 /* VMIDSEL - [8:7] */
#define WM8961_VREF 0x0040 /* VREF */
#define WM8961_VREF_MASK 0x0040 /* VREF */
#define WM8961_VREF_SHIFT 6 /* VREF */
#define WM8961_VREF_WIDTH 1 /* VREF */
#define WM8961_AINL 0x0020 /* AINL */
#define WM8961_AINL_MASK 0x0020 /* AINL */
#define WM8961_AINL_SHIFT 5 /* AINL */
#define WM8961_AINL_WIDTH 1 /* AINL */
#define WM8961_AINR 0x0010 /* AINR */
#define WM8961_AINR_MASK 0x0010 /* AINR */
#define WM8961_AINR_SHIFT 4 /* AINR */
#define WM8961_AINR_WIDTH 1 /* AINR */
#define WM8961_ADCL 0x0008 /* ADCL */
#define WM8961_ADCL_MASK 0x0008 /* ADCL */
#define WM8961_ADCL_SHIFT 3 /* ADCL */
#define WM8961_ADCL_WIDTH 1 /* ADCL */
#define WM8961_ADCR 0x0004 /* ADCR */
#define WM8961_ADCR_MASK 0x0004 /* ADCR */
#define WM8961_ADCR_SHIFT 2 /* ADCR */
#define WM8961_ADCR_WIDTH 1 /* ADCR */
#define WM8961_MICB 0x0002 /* MICB */
#define WM8961_MICB_MASK 0x0002 /* MICB */
#define WM8961_MICB_SHIFT 1 /* MICB */
#define WM8961_MICB_WIDTH 1 /* MICB */
/*
* R26 (0x1A) - Pwr Mgmt (2)
*/
#define WM8961_DACL 0x0100 /* DACL */
#define WM8961_DACL_MASK 0x0100 /* DACL */
#define WM8961_DACL_SHIFT 8 /* DACL */
#define WM8961_DACL_WIDTH 1 /* DACL */
#define WM8961_DACR 0x0080 /* DACR */
#define WM8961_DACR_MASK 0x0080 /* DACR */
#define WM8961_DACR_SHIFT 7 /* DACR */
#define WM8961_DACR_WIDTH 1 /* DACR */
#define WM8961_LOUT1_PGA 0x0040 /* LOUT1_PGA */
#define WM8961_LOUT1_PGA_MASK 0x0040 /* LOUT1_PGA */
#define WM8961_LOUT1_PGA_SHIFT 6 /* LOUT1_PGA */
#define WM8961_LOUT1_PGA_WIDTH 1 /* LOUT1_PGA */
#define WM8961_ROUT1_PGA 0x0020 /* ROUT1_PGA */
#define WM8961_ROUT1_PGA_MASK 0x0020 /* ROUT1_PGA */
#define WM8961_ROUT1_PGA_SHIFT 5 /* ROUT1_PGA */
#define WM8961_ROUT1_PGA_WIDTH 1 /* ROUT1_PGA */
#define WM8961_SPKL_PGA 0x0010 /* SPKL_PGA */
#define WM8961_SPKL_PGA_MASK 0x0010 /* SPKL_PGA */
#define WM8961_SPKL_PGA_SHIFT 4 /* SPKL_PGA */
#define WM8961_SPKL_PGA_WIDTH 1 /* SPKL_PGA */
#define WM8961_SPKR_PGA 0x0008 /* SPKR_PGA */
#define WM8961_SPKR_PGA_MASK 0x0008 /* SPKR_PGA */
#define WM8961_SPKR_PGA_SHIFT 3 /* SPKR_PGA */
#define WM8961_SPKR_PGA_WIDTH 1 /* SPKR_PGA */
/*
* R27 (0x1B) - Additional Control (3)
*/
#define WM8961_SAMPLE_RATE_MASK 0x0007 /* SAMPLE_RATE - [2:0] */
#define WM8961_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [2:0] */
#define WM8961_SAMPLE_RATE_WIDTH 3 /* SAMPLE_RATE - [2:0] */
/*
* R28 (0x1C) - Anti-pop
*/
#define WM8961_BUFDCOPEN 0x0010 /* BUFDCOPEN */
#define WM8961_BUFDCOPEN_MASK 0x0010 /* BUFDCOPEN */
#define WM8961_BUFDCOPEN_SHIFT 4 /* BUFDCOPEN */
#define WM8961_BUFDCOPEN_WIDTH 1 /* BUFDCOPEN */
#define WM8961_BUFIOEN 0x0008 /* BUFIOEN */
#define WM8961_BUFIOEN_MASK 0x0008 /* BUFIOEN */
#define WM8961_BUFIOEN_SHIFT 3 /* BUFIOEN */
#define WM8961_BUFIOEN_WIDTH 1 /* BUFIOEN */
#define WM8961_SOFT_ST 0x0004 /* SOFT_ST */
#define WM8961_SOFT_ST_MASK 0x0004 /* SOFT_ST */
#define WM8961_SOFT_ST_SHIFT 2 /* SOFT_ST */
#define WM8961_SOFT_ST_WIDTH 1 /* SOFT_ST */
/*
* R30 (0x1E) - Clocking 3
*/
#define WM8961_CLK_TO_DIV_MASK 0x0180 /* CLK_TO_DIV - [8:7] */
#define WM8961_CLK_TO_DIV_SHIFT 7 /* CLK_TO_DIV - [8:7] */
#define WM8961_CLK_TO_DIV_WIDTH 2 /* CLK_TO_DIV - [8:7] */
#define WM8961_CLK_256K_DIV_MASK 0x007E /* CLK_256K_DIV - [6:1] */
#define WM8961_CLK_256K_DIV_SHIFT 1 /* CLK_256K_DIV - [6:1] */
#define WM8961_CLK_256K_DIV_WIDTH 6 /* CLK_256K_DIV - [6:1] */
#define WM8961_MANUAL_MODE 0x0001 /* MANUAL_MODE */
#define WM8961_MANUAL_MODE_MASK 0x0001 /* MANUAL_MODE */
#define WM8961_MANUAL_MODE_SHIFT 0 /* MANUAL_MODE */
#define WM8961_MANUAL_MODE_WIDTH 1 /* MANUAL_MODE */
/*
* R32 (0x20) - ADCL signal path
*/
#define WM8961_LMICBOOST_MASK 0x0030 /* LMICBOOST - [5:4] */
#define WM8961_LMICBOOST_SHIFT 4 /* LMICBOOST - [5:4] */
#define WM8961_LMICBOOST_WIDTH 2 /* LMICBOOST - [5:4] */
/*
* R33 (0x21) - ADCR signal path
*/
#define WM8961_RMICBOOST_MASK 0x0030 /* RMICBOOST - [5:4] */
#define WM8961_RMICBOOST_SHIFT 4 /* RMICBOOST - [5:4] */
#define WM8961_RMICBOOST_WIDTH 2 /* RMICBOOST - [5:4] */
/*
* R40 (0x28) - LOUT2 volume
*/
#define WM8961_SPKVU 0x0100 /* SPKVU */
#define WM8961_SPKVU_MASK 0x0100 /* SPKVU */
#define WM8961_SPKVU_SHIFT 8 /* SPKVU */
#define WM8961_SPKVU_WIDTH 1 /* SPKVU */
#define WM8961_SPKLZC 0x0080 /* SPKLZC */
#define WM8961_SPKLZC_MASK 0x0080 /* SPKLZC */
#define WM8961_SPKLZC_SHIFT 7 /* SPKLZC */
#define WM8961_SPKLZC_WIDTH 1 /* SPKLZC */
#define WM8961_SPKLVOL_MASK 0x007F /* SPKLVOL - [6:0] */
#define WM8961_SPKLVOL_SHIFT 0 /* SPKLVOL - [6:0] */
#define WM8961_SPKLVOL_WIDTH 7 /* SPKLVOL - [6:0] */
/*
* R41 (0x29) - ROUT2 volume
*/
#define WM8961_SPKVU 0x0100 /* SPKVU */
#define WM8961_SPKVU_MASK 0x0100 /* SPKVU */
#define WM8961_SPKVU_SHIFT 8 /* SPKVU */
#define WM8961_SPKVU_WIDTH 1 /* SPKVU */
#define WM8961_SPKRZC 0x0080 /* SPKRZC */
#define WM8961_SPKRZC_MASK 0x0080 /* SPKRZC */
#define WM8961_SPKRZC_SHIFT 7 /* SPKRZC */
#define WM8961_SPKRZC_WIDTH 1 /* SPKRZC */
#define WM8961_SPKRVOL_MASK 0x007F /* SPKRVOL - [6:0] */
#define WM8961_SPKRVOL_SHIFT 0 /* SPKRVOL - [6:0] */
#define WM8961_SPKRVOL_WIDTH 7 /* SPKRVOL - [6:0] */
/*
* R47 (0x2F) - Pwr Mgmt (3)
*/
#define WM8961_TEMP_SHUT 0x0002 /* TEMP_SHUT */
#define WM8961_TEMP_SHUT_MASK 0x0002 /* TEMP_SHUT */
#define WM8961_TEMP_SHUT_SHIFT 1 /* TEMP_SHUT */
#define WM8961_TEMP_SHUT_WIDTH 1 /* TEMP_SHUT */
#define WM8961_TEMP_WARN 0x0001 /* TEMP_WARN */
#define WM8961_TEMP_WARN_MASK 0x0001 /* TEMP_WARN */
#define WM8961_TEMP_WARN_SHIFT 0 /* TEMP_WARN */
#define WM8961_TEMP_WARN_WIDTH 1 /* TEMP_WARN */
/*
* R48 (0x30) - Additional Control (4)
*/
#define WM8961_TSENSEN 0x0002 /* TSENSEN */
#define WM8961_TSENSEN_MASK 0x0002 /* TSENSEN */
#define WM8961_TSENSEN_SHIFT 1 /* TSENSEN */
#define WM8961_TSENSEN_WIDTH 1 /* TSENSEN */
#define WM8961_MBSEL 0x0001 /* MBSEL */
#define WM8961_MBSEL_MASK 0x0001 /* MBSEL */
#define WM8961_MBSEL_SHIFT 0 /* MBSEL */
#define WM8961_MBSEL_WIDTH 1 /* MBSEL */
/*
* R49 (0x31) - Class D Control 1
*/
#define WM8961_SPKR_ENA 0x0080 /* SPKR_ENA */
#define WM8961_SPKR_ENA_MASK 0x0080 /* SPKR_ENA */
#define WM8961_SPKR_ENA_SHIFT 7 /* SPKR_ENA */
#define WM8961_SPKR_ENA_WIDTH 1 /* SPKR_ENA */
#define WM8961_SPKL_ENA 0x0040 /* SPKL_ENA */
#define WM8961_SPKL_ENA_MASK 0x0040 /* SPKL_ENA */
#define WM8961_SPKL_ENA_SHIFT 6 /* SPKL_ENA */
#define WM8961_SPKL_ENA_WIDTH 1 /* SPKL_ENA */
/*
* R51 (0x33) - Class D Control 2
*/
#define WM8961_CLASSD_ACGAIN_MASK 0x0007 /* CLASSD_ACGAIN - [2:0] */
#define WM8961_CLASSD_ACGAIN_SHIFT 0 /* CLASSD_ACGAIN - [2:0] */
#define WM8961_CLASSD_ACGAIN_WIDTH 3 /* CLASSD_ACGAIN - [2:0] */
/*
* R56 (0x38) - Clocking 4
*/
#define WM8961_CLK_DCS_DIV_MASK 0x01E0 /* CLK_DCS_DIV - [8:5] */
#define WM8961_CLK_DCS_DIV_SHIFT 5 /* CLK_DCS_DIV - [8:5] */
#define WM8961_CLK_DCS_DIV_WIDTH 4 /* CLK_DCS_DIV - [8:5] */
#define WM8961_CLK_SYS_RATE_MASK 0x001E /* CLK_SYS_RATE - [4:1] */
#define WM8961_CLK_SYS_RATE_SHIFT 1 /* CLK_SYS_RATE - [4:1] */
#define WM8961_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [4:1] */
/*
* R57 (0x39) - DSP Sidetone 0
*/
#define WM8961_ADCR_DAC_SVOL_MASK 0x00F0 /* ADCR_DAC_SVOL - [7:4] */
#define WM8961_ADCR_DAC_SVOL_SHIFT 4 /* ADCR_DAC_SVOL - [7:4] */
#define WM8961_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [7:4] */
#define WM8961_ADC_TO_DACR_MASK 0x000C /* ADC_TO_DACR - [3:2] */
#define WM8961_ADC_TO_DACR_SHIFT 2 /* ADC_TO_DACR - [3:2] */
#define WM8961_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [3:2] */
/*
* R58 (0x3A) - DSP Sidetone 1
*/
#define WM8961_ADCL_DAC_SVOL_MASK 0x00F0 /* ADCL_DAC_SVOL - [7:4] */
#define WM8961_ADCL_DAC_SVOL_SHIFT 4 /* ADCL_DAC_SVOL - [7:4] */
#define WM8961_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [7:4] */
#define WM8961_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */
#define WM8961_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */
#define WM8961_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */
/*
* R60 (0x3C) - DC Servo 0
*/
#define WM8961_DCS_ENA_CHAN_INL 0x0080 /* DCS_ENA_CHAN_INL */
#define WM8961_DCS_ENA_CHAN_INL_MASK 0x0080 /* DCS_ENA_CHAN_INL */
#define WM8961_DCS_ENA_CHAN_INL_SHIFT 7 /* DCS_ENA_CHAN_INL */
#define WM8961_DCS_ENA_CHAN_INL_WIDTH 1 /* DCS_ENA_CHAN_INL */
#define WM8961_DCS_TRIG_STARTUP_INL 0x0040 /* DCS_TRIG_STARTUP_INL */
#define WM8961_DCS_TRIG_STARTUP_INL_MASK 0x0040 /* DCS_TRIG_STARTUP_INL */
#define WM8961_DCS_TRIG_STARTUP_INL_SHIFT 6 /* DCS_TRIG_STARTUP_INL */
#define WM8961_DCS_TRIG_STARTUP_INL_WIDTH 1 /* DCS_TRIG_STARTUP_INL */
#define WM8961_DCS_TRIG_SERIES_INL 0x0010 /* DCS_TRIG_SERIES_INL */
#define WM8961_DCS_TRIG_SERIES_INL_MASK 0x0010 /* DCS_TRIG_SERIES_INL */
#define WM8961_DCS_TRIG_SERIES_INL_SHIFT 4 /* DCS_TRIG_SERIES_INL */
#define WM8961_DCS_TRIG_SERIES_INL_WIDTH 1 /* DCS_TRIG_SERIES_INL */
#define WM8961_DCS_ENA_CHAN_INR 0x0008 /* DCS_ENA_CHAN_INR */
#define WM8961_DCS_ENA_CHAN_INR_MASK 0x0008 /* DCS_ENA_CHAN_INR */
#define WM8961_DCS_ENA_CHAN_INR_SHIFT 3 /* DCS_ENA_CHAN_INR */
#define WM8961_DCS_ENA_CHAN_INR_WIDTH 1 /* DCS_ENA_CHAN_INR */
#define WM8961_DCS_TRIG_STARTUP_INR 0x0004 /* DCS_TRIG_STARTUP_INR */
#define WM8961_DCS_TRIG_STARTUP_INR_MASK 0x0004 /* DCS_TRIG_STARTUP_INR */
#define WM8961_DCS_TRIG_STARTUP_INR_SHIFT 2 /* DCS_TRIG_STARTUP_INR */
#define WM8961_DCS_TRIG_STARTUP_INR_WIDTH 1 /* DCS_TRIG_STARTUP_INR */
#define WM8961_DCS_TRIG_SERIES_INR 0x0001 /* DCS_TRIG_SERIES_INR */
#define WM8961_DCS_TRIG_SERIES_INR_MASK 0x0001 /* DCS_TRIG_SERIES_INR */
#define WM8961_DCS_TRIG_SERIES_INR_SHIFT 0 /* DCS_TRIG_SERIES_INR */
#define WM8961_DCS_TRIG_SERIES_INR_WIDTH 1 /* DCS_TRIG_SERIES_INR */
/*
* R61 (0x3D) - DC Servo 1
*/
#define WM8961_DCS_ENA_CHAN_HPL 0x0080 /* DCS_ENA_CHAN_HPL */
#define WM8961_DCS_ENA_CHAN_HPL_MASK 0x0080 /* DCS_ENA_CHAN_HPL */
#define WM8961_DCS_ENA_CHAN_HPL_SHIFT 7 /* DCS_ENA_CHAN_HPL */
#define WM8961_DCS_ENA_CHAN_HPL_WIDTH 1 /* DCS_ENA_CHAN_HPL */
#define WM8961_DCS_TRIG_STARTUP_HPL 0x0040 /* DCS_TRIG_STARTUP_HPL */
#define WM8961_DCS_TRIG_STARTUP_HPL_MASK 0x0040 /* DCS_TRIG_STARTUP_HPL */
#define WM8961_DCS_TRIG_STARTUP_HPL_SHIFT 6 /* DCS_TRIG_STARTUP_HPL */
#define WM8961_DCS_TRIG_STARTUP_HPL_WIDTH 1 /* DCS_TRIG_STARTUP_HPL */
#define WM8961_DCS_TRIG_SERIES_HPL 0x0010 /* DCS_TRIG_SERIES_HPL */
#define WM8961_DCS_TRIG_SERIES_HPL_MASK 0x0010 /* DCS_TRIG_SERIES_HPL */
#define WM8961_DCS_TRIG_SERIES_HPL_SHIFT 4 /* DCS_TRIG_SERIES_HPL */
#define WM8961_DCS_TRIG_SERIES_HPL_WIDTH 1 /* DCS_TRIG_SERIES_HPL */
#define WM8961_DCS_ENA_CHAN_HPR 0x0008 /* DCS_ENA_CHAN_HPR */
#define WM8961_DCS_ENA_CHAN_HPR_MASK 0x0008 /* DCS_ENA_CHAN_HPR */
#define WM8961_DCS_ENA_CHAN_HPR_SHIFT 3 /* DCS_ENA_CHAN_HPR */
#define WM8961_DCS_ENA_CHAN_HPR_WIDTH 1 /* DCS_ENA_CHAN_HPR */
#define WM8961_DCS_TRIG_STARTUP_HPR 0x0004 /* DCS_TRIG_STARTUP_HPR */
#define WM8961_DCS_TRIG_STARTUP_HPR_MASK 0x0004 /* DCS_TRIG_STARTUP_HPR */
#define WM8961_DCS_TRIG_STARTUP_HPR_SHIFT 2 /* DCS_TRIG_STARTUP_HPR */
#define WM8961_DCS_TRIG_STARTUP_HPR_WIDTH 1 /* DCS_TRIG_STARTUP_HPR */
#define WM8961_DCS_TRIG_SERIES_HPR 0x0001 /* DCS_TRIG_SERIES_HPR */
#define WM8961_DCS_TRIG_SERIES_HPR_MASK 0x0001 /* DCS_TRIG_SERIES_HPR */
#define WM8961_DCS_TRIG_SERIES_HPR_SHIFT 0 /* DCS_TRIG_SERIES_HPR */
#define WM8961_DCS_TRIG_SERIES_HPR_WIDTH 1 /* DCS_TRIG_SERIES_HPR */
/*
* R63 (0x3F) - DC Servo 3
*/
#define WM8961_DCS_FILT_BW_SERIES_MASK 0x0030 /* DCS_FILT_BW_SERIES - [5:4] */
#define WM8961_DCS_FILT_BW_SERIES_SHIFT 4 /* DCS_FILT_BW_SERIES - [5:4] */
#define WM8961_DCS_FILT_BW_SERIES_WIDTH 2 /* DCS_FILT_BW_SERIES - [5:4] */
/*
* R65 (0x41) - DC Servo 5
*/
#define WM8961_DCS_SERIES_NO_HP_MASK 0x007F /* DCS_SERIES_NO_HP - [6:0] */
#define WM8961_DCS_SERIES_NO_HP_SHIFT 0 /* DCS_SERIES_NO_HP - [6:0] */
#define WM8961_DCS_SERIES_NO_HP_WIDTH 7 /* DCS_SERIES_NO_HP - [6:0] */
/*
* R68 (0x44) - Analogue PGA Bias
*/
#define WM8961_HP_PGAS_BIAS_MASK 0x0007 /* HP_PGAS_BIAS - [2:0] */
#define WM8961_HP_PGAS_BIAS_SHIFT 0 /* HP_PGAS_BIAS - [2:0] */
#define WM8961_HP_PGAS_BIAS_WIDTH 3 /* HP_PGAS_BIAS - [2:0] */
/*
* R69 (0x45) - Analogue HP 0
*/
#define WM8961_HPL_RMV_SHORT 0x0080 /* HPL_RMV_SHORT */
#define WM8961_HPL_RMV_SHORT_MASK 0x0080 /* HPL_RMV_SHORT */
#define WM8961_HPL_RMV_SHORT_SHIFT 7 /* HPL_RMV_SHORT */
#define WM8961_HPL_RMV_SHORT_WIDTH 1 /* HPL_RMV_SHORT */
#define WM8961_HPL_ENA_OUTP 0x0040 /* HPL_ENA_OUTP */
#define WM8961_HPL_ENA_OUTP_MASK 0x0040 /* HPL_ENA_OUTP */
#define WM8961_HPL_ENA_OUTP_SHIFT 6 /* HPL_ENA_OUTP */
#define WM8961_HPL_ENA_OUTP_WIDTH 1 /* HPL_ENA_OUTP */
#define WM8961_HPL_ENA_DLY 0x0020 /* HPL_ENA_DLY */
#define WM8961_HPL_ENA_DLY_MASK 0x0020 /* HPL_ENA_DLY */
#define WM8961_HPL_ENA_DLY_SHIFT 5 /* HPL_ENA_DLY */
#define WM8961_HPL_ENA_DLY_WIDTH 1 /* HPL_ENA_DLY */
#define WM8961_HPL_ENA 0x0010 /* HPL_ENA */
#define WM8961_HPL_ENA_MASK 0x0010 /* HPL_ENA */
#define WM8961_HPL_ENA_SHIFT 4 /* HPL_ENA */
#define WM8961_HPL_ENA_WIDTH 1 /* HPL_ENA */
#define WM8961_HPR_RMV_SHORT 0x0008 /* HPR_RMV_SHORT */
#define WM8961_HPR_RMV_SHORT_MASK 0x0008 /* HPR_RMV_SHORT */
#define WM8961_HPR_RMV_SHORT_SHIFT 3 /* HPR_RMV_SHORT */
#define WM8961_HPR_RMV_SHORT_WIDTH 1 /* HPR_RMV_SHORT */
#define WM8961_HPR_ENA_OUTP 0x0004 /* HPR_ENA_OUTP */
#define WM8961_HPR_ENA_OUTP_MASK 0x0004 /* HPR_ENA_OUTP */
#define WM8961_HPR_ENA_OUTP_SHIFT 2 /* HPR_ENA_OUTP */
#define WM8961_HPR_ENA_OUTP_WIDTH 1 /* HPR_ENA_OUTP */
#define WM8961_HPR_ENA_DLY 0x0002 /* HPR_ENA_DLY */
#define WM8961_HPR_ENA_DLY_MASK 0x0002 /* HPR_ENA_DLY */
#define WM8961_HPR_ENA_DLY_SHIFT 1 /* HPR_ENA_DLY */
#define WM8961_HPR_ENA_DLY_WIDTH 1 /* HPR_ENA_DLY */
#define WM8961_HPR_ENA 0x0001 /* HPR_ENA */
#define WM8961_HPR_ENA_MASK 0x0001 /* HPR_ENA */
#define WM8961_HPR_ENA_SHIFT 0 /* HPR_ENA */
#define WM8961_HPR_ENA_WIDTH 1 /* HPR_ENA */
/*
* R71 (0x47) - Analogue HP 2
*/
#define WM8961_HPL_VOL_MASK 0x01C0 /* HPL_VOL - [8:6] */
#define WM8961_HPL_VOL_SHIFT 6 /* HPL_VOL - [8:6] */
#define WM8961_HPL_VOL_WIDTH 3 /* HPL_VOL - [8:6] */
#define WM8961_HPR_VOL_MASK 0x0038 /* HPR_VOL - [5:3] */
#define WM8961_HPR_VOL_SHIFT 3 /* HPR_VOL - [5:3] */
#define WM8961_HPR_VOL_WIDTH 3 /* HPR_VOL - [5:3] */
#define WM8961_HP_BIAS_BOOST_MASK 0x0007 /* HP_BIAS_BOOST - [2:0] */
#define WM8961_HP_BIAS_BOOST_SHIFT 0 /* HP_BIAS_BOOST - [2:0] */
#define WM8961_HP_BIAS_BOOST_WIDTH 3 /* HP_BIAS_BOOST - [2:0] */
/*
* R72 (0x48) - Charge Pump 1
*/
#define WM8961_CP_ENA 0x0001 /* CP_ENA */
#define WM8961_CP_ENA_MASK 0x0001 /* CP_ENA */
#define WM8961_CP_ENA_SHIFT 0 /* CP_ENA */
#define WM8961_CP_ENA_WIDTH 1 /* CP_ENA */
/*
* R82 (0x52) - Charge Pump B
*/
#define WM8961_CP_DYN_PWR_MASK 0x0003 /* CP_DYN_PWR - [1:0] */
#define WM8961_CP_DYN_PWR_SHIFT 0 /* CP_DYN_PWR - [1:0] */
#define WM8961_CP_DYN_PWR_WIDTH 2 /* CP_DYN_PWR - [1:0] */
/*
* R87 (0x57) - Write Sequencer 1
*/
#define WM8961_WSEQ_ENA 0x0020 /* WSEQ_ENA */
#define WM8961_WSEQ_ENA_MASK 0x0020 /* WSEQ_ENA */
#define WM8961_WSEQ_ENA_SHIFT 5 /* WSEQ_ENA */
#define WM8961_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
#define WM8961_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */
#define WM8961_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */
#define WM8961_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */
/*
* R88 (0x58) - Write Sequencer 2
*/
#define WM8961_WSEQ_EOS 0x0100 /* WSEQ_EOS */
#define WM8961_WSEQ_EOS_MASK 0x0100 /* WSEQ_EOS */
#define WM8961_WSEQ_EOS_SHIFT 8 /* WSEQ_EOS */
#define WM8961_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */
#define WM8961_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */
#define WM8961_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */
#define WM8961_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */
/*
* R89 (0x59) - Write Sequencer 3
*/
#define WM8961_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */
#define WM8961_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */
#define WM8961_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */
/*
* R90 (0x5A) - Write Sequencer 4
*/
#define WM8961_WSEQ_ABORT 0x0100 /* WSEQ_ABORT */
#define WM8961_WSEQ_ABORT_MASK 0x0100 /* WSEQ_ABORT */
#define WM8961_WSEQ_ABORT_SHIFT 8 /* WSEQ_ABORT */
#define WM8961_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
#define WM8961_WSEQ_START 0x0080 /* WSEQ_START */
#define WM8961_WSEQ_START_MASK 0x0080 /* WSEQ_START */
#define WM8961_WSEQ_START_SHIFT 7 /* WSEQ_START */
#define WM8961_WSEQ_START_WIDTH 1 /* WSEQ_START */
#define WM8961_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */
#define WM8961_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */
#define WM8961_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */
/*
* R91 (0x5B) - Write Sequencer 5
*/
#define WM8961_WSEQ_DATA_WIDTH_MASK 0x0070 /* WSEQ_DATA_WIDTH - [6:4] */
#define WM8961_WSEQ_DATA_WIDTH_SHIFT 4 /* WSEQ_DATA_WIDTH - [6:4] */
#define WM8961_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [6:4] */
#define WM8961_WSEQ_DATA_START_MASK 0x000F /* WSEQ_DATA_START - [3:0] */
#define WM8961_WSEQ_DATA_START_SHIFT 0 /* WSEQ_DATA_START - [3:0] */
#define WM8961_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [3:0] */
/*
* R92 (0x5C) - Write Sequencer 6
*/
#define WM8961_WSEQ_DELAY_MASK 0x000F /* WSEQ_DELAY - [3:0] */
#define WM8961_WSEQ_DELAY_SHIFT 0 /* WSEQ_DELAY - [3:0] */
#define WM8961_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [3:0] */
/*
* R93 (0x5D) - Write Sequencer 7
*/
#define WM8961_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */
#define WM8961_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */
#define WM8961_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */
#define WM8961_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
/*
* R252 (0xFC) - General test 1
*/
#define WM8961_ARA_ENA 0x0002 /* ARA_ENA */
#define WM8961_ARA_ENA_MASK 0x0002 /* ARA_ENA */
#define WM8961_ARA_ENA_SHIFT 1 /* ARA_ENA */
#define WM8961_ARA_ENA_WIDTH 1 /* ARA_ENA */
#define WM8961_AUTO_INC 0x0001 /* AUTO_INC */
#define WM8961_AUTO_INC_MASK 0x0001 /* AUTO_INC */
#define WM8961_AUTO_INC_SHIFT 0 /* AUTO_INC */
#define WM8961_AUTO_INC_WIDTH 1 /* AUTO_INC */
#endif
...@@ -981,6 +981,21 @@ static int wm8988_i2c_remove(struct i2c_client *client) ...@@ -981,6 +981,21 @@ static int wm8988_i2c_remove(struct i2c_client *client)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm8988_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8988_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8988_i2c_suspend NULL
#define wm8988_i2c_resume NULL
#endif
static const struct i2c_device_id wm8988_i2c_id[] = { static const struct i2c_device_id wm8988_i2c_id[] = {
{ "wm8988", 0 }, { "wm8988", 0 },
{ } { }
...@@ -994,6 +1009,8 @@ static struct i2c_driver wm8988_i2c_driver = { ...@@ -994,6 +1009,8 @@ static struct i2c_driver wm8988_i2c_driver = {
}, },
.probe = wm8988_i2c_probe, .probe = wm8988_i2c_probe,
.remove = wm8988_i2c_remove, .remove = wm8988_i2c_remove,
.suspend = wm8988_i2c_suspend,
.resume = wm8988_i2c_resume,
.id_table = wm8988_i2c_id, .id_table = wm8988_i2c_id,
}; };
#endif #endif
...@@ -1051,6 +1068,21 @@ static int __devexit wm8988_spi_remove(struct spi_device *spi) ...@@ -1051,6 +1068,21 @@ static int __devexit wm8988_spi_remove(struct spi_device *spi)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm8988_spi_suspend(struct spi_device *spi, pm_message_t msg)
{
return snd_soc_suspend_device(&spi->dev);
}
static int wm8988_spi_resume(struct spi_device *spi)
{
return snd_soc_resume_device(&spi->dev);
}
#else
#define wm8988_spi_suspend NULL
#define wm8988_spi_resume NULL
#endif
static struct spi_driver wm8988_spi_driver = { static struct spi_driver wm8988_spi_driver = {
.driver = { .driver = {
.name = "wm8988", .name = "wm8988",
...@@ -1059,6 +1091,8 @@ static struct spi_driver wm8988_spi_driver = { ...@@ -1059,6 +1091,8 @@ static struct spi_driver wm8988_spi_driver = {
}, },
.probe = wm8988_spi_probe, .probe = wm8988_spi_probe,
.remove = __devexit_p(wm8988_spi_remove), .remove = __devexit_p(wm8988_spi_remove),
.suspend = wm8988_spi_suspend,
.resume = wm8988_spi_resume,
}; };
#endif #endif
......
...@@ -1492,6 +1492,21 @@ static __devexit int wm9081_i2c_remove(struct i2c_client *client) ...@@ -1492,6 +1492,21 @@ static __devexit int wm9081_i2c_remove(struct i2c_client *client)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int wm9081_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm9081_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm9081_i2c_suspend NULL
#define wm9081_i2c_resume NULL
#endif
static const struct i2c_device_id wm9081_i2c_id[] = { static const struct i2c_device_id wm9081_i2c_id[] = {
{ "wm9081", 0 }, { "wm9081", 0 },
{ } { }
...@@ -1505,6 +1520,8 @@ static struct i2c_driver wm9081_i2c_driver = { ...@@ -1505,6 +1520,8 @@ static struct i2c_driver wm9081_i2c_driver = {
}, },
.probe = wm9081_i2c_probe, .probe = wm9081_i2c_probe,
.remove = __devexit_p(wm9081_i2c_remove), .remove = __devexit_p(wm9081_i2c_remove),
.suspend = wm9081_i2c_suspend,
.resume = wm9081_i2c_resume,
.id_table = wm9081_i2c_id, .id_table = wm9081_i2c_id,
}; };
......
...@@ -72,4 +72,11 @@ config SND_OMAP_SOC_OMAP3_BEAGLE ...@@ -72,4 +72,11 @@ config SND_OMAP_SOC_OMAP3_BEAGLE
help help
Say Y if you want to add support for SoC audio on the Beagleboard. Say Y if you want to add support for SoC audio on the Beagleboard.
config SND_OMAP_SOC_ZOOM2
tristate "SoC Audio support for Zoom2"
depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_ZOOM2
select SND_OMAP_SOC_MCBSP
select SND_SOC_TWL4030
help
Say Y if you want to add support for Soc audio on Zoom2 board.
...@@ -14,6 +14,7 @@ snd-soc-omap3evm-objs := omap3evm.o ...@@ -14,6 +14,7 @@ snd-soc-omap3evm-objs := omap3evm.o
snd-soc-sdp3430-objs := sdp3430.o snd-soc-sdp3430-objs := sdp3430.o
snd-soc-omap3pandora-objs := omap3pandora.o snd-soc-omap3pandora-objs := omap3pandora.o
snd-soc-omap3beagle-objs := omap3beagle.o snd-soc-omap3beagle-objs := omap3beagle.o
snd-soc-zoom2-objs := zoom2.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
...@@ -23,3 +24,4 @@ obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o ...@@ -23,3 +24,4 @@ obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o
obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
...@@ -96,7 +96,7 @@ static int sdp3430_hw_voice_params(struct snd_pcm_substream *substream, ...@@ -96,7 +96,7 @@ static int sdp3430_hw_voice_params(struct snd_pcm_substream *substream,
ret = snd_soc_dai_set_fmt(codec_dai, ret = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_IB_NF |
SND_SOC_DAIFMT_CBS_CFM); SND_SOC_DAIFMT_CBM_CFM);
if (ret) { if (ret) {
printk(KERN_ERR "can't set codec DAI configuration\n"); printk(KERN_ERR "can't set codec DAI configuration\n");
return ret; return ret;
......
/*
* zoom2.c -- SoC audio for Zoom2
*
* Author: Misael Lopez Cruz <x0052729@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include <mach/gpio.h>
#include <mach/mcbsp.h>
#include "omap-mcbsp.h"
#include "omap-pcm.h"
#include "../codecs/twl4030.h"
#define ZOOM2_HEADSET_MUX_GPIO (OMAP_MAX_GPIO_LINES + 15)
static int zoom2_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int ret;
/* Set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0) {
printk(KERN_ERR "can't set codec DAI configuration\n");
return ret;
}
/* Set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0) {
printk(KERN_ERR "can't set cpu DAI configuration\n");
return ret;
}
/* Set the codec system clock for DAC and ADC */
ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
SND_SOC_CLOCK_IN);
if (ret < 0) {
printk(KERN_ERR "can't set codec system clock\n");
return ret;
}
return 0;
}
static struct snd_soc_ops zoom2_ops = {
.hw_params = zoom2_hw_params,
};
static int zoom2_hw_voice_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int ret;
/* Set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_NF |
SND_SOC_DAIFMT_CBM_CFM);
if (ret) {
printk(KERN_ERR "can't set codec DAI configuration\n");
return ret;
}
/* Set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_NF |
SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0) {
printk(KERN_ERR "can't set cpu DAI configuration\n");
return ret;
}
/* Set the codec system clock for DAC and ADC */
ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
SND_SOC_CLOCK_IN);
if (ret < 0) {
printk(KERN_ERR "can't set codec system clock\n");
return ret;
}
return 0;
}
static struct snd_soc_ops zoom2_voice_ops = {
.hw_params = zoom2_hw_voice_params,
};
/* Zoom2 machine DAPM */
static const struct snd_soc_dapm_widget zoom2_twl4030_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Ext Mic", NULL),
SND_SOC_DAPM_SPK("Ext Spk", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_HP("Headset Stereophone", NULL),
SND_SOC_DAPM_LINE("Aux In", NULL),
};
static const struct snd_soc_dapm_route audio_map[] = {
/* External Mics: MAINMIC, SUBMIC with bias*/
{"MAINMIC", NULL, "Mic Bias 1"},
{"SUBMIC", NULL, "Mic Bias 2"},
{"Mic Bias 1", NULL, "Ext Mic"},
{"Mic Bias 2", NULL, "Ext Mic"},
/* External Speakers: HFL, HFR */
{"Ext Spk", NULL, "HFL"},
{"Ext Spk", NULL, "HFR"},
/* Headset Stereophone: HSOL, HSOR */
{"Headset Stereophone", NULL, "HSOL"},
{"Headset Stereophone", NULL, "HSOR"},
/* Headset Mic: HSMIC with bias */
{"HSMIC", NULL, "Headset Mic Bias"},
{"Headset Mic Bias", NULL, "Headset Mic"},
/* Aux In: AUXL, AUXR */
{"Aux In", NULL, "AUXL"},
{"Aux In", NULL, "AUXR"},
};
static int zoom2_twl4030_init(struct snd_soc_codec *codec)
{
int ret;
/* Add Zoom2 specific widgets */
ret = snd_soc_dapm_new_controls(codec, zoom2_twl4030_dapm_widgets,
ARRAY_SIZE(zoom2_twl4030_dapm_widgets));
if (ret)
return ret;
/* Set up Zoom2 specific audio path audio_map */
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
/* Zoom2 connected pins */
snd_soc_dapm_enable_pin(codec, "Ext Mic");
snd_soc_dapm_enable_pin(codec, "Ext Spk");
snd_soc_dapm_enable_pin(codec, "Headset Mic");
snd_soc_dapm_enable_pin(codec, "Headset Stereophone");
snd_soc_dapm_enable_pin(codec, "Aux In");
/* TWL4030 not connected pins */
snd_soc_dapm_nc_pin(codec, "CARKITMIC");
snd_soc_dapm_nc_pin(codec, "DIGIMIC0");
snd_soc_dapm_nc_pin(codec, "DIGIMIC1");
snd_soc_dapm_nc_pin(codec, "OUTL");
snd_soc_dapm_nc_pin(codec, "OUTR");
snd_soc_dapm_nc_pin(codec, "EARPIECE");
snd_soc_dapm_nc_pin(codec, "PREDRIVEL");
snd_soc_dapm_nc_pin(codec, "PREDRIVER");
snd_soc_dapm_nc_pin(codec, "CARKITL");
snd_soc_dapm_nc_pin(codec, "CARKITR");
ret = snd_soc_dapm_sync(codec);
return ret;
}
static int zoom2_twl4030_voice_init(struct snd_soc_codec *codec)
{
unsigned short reg;
/* Enable voice interface */
reg = codec->read(codec, TWL4030_REG_VOICE_IF);
reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN;
codec->write(codec, TWL4030_REG_VOICE_IF, reg);
return 0;
}
/* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link zoom2_dai[] = {
{
.name = "TWL4030 I2S",
.stream_name = "TWL4030 Audio",
.cpu_dai = &omap_mcbsp_dai[0],
.codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
.init = zoom2_twl4030_init,
.ops = &zoom2_ops,
},
{
.name = "TWL4030 PCM",
.stream_name = "TWL4030 Voice",
.cpu_dai = &omap_mcbsp_dai[1],
.codec_dai = &twl4030_dai[TWL4030_DAI_VOICE],
.init = zoom2_twl4030_voice_init,
.ops = &zoom2_voice_ops,
},
};
/* Audio machine driver */
static struct snd_soc_card snd_soc_zoom2 = {
.name = "Zoom2",
.platform = &omap_soc_platform,
.dai_link = zoom2_dai,
.num_links = ARRAY_SIZE(zoom2_dai),
};
/* twl4030 setup */
static struct twl4030_setup_data twl4030_setup = {
.ramp_delay_value = 2, /* 81 ms */
.sysclk = 26000,
};
/* Audio subsystem */
static struct snd_soc_device zoom2_snd_devdata = {
.card = &snd_soc_zoom2,
.codec_dev = &soc_codec_dev_twl4030,
.codec_data = &twl4030_setup,
};
static struct platform_device *zoom2_snd_device;
static int __init zoom2_soc_init(void)
{
int ret;
if (!machine_is_omap_zoom2()) {
pr_debug("Not Zoom2!\n");
return -ENODEV;
}
printk(KERN_INFO "Zoom2 SoC init\n");
zoom2_snd_device = platform_device_alloc("soc-audio", -1);
if (!zoom2_snd_device) {
printk(KERN_ERR "Platform device allocation failed\n");
return -ENOMEM;
}
platform_set_drvdata(zoom2_snd_device, &zoom2_snd_devdata);
zoom2_snd_devdata.dev = &zoom2_snd_device->dev;
*(unsigned int *)zoom2_dai[0].cpu_dai->private_data = 1; /* McBSP2 */
*(unsigned int *)zoom2_dai[1].cpu_dai->private_data = 2; /* McBSP3 */
ret = platform_device_add(zoom2_snd_device);
if (ret)
goto err1;
BUG_ON(gpio_request(ZOOM2_HEADSET_MUX_GPIO, "hs_mux") < 0);
gpio_direction_output(ZOOM2_HEADSET_MUX_GPIO, 0);
return 0;
err1:
printk(KERN_ERR "Unable to add platform device\n");
platform_device_put(zoom2_snd_device);
return ret;
}
module_init(zoom2_soc_init);
static void __exit zoom2_soc_exit(void)
{
gpio_free(ZOOM2_HEADSET_MUX_GPIO);
platform_device_unregister(zoom2_snd_device);
}
module_exit(zoom2_soc_exit);
MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
MODULE_DESCRIPTION("ALSA SoC Zoom2");
MODULE_LICENSE("GPL");
...@@ -20,12 +20,14 @@ ...@@ -20,12 +20,14 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/i2c.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/soc-dapm.h> #include <sound/soc-dapm.h>
#include <sound/uda1380.h>
#include <mach/magician.h> #include <mach/magician.h>
#include <asm/mach-types.h> #include <asm/mach-types.h>
...@@ -447,34 +449,47 @@ static struct snd_soc_card snd_soc_card_magician = { ...@@ -447,34 +449,47 @@ static struct snd_soc_card snd_soc_card_magician = {
.platform = &pxa2xx_soc_platform, .platform = &pxa2xx_soc_platform,
}; };
/* magician audio private data */
static struct uda1380_setup_data magician_uda1380_setup = {
.i2c_address = 0x18,
.dac_clk = UDA1380_DAC_CLK_WSPLL,
};
/* magician audio subsystem */ /* magician audio subsystem */
static struct snd_soc_device magician_snd_devdata = { static struct snd_soc_device magician_snd_devdata = {
.card = &snd_soc_card_magician, .card = &snd_soc_card_magician,
.codec_dev = &soc_codec_dev_uda1380, .codec_dev = &soc_codec_dev_uda1380,
.codec_data = &magician_uda1380_setup,
}; };
static struct platform_device *magician_snd_device; static struct platform_device *magician_snd_device;
/*
* FIXME: move into magician board file once merged into the pxa tree
*/
static struct uda1380_platform_data uda1380_info = {
.gpio_power = EGPIO_MAGICIAN_CODEC_POWER,
.gpio_reset = EGPIO_MAGICIAN_CODEC_RESET,
.dac_clk = UDA1380_DAC_CLK_WSPLL,
};
static struct i2c_board_info i2c_board_info[] = {
{
I2C_BOARD_INFO("uda1380", 0x18),
.platform_data = &uda1380_info,
},
};
static int __init magician_init(void) static int __init magician_init(void)
{ {
int ret; int ret;
struct i2c_adapter *adapter;
struct i2c_client *client;
if (!machine_is_magician()) if (!machine_is_magician())
return -ENODEV; return -ENODEV;
ret = gpio_request(EGPIO_MAGICIAN_CODEC_POWER, "CODEC_POWER"); adapter = i2c_get_adapter(0);
if (ret) if (!adapter)
goto err_request_power; return -ENODEV;
ret = gpio_request(EGPIO_MAGICIAN_CODEC_RESET, "CODEC_RESET"); client = i2c_new_device(adapter, i2c_board_info);
if (ret) i2c_put_adapter(adapter);
goto err_request_reset; if (!client)
return -ENODEV;
ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER"); ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER");
if (ret) if (ret)
goto err_request_spk; goto err_request_spk;
...@@ -491,14 +506,8 @@ static int __init magician_init(void) ...@@ -491,14 +506,8 @@ static int __init magician_init(void)
if (ret) if (ret)
goto err_request_in_sel1; goto err_request_in_sel1;
gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 1);
gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0); gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0);
/* we may need to have the clock running here - pH5 */
gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 1);
udelay(5);
gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 0);
magician_snd_device = platform_device_alloc("soc-audio", -1); magician_snd_device = platform_device_alloc("soc-audio", -1);
if (!magician_snd_device) { if (!magician_snd_device) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -526,10 +535,6 @@ static int __init magician_init(void) ...@@ -526,10 +535,6 @@ static int __init magician_init(void)
err_request_ep: err_request_ep:
gpio_free(EGPIO_MAGICIAN_SPK_POWER); gpio_free(EGPIO_MAGICIAN_SPK_POWER);
err_request_spk: err_request_spk:
gpio_free(EGPIO_MAGICIAN_CODEC_RESET);
err_request_reset:
gpio_free(EGPIO_MAGICIAN_CODEC_POWER);
err_request_power:
return ret; return ret;
} }
...@@ -540,15 +545,12 @@ static void __exit magician_exit(void) ...@@ -540,15 +545,12 @@ static void __exit magician_exit(void)
gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0); gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0);
gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0); gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0);
gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0); gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0);
gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 0);
gpio_free(EGPIO_MAGICIAN_IN_SEL1); gpio_free(EGPIO_MAGICIAN_IN_SEL1);
gpio_free(EGPIO_MAGICIAN_IN_SEL0); gpio_free(EGPIO_MAGICIAN_IN_SEL0);
gpio_free(EGPIO_MAGICIAN_MIC_POWER); gpio_free(EGPIO_MAGICIAN_MIC_POWER);
gpio_free(EGPIO_MAGICIAN_EP_POWER); gpio_free(EGPIO_MAGICIAN_EP_POWER);
gpio_free(EGPIO_MAGICIAN_SPK_POWER); gpio_free(EGPIO_MAGICIAN_SPK_POWER);
gpio_free(EGPIO_MAGICIAN_CODEC_RESET);
gpio_free(EGPIO_MAGICIAN_CODEC_POWER);
} }
module_init(magician_init); module_init(magician_init);
......
...@@ -457,31 +457,27 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, ...@@ -457,31 +457,27 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return -EINVAL; return -EINVAL;
} }
ssp_write_reg(ssp, SSCR0, sscr0); switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
ssp_write_reg(ssp, SSCR1, sscr1); case SND_SOC_DAIFMT_NB_NF:
ssp_write_reg(ssp, SSPSP, sspsp); sspsp |= SSPSP_SFRMP;
break;
case SND_SOC_DAIFMT_NB_IF:
break;
case SND_SOC_DAIFMT_IB_IF:
sspsp |= SSPSP_SCMODE(2);
break;
case SND_SOC_DAIFMT_IB_NF:
sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_I2S:
sscr0 |= SSCR0_PSP; sscr0 |= SSCR0_PSP;
sscr1 |= SSCR1_RWOT | SSCR1_TRAIL; sscr1 |= SSCR1_RWOT | SSCR1_TRAIL;
/* See hw_params() */ /* See hw_params() */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
sspsp |= SSPSP_SFRMP;
break;
case SND_SOC_DAIFMT_NB_IF:
break;
case SND_SOC_DAIFMT_IB_IF:
sspsp |= SSPSP_SCMODE(2);
break;
case SND_SOC_DAIFMT_IB_NF:
sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
break;
default:
return -EINVAL;
}
break; break;
case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_A:
...@@ -489,22 +485,6 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, ...@@ -489,22 +485,6 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
case SND_SOC_DAIFMT_DSP_B: case SND_SOC_DAIFMT_DSP_B:
sscr0 |= SSCR0_MOD | SSCR0_PSP; sscr0 |= SSCR0_MOD | SSCR0_PSP;
sscr1 |= SSCR1_TRAIL | SSCR1_RWOT; sscr1 |= SSCR1_TRAIL | SSCR1_RWOT;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
sspsp |= SSPSP_SFRMP;
break;
case SND_SOC_DAIFMT_NB_IF:
break;
case SND_SOC_DAIFMT_IB_IF:
sspsp |= SSPSP_SCMODE(2);
break;
case SND_SOC_DAIFMT_IB_NF:
sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
break;
default:
return -EINVAL;
}
break; break;
default: default:
......
...@@ -788,6 +788,45 @@ static int soc_resume(struct platform_device *pdev) ...@@ -788,6 +788,45 @@ static int soc_resume(struct platform_device *pdev)
return 0; return 0;
} }
/**
* snd_soc_suspend_device: Notify core of device suspend
*
* @dev: Device being suspended.
*
* In order to ensure that the entire audio subsystem is suspended in a
* coordinated fashion ASoC devices should suspend themselves when
* called by ASoC. When the standard kernel suspend process asks the
* device to suspend it should call this function to initiate a suspend
* of the entire ASoC card.
*
* \note Currently this function is stubbed out.
*/
int snd_soc_suspend_device(struct device *dev)
{
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_suspend_device);
/**
* snd_soc_resume_device: Notify core of device resume
*
* @dev: Device being resumed.
*
* In order to ensure that the entire audio subsystem is resumed in a
* coordinated fashion ASoC devices should resume themselves when called
* by ASoC. When the standard kernel resume process asks the device
* to resume it should call this function. Once all the components of
* the card have notified that they are ready to be resumed the card
* will be resumed.
*
* \note Currently this function is stubbed out.
*/
int snd_soc_resume_device(struct device *dev)
{
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_resume_device);
#else #else
#define soc_suspend NULL #define soc_suspend NULL
#define soc_resume NULL #define soc_resume NULL
...@@ -981,6 +1020,21 @@ static int soc_remove(struct platform_device *pdev) ...@@ -981,6 +1020,21 @@ static int soc_remove(struct platform_device *pdev)
return 0; return 0;
} }
static void soc_shutdown(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_card *card = socdev->card;
if (!card->instantiated)
return;
/* Flush out pmdown_time work - we actually do want to run it
* now, we're shutting down so no imminent restart. */
run_delayed_work(&card->delayed_work);
snd_soc_dapm_shutdown(socdev);
}
/* ASoC platform driver */ /* ASoC platform driver */
static struct platform_driver soc_driver = { static struct platform_driver soc_driver = {
.driver = { .driver = {
...@@ -991,6 +1045,7 @@ static struct platform_driver soc_driver = { ...@@ -991,6 +1045,7 @@ static struct platform_driver soc_driver = {
.remove = soc_remove, .remove = soc_remove,
.suspend = soc_suspend, .suspend = soc_suspend,
.resume = soc_resume, .resume = soc_resume,
.shutdown = soc_shutdown,
}; };
/* create a new pcm */ /* create a new pcm */
...@@ -1264,10 +1319,10 @@ EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec); ...@@ -1264,10 +1319,10 @@ EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
* Returns 1 for change else 0. * Returns 1 for change else 0.
*/ */
int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
unsigned short mask, unsigned short value) unsigned int mask, unsigned int value)
{ {
int change; int change;
unsigned short old, new; unsigned int old, new;
mutex_lock(&io_mutex); mutex_lock(&io_mutex);
old = snd_soc_read(codec, reg); old = snd_soc_read(codec, reg);
...@@ -1294,10 +1349,10 @@ EXPORT_SYMBOL_GPL(snd_soc_update_bits); ...@@ -1294,10 +1349,10 @@ EXPORT_SYMBOL_GPL(snd_soc_update_bits);
* Returns 1 for change else 0. * Returns 1 for change else 0.
*/ */
int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
unsigned short mask, unsigned short value) unsigned int mask, unsigned int value)
{ {
int change; int change;
unsigned short old, new; unsigned int old, new;
mutex_lock(&io_mutex); mutex_lock(&io_mutex);
old = snd_soc_read(codec, reg); old = snd_soc_read(codec, reg);
...@@ -1586,7 +1641,7 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, ...@@ -1586,7 +1641,7 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
{ {
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned short val, bitmask; unsigned int val, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1) for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
; ;
...@@ -1615,8 +1670,8 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, ...@@ -1615,8 +1670,8 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
{ {
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned short val; unsigned int val;
unsigned short mask, bitmask; unsigned int mask, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1) for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
; ;
...@@ -1652,7 +1707,7 @@ int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol, ...@@ -1652,7 +1707,7 @@ int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol,
{ {
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned short reg_val, val, mux; unsigned int reg_val, val, mux;
reg_val = snd_soc_read(codec, e->reg); reg_val = snd_soc_read(codec, e->reg);
val = (reg_val >> e->shift_l) & e->mask; val = (reg_val >> e->shift_l) & e->mask;
...@@ -1691,8 +1746,8 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, ...@@ -1691,8 +1746,8 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
{ {
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned short val; unsigned int val;
unsigned short mask; unsigned int mask;
if (ucontrol->value.enumerated.item[0] > e->max - 1) if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL; return -EINVAL;
...@@ -1852,7 +1907,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, ...@@ -1852,7 +1907,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
int max = mc->max; int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1; unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert; unsigned int invert = mc->invert;
unsigned short val, val2, val_mask; unsigned int val, val2, val_mask;
val = (ucontrol->value.integer.value[0] & mask); val = (ucontrol->value.integer.value[0] & mask);
if (invert) if (invert)
...@@ -1918,7 +1973,7 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol, ...@@ -1918,7 +1973,7 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
unsigned int reg2 = mc->rreg; unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift; unsigned int shift = mc->shift;
int max = mc->max; int max = mc->max;
unsigned int mask = (1<<fls(max))-1; unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert; unsigned int invert = mc->invert;
ucontrol->value.integer.value[0] = ucontrol->value.integer.value[0] =
...@@ -1958,7 +2013,7 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, ...@@ -1958,7 +2013,7 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
unsigned int mask = (1 << fls(max)) - 1; unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert; unsigned int invert = mc->invert;
int err; int err;
unsigned short val, val2, val_mask; unsigned int val, val2, val_mask;
val_mask = mask << shift; val_mask = mask << shift;
val = (ucontrol->value.integer.value[0] & mask); val = (ucontrol->value.integer.value[0] & mask);
...@@ -2050,7 +2105,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, ...@@ -2050,7 +2105,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg; unsigned int reg = mc->reg;
int min = mc->min; int min = mc->min;
unsigned short val; unsigned int val;
val = (ucontrol->value.integer.value[0]+min) & 0xff; val = (ucontrol->value.integer.value[0]+min) & 0xff;
val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8; val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
......
...@@ -52,19 +52,37 @@ ...@@ -52,19 +52,37 @@
/* dapm power sequences - make this per codec in the future */ /* dapm power sequences - make this per codec in the future */
static int dapm_up_seq[] = { static int dapm_up_seq[] = {
snd_soc_dapm_pre, snd_soc_dapm_supply, snd_soc_dapm_micbias, [snd_soc_dapm_pre] = 0,
snd_soc_dapm_mic, snd_soc_dapm_mux, snd_soc_dapm_value_mux, [snd_soc_dapm_supply] = 1,
snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl, [snd_soc_dapm_micbias] = 2,
snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, [snd_soc_dapm_mic] = 3,
snd_soc_dapm_post [snd_soc_dapm_mux] = 4,
[snd_soc_dapm_value_mux] = 4,
[snd_soc_dapm_dac] = 5,
[snd_soc_dapm_mixer] = 6,
[snd_soc_dapm_mixer_named_ctl] = 6,
[snd_soc_dapm_pga] = 7,
[snd_soc_dapm_adc] = 8,
[snd_soc_dapm_hp] = 9,
[snd_soc_dapm_spk] = 10,
[snd_soc_dapm_post] = 11,
}; };
static int dapm_down_seq[] = { static int dapm_down_seq[] = {
snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, [snd_soc_dapm_pre] = 0,
snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer, [snd_soc_dapm_adc] = 1,
snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias, [snd_soc_dapm_hp] = 2,
snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_supply, [snd_soc_dapm_spk] = 3,
snd_soc_dapm_post [snd_soc_dapm_pga] = 4,
[snd_soc_dapm_mixer_named_ctl] = 5,
[snd_soc_dapm_mixer] = 5,
[snd_soc_dapm_dac] = 6,
[snd_soc_dapm_mic] = 7,
[snd_soc_dapm_micbias] = 8,
[snd_soc_dapm_mux] = 9,
[snd_soc_dapm_value_mux] = 9,
[snd_soc_dapm_supply] = 10,
[snd_soc_dapm_post] = 11,
}; };
static void pop_wait(u32 pop_time) static void pop_wait(u32 pop_time)
...@@ -268,7 +286,7 @@ static int dapm_connect_mixer(struct snd_soc_codec *codec, ...@@ -268,7 +286,7 @@ static int dapm_connect_mixer(struct snd_soc_codec *codec,
static int dapm_update_bits(struct snd_soc_dapm_widget *widget) static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
{ {
int change, power; int change, power;
unsigned short old, new; unsigned int old, new;
struct snd_soc_codec *codec = widget->codec; struct snd_soc_codec *codec = widget->codec;
/* check for valid widgets */ /* check for valid widgets */
...@@ -689,53 +707,211 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) ...@@ -689,53 +707,211 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
return power; return power;
} }
/* static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
* Scan a single DAPM widget for a complete audio path and update the struct snd_soc_dapm_widget *b,
* power status appropriately. int sort[])
*/
static int dapm_power_widget(struct snd_soc_codec *codec, int event,
struct snd_soc_dapm_widget *w)
{ {
int ret; if (sort[a->id] != sort[b->id])
return sort[a->id] - sort[b->id];
if (a->reg != b->reg)
return a->reg - b->reg;
switch (w->id) { return 0;
case snd_soc_dapm_pre: }
if (!w->event)
return 0;
if (event == SND_SOC_DAPM_STREAM_START) { /* Insert a widget in order into a DAPM power sequence. */
ret = w->event(w, static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget,
NULL, SND_SOC_DAPM_PRE_PMU); struct list_head *list,
int sort[])
{
struct snd_soc_dapm_widget *w;
list_for_each_entry(w, list, power_list)
if (dapm_seq_compare(new_widget, w, sort) < 0) {
list_add_tail(&new_widget->power_list, &w->power_list);
return;
}
list_add_tail(&new_widget->power_list, list);
}
/* Apply the coalesced changes from a DAPM sequence */
static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
struct list_head *pending)
{
struct snd_soc_dapm_widget *w;
int reg, power, ret;
unsigned int value = 0;
unsigned int mask = 0;
unsigned int cur_mask;
reg = list_first_entry(pending, struct snd_soc_dapm_widget,
power_list)->reg;
list_for_each_entry(w, pending, power_list) {
cur_mask = 1 << w->shift;
BUG_ON(reg != w->reg);
if (w->invert)
power = !w->power;
else
power = w->power;
mask |= cur_mask;
if (power)
value |= cur_mask;
pop_dbg(codec->pop_time,
"pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
w->name, reg, value, mask);
/* power up pre event */
if (w->power && w->event &&
(w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
pop_dbg(codec->pop_time, "pop test : %s PRE_PMU\n",
w->name);
ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
if (ret < 0) if (ret < 0)
return ret; pr_err("%s: pre event failed: %d\n",
} else if (event == SND_SOC_DAPM_STREAM_STOP) { w->name, ret);
ret = w->event(w, }
NULL, SND_SOC_DAPM_PRE_PMD);
/* power down pre event */
if (!w->power && w->event &&
(w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
pop_dbg(codec->pop_time, "pop test : %s PRE_PMD\n",
w->name);
ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
if (ret < 0) if (ret < 0)
return ret; pr_err("%s: pre event failed: %d\n",
w->name, ret);
} }
return 0;
case snd_soc_dapm_post: /* Lower PGA volume to reduce pops */
if (!w->event) if (w->id == snd_soc_dapm_pga && !w->power)
return 0; dapm_set_pga(w, w->power);
}
if (event == SND_SOC_DAPM_STREAM_START) { if (reg >= 0) {
pop_dbg(codec->pop_time,
"pop test : Applying 0x%x/0x%x to %x in %dms\n",
value, mask, reg, codec->pop_time);
pop_wait(codec->pop_time);
snd_soc_update_bits(codec, reg, mask, value);
}
list_for_each_entry(w, pending, power_list) {
/* Raise PGA volume to reduce pops */
if (w->id == snd_soc_dapm_pga && w->power)
dapm_set_pga(w, w->power);
/* power up post event */
if (w->power && w->event &&
(w->event_flags & SND_SOC_DAPM_POST_PMU)) {
pop_dbg(codec->pop_time, "pop test : %s POST_PMU\n",
w->name);
ret = w->event(w, ret = w->event(w,
NULL, SND_SOC_DAPM_POST_PMU); NULL, SND_SOC_DAPM_POST_PMU);
if (ret < 0) if (ret < 0)
return ret; pr_err("%s: post event failed: %d\n",
} else if (event == SND_SOC_DAPM_STREAM_STOP) { w->name, ret);
ret = w->event(w, }
NULL, SND_SOC_DAPM_POST_PMD);
/* power down post event */
if (!w->power && w->event &&
(w->event_flags & SND_SOC_DAPM_POST_PMD)) {
pop_dbg(codec->pop_time, "pop test : %s POST_PMD\n",
w->name);
ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
if (ret < 0) if (ret < 0)
return ret; pr_err("%s: post event failed: %d\n",
w->name, ret);
} }
return 0; }
}
default: /* Apply a DAPM power sequence.
return dapm_generic_apply_power(w); *
* We walk over a pre-sorted list of widgets to apply power to. In
* order to minimise the number of writes to the device required
* multiple widgets will be updated in a single write where possible.
* Currently anything that requires more than a single write is not
* handled.
*/
static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
int event, int sort[])
{
struct snd_soc_dapm_widget *w, *n;
LIST_HEAD(pending);
int cur_sort = -1;
int cur_reg = SND_SOC_NOPM;
int ret;
list_for_each_entry_safe(w, n, list, power_list) {
ret = 0;
/* Do we need to apply any queued changes? */
if (sort[w->id] != cur_sort || w->reg != cur_reg) {
if (!list_empty(&pending))
dapm_seq_run_coalesced(codec, &pending);
INIT_LIST_HEAD(&pending);
cur_sort = -1;
cur_reg = SND_SOC_NOPM;
}
switch (w->id) {
case snd_soc_dapm_pre:
if (!w->event)
list_for_each_entry_safe_continue(w, n, list,
power_list);
if (event == SND_SOC_DAPM_STREAM_START)
ret = w->event(w,
NULL, SND_SOC_DAPM_PRE_PMU);
else if (event == SND_SOC_DAPM_STREAM_STOP)
ret = w->event(w,
NULL, SND_SOC_DAPM_PRE_PMD);
break;
case snd_soc_dapm_post:
if (!w->event)
list_for_each_entry_safe_continue(w, n, list,
power_list);
if (event == SND_SOC_DAPM_STREAM_START)
ret = w->event(w,
NULL, SND_SOC_DAPM_POST_PMU);
else if (event == SND_SOC_DAPM_STREAM_STOP)
ret = w->event(w,
NULL, SND_SOC_DAPM_POST_PMD);
break;
case snd_soc_dapm_input:
case snd_soc_dapm_output:
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_line:
case snd_soc_dapm_spk:
/* No register support currently */
ret = dapm_generic_apply_power(w);
break;
default:
/* Queue it up for application */
cur_sort = sort[w->id];
cur_reg = w->reg;
list_move(&w->power_list, &pending);
break;
}
if (ret < 0)
pr_err("Failed to apply widget power: %d\n",
ret);
} }
if (!list_empty(&pending))
dapm_seq_run_coalesced(codec, &pending);
} }
/* /*
...@@ -751,23 +927,22 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) ...@@ -751,23 +927,22 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
{ {
struct snd_soc_device *socdev = codec->socdev; struct snd_soc_device *socdev = codec->socdev;
struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *w;
LIST_HEAD(up_list);
LIST_HEAD(down_list);
int ret = 0; int ret = 0;
int i, power; int power;
int sys_power = 0; int sys_power = 0;
INIT_LIST_HEAD(&codec->up_list);
INIT_LIST_HEAD(&codec->down_list);
/* Check which widgets we need to power and store them in /* Check which widgets we need to power and store them in
* lists indicating if they should be powered up or down. * lists indicating if they should be powered up or down.
*/ */
list_for_each_entry(w, &codec->dapm_widgets, list) { list_for_each_entry(w, &codec->dapm_widgets, list) {
switch (w->id) { switch (w->id) {
case snd_soc_dapm_pre: case snd_soc_dapm_pre:
list_add_tail(&codec->down_list, &w->power_list); dapm_seq_insert(w, &down_list, dapm_down_seq);
break; break;
case snd_soc_dapm_post: case snd_soc_dapm_post:
list_add_tail(&codec->up_list, &w->power_list); dapm_seq_insert(w, &up_list, dapm_up_seq);
break; break;
default: default:
...@@ -782,10 +957,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) ...@@ -782,10 +957,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
continue; continue;
if (power) if (power)
list_add_tail(&w->power_list, &codec->up_list); dapm_seq_insert(w, &up_list, dapm_up_seq);
else else
list_add_tail(&w->power_list, dapm_seq_insert(w, &down_list, dapm_down_seq);
&codec->down_list);
w->power = power; w->power = power;
break; break;
...@@ -802,32 +976,10 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) ...@@ -802,32 +976,10 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
} }
/* Power down widgets first; try to avoid amplifying pops. */ /* Power down widgets first; try to avoid amplifying pops. */
for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) { dapm_seq_run(codec, &down_list, event, dapm_down_seq);
list_for_each_entry(w, &codec->down_list, power_list) {
/* is widget in stream order */
if (w->id != dapm_down_seq[i])
continue;
ret = dapm_power_widget(codec, event, w);
if (ret != 0)
pr_err("Failed to power down %s: %d\n",
w->name, ret);
}
}
/* Now power up. */ /* Now power up. */
for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) { dapm_seq_run(codec, &up_list, event, dapm_up_seq);
list_for_each_entry(w, &codec->up_list, power_list) {
/* is widget in stream order */
if (w->id != dapm_up_seq[i])
continue;
ret = dapm_power_widget(codec, event, w);
if (ret != 0)
pr_err("Failed to power up %s: %d\n",
w->name, ret);
}
}
/* If we just powered the last thing off drop to standby bias */ /* If we just powered the last thing off drop to standby bias */
if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) { if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {
...@@ -1372,7 +1524,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, ...@@ -1372,7 +1524,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
int max = mc->max; int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1; unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert; unsigned int invert = mc->invert;
unsigned short val, val2, val_mask; unsigned int val, val2, val_mask;
int ret; int ret;
val = (ucontrol->value.integer.value[0] & mask); val = (ucontrol->value.integer.value[0] & mask);
...@@ -1436,7 +1588,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, ...@@ -1436,7 +1588,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned short val, bitmask; unsigned int val, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1) for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
; ;
...@@ -1464,8 +1616,8 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, ...@@ -1464,8 +1616,8 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned short val, mux; unsigned int val, mux;
unsigned short mask, bitmask; unsigned int mask, bitmask;
int ret = 0; int ret = 0;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1) for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
...@@ -1523,7 +1675,7 @@ int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, ...@@ -1523,7 +1675,7 @@ int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned short reg_val, val, mux; unsigned int reg_val, val, mux;
reg_val = snd_soc_read(widget->codec, e->reg); reg_val = snd_soc_read(widget->codec, e->reg);
val = (reg_val >> e->shift_l) & e->mask; val = (reg_val >> e->shift_l) & e->mask;
...@@ -1563,8 +1715,8 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, ...@@ -1563,8 +1715,8 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned short val, mux; unsigned int val, mux;
unsigned short mask; unsigned int mask;
int ret = 0; int ret = 0;
if (ucontrol->value.enumerated.item[0] > e->max - 1) if (ucontrol->value.enumerated.item[0] > e->max - 1)
...@@ -1880,6 +2032,35 @@ void snd_soc_dapm_free(struct snd_soc_device *socdev) ...@@ -1880,6 +2032,35 @@ void snd_soc_dapm_free(struct snd_soc_device *socdev)
} }
EXPORT_SYMBOL_GPL(snd_soc_dapm_free); EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
/*
* snd_soc_dapm_shutdown - callback for system shutdown
*/
void snd_soc_dapm_shutdown(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->card->codec;
struct snd_soc_dapm_widget *w;
LIST_HEAD(down_list);
int powerdown = 0;
list_for_each_entry(w, &codec->dapm_widgets, list) {
if (w->power) {
dapm_seq_insert(w, &down_list, dapm_down_seq);
powerdown = 1;
}
}
/* If there were no widgets to power down we're already in
* standby.
*/
if (powerdown) {
snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_PREPARE);
dapm_seq_run(codec, &down_list, 0, dapm_down_seq);
snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_STANDBY);
}
snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_OFF);
}
/* Module information */ /* Module information */
MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC"); MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment