summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/regmap/Kconfig5
-rw-r--r--drivers/base/regmap/Makefile1
-rw-r--r--drivers/base/regmap/regmap-ac97.c114
-rw-r--r--include/linux/regmap.h7
-rw-r--r--include/sound/soc-dai.h4
-rw-r--r--include/sound/soc.h64
-rw-r--r--sound/soc/Makefile4
-rw-r--r--sound/soc/au1x/ac97c.c2
-rw-r--r--sound/soc/au1x/psc-ac97.c2
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c2
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c2
-rw-r--r--sound/soc/cirrus/ep93xx-ac97.c2
-rw-r--r--sound/soc/codecs/Kconfig1
-rw-r--r--sound/soc/codecs/ac97.c18
-rw-r--r--sound/soc/codecs/ad193x.c14
-rw-r--r--sound/soc/codecs/ad1980.c212
-rw-r--r--sound/soc/codecs/ad1980.h26
-rw-r--r--sound/soc/codecs/adau1373.c6
-rw-r--r--sound/soc/codecs/adau1761.c3
-rw-r--r--sound/soc/codecs/adau1781.c2
-rw-r--r--sound/soc/codecs/adau17x1.c3
-rw-r--r--sound/soc/codecs/stac9766.c40
-rw-r--r--sound/soc/codecs/wm9705.c46
-rw-r--r--sound/soc/codecs/wm9712.c209
-rw-r--r--sound/soc/codecs/wm9713.c228
-rw-r--r--sound/soc/fsl/fsl_ssi.c2
-rw-r--r--sound/soc/fsl/imx-ssi.c2
-rw-r--r--sound/soc/fsl/mpc5200_dma.c3
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.c6
-rw-r--r--sound/soc/nuc900/nuc900-ac97.c2
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c6
-rw-r--r--sound/soc/samsung/ac97.c4
-rw-r--r--sound/soc/sh/hac.c2
-rw-r--r--sound/soc/soc-ac97.c256
-rw-r--r--sound/soc/soc-core.c434
-rw-r--r--sound/soc/tegra/tegra20_ac97.c2
-rw-r--r--sound/soc/txx9/txx9aclc-ac97.c2
37 files changed, 987 insertions, 751 deletions
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index 8a3f51f7b1b9..db9d00c36a3e 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -3,12 +3,15 @@
# subsystems should select the appropriate symbols.
config REGMAP
- default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ)
+ default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ)
select LZO_COMPRESS
select LZO_DECOMPRESS
select IRQ_DOMAIN if REGMAP_IRQ
bool
+config REGMAP_AC97
+ tristate
+
config REGMAP_I2C
tristate
depends on I2C
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index a7c670b4123a..0a533653ef3b 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_REGMAP) += regmap.o regcache.o
obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
+obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
diff --git a/drivers/base/regmap/regmap-ac97.c b/drivers/base/regmap/regmap-ac97.c
new file mode 100644
index 000000000000..e4c45d2299c1
--- /dev/null
+++ b/drivers/base/regmap/regmap-ac97.c
@@ -0,0 +1,114 @@
+/*
+ * Register map access API - AC'97 support
+ *
+ * Copyright 2013 Linaro Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/ac97_codec.h>
+
+bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AC97_RESET:
+ case AC97_POWERDOWN:
+ case AC97_INT_PAGING:
+ case AC97_EXTENDED_ID:
+ case AC97_EXTENDED_STATUS:
+ case AC97_EXTENDED_MID:
+ case AC97_EXTENDED_MSTATUS:
+ case AC97_GPIO_STATUS:
+ case AC97_MISC_AFE:
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ case AC97_CODEC_CLASS_REV:
+ case AC97_PCI_SVID:
+ case AC97_PCI_SID:
+ case AC97_FUNC_SELECT:
+ case AC97_FUNC_INFO:
+ case AC97_SENSE_INFO:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_GPL(regmap_ac97_default_volatile);
+
+static int regmap_ac97_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct snd_ac97 *ac97 = context;
+
+ *val = ac97->bus->ops->read(ac97, reg);
+
+ return 0;
+}
+
+static int regmap_ac97_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct snd_ac97 *ac97 = context;
+
+ ac97->bus->ops->write(ac97, reg, val);
+
+ return 0;
+}
+
+static const struct regmap_bus ac97_regmap_bus = {
+ .reg_write = regmap_ac97_reg_write,
+ .reg_read = regmap_ac97_reg_read,
+};
+
+/**
+ * regmap_init_ac97(): Initialise AC'97 register map
+ *
+ * @ac97: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
+ const struct regmap_config *config)
+{
+ return regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
+}
+EXPORT_SYMBOL_GPL(regmap_init_ac97);
+
+/**
+ * devm_regmap_init_ac97(): Initialise AC'97 register map
+ *
+ * @ac97: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap. The regmap will be automatically freed by the
+ * device management code.
+ */
+struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
+ const struct regmap_config *config)
+{
+ return devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_ac97);
+
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index c5ed83f49c4e..4419b99d8d6e 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -27,6 +27,7 @@ struct spmi_device;
struct regmap;
struct regmap_range_cfg;
struct regmap_field;
+struct snd_ac97;
/* An enum of all the supported cache types */
enum regcache_type {
@@ -340,6 +341,8 @@ struct regmap *regmap_init_spmi_ext(struct spmi_device *dev,
struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs,
const struct regmap_config *config);
+struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
+ const struct regmap_config *config);
struct regmap *devm_regmap_init(struct device *dev,
const struct regmap_bus *bus,
@@ -356,6 +359,10 @@ struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *dev,
struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs,
const struct regmap_config *config);
+struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
+ const struct regmap_config *config);
+
+bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
/**
* regmap_init_mmio(): Initialise register map
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 373d1775ecba..2df96b1384c7 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -206,7 +206,6 @@ struct snd_soc_dai_driver {
/* DAI description */
const char *name;
unsigned int id;
- int ac97_control;
unsigned int base;
/* DAI driver callbacks */
@@ -216,6 +215,8 @@ struct snd_soc_dai_driver {
int (*resume)(struct snd_soc_dai *dai);
/* compress dai */
bool compress_dai;
+ /* DAI is also used for the control bus */
+ bool bus_control;
/* ops */
const struct snd_soc_dai_ops *ops;
@@ -241,7 +242,6 @@ struct snd_soc_dai {
const char *name;
int id;
struct device *dev;
- void *ac97_pdata; /* platform_data for the ac97 codec */
/* driver ops */
struct snd_soc_dai_driver *driver;
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 38769cc900c9..a765c684bd7b 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -369,8 +369,6 @@ struct snd_soc_jack_gpio;
typedef int (*hw_write_t)(void *,const char* ,int);
-extern struct snd_ac97_bus_ops *soc_ac97_ops;
-
enum snd_soc_pcm_subclass {
SND_SOC_PCM_CLASS_PCM = 0,
SND_SOC_PCM_CLASS_BE = 1,
@@ -499,14 +497,28 @@ int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg,
unsigned int mask, unsigned int value);
-int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
- struct snd_ac97_bus_ops *ops, int num);
-void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
+#ifdef CONFIG_SND_SOC_AC97_BUS
+struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec);
+void snd_soc_free_ac97_codec(struct snd_ac97 *ac97);
int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops);
int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
struct platform_device *pdev);
+extern struct snd_ac97_bus_ops *soc_ac97_ops;
+#else
+static inline int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
+ struct platform_device *pdev)
+{
+ return 0;
+}
+
+static inline int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
+{
+ return 0;
+}
+#endif
+
/*
*Controls
*/
@@ -778,11 +790,8 @@ struct snd_soc_codec {
struct list_head card_list;
/* runtime */
- struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
unsigned int cache_bypass:1; /* Suppress access to the cache */
unsigned int suspended:1; /* Codec is in suspend PM state */
- unsigned int ac97_registered:1; /* Codec has been AC97 registered */
- unsigned int ac97_created:1; /* Codec has been created by SoC */
unsigned int cache_init:1; /* codec cache has been initialized */
/* codec IO */
@@ -1275,6 +1284,45 @@ void snd_soc_component_async_complete(struct snd_soc_component *component);
int snd_soc_component_test_bits(struct snd_soc_component *component,
unsigned int reg, unsigned int mask, unsigned int value);
+#ifdef CONFIG_REGMAP
+
+void snd_soc_component_init_regmap(struct snd_soc_component *component,
+ struct regmap *regmap);
+void snd_soc_component_exit_regmap(struct snd_soc_component *component);
+
+/**
+ * snd_soc_codec_init_regmap() - Initialize regmap instance for the CODEC
+ * @codec: The CODEC for which to initialize the regmap instance
+ * @regmap: The regmap instance that should be used by the CODEC
+ *
+ * This function allows deferred assignment of the regmap instance that is
+ * associated with the CODEC. Only use this if the regmap instance is not yet
+ * ready when the CODEC is registered. The function must also be called before
+ * the first IO attempt of the CODEC.
+ */
+static inline void snd_soc_codec_init_regmap(struct snd_soc_codec *codec,
+ struct regmap *regmap)
+{
+ snd_soc_component_init_regmap(&codec->component, regmap);
+}
+
+/**
+ * snd_soc_codec_exit_regmap() - De-initialize regmap instance for the CODEC
+ * @codec: The CODEC for which to de-initialize the regmap instance
+ *
+ * Calls regmap_exit() on the regmap instance associated to the CODEC and
+ * removes the regmap instance from the CODEC.
+ *
+ * This function should only be used if snd_soc_codec_init_regmap() was used to
+ * initialize the regmap instance.
+ */
+static inline void snd_soc_codec_exit_regmap(struct snd_soc_codec *codec)
+{
+ snd_soc_component_exit_regmap(&codec->component);
+}
+
+#endif
+
/* device driver data */
static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 2a043804a2bc..865e090c8061 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -5,6 +5,10 @@ ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
snd-soc-core-objs += soc-generic-dmaengine-pcm.o
endif
+ifneq ($(CONFIG_SND_SOC_AC97_BUS),)
+snd-soc-core-objs += soc-ac97.o
+endif
+
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
obj-$(CONFIG_SND_SOC) += generic/
diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c
index c8a2de103c5f..5159a50a45a6 100644
--- a/sound/soc/au1x/ac97c.c
+++ b/sound/soc/au1x/ac97c.c
@@ -205,7 +205,7 @@ static int au1xac97c_dai_probe(struct snd_soc_dai *dai)
static struct snd_soc_dai_driver au1xac97c_dai_driver = {
.name = "alchemy-ac97c",
- .ac97_control = 1,
+ .bus_control = true,
.probe = au1xac97c_dai_probe,
.playback = {
.rates = AC97_RATES,
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index 84f31e1f9d24..c6daec98ff89 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -343,7 +343,7 @@ static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
};
static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
- .ac97_control = 1,
+ .bus_control = true,
.probe = au1xpsc_ac97_probe,
.playback = {
.rates = AC97_RATES,
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index e82eb373a731..6bf21a6c02e4 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -260,7 +260,7 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
#endif
static struct snd_soc_dai_driver bfin_ac97_dai = {
- .ac97_control = 1,
+ .bus_control = true,
.suspend = bf5xx_ac97_suspend,
.resume = bf5xx_ac97_resume,
.playback = {
diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c
index 3450e8f9080d..0fa81a523b8a 100644
--- a/sound/soc/blackfin/bf5xx-ad1980.c
+++ b/sound/soc/blackfin/bf5xx-ad1980.c
@@ -46,8 +46,6 @@
#include <linux/gpio.h>
#include <asm/portmux.h>
-#include "../codecs/ad1980.h"
-
#include "bf5xx-ac97.h"
static struct snd_soc_card bf5xx_board;
diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c
index f30dadf85b99..6b8a366b0211 100644
--- a/sound/soc/cirrus/ep93xx-ac97.c
+++ b/sound/soc/cirrus/ep93xx-ac97.c
@@ -338,7 +338,7 @@ static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
static struct snd_soc_dai_driver ep93xx_ac97_dai = {
.name = "ep93xx-ac97",
.id = 0,
- .ac97_control = 1,
+ .bus_control = true,
.probe = ep93xx_ac97_dai_probe,
.playback = {
.stream_name = "AC97 Playback",
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index a68d1731a8fd..6a66216a9c0f 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -223,6 +223,7 @@ config SND_SOC_AD193X_I2C
select SND_SOC_AD193X
config SND_SOC_AD1980
+ select REGMAP_AC97
tristate
config SND_SOC_AD73311
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index bd9b1839c8b0..c6e5a313ebf4 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -37,10 +37,11 @@ static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
- return snd_ac97_set_rate(codec->ac97, reg, substream->runtime->rate);
+ return snd_ac97_set_rate(ac97, reg, substream->runtime->rate);
}
#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
@@ -53,7 +54,6 @@ static const struct snd_soc_dai_ops ac97_dai_ops = {
static struct snd_soc_dai_driver ac97_dai = {
.name = "ac97-hifi",
- .ac97_control = 1,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 1,
@@ -71,6 +71,7 @@ static struct snd_soc_dai_driver ac97_dai = {
static int ac97_soc_probe(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97;
struct snd_ac97_bus *ac97_bus;
struct snd_ac97_template ac97_template;
int ret;
@@ -82,24 +83,31 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
return ret;
memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
- ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97);
+ ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
if (ret < 0)
return ret;
+ snd_soc_codec_set_drvdata(codec, ac97);
+
return 0;
}
#ifdef CONFIG_PM
static int ac97_soc_suspend(struct snd_soc_codec *codec)
{
- snd_ac97_suspend(codec->ac97);
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_ac97_suspend(ac97);
return 0;
}
static int ac97_soc_resume(struct snd_soc_codec *codec)
{
- snd_ac97_resume(codec->ac97);
+
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_ac97_resume(ac97);
return 0;
}
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index 6844d0b2af68..387530b0b0fd 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -72,11 +72,13 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
};
static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
- SND_SOC_DAPM_DAC("DAC", "Playback", AD193X_DAC_CTRL0, 0, 1),
+ SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0),
+ SND_SOC_DAPM_VMID("VMID"),
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
@@ -87,13 +89,15 @@ static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
static const struct snd_soc_dapm_route audio_paths[] = {
{ "DAC", NULL, "SYSCLK" },
+ { "DAC Output", NULL, "DAC" },
+ { "DAC Output", NULL, "VMID" },
{ "ADC", NULL, "SYSCLK" },
{ "DAC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC_PWR" },
- { "DAC1OUT", NULL, "DAC" },
- { "DAC2OUT", NULL, "DAC" },
- { "DAC3OUT", NULL, "DAC" },
- { "DAC4OUT", NULL, "DAC" },
+ { "DAC1OUT", NULL, "DAC Output" },
+ { "DAC2OUT", NULL, "DAC Output" },
+ { "DAC3OUT", NULL, "DAC Output" },
+ { "DAC4OUT", NULL, "DAC Output" },
{ "ADC", NULL, "ADC1IN" },
{ "ADC", NULL, "ADC2IN" },
{ "SYSCLK", NULL, "PLL_PWR" },
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 304d3003339a..2860eef8610c 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -24,34 +24,86 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
+#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
-#include "ad1980.h"
+static const struct reg_default ad1980_reg_defaults[] = {
+ { 0x02, 0x8000 },
+ { 0x04, 0x8000 },
+ { 0x06, 0x8000 },
+ { 0x0c, 0x8008 },
+ { 0x0e, 0x8008 },
+ { 0x10, 0x8808 },
+ { 0x12, 0x8808 },
+ { 0x16, 0x8808 },
+ { 0x18, 0x8808 },
+ { 0x1a, 0x0000 },
+ { 0x1c, 0x8000 },
+ { 0x20, 0x0000 },
+ { 0x28, 0x03c7 },
+ { 0x2c, 0xbb80 },
+ { 0x2e, 0xbb80 },
+ { 0x30, 0xbb80 },
+ { 0x32, 0xbb80 },
+ { 0x36, 0x8080 },
+ { 0x38, 0x8080 },
+ { 0x3a, 0x2000 },
+ { 0x60, 0x0000 },
+ { 0x62, 0x0000 },
+ { 0x72, 0x0000 },
+ { 0x74, 0x1001 },
+ { 0x76, 0x0000 },
+};
-/*
- * AD1980 register cache
- */
-static const u16 ad1980_reg[] = {
- 0x0090, 0x8000, 0x8000, 0x8000, /* 0 - 6 */
- 0x0000, 0x0000, 0x8008, 0x8008, /* 8 - e */
- 0x8808, 0x8808, 0x0000, 0x8808, /* 10 - 16 */
- 0x8808, 0x0000, 0x8000, 0x0000, /* 18 - 1e */
- 0x0000, 0x0000, 0x0000, 0x0000, /* 20 - 26 */
- 0x03c7, 0x0000, 0xbb80, 0xbb80, /* 28 - 2e */
- 0xbb80, 0xbb80, 0x0000, 0x8080, /* 30 - 36 */
- 0x8080, 0x2000, 0x0000, 0x0000, /* 38 - 3e */
- 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
- 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
- 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
- 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
- 0x8080, 0x0000, 0x0000, 0x0000, /* 60 - 66 */
- 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
- 0x0000, 0x0000, 0x1001, 0x0000, /* 70 - 76 */
- 0x0000, 0x0000, 0x4144, 0x5370 /* 78 - 7e */
+static bool ad1980_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AC97_RESET ... AC97_MASTER_MONO:
+ case AC97_PHONE ... AC97_CD:
+ case AC97_AUX ... AC97_GENERAL_PURPOSE:
+ case AC97_POWERDOWN ... AC97_PCM_LR_ADC_RATE:
+ case AC97_SPDIF:
+ case AC97_CODEC_CLASS_REV:
+ case AC97_PCI_SVID:
+ case AC97_AD_CODEC_CFG:
+ case AC97_AD_JACK_SPDIF:
+ case AC97_AD_SERIAL_CFG:
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool ad1980_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ return false;
+ default:
+ return ad1980_readable_reg(dev, reg);
+ }
+}
+
+static const struct regmap_config ad1980_regmap_config = {
+ .reg_bits = 16,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .max_register = 0x7e,
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = regmap_ac97_default_volatile,
+ .readable_reg = ad1980_readable_reg,
+ .writeable_reg = ad1980_writeable_reg,
+
+ .reg_defaults = ad1980_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ad1980_reg_defaults),
};
static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line",
@@ -134,45 +186,8 @@ static const struct snd_soc_dapm_route ad1980_dapm_routes[] = {
{ "HP_OUT_R", NULL, "Playback" },
};
-static unsigned int ac97_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
-
- switch (reg) {
- case AC97_RESET:
- case AC97_INT_PAGING:
- case AC97_POWERDOWN:
- case AC97_EXTENDED_STATUS:
- case AC97_VENDOR_ID1:
- case AC97_VENDOR_ID2:
- return soc_ac97_ops->read(codec->ac97, reg);
- default:
- reg = reg >> 1;
-
- if (reg >= ARRAY_SIZE(ad1980_reg))
- return -EINVAL;
-
- return cache[reg];
- }
-}
-
-static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int val)
-{
- u16 *cache = codec->reg_cache;
-
- soc_ac97_ops->write(codec->ac97, reg, val);
- reg = reg >> 1;
- if (reg < ARRAY_SIZE(ad1980_reg))
- cache[reg] = val;
-
- return 0;
-}
-
static struct snd_soc_dai_driver ad1980_dai = {
.name = "ad1980-hifi",
- .ac97_control = 1,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
@@ -189,108 +204,115 @@ static struct snd_soc_dai_driver ad1980_dai = {
static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
unsigned int retry_cnt = 0;
do {
if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(codec->ac97);
- if (ac97_read(codec, AC97_RESET) == 0x0090)
+ soc_ac97_ops->warm_reset(ac97);
+ if (snd_soc_read(codec, AC97_RESET) == 0x0090)
return 1;
}
- soc_ac97_ops->reset(codec->ac97);
+ soc_ac97_ops->reset(ac97);
/*
* Set bit 16slot in register 74h, then every slot will has only
* 16 bits. This command is sent out in 20bit mode, in which
* case the first nibble of data is eaten by the addr. (Tag is
* always 16 bit)
*/
- ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
+ snd_soc_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
- if (ac97_read(codec, AC97_RESET) == 0x0090)
+ if (snd_soc_read(codec, AC97_RESET) == 0x0090)
return 0;
} while (retry_cnt++ < 10);
- printk(KERN_ERR "AD1980 AC97 reset failed\n");
+ dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+
return -EIO;
}
static int ad1980_soc_probe(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97;
+ struct regmap *regmap;
int ret;
u16 vendor_id2;
u16 ext_status;
- printk(KERN_INFO "AD1980 SoC Audio Codec\n");
-
- ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
- if (ret < 0) {
- printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
+ ac97 = snd_soc_new_ac97_codec(codec);
+ if (IS_ERR(ac97)) {
+ ret = PTR_ERR(ac97);
+ dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
return ret;
}
+ regmap = regmap_init_ac97(ac97, &ad1980_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ goto err_free_ac97;
+ }
+
+ snd_soc_codec_init_regmap(codec, regmap);
+ snd_soc_codec_set_drvdata(codec, ac97);
+
ret = ad1980_reset(codec, 0);
- if (ret < 0) {
- printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
+ if (ret < 0)
goto reset_err;
- }
/* Read out vendor ID to make sure it is ad1980 */
- if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) {
+ if (snd_soc_read(codec, AC97_VENDOR_ID1) != 0x4144) {
ret = -ENODEV;
goto reset_err;
}
- vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);
+ vendor_id2 = snd_soc_read(codec, AC97_VENDOR_ID2);
if (vendor_id2 != 0x5370) {
if (vendor_id2 != 0x5374) {
ret = -ENODEV;
goto reset_err;
} else {
- printk(KERN_WARNING "ad1980: "
- "Found AD1981 - only 2/2 IN/OUT Channels "
- "supported\n");
+ dev_warn(codec->dev,
+ "Found AD1981 - only 2/2 IN/OUT Channels supported\n");
}
}
/* unmute captures and playbacks volume */
- ac97_write(codec, AC97_MASTER, 0x0000);
- ac97_write(codec, AC97_PCM, 0x0000);
- ac97_write(codec, AC97_REC_GAIN, 0x0000);
- ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
- ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);
+ snd_soc_write(codec, AC97_MASTER, 0x0000);
+ snd_soc_write(codec, AC97_PCM, 0x0000);
+ snd_soc_write(codec, AC97_REC_GAIN, 0x0000);
+ snd_soc_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
+ snd_soc_write(codec, AC97_SURROUND_MASTER, 0x0000);
/*power on LFE/CENTER/Surround DACs*/
- ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
- ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
-
- snd_soc_add_codec_controls(codec, ad1980_snd_ac97_controls,
- ARRAY_SIZE(ad1980_snd_ac97_controls));
+ ext_status = snd_soc_read(codec, AC97_EXTENDED_STATUS);
+ snd_soc_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
return 0;
reset_err:
- snd_soc_free_ac97_codec(codec);
+ snd_soc_codec_exit_regmap(codec);
+err_free_ac97:
+ snd_soc_free_ac97_codec(ac97);
return ret;
}
static int ad1980_soc_remove(struct snd_soc_codec *codec)
{
- snd_soc_free_ac97_codec(codec);
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_codec_exit_regmap(codec);
+ snd_soc_free_ac97_codec(ac97);
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
.probe = ad1980_soc_probe,
.remove = ad1980_soc_remove,
- .reg_cache_size = ARRAY_SIZE(ad1980_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = ad1980_reg,
- .reg_cache_step = 2,
- .write = ac97_write,
- .read = ac97_read,
+ .controls = ad1980_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls),
.dapm_widgets = ad1980_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
.dapm_routes = ad1980_dapm_routes,
diff --git a/sound/soc/codecs/ad1980.h b/sound/soc/codecs/ad1980.h
deleted file mode 100644
index eb0af44ad3df..000000000000
--- a/sound/soc/codecs/ad1980.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * ad1980.h -- ad1980 Soc Audio driver
- *
- * WARNING:
- *
- * Because Analog Devices Inc. discontinued the ad1980 sound chip since
- * Sep. 2009, this ad1980 driver is not maintained, tested and supported
- * by ADI now.
- */
-
-#ifndef _AD1980_H
-#define _AD1980_H
-/* Bit definition of Power-Down Control/Status Register */
-#define ADC 0x0001
-#define DAC 0x0002
-#define ANL 0x0004
-#define REF 0x0008
-#define PR0 0x0100
-#define PR1 0x0200
-#define PR2 0x0400
-#define PR3 0x0800
-#define PR4 0x1000
-#define PR5 0x2000
-#define PR6 0x4000
-
-#endif
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index 7c784ad3e8b2..783dcb57043a 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -551,7 +551,7 @@ static const struct snd_kcontrol_new adau1373_drc_controls[] = {
static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
unsigned int pll_id = w->name[3] - '1';
unsigned int val;
@@ -823,7 +823,7 @@ static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = source->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
unsigned int dai;
const char *clk;
@@ -844,7 +844,7 @@ static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
static int adau1373_check_src(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = source->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
unsigned int dai;
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index 91f60282fd2f..16093dc89441 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -255,7 +255,8 @@ static const struct snd_kcontrol_new adau1761_input_mux_control =
static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
/* After any power changes have been made the dejitter circuit
* has to be reinitialized. */
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
index e9fc00fb13dd..aa6a37cc44b7 100644
--- a/sound/soc/codecs/adau1781.c
+++ b/sound/soc/codecs/adau1781.c
@@ -174,7 +174,7 @@ static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
/* After any power changes have been made the dejitter circuit
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index 3e16c1c64115..427ad77bfe56 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -61,7 +61,8 @@ static const struct snd_kcontrol_new adau17x1_controls[] = {
static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
int ret;
if (SND_SOC_DAPM_EVENT_ON(event)) {
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 53b810d23fea..f37a79ec45e6 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -139,18 +139,19 @@ static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
if (reg > AC97_STAC_PAGE0) {
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- soc_ac97_ops->write(codec->ac97, reg, val);
+ soc_ac97_ops->write(ac97, reg, val);
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return 0;
}
if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
return -EIO;
- soc_ac97_ops->write(codec->ac97, reg, val);
+ soc_ac97_ops->write(ac97, reg, val);
cache[reg / 2] = val;
return 0;
}
@@ -158,11 +159,12 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
u16 val = 0, *cache = codec->reg_cache;
if (reg > AC97_STAC_PAGE0) {
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- val = soc_ac97_ops->read(codec->ac97, reg - AC97_STAC_PAGE0);
+ val = soc_ac97_ops->read(ac97, reg - AC97_STAC_PAGE0);
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return val;
}
@@ -173,7 +175,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
reg == AC97_VENDOR_ID2) {
- val = soc_ac97_ops->read(codec->ac97, reg);
+ val = soc_ac97_ops->read(ac97, reg);
return val;
}
return cache[reg / 2];
@@ -240,15 +242,17 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec,
static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(codec->ac97);
+ soc_ac97_ops->warm_reset(ac97);
if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
return 1;
}
- soc_ac97_ops->reset(codec->ac97);
+ soc_ac97_ops->reset(ac97);
if (soc_ac97_ops->warm_reset)
- soc_ac97_ops->warm_reset(codec->ac97);
+ soc_ac97_ops->warm_reset(ac97);
if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
return -EIO;
return 0;
@@ -262,6 +266,7 @@ static int stac9766_codec_suspend(struct snd_soc_codec *codec)
static int stac9766_codec_resume(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
u16 id, reset;
reset = 0;
@@ -271,8 +276,8 @@ reset:
printk(KERN_ERR "stac9766 failed to resume");
return -EIO;
}
- codec->ac97->bus->ops->warm_reset(codec->ac97);
- id = soc_ac97_ops->read(codec->ac97, AC97_VENDOR_ID2);
+ ac97->bus->ops->warm_reset(ac97);
+ id = soc_ac97_ops->read(ac97, AC97_VENDOR_ID2);
if (id != 0x4c13) {
stac9766_reset(codec, 0);
reset++;
@@ -294,7 +299,6 @@ static const struct snd_soc_dai_ops stac9766_dai_ops_digital = {
static struct snd_soc_dai_driver stac9766_dai[] = {
{
.name = "stac9766-hifi-analog",
- .ac97_control = 1,
/* stream cababilities */
.playback = {
@@ -316,7 +320,6 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
},
{
.name = "stac9766-hifi-IEC958",
- .ac97_control = 1,
/* stream cababilities */
.playback = {
@@ -334,11 +337,14 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
static int stac9766_codec_probe(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97;
int ret = 0;
- ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
- if (ret < 0)
- goto codec_err;
+ ac97 = snd_soc_new_ac97_codec(codec);
+ if (IS_ERR(ac97))
+ return PTR_ERR(ac97);
+
+ snd_soc_codec_set_drvdata(codec, ac97);
/* do a cold reset for the controller and then try
* a warm reset followed by an optional cold reset for codec */
@@ -357,13 +363,15 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec)
return 0;
codec_err:
- snd_soc_free_ac97_codec(codec);
+ snd_soc_free_ac97_codec(ac97);
return ret;
}
static int stac9766_codec_remove(struct snd_soc_codec *codec)
{
- snd_soc_free_ac97_codec(codec);
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_free_ac97_codec(ac97);
return 0;
}
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index c0b7f45dfa37..d3a800fa6f06 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -203,13 +203,14 @@ static const struct snd_soc_dapm_route wm9705_audio_map[] = {
/* We use a register cache to enhance read performance. */
static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
switch (reg) {
case AC97_RESET:
case AC97_VENDOR_ID1:
case AC97_VENDOR_ID2:
- return soc_ac97_ops->read(codec->ac97, reg);
+ return soc_ac97_ops->read(ac97, reg);
default:
reg = reg >> 1;
@@ -223,9 +224,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
- soc_ac97_ops->write(codec->ac97, reg, val);
+ soc_ac97_ops->write(ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9705_reg)))
cache[reg] = val;
@@ -263,7 +265,6 @@ static const struct snd_soc_dai_ops wm9705_dai_ops = {
static struct snd_soc_dai_driver wm9705_dai[] = {
{
.name = "wm9705-hifi",
- .ac97_control = 1,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
@@ -294,36 +295,41 @@ static struct snd_soc_dai_driver wm9705_dai[] = {
static int wm9705_reset(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
if (soc_ac97_ops->reset) {
- soc_ac97_ops->reset(codec->ac97);
+ soc_ac97_ops->reset(ac97);
if (ac97_read(codec, 0) == wm9705_reg[0])
return 0; /* Success */
}
+ dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+
return -EIO;
}
#ifdef CONFIG_PM
static int wm9705_soc_suspend(struct snd_soc_codec *codec)
{
- soc_ac97_ops->write(codec->ac97, AC97_POWERDOWN, 0xffff);
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ soc_ac97_ops->write(ac97, AC97_POWERDOWN, 0xffff);
return 0;
}
static int wm9705_soc_resume(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
int i, ret;
u16 *cache = codec->reg_cache;
ret = wm9705_reset(codec);
- if (ret < 0) {
- printk(KERN_ERR "could not reset AC97 codec\n");
+ if (ret < 0)
return ret;
- }
for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) {
- soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(ac97, i, cache[i>>1]);
}
return 0;
@@ -335,31 +341,34 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
static int wm9705_soc_probe(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97;
int ret = 0;
- ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
- if (ret < 0) {
- printk(KERN_ERR "wm9705: failed to register AC97 codec\n");
+ ac97 = snd_soc_new_ac97_codec(codec);
+ if (IS_ERR(ac97)) {
+ ret = PTR_ERR(ac97);
+ dev_err(codec->dev, "Failed to register AC97 codec\n");
return ret;
}
+ snd_soc_codec_set_drvdata(codec, ac97);
+
ret = wm9705_reset(codec);
if (ret)
goto reset_err;
- snd_soc_add_codec_controls(codec, wm9705_snd_ac97_controls,
- ARRAY_SIZE(wm9705_snd_ac97_controls));
-
return 0;
reset_err:
- snd_soc_free_ac97_codec(codec);
+ snd_soc_free_ac97_codec(ac97);
return ret;
}
static int wm9705_soc_remove(struct snd_soc_codec *codec)
{
- snd_soc_free_ac97_codec(codec);
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_free_ac97_codec(ac97);
return 0;
}
@@ -374,6 +383,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9705_reg,
+
+ .controls = wm9705_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls),
.dapm_widgets = wm9705_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
.dapm_routes = wm9705_audio_map,
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index c5eb746087b4..52a211be5b47 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -23,6 +23,12 @@
#include <sound/tlv.h>
#include "wm9712.h"
+struct wm9712_priv {
+ struct snd_ac97 *ac97;
+ unsigned int hp_mixer[2];
+ struct mutex lock;
+};
+
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg);
static int ac97_write(struct snd_soc_codec *codec,
@@ -48,12 +54,10 @@ static const u16 wm9712_reg[] = {
0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */
- 0x0000, 0x0000 /* virtual hp mixers */
};
-/* virtual HP mixers regs */
-#define HPL_MIXER 0x80
-#define HPR_MIXER 0x82
+#define HPL_MIXER 0x0
+#define HPR_MIXER 0x1
static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"};
@@ -157,75 +161,108 @@ SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv),
};
+static const unsigned int wm9712_mixer_mute_regs[] = {
+ AC97_VIDEO,
+ AC97_PCM,
+ AC97_LINE,
+ AC97_PHONE,
+ AC97_CD,
+ AC97_PC_BEEP,
+};
+
/* We have to create a fake left and right HP mixers because
* the codec only has a single control that is shared by both channels.
* This makes it impossible to determine the audio path.
*/
-static int mixer_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
+static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- u16 l, r, beep, line, phone, mic, pcm, aux;
-
- l = ac97_read(w->codec, HPL_MIXER);
- r = ac97_read(w->codec, HPR_MIXER);
- beep = ac97_read(w->codec, AC97_PC_BEEP);
- mic = ac97_read(w->codec, AC97_VIDEO);
- phone = ac97_read(w->codec, AC97_PHONE);
- line = ac97_read(w->codec, AC97_LINE);
- pcm = ac97_read(w->codec, AC97_PCM);
- aux = ac97_read(w->codec, AC97_CD);
-
- if (l & 0x1 || r & 0x1)
- ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int mixer, mask, shift, old;
+ struct snd_soc_dapm_update update;
+ bool change;
+
+ mixer = mc->shift >> 8;
+ shift = mc->shift & 0xff;
+ mask = 1 << shift;
+
+ mutex_lock(&wm9712->lock);
+ old = wm9712->hp_mixer[mixer];
+ if (ucontrol->value.enumerated.item[0])
+ wm9712->hp_mixer[mixer] |= mask;
else
- ac97_write(w->codec, AC97_VIDEO, mic | 0x8000);
+ wm9712->hp_mixer[mixer] &= ~mask;
+
+ change = old != wm9712->hp_mixer[mixer];
+ if (change) {
+ update.kcontrol = kcontrol;
+ update.reg = wm9712_mixer_mute_regs[shift];
+ update.mask = 0x8000;
+ if ((wm9712->hp_mixer[0] & mask) ||
+ (wm9712->hp_mixer[1] & mask))
+ update.val = 0x0;
+ else
+ update.val = 0x8000;
+
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
+ &update);
+ }
- if (l & 0x2 || r & 0x2)
- ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
- else
- ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
+ mutex_unlock(&wm9712->lock);
- if (l & 0x4 || r & 0x4)
- ac97_write(w->codec, AC97_LINE, line & 0x7fff);
- else
- ac97_write(w->codec, AC97_LINE, line | 0x8000);
+ return change;
+}
- if (l & 0x8 || r & 0x8)
- ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
- else
- ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
+static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int shift, mixer;
- if (l & 0x10 || r & 0x10)
- ac97_write(w->codec, AC97_CD, aux & 0x7fff);
- else
- ac97_write(w->codec, AC97_CD, aux | 0x8000);
+ mixer = mc->shift >> 8;
+ shift = mc->shift & 0xff;
- if (l & 0x20 || r & 0x20)
- ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
- else
- ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
+ ucontrol->value.enumerated.item[0] =
+ (wm9712->hp_mixer[mixer] >> shift) & 1;
return 0;
}
+#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, \
+ .get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \
+ .private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \
+ (xmixer << 8) | xshift, 1, 0, 0) \
+}
+
/* Left Headphone Mixers */
static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
- SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0),
- SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0),
- SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0),
- SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0),
- SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0),
- SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0),
+ WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5),
+ WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4),
+ WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3),
+ WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2),
+ WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1),
+ WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0),
};
/* Right Headphone Mixers */
static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
- SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0),
- SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0),
- SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0),
- SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0),
- SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0),
- SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0),
+ WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5),
+ WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4),
+ WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3),
+ WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2),
+ WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1),
+ WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0),
};
/* Speaker Mixer */
@@ -299,12 +336,10 @@ SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0,
SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
&wm9712_diff_sel_controls),
SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
-SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1,
- &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls),
- mixer_event, SND_SOC_DAPM_POST_REG),
-SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1,
- &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls),
- mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1,
+ &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1,
+ &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)),
SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
&wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
@@ -450,12 +485,13 @@ static const struct snd_soc_dapm_route wm9712_audio_map[] = {
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
reg == AC97_REC_GAIN)
- return soc_ac97_ops->read(codec->ac97, reg);
+ return soc_ac97_ops->read(wm9712->ac97, reg);
else {
reg = reg >> 1;
@@ -469,10 +505,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
- if (reg < 0x7c)
- soc_ac97_ops->write(codec->ac97, reg, val);
+ soc_ac97_ops->write(wm9712->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9712_reg)))
cache[reg] = val;
@@ -532,7 +568,6 @@ static const struct snd_soc_dai_ops wm9712_dai_ops_aux = {
static struct snd_soc_dai_driver wm9712_dai[] = {
{
.name = "wm9712-hifi",
- .ac97_control = 1,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
@@ -581,21 +616,23 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec,
static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+
if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(codec->ac97);
+ soc_ac97_ops->warm_reset(wm9712->ac97);
if (ac97_read(codec, 0) == wm9712_reg[0])
return 1;
}
- soc_ac97_ops->reset(codec->ac97);
+ soc_ac97_ops->reset(wm9712->ac97);
if (soc_ac97_ops->warm_reset)
- soc_ac97_ops->warm_reset(codec->ac97);
+ soc_ac97_ops->warm_reset(wm9712->ac97);
if (ac97_read(codec, 0) != wm9712_reg[0])
goto err;
return 0;
err:
- printk(KERN_ERR "WM9712 AC97 reset failed\n");
+ dev_err(codec->dev, "Failed to reset: AC97 link error\n");
return -EIO;
}
@@ -607,14 +644,13 @@ static int wm9712_soc_suspend(struct snd_soc_codec *codec)
static int wm9712_soc_resume(struct snd_soc_codec *codec)
{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
int i, ret;
u16 *cache = codec->reg_cache;
ret = wm9712_reset(codec, 1);
- if (ret < 0) {
- printk(KERN_ERR "could not reset AC97 codec\n");
+ if (ret < 0)
return ret;
- }
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -624,7 +660,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
(i > 0x58 && i != 0x5c))
continue;
- soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(wm9712->ac97, i, cache[i>>1]);
}
}
@@ -633,37 +669,37 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
static int wm9712_soc_probe(struct snd_soc_codec *codec)
{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
- ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
- if (ret < 0) {
- printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
+ wm9712->ac97 = snd_soc_new_ac97_codec(codec);
+ if (IS_ERR(wm9712->ac97)) {
+ ret = PTR_ERR(wm9712->ac97);
+ dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
return ret;
}
ret = wm9712_reset(codec, 0);
- if (ret < 0) {
- printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n");
+ if (ret < 0)
goto reset_err;
- }
/* set alc mux to none */
ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- snd_soc_add_codec_controls(codec, wm9712_snd_ac97_controls,
- ARRAY_SIZE(wm9712_snd_ac97_controls));
return 0;
reset_err:
- snd_soc_free_ac97_codec(codec);
+ snd_soc_free_ac97_codec(wm9712->ac97);
return ret;
}
static int wm9712_soc_remove(struct snd_soc_codec *codec)
{
- snd_soc_free_ac97_codec(codec);
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_free_ac97_codec(wm9712->ac97);
return 0;
}
@@ -679,6 +715,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9712_reg,
+
+ .controls = wm9712_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls),
.dapm_widgets = wm9712_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
.dapm_routes = wm9712_audio_map,
@@ -687,6 +726,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
static int wm9712_probe(struct platform_device *pdev)
{
+ struct wm9712_priv *wm9712;
+
+ wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL);
+ if (wm9712 == NULL)
+ return -ENOMEM;
+
+ mutex_init(&wm9712->lock);
+
+ platform_set_drvdata(pdev, wm9712);
+
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
}
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index bddee30a4bc7..6c95d98b0eb1 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -30,7 +30,10 @@
#include "wm9713.h"
struct wm9713_priv {
+ struct snd_ac97 *ac97;
u32 pll_in; /* PLL input frequency */
+ unsigned int hp_mixer[2];
+ struct mutex lock;
};
static unsigned int ac97_read(struct snd_soc_codec *codec,
@@ -59,13 +62,10 @@ static const u16 wm9713_reg[] = {
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0006,
0x0001, 0x0000, 0x574d, 0x4c13,
- 0x0000, 0x0000, 0x0000
};
-/* virtual HP mixers regs */
-#define HPL_MIXER 0x80
-#define HPR_MIXER 0x82
-#define MICB_MUX 0x82
+#define HPL_MIXER 0
+#define HPR_MIXER 1
static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
@@ -110,7 +110,7 @@ SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */
SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */
SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */
SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */
-SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
+SOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */
};
static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0);
@@ -234,6 +234,14 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
return 0;
}
+static const unsigned int wm9713_mixer_mute_regs[] = {
+ AC97_PC_BEEP,
+ AC97_MASTER_TONE,
+ AC97_PHONE,
+ AC97_REC_SEL,
+ AC97_PCM,
+ AC97_AUX,
+};
/* We have to create a fake left and right HP mixers because
* the codec only has a single control that is shared by both channels.
@@ -241,73 +249,95 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
* register map, thus we add a new (virtual) register to help determine the
* audio route within the device.
*/
-static int mixer_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- u16 l, r, beep, tone, phone, rec, pcm, aux;
-
- l = ac97_read(w->codec, HPL_MIXER);
- r = ac97_read(w->codec, HPR_MIXER);
- beep = ac97_read(w->codec, AC97_PC_BEEP);
- tone = ac97_read(w->codec, AC97_MASTER_TONE);
- phone = ac97_read(w->codec, AC97_PHONE);
- rec = ac97_read(w->codec, AC97_REC_SEL);
- pcm = ac97_read(w->codec, AC97_PCM);
- aux = ac97_read(w->codec, AC97_AUX);
-
- if (event & SND_SOC_DAPM_PRE_REG)
- return 0;
- if ((l & 0x1) || (r & 0x1))
- ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int mixer, mask, shift, old;
+ struct snd_soc_dapm_update update;
+ bool change;
+
+ mixer = mc->shift >> 8;
+ shift = mc->shift & 0xff;
+ mask = (1 << shift);
+
+ mutex_lock(&wm9713->lock);
+ old = wm9713->hp_mixer[mixer];
+ if (ucontrol->value.enumerated.item[0])
+ wm9713->hp_mixer[mixer] |= mask;
else
- ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
+ wm9713->hp_mixer[mixer] &= ~mask;
+
+ change = old != wm9713->hp_mixer[mixer];
+ if (change) {
+ update.kcontrol = kcontrol;
+ update.reg = wm9713_mixer_mute_regs[shift];
+ update.mask = 0x8000;
+ if ((wm9713->hp_mixer[0] & mask) ||
+ (wm9713->hp_mixer[1] & mask))
+ update.val = 0x0;
+ else
+ update.val = 0x8000;
+
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
+ &update);
+ }
- if ((l & 0x2) || (r & 0x2))
- ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff);
- else
- ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000);
+ mutex_unlock(&wm9713->lock);
- if ((l & 0x4) || (r & 0x4))
- ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
- else
- ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
+ return change;
+}
- if ((l & 0x8) || (r & 0x8))
- ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff);
- else
- ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000);
+static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int mixer, shift;
- if ((l & 0x10) || (r & 0x10))
- ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
- else
- ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
+ mixer = mc->shift >> 8;
+ shift = mc->shift & 0xff;
- if ((l & 0x20) || (r & 0x20))
- ac97_write(w->codec, AC97_AUX, aux & 0x7fff);
- else
- ac97_write(w->codec, AC97_AUX, aux | 0x8000);
+ ucontrol->value.enumerated.item[0] =
+ (wm9713->hp_mixer[mixer] >> shift) & 1;
return 0;
}
+#define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, \
+ .get = wm9713_hp_mixer_get, .put = wm9713_hp_mixer_put, \
+ .private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \
+ xshift, xmixer, 1, 0, 0) \
+}
+
/* Left Headphone Mixers */
static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
-SOC_DAPM_SINGLE("Beep Playback Switch", HPL_MIXER, 5, 1, 0),
-SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0),
-SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0),
-SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0),
-SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0),
-SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0),
+WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5),
+WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4),
+WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3),
+WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2),
+WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1),
+WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0),
};
/* Right Headphone Mixers */
static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
-SOC_DAPM_SINGLE("Beep Playback Switch", HPR_MIXER, 5, 1, 0),
-SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0),
-SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0),
-SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0),
-SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0),
-SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0),
+WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5),
+WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4),
+WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3),
+WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2),
+WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1),
+WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0),
};
/* headphone capture mux */
@@ -429,12 +459,10 @@ SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0,
&wm9713_mic_sel_mux_controls),
SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
&wm9713_micb_sel_mux_controls),
-SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
- &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls),
- mixer_event, SND_SOC_DAPM_POST_REG),
-SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
- &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls),
- mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
+ &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
+ &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls)),
SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
&wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1,
@@ -647,12 +675,13 @@ static const struct snd_soc_dapm_route wm9713_audio_map[] = {
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
reg == AC97_CD)
- return soc_ac97_ops->read(codec->ac97, reg);
+ return soc_ac97_ops->read(wm9713->ac97, reg);
else {
reg = reg >> 1;
@@ -666,9 +695,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+
u16 *cache = codec->reg_cache;
- if (reg < 0x7c)
- soc_ac97_ops->write(codec->ac97, reg, val);
+ soc_ac97_ops->write(wm9713->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9713_reg)))
cache[reg] = val;
@@ -689,7 +719,8 @@ struct _pll_div {
* to allow rounding later */
#define FIXED_PLL_SIZE ((1 << 22) * 10)
-static void pll_factors(struct _pll_div *pll_div, unsigned int source)
+static void pll_factors(struct snd_soc_codec *codec,
+ struct _pll_div *pll_div, unsigned int source)
{
u64 Kpart;
unsigned int K, Ndiv, Nmod, target;
@@ -724,7 +755,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int source)
Ndiv = target / source;
if ((Ndiv < 5) || (Ndiv > 12))
- printk(KERN_WARNING
+ dev_warn(codec->dev,
"WM9713 PLL N value %u out of recommended range!\n",
Ndiv);
@@ -768,7 +799,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
return 0;
}
- pll_factors(&pll_div, freq_in);
+ pll_factors(codec, &pll_div, freq_in);
if (pll_div.k == 0) {
reg = (pll_div.n << 12) | (pll_div.lf << 11) |
@@ -1049,7 +1080,6 @@ static const struct snd_soc_dai_ops wm9713_dai_ops_voice = {
static struct snd_soc_dai_driver wm9713_dai[] = {
{
.name = "wm9713-hifi",
- .ac97_control = 1,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
@@ -1095,17 +1125,22 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
{
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+
if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(codec->ac97);
+ soc_ac97_ops->warm_reset(wm9713->ac97);
if (ac97_read(codec, 0) == wm9713_reg[0])
return 1;
}
- soc_ac97_ops->reset(codec->ac97);
+ soc_ac97_ops->reset(wm9713->ac97);
if (soc_ac97_ops->warm_reset)
- soc_ac97_ops->warm_reset(codec->ac97);
- if (ac97_read(codec, 0) != wm9713_reg[0])
+ soc_ac97_ops->warm_reset(wm9713->ac97);
+ if (ac97_read(codec, 0) != wm9713_reg[0]) {
+ dev_err(codec->dev, "Failed to reset: AC97 link error\n");
return -EIO;
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(wm9713_reset);
@@ -1163,10 +1198,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
u16 *cache = codec->reg_cache;
ret = wm9713_reset(codec, 1);
- if (ret < 0) {
- printk(KERN_ERR "could not reset AC97 codec\n");
+ if (ret < 0)
return ret;
- }
wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1180,7 +1213,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
i == AC97_EXTENDED_MSTATUS || i > 0x66)
continue;
- soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(wm9713->ac97, i, cache[i>>1]);
}
}
@@ -1189,26 +1222,19 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
static int wm9713_soc_probe(struct snd_soc_codec *codec)
{
- struct wm9713_priv *wm9713;
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
int ret = 0, reg;
- wm9713 = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
- if (wm9713 == NULL)
- return -ENOMEM;
- snd_soc_codec_set_drvdata(codec, wm9713);
-
- ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
- if (ret < 0)
- goto codec_err;
+ wm9713->ac97 = snd_soc_new_ac97_codec(codec);
+ if (IS_ERR(wm9713->ac97))
+ return PTR_ERR(wm9713->ac97);
/* do a cold reset for the controller and then try
* a warm reset followed by an optional cold reset for codec */
wm9713_reset(codec, 0);
ret = wm9713_reset(codec, 1);
- if (ret < 0) {
- printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n");
+ if (ret < 0)
goto reset_err;
- }
wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1216,23 +1242,18 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec)
reg = ac97_read(codec, AC97_CD) & 0x7fff;
ac97_write(codec, AC97_CD, reg);
- snd_soc_add_codec_controls(codec, wm9713_snd_ac97_controls,
- ARRAY_SIZE(wm9713_snd_ac97_controls));
-
return 0;
reset_err:
- snd_soc_free_ac97_codec(codec);
-codec_err:
- kfree(wm9713);
+ snd_soc_free_ac97_codec(wm9713->ac97);
return ret;
}
static int wm9713_soc_remove(struct snd_soc_codec *codec)
{
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
- snd_soc_free_ac97_codec(codec);
- kfree(wm9713);
+
+ snd_soc_free_ac97_codec(wm9713->ac97);
return 0;
}
@@ -1248,6 +1269,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9713_reg,
+
+ .controls = wm9713_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls),
.dapm_widgets = wm9713_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
.dapm_routes = wm9713_audio_map,
@@ -1256,6 +1280,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
static int wm9713_probe(struct platform_device *pdev)
{
+ struct wm9713_priv *wm9713;
+
+ wm9713 = devm_kzalloc(&pdev->dev, sizeof(*wm9713), GFP_KERNEL);
+ if (wm9713 == NULL)
+ return -ENOMEM;
+
+ mutex_init(&wm9713->lock);
+
+ platform_set_drvdata(pdev, wm9713);
+
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai));
}
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index e6955170dc42..7fd3cbcd74c0 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -1099,7 +1099,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
};
static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
index ab2fdd76b693..60b0a5b1f1f1 100644
--- a/sound/soc/fsl/imx-ssi.c
+++ b/sound/soc/fsl/imx-ssi.c
@@ -382,7 +382,7 @@ static struct snd_soc_dai_driver imx_ssi_dai = {
static struct snd_soc_dai_driver imx_ac97_dai = {
.probe = imx_ssi_dai_probe,
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c
index f2b5d756b1f3..0b82e209b6e3 100644
--- a/sound/soc/fsl/mpc5200_dma.c
+++ b/sound/soc/fsl/mpc5200_dma.c
@@ -327,9 +327,6 @@ static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
goto capture_alloc_err;
}
- if (rtd->codec->ac97)
- rtd->codec->ac97->private_data = psc_dma;
-
return 0;
capture_alloc_err:
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c
index 24eafa2cfbf4..c6ed6ba965a9 100644
--- a/sound/soc/fsl/mpc5200_psc_ac97.c
+++ b/sound/soc/fsl/mpc5200_psc_ac97.c
@@ -237,7 +237,7 @@ static const struct snd_soc_dai_ops psc_ac97_digital_ops = {
static struct snd_soc_dai_driver psc_ac97_dai[] = {
{
.name = "mpc5200-psc-ac97.0",
- .ac97_control = 1,
+ .bus_control = true,
.probe = psc_ac97_probe,
.playback = {
.stream_name = "AC97 Playback",
@@ -257,7 +257,7 @@ static struct snd_soc_dai_driver psc_ac97_dai[] = {
},
{
.name = "mpc5200-psc-ac97.1",
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.stream_name = "AC97 SPDIF",
.channels_min = 1,
@@ -282,7 +282,6 @@ static const struct snd_soc_component_driver psc_ac97_component = {
static int psc_ac97_of_probe(struct platform_device *op)
{
int rc;
- struct snd_ac97 ac97;
struct mpc52xx_psc __iomem *regs;
rc = mpc5200_audio_dma_create(op);
@@ -304,7 +303,6 @@ static int psc_ac97_of_probe(struct platform_device *op)
psc_dma = dev_get_drvdata(&op->dev);
regs = psc_dma->psc_regs;
- ac97.private_data = psc_dma;
psc_dma->imr = 0;
out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c
index f2f67942b229..dff443e4b657 100644
--- a/sound/soc/nuc900/nuc900-ac97.c
+++ b/sound/soc/nuc900/nuc900-ac97.c
@@ -298,7 +298,7 @@ static const struct snd_soc_dai_ops nuc900_ac97_dai_ops = {
static struct snd_soc_dai_driver nuc900_ac97_dai = {
.probe = nuc900_ac97_probe,
.remove = nuc900_ac97_remove,
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index ae956e3f4b9d..73ca2820c08c 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -157,7 +157,7 @@ static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
{
.name = "pxa2xx-ac97",
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
@@ -174,7 +174,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
},
{
.name = "pxa2xx-ac97-aux",
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.stream_name = "AC97 Aux Playback",
.channels_min = 1,
@@ -191,7 +191,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
},
{
.name = "pxa2xx-ac97-mic",
- .ac97_control = 1,
+ .bus_control = true,
.capture = {
.stream_name = "AC97 Mic Capture",
.channels_min = 1,
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index e1615113fd84..7952a625669d 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -288,7 +288,7 @@ static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai)
static struct snd_soc_dai_driver s3c_ac97_dai[] = {
[S3C_AC97_DAI_PCM] = {
.name = "samsung-ac97",
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
@@ -306,7 +306,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = {
},
[S3C_AC97_DAI_MIC] = {
.name = "samsung-ac97-mic",
- .ac97_control = 1,
+ .bus_control = true,
.capture = {
.stream_name = "AC97 Mic Capture",
.channels_min = 1,
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
index 0af2e4dfd139..d5f567e085ff 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/sh/hac.c
@@ -272,7 +272,7 @@ static const struct snd_soc_dai_ops hac_dai_ops = {
static struct snd_soc_dai_driver sh4_hac_dai[] = {
{
.name = "hac-dai.0",
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c
new file mode 100644
index 000000000000..2e10e9a38376
--- /dev/null
+++ b/sound/soc/soc-ac97.c
@@ -0,0 +1,256 @@
+/*
+ * soc-ac97.c -- ALSA SoC Audio Layer AC97 support
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright (C) 2010 Slimlogic Ltd.
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * with code, comments and ideas from :-
+ * Richard Purdie <richard@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/slab.h>
+#include <sound/ac97_codec.h>
+#include <sound/soc.h>
+
+struct snd_ac97_reset_cfg {
+ struct pinctrl *pctl;
+ struct pinctrl_state *pstate_reset;
+ struct pinctrl_state *pstate_warm_reset;
+ struct pinctrl_state *pstate_run;
+ int gpio_sdata;
+ int gpio_sync;
+ int gpio_reset;
+};
+
+static struct snd_ac97_bus soc_ac97_bus = {
+ .ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */
+};
+
+static void soc_ac97_device_release(struct device *dev)
+{
+ kfree(to_ac97_t(dev));
+}
+
+/**
+ * snd_soc_new_ac97_codec - initailise AC97 device
+ * @codec: audio codec
+ *
+ * Initialises AC97 codec resources for use by ad-hoc devices only.
+ */
+struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec)
+{
+ struct snd_ac97 *ac97;
+ int ret;
+
+ ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
+ if (ac97 == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ac97->bus = &soc_ac97_bus;
+ ac97->num = 0;
+
+ ac97->dev.bus = &ac97_bus_type;
+ ac97->dev.parent = codec->component.card->dev;
+ ac97->dev.release = soc_ac97_device_release;
+
+ dev_set_name(&ac97->dev, "%d-%d:%s",
+ codec->component.card->snd_card->number, 0,
+ codec->component.name);
+
+ ret = device_register(&ac97->dev);
+ if (ret) {
+ put_device(&ac97->dev);
+ return ERR_PTR(ret);
+ }
+
+ return ac97;
+}
+EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
+
+/**
+ * snd_soc_free_ac97_codec - free AC97 codec device
+ * @codec: audio codec
+ *
+ * Frees AC97 codec device resources.
+ */
+void snd_soc_free_ac97_codec(struct snd_ac97 *ac97)
+{
+ device_del(&ac97->dev);
+ ac97->bus = NULL;
+ put_device(&ac97->dev);
+}
+EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
+
+static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
+
+static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+ pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
+
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
+
+ udelay(10);
+
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+
+ pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+ msleep(2);
+}
+
+static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
+{
+ struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+ pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
+
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
+
+ udelay(10);
+
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
+
+ pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+ msleep(2);
+}
+
+static int snd_soc_ac97_parse_pinctl(struct device *dev,
+ struct snd_ac97_reset_cfg *cfg)
+{
+ struct pinctrl *p;
+ struct pinctrl_state *state;
+ int gpio;
+ int ret;
+
+ p = devm_pinctrl_get(dev);
+ if (IS_ERR(p)) {
+ dev_err(dev, "Failed to get pinctrl\n");
+ return PTR_ERR(p);
+ }
+ cfg->pctl = p;
+
+ state = pinctrl_lookup_state(p, "ac97-reset");
+ if (IS_ERR(state)) {
+ dev_err(dev, "Can't find pinctrl state ac97-reset\n");
+ return PTR_ERR(state);
+ }
+ cfg->pstate_reset = state;
+
+ state = pinctrl_lookup_state(p, "ac97-warm-reset");
+ if (IS_ERR(state)) {
+ dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
+ return PTR_ERR(state);
+ }
+ cfg->pstate_warm_reset = state;
+
+ state = pinctrl_lookup_state(p, "ac97-running");
+ if (IS_ERR(state)) {
+ dev_err(dev, "Can't find pinctrl state ac97-running\n");
+ return PTR_ERR(state);
+ }
+ cfg->pstate_run = state;
+
+ gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
+ if (gpio < 0) {
+ dev_err(dev, "Can't find ac97-sync gpio\n");
+ return gpio;
+ }
+ ret = devm_gpio_request(dev, gpio, "AC97 link sync");
+ if (ret) {
+ dev_err(dev, "Failed requesting ac97-sync gpio\n");
+ return ret;
+ }
+ cfg->gpio_sync = gpio;
+
+ gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
+ if (gpio < 0) {
+ dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
+ return gpio;
+ }
+ ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
+ if (ret) {
+ dev_err(dev, "Failed requesting ac97-sdata gpio\n");
+ return ret;
+ }
+ cfg->gpio_sdata = gpio;
+
+ gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
+ if (gpio < 0) {
+ dev_err(dev, "Can't find ac97-reset gpio\n");
+ return gpio;
+ }
+ ret = devm_gpio_request(dev, gpio, "AC97 link reset");
+ if (ret) {
+ dev_err(dev, "Failed requesting ac97-reset gpio\n");
+ return ret;
+ }
+ cfg->gpio_reset = gpio;
+
+ return 0;
+}
+
+struct snd_ac97_bus_ops *soc_ac97_ops;
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
+{
+ if (ops == soc_ac97_ops)
+ return 0;
+
+ if (soc_ac97_ops && ops)
+ return -EBUSY;
+
+ soc_ac97_ops = ops;
+ soc_ac97_bus.ops = ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
+
+/**
+ * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
+ *
+ * This function sets the reset and warm_reset properties of ops and parses
+ * the device node of pdev to get pinctrl states and gpio numbers to use.
+ */
+int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snd_ac97_reset_cfg cfg;
+ int ret;
+
+ ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_set_ac97_ops(ops);
+ if (ret)
+ return ret;
+
+ ops->warm_reset = snd_soc_ac97_warm_reset;
+ ops->reset = snd_soc_ac97_reset;
+
+ snd_ac97_rst_cfg = cfg;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index d8d5449cc48e..e0d3b6f109c4 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -34,9 +34,6 @@
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
-#include <sound/ac97_codec.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
@@ -69,16 +66,6 @@ static int pmdown_time = 5000;
module_param(pmdown_time, int, 0);
MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
-struct snd_ac97_reset_cfg {
- struct pinctrl *pctl;
- struct pinctrl_state *pstate_reset;
- struct pinctrl_state *pstate_warm_reset;
- struct pinctrl_state *pstate_run;
- int gpio_sdata;
- int gpio_sync;
- int gpio_reset;
-};
-
/* returns the minimum number of bytes needed to represent
* a particular given value */
static int min_bytes_needed(unsigned long val)
@@ -496,40 +483,6 @@ struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
-#ifdef CONFIG_SND_SOC_AC97_BUS
-/* unregister ac97 codec */
-static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
-{
- if (codec->ac97->dev.bus)
- device_unregister(&codec->ac97->dev);
- return 0;
-}
-
-/* stop no dev release warning */
-static void soc_ac97_device_release(struct device *dev){}
-
-/* register ac97 codec to bus */
-static int soc_ac97_dev_register(struct snd_soc_codec *codec)
-{
- int err;
-
- codec->ac97->dev.bus = &ac97_bus_type;
- codec->ac97->dev.parent = codec->component.card->dev;
- codec->ac97->dev.release = soc_ac97_device_release;
-
- dev_set_name(&codec->ac97->dev, "%d-%d:%s",
- codec->component.card->snd_card->number, 0,
- codec->component.name);
- err = device_register(&codec->ac97->dev);
- if (err < 0) {
- dev_err(codec->dev, "ASoC: Can't register ac97 bus\n");
- codec->ac97->dev.bus = NULL;
- return err;
- }
- return 0;
-}
-#endif
-
static void codec2codec_close_delayed_work(struct work_struct *work)
{
/* Currently nothing to do for c2c links
@@ -593,7 +546,7 @@ int snd_soc_suspend(struct device *dev)
if (card->rtd[i].dai_link->ignore_suspend)
continue;
- if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control)
+ if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control)
cpu_dai->driver->suspend(cpu_dai);
}
@@ -667,7 +620,7 @@ int snd_soc_suspend(struct device *dev)
if (card->rtd[i].dai_link->ignore_suspend)
continue;
- if (cpu_dai->driver->suspend && cpu_dai->driver->ac97_control)
+ if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
cpu_dai->driver->suspend(cpu_dai);
/* deactivate pins to sleep state */
@@ -703,14 +656,14 @@ static void soc_resume_deferred(struct work_struct *work)
if (card->resume_pre)
card->resume_pre(card);
- /* resume AC97 DAIs */
+ /* resume control bus DAIs */
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
- if (cpu_dai->driver->resume && cpu_dai->driver->ac97_control)
+ if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
cpu_dai->driver->resume(cpu_dai);
}
@@ -770,7 +723,7 @@ static void soc_resume_deferred(struct work_struct *work)
if (card->rtd[i].dai_link->ignore_suspend)
continue;
- if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control)
+ if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
cpu_dai->driver->resume(cpu_dai);
}
@@ -791,7 +744,8 @@ static void soc_resume_deferred(struct work_struct *work)
int snd_soc_resume(struct device *dev)
{
struct snd_soc_card *card = dev_get_drvdata(dev);
- int i, ac97_control = 0;
+ bool bus_control = false;
+ int i;
/* If the card is not initialized yet there is nothing to do */
if (!card->instantiated)
@@ -814,17 +768,18 @@ int snd_soc_resume(struct device *dev)
}
}
- /* AC97 devices might have other drivers hanging off them so
- * need to resume immediately. Other drivers don't have that
- * problem and may take a substantial amount of time to resume
+ /*
+ * DAIs that also act as the control bus master might have other drivers
+ * hanging off them so need to resume immediately. Other drivers don't
+ * have that problem and may take a substantial amount of time to resume
* due to I/O costs and anti-pop so handle them out of line.
*/
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
- ac97_control |= cpu_dai->driver->ac97_control;
+ bus_control |= cpu_dai->driver->bus_control;
}
- if (ac97_control) {
- dev_dbg(dev, "ASoC: Resuming AC97 immediately\n");
+ if (bus_control) {
+ dev_dbg(dev, "ASoC: Resuming control bus master immediately\n");
soc_resume_deferred(&card->deferred_resume_work);
} else {
dev_dbg(dev, "ASoC: Scheduling resume work\n");
@@ -1387,84 +1342,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
}
}
- /* add platform data for AC97 devices */
- for (i = 0; i < rtd->num_codecs; i++) {
- if (rtd->codec_dais[i]->driver->ac97_control)
- snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97,
- rtd->cpu_dai->ac97_pdata);
- }
-
return 0;
}
-#ifdef CONFIG_SND_SOC_AC97_BUS
-static int soc_register_ac97_codec(struct snd_soc_codec *codec,
- struct snd_soc_dai *codec_dai)
-{
- int ret;
-
- /* Only instantiate AC97 if not already done by the adaptor
- * for the generic AC97 subsystem.
- */
- if (codec_dai->driver->ac97_control && !codec->ac97_registered) {
- /*
- * It is possible that the AC97 device is already registered to
- * the device subsystem. This happens when the device is created
- * via snd_ac97_mixer(). Currently only SoC codec that does so
- * is the generic AC97 glue but others migh emerge.
- *
- * In those cases we don't try to register the device again.
- */
- if (!codec->ac97_created)
- return 0;
-
- ret = soc_ac97_dev_register(codec);
- if (ret < 0) {
- dev_err(codec->dev,
- "ASoC: AC97 device register failed: %d\n", ret);
- return ret;
- }
-
- codec->ac97_registered = 1;
- }
- return 0;
-}
-
-static void soc_unregister_ac97_codec(struct snd_soc_codec *codec)
-{
- if (codec->ac97_registered) {
- soc_ac97_dev_unregister(codec);
- codec->ac97_registered = 0;
- }
-}
-
-static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
- int i, ret;
-
- for (i = 0; i < rtd->num_codecs; i++) {
- struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
-
- ret = soc_register_ac97_codec(codec_dai->codec, codec_dai);
- if (ret) {
- while (--i >= 0)
- soc_unregister_ac97_codec(codec_dai->codec);
- return ret;
- }
- }
-
- return 0;
-}
-
-static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
- int i;
-
- for (i = 0; i < rtd->num_codecs; i++)
- soc_unregister_ac97_codec(rtd->codec_dais[i]->codec);
-}
-#endif
-
static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
{
struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
@@ -1758,20 +1638,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
goto probe_aux_dev_err;
}
-#ifdef CONFIG_SND_SOC_AC97_BUS
- /* register any AC97 codecs */
- for (i = 0; i < card->num_rtd; i++) {
- ret = soc_register_ac97_dai_link(&card->rtd[i]);
- if (ret < 0) {
- dev_err(card->dev,
- "ASoC: failed to register AC97: %d\n", ret);
- while (--i >= 0)
- soc_unregister_ac97_dai_link(&card->rtd[i]);
- goto probe_aux_dev_err;
- }
- }
-#endif
-
card->instantiated = 1;
snd_soc_dapm_sync(&card->dapm);
mutex_unlock(&card->mutex);
@@ -1914,216 +1780,6 @@ static struct platform_driver soc_driver = {
};
/**
- * snd_soc_new_ac97_codec - initailise AC97 device
- * @codec: audio codec
- * @ops: AC97 bus operations
- * @num: AC97 codec number
- *
- * Initialises AC97 codec resources for use by ad-hoc devices only.
- */
-int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
- struct snd_ac97_bus_ops *ops, int num)
-{
- codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
- if (codec->ac97 == NULL)
- return -ENOMEM;
-
- codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL);
- if (codec->ac97->bus == NULL) {
- kfree(codec->ac97);
- codec->ac97 = NULL;
- return -ENOMEM;
- }
-
- codec->ac97->bus->ops = ops;
- codec->ac97->num = num;
-
- /*
- * Mark the AC97 device to be created by us. This way we ensure that the
- * device will be registered with the device subsystem later on.
- */
- codec->ac97_created = 1;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
-
-static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
-
-static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
-{
- struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
-
- pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
-
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
-
- udelay(10);
-
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
-
- pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
- msleep(2);
-}
-
-static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
-{
- struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
-
- pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
-
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
-
- udelay(10);
-
- gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
-
- pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
- msleep(2);
-}
-
-static int snd_soc_ac97_parse_pinctl(struct device *dev,
- struct snd_ac97_reset_cfg *cfg)
-{
- struct pinctrl *p;
- struct pinctrl_state *state;
- int gpio;
- int ret;
-
- p = devm_pinctrl_get(dev);
- if (IS_ERR(p)) {
- dev_err(dev, "Failed to get pinctrl\n");
- return PTR_ERR(p);
- }
- cfg->pctl = p;
-
- state = pinctrl_lookup_state(p, "ac97-reset");
- if (IS_ERR(state)) {
- dev_err(dev, "Can't find pinctrl state ac97-reset\n");
- return PTR_ERR(state);
- }
- cfg->pstate_reset = state;
-
- state = pinctrl_lookup_state(p, "ac97-warm-reset");
- if (IS_ERR(state)) {
- dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
- return PTR_ERR(state);
- }
- cfg->pstate_warm_reset = state;
-
- state = pinctrl_lookup_state(p, "ac97-running");
- if (IS_ERR(state)) {
- dev_err(dev, "Can't find pinctrl state ac97-running\n");
- return PTR_ERR(state);
- }
- cfg->pstate_run = state;
-
- gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
- if (gpio < 0) {
- dev_err(dev, "Can't find ac97-sync gpio\n");
- return gpio;
- }
- ret = devm_gpio_request(dev, gpio, "AC97 link sync");
- if (ret) {
- dev_err(dev, "Failed requesting ac97-sync gpio\n");
- return ret;
- }
- cfg->gpio_sync = gpio;
-
- gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
- if (gpio < 0) {
- dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
- return gpio;
- }
- ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
- if (ret) {
- dev_err(dev, "Failed requesting ac97-sdata gpio\n");
- return ret;
- }
- cfg->gpio_sdata = gpio;
-
- gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
- if (gpio < 0) {
- dev_err(dev, "Can't find ac97-reset gpio\n");
- return gpio;
- }
- ret = devm_gpio_request(dev, gpio, "AC97 link reset");
- if (ret) {
- dev_err(dev, "Failed requesting ac97-reset gpio\n");
- return ret;
- }
- cfg->gpio_reset = gpio;
-
- return 0;
-}
-
-struct snd_ac97_bus_ops *soc_ac97_ops;
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
-
-int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
-{
- if (ops == soc_ac97_ops)
- return 0;
-
- if (soc_ac97_ops && ops)
- return -EBUSY;
-
- soc_ac97_ops = ops;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
-
-/**
- * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
- *
- * This function sets the reset and warm_reset properties of ops and parses
- * the device node of pdev to get pinctrl states and gpio numbers to use.
- */
-int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
- struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct snd_ac97_reset_cfg cfg;
- int ret;
-
- ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
- if (ret)
- return ret;
-
- ret = snd_soc_set_ac97_ops(ops);
- if (ret)
- return ret;
-
- ops->warm_reset = snd_soc_ac97_warm_reset;
- ops->reset = snd_soc_ac97_reset;
-
- snd_ac97_rst_cfg = cfg;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
-
-/**
- * snd_soc_free_ac97_codec - free AC97 codec device
- * @codec: audio codec
- *
- * Frees AC97 codec device resources.
- */
-void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
-{
-#ifdef CONFIG_SND_SOC_AC97_BUS
- soc_unregister_ac97_codec(codec);
-#endif
- kfree(codec->ac97->bus);
- kfree(codec->ac97);
- codec->ac97 = NULL;
- codec->ac97_created = 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
-
-/**
* snd_soc_cnew - create new control
* @_template: control template
* @data: control private data
@@ -2947,22 +2603,62 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
return 0;
}
-static void snd_soc_component_init_regmap(struct snd_soc_component *component)
+static void snd_soc_component_setup_regmap(struct snd_soc_component *component)
{
- if (!component->regmap)
- component->regmap = dev_get_regmap(component->dev, NULL);
- if (component->regmap) {
- int val_bytes = regmap_get_val_bytes(component->regmap);
- /* Errors are legitimate for non-integer byte multiples */
- if (val_bytes > 0)
- component->val_bytes = val_bytes;
- }
+ int val_bytes = regmap_get_val_bytes(component->regmap);
+
+ /* Errors are legitimate for non-integer byte multiples */
+ if (val_bytes > 0)
+ component->val_bytes = val_bytes;
+}
+
+#ifdef CONFIG_REGMAP
+
+/**
+ * snd_soc_component_init_regmap() - Initialize regmap instance for the component
+ * @component: The component for which to initialize the regmap instance
+ * @regmap: The regmap instance that should be used by the component
+ *
+ * This function allows deferred assignment of the regmap instance that is
+ * associated with the component. Only use this if the regmap instance is not
+ * yet ready when the component is registered. The function must also be called
+ * before the first IO attempt of the component.
+ */
+void snd_soc_component_init_regmap(struct snd_soc_component *component,
+ struct regmap *regmap)
+{
+ component->regmap = regmap;
+ snd_soc_component_setup_regmap(component);
}
+EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap);
+
+/**
+ * snd_soc_component_exit_regmap() - De-initialize regmap instance for the component
+ * @component: The component for which to de-initialize the regmap instance
+ *
+ * Calls regmap_exit() on the regmap instance associated to the component and
+ * removes the regmap instance from the component.
+ *
+ * This function should only be used if snd_soc_component_init_regmap() was used
+ * to initialize the regmap instance.
+ */
+void snd_soc_component_exit_regmap(struct snd_soc_component *component)
+{
+ regmap_exit(component->regmap);
+ component->regmap = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap);
+
+#endif
static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
{
- if (!component->write && !component->read)
- snd_soc_component_init_regmap(component);
+ if (!component->write && !component->read) {
+ if (!component->regmap)
+ component->regmap = dev_get_regmap(component->dev, NULL);
+ if (component->regmap)
+ snd_soc_component_setup_regmap(component);
+ }
list_add(&component->list, &component_list);
}
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index 3b0fa12dbff7..29a9957d335a 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -228,7 +228,7 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai)
static struct snd_soc_dai_driver tegra20_ac97_dai = {
.name = "tegra-ac97-pcm",
- .ac97_control = 1,
+ .bus_control = true,
.probe = tegra20_ac97_probe,
.playback = {
.stream_name = "PCM Playback",
diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c
index 9edd68db9f48..f7135cdaa2ca 100644
--- a/sound/soc/txx9/txx9aclc-ac97.c
+++ b/sound/soc/txx9/txx9aclc-ac97.c
@@ -152,7 +152,7 @@ static int txx9aclc_ac97_remove(struct snd_soc_dai *dai)
}
static struct snd_soc_dai_driver txx9aclc_ac97_dai = {
- .ac97_control = 1,
+ .bus_control = true,
.probe = txx9aclc_ac97_probe,
.remove = txx9aclc_ac97_remove,
.playback = {