diff options
Diffstat (limited to 'sound/soc')
98 files changed, 2984 insertions, 4524 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 0e9623368ab0..7d5d6444a837 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -49,7 +49,6 @@ source "sound/soc/mxs/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/rockchip/Kconfig" source "sound/soc/samsung/Kconfig" -source "sound/soc/s6000/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/sirf/Kconfig" source "sound/soc/spear/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 534714a1ca44..865e090c8061 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,10 +1,14 @@ snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o -snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o +snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o 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/ @@ -26,7 +30,6 @@ obj-$(CONFIG_SND_SOC) += kirkwood/ obj-$(CONFIG_SND_SOC) += pxa/ obj-$(CONFIG_SND_SOC) += rockchip/ obj-$(CONFIG_SND_SOC) += samsung/ -obj-$(CONFIG_SND_SOC) += s6000/ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sirf/ obj-$(CONFIG_SND_SOC) += spear/ diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 27e3fc4a536b..fb3878312bf8 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -52,12 +52,3 @@ config SND_AT91_SOC_SAM9X5_WM8731 help Say Y if you want to add support for audio SoC on an at91sam9x5 based board that is using WM8731 codec. - -config SND_AT91_SOC_AFEB9260 - tristate "SoC Audio support for AFEB9260 board" - depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC - select SND_ATMEL_SOC_PDC - select SND_ATMEL_SOC_SSC - select SND_SOC_TLV320AIC23_I2C - help - Say Y here to support sound on AFEB9260 board. diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 5baabc8bde3a..466a821da98c 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -17,4 +17,3 @@ snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o -obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index f403f399808a..b1cc2a4a7fc0 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -310,7 +310,10 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, * transmit and receive, so if a value has already * been set, it must match this value. */ - if (ssc_p->cmr_div == 0) + if (ssc_p->dir_mask != + (SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE)) + ssc_p->cmr_div = div; + else if (ssc_p->cmr_div == 0) ssc_p->cmr_div = div; else if (div != ssc_p->cmr_div) diff --git a/sound/soc/atmel/snd-soc-afeb9260.c b/sound/soc/atmel/snd-soc-afeb9260.c deleted file mode 100644 index 9579799ace54..000000000000 --- a/sound/soc/atmel/snd-soc-afeb9260.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * afeb9260.c -- SoC audio for AFEB9260 - * - * Copyright (C) 2009 Sergey Lapin <slapin@ossfans.org> - * - * 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/module.h> -#include <linux/moduleparam.h> -#include <linux/kernel.h> -#include <linux/clk.h> -#include <linux/platform_device.h> - -#include <linux/atmel-ssc.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> - -#include <asm/mach-types.h> -#include <mach/hardware.h> -#include <linux/gpio.h> - -#include "../codecs/tlv320aic23.h" -#include "atmel-pcm.h" -#include "atmel_ssc_dai.h" - -#define CODEC_CLOCK 12000000 - -static int afeb9260_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->codec_dai; - int err; - - /* Set the codec system clock for DAC and ADC */ - err = - snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN); - - if (err < 0) { - printk(KERN_ERR "can't set codec system clock\n"); - return err; - } - - return err; -} - -static struct snd_soc_ops afeb9260_ops = { - .hw_params = afeb9260_hw_params, -}; - -static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_LINE("Line In", NULL), - SND_SOC_DAPM_MIC("Mic Jack", NULL), -}; - -static const struct snd_soc_dapm_route afeb9260_audio_map[] = { - {"Headphone Jack", NULL, "LHPOUT"}, - {"Headphone Jack", NULL, "RHPOUT"}, - - {"LLINEIN", NULL, "Line In"}, - {"RLINEIN", NULL, "Line In"}, - - {"MICIN", NULL, "Mic Jack"}, -}; - - -/* Digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link afeb9260_dai = { - .name = "TLV320AIC23", - .stream_name = "AIC23", - .cpu_dai_name = "atmel-ssc-dai.0", - .codec_dai_name = "tlv320aic23-hifi", - .platform_name = "atmel_pcm-audio", - .codec_name = "tlv320aic23-codec.0-001a", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | - SND_SOC_DAIFMT_CBM_CFM, - .ops = &afeb9260_ops, -}; - -/* Audio machine driver */ -static struct snd_soc_card snd_soc_machine_afeb9260 = { - .name = "AFEB9260", - .owner = THIS_MODULE, - .dai_link = &afeb9260_dai, - .num_links = 1, - - .dapm_widgets = tlv320aic23_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets), - .dapm_routes = afeb9260_audio_map, - .num_dapm_routes = ARRAY_SIZE(afeb9260_audio_map), -}; - -static struct platform_device *afeb9260_snd_device; - -static int __init afeb9260_soc_init(void) -{ - int err; - struct device *dev; - - if (!(machine_is_afeb9260())) - return -ENODEV; - - - afeb9260_snd_device = platform_device_alloc("soc-audio", -1); - if (!afeb9260_snd_device) { - printk(KERN_ERR "ASoC: Platform device allocation failed\n"); - return -ENOMEM; - } - - platform_set_drvdata(afeb9260_snd_device, &snd_soc_machine_afeb9260); - err = platform_device_add(afeb9260_snd_device); - if (err) - goto err1; - - dev = &afeb9260_snd_device->dev; - - return 0; -err1: - platform_device_put(afeb9260_snd_device); - return err; -} - -static void __exit afeb9260_soc_exit(void) -{ - platform_device_unregister(afeb9260_snd_device); -} - -module_init(afeb9260_soc_init); -module_exit(afeb9260_soc_exit); - -MODULE_AUTHOR("Sergey Lapin <slapin@ossfans.org>"); -MODULE_DESCRIPTION("ALSA SoC for AFEB9260"); -MODULE_LICENSE("GPL"); - 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/Kconfig b/sound/soc/cirrus/Kconfig index 5477c5475923..7b7fbcd49e5e 100644 --- a/sound/soc/cirrus/Kconfig +++ b/sound/soc/cirrus/Kconfig @@ -36,7 +36,8 @@ config SND_EP93XX_SOC_EDB93XX tristate "SoC Audio support for Cirrus Logic EDB93xx boards" depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A) select SND_EP93XX_SOC_I2S - select SND_SOC_CS4271 + select SND_SOC_CS4271_I2C if I2C + select SND_SOC_CS4271_SPI if SPI_MASTER help Say Y or M here if you want to add support for I2S audio on the Cirrus Logic EDB93xx boards. 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..1362edd380e5 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -50,7 +50,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS42L73 if I2C select SND_SOC_CS4265 if I2C select SND_SOC_CS4270 if I2C - select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI + select SND_SOC_CS4271_I2C if I2C + select SND_SOC_CS4271_SPI if SPI_MASTER select SND_SOC_CS42XX8_I2C if I2C select SND_SOC_CX20442 if TTY select SND_SOC_DA7210 if I2C @@ -223,6 +224,7 @@ config SND_SOC_AD193X_I2C select SND_SOC_AD193X config SND_SOC_AD1980 + select REGMAP_AC97 tristate config SND_SOC_AD73311 @@ -336,7 +338,8 @@ config SND_SOC_CS42L51 tristate config SND_SOC_CS42L51_I2C - tristate + tristate "Cirrus Logic CS42L51 CODEC (I2C)" + depends on I2C select SND_SOC_CS42L51 config SND_SOC_CS42L52 @@ -370,8 +373,19 @@ config SND_SOC_CS4270_VD33_ERRATA depends on SND_SOC_CS4270 config SND_SOC_CS4271 - tristate "Cirrus Logic CS4271 CODEC" - depends on SND_SOC_I2C_AND_SPI + tristate + +config SND_SOC_CS4271_I2C + tristate "Cirrus Logic CS4271 CODEC (I2C)" + depends on I2C + select SND_SOC_CS4271 + select REGMAP_I2C + +config SND_SOC_CS4271_SPI + tristate "Cirrus Logic CS4271 CODEC (SPI)" + depends on SPI_MASTER + select SND_SOC_CS4271 + select REGMAP_SPI config SND_SOC_CS42XX8 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5dce451661e4..ac7ec31f8cbe 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -41,6 +41,8 @@ snd-soc-cs42l73-objs := cs42l73.o snd-soc-cs4265-objs := cs4265.o snd-soc-cs4270-objs := cs4270.o snd-soc-cs4271-objs := cs4271.o +snd-soc-cs4271-i2c-objs := cs4271-i2c.o +snd-soc-cs4271-spi-objs := cs4271-spi.o snd-soc-cs42xx8-objs := cs42xx8.o snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o snd-soc-cx20442-objs := cx20442.o @@ -217,6 +219,8 @@ obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o +obj-$(CONFIG_SND_SOC_CS4271_I2C) += snd-soc-cs4271-i2c.o +obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index fd43827bb856..7dfbc9921e91 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -126,13 +126,13 @@ struct ab8500_codec_drvdata_dbg { /* Private data for AB8500 device-driver */ struct ab8500_codec_drvdata { struct regmap *regmap; + struct mutex ctrl_lock; /* Sidetone */ long *sid_fir_values; enum sid_state sid_status; /* ANC */ - struct mutex anc_lock; long *anc_fir_values; long *anc_iir_values; enum anc_state anc_status; @@ -1129,9 +1129,9 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); - mutex_lock(&codec->mutex); + mutex_lock(&drvdata->ctrl_lock); ucontrol->value.integer.value[0] = drvdata->sid_status; - mutex_unlock(&codec->mutex); + mutex_unlock(&drvdata->ctrl_lock); return 0; } @@ -1154,7 +1154,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, return -EIO; } - mutex_lock(&codec->mutex); + mutex_lock(&drvdata->ctrl_lock); sidconf = snd_soc_read(codec, AB8500_SIDFIRCONF); if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) { @@ -1185,7 +1185,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, drvdata->sid_status = SID_FIR_CONFIGURED; out: - mutex_unlock(&codec->mutex); + mutex_unlock(&drvdata->ctrl_lock); dev_dbg(codec->dev, "%s: Exit\n", __func__); @@ -1198,9 +1198,9 @@ static int anc_status_control_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); - mutex_lock(&codec->mutex); + mutex_lock(&drvdata->ctrl_lock); ucontrol->value.integer.value[0] = drvdata->anc_status; - mutex_unlock(&codec->mutex); + mutex_unlock(&drvdata->ctrl_lock); return 0; } @@ -1217,7 +1217,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, dev_dbg(dev, "%s: Enter.\n", __func__); - mutex_lock(&drvdata->anc_lock); + mutex_lock(&drvdata->ctrl_lock); req = ucontrol->value.integer.value[0]; if (req >= ARRAY_SIZE(enum_anc_state)) { @@ -1244,9 +1244,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, } snd_soc_dapm_sync(&codec->dapm); - mutex_lock(&codec->mutex); anc_configure(codec, apply_fir, apply_iir); - mutex_unlock(&codec->mutex); if (apply_fir) { if (drvdata->anc_status == ANC_IIR_CONFIGURED) @@ -1265,7 +1263,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, snd_soc_dapm_sync(&codec->dapm); cleanup: - mutex_unlock(&drvdata->anc_lock); + mutex_unlock(&drvdata->ctrl_lock); if (status < 0) dev_err(dev, "%s: Unable to configure ANC! (status = %d)\n", @@ -1294,14 +1292,15 @@ static int filter_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec); struct filter_control *fc = (struct filter_control *)kcontrol->private_value; unsigned int i; - mutex_lock(&codec->mutex); + mutex_lock(&drvdata->ctrl_lock); for (i = 0; i < fc->count; i++) ucontrol->value.integer.value[i] = fc->value[i]; - mutex_unlock(&codec->mutex); + mutex_unlock(&drvdata->ctrl_lock); return 0; } @@ -1310,14 +1309,15 @@ static int filter_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec); struct filter_control *fc = (struct filter_control *)kcontrol->private_value; unsigned int i; - mutex_lock(&codec->mutex); + mutex_lock(&drvdata->ctrl_lock); for (i = 0; i < fc->count; i++) fc->value[i] = ucontrol->value.integer.value[i]; - mutex_unlock(&codec->mutex); + mutex_unlock(&drvdata->ctrl_lock); return 0; } @@ -2545,7 +2545,7 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec) (void)snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input"); - mutex_init(&drvdata->anc_lock); + mutex_init(&drvdata->ctrl_lock); return status; } 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 5518ebd6947c..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. */ @@ -405,6 +406,7 @@ static const struct snd_soc_dapm_widget adau1761_dapm_widgets[] = { 2, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("Slew Clock", ADAU1761_CLK_ENABLE0, 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ALC Clock", ADAU1761_CLK_ENABLE0, 5, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("Digital Clock 0", 1, ADAU1761_CLK_ENABLE1, 0, 0, NULL, 0), @@ -436,6 +438,9 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = { { "Right Playback Mixer", NULL, "Slew Clock" }, { "Left Playback Mixer", NULL, "Slew Clock" }, + { "Left Input Mixer", NULL, "ALC Clock" }, + { "Right Input Mixer", NULL, "ALC Clock" }, + { "Digital Clock 0", NULL, "SYSCLK" }, { "Digital Clock 1", NULL, "SYSCLK" }, }; 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/adav80x.c b/sound/soc/codecs/adav80x.c index ce3cdca9fc62..b67480f1b1aa 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -212,7 +212,7 @@ static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = { static int adav80x_dapm_sysclk_check(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 adav80x *adav80x = snd_soc_codec_get_drvdata(codec); const char *clk; @@ -236,7 +236,7 @@ static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source, static int adav80x_dapm_pll_check(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 adav80x *adav80x = snd_soc_codec_get_drvdata(codec); return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL; diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 30e297890fec..9130d916f2f4 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -373,33 +373,9 @@ static struct snd_soc_dai_driver ak4535_dai = { .ops = &ak4535_dai_ops, }; -static int ak4535_suspend(struct snd_soc_codec *codec) -{ - ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static int ak4535_resume(struct snd_soc_codec *codec) { snd_soc_cache_sync(codec); - ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - -static int ak4535_probe(struct snd_soc_codec *codec) -{ - /* power on device */ - ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - snd_soc_add_codec_controls(codec, ak4535_snd_controls, - ARRAY_SIZE(ak4535_snd_controls)); - return 0; -} - -/* power down chip */ -static int ak4535_remove(struct snd_soc_codec *codec) -{ - ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -416,11 +392,12 @@ static const struct regmap_config ak4535_regmap = { }; static struct snd_soc_codec_driver soc_codec_dev_ak4535 = { - .probe = ak4535_probe, - .remove = ak4535_remove, - .suspend = ak4535_suspend, .resume = ak4535_resume, .set_bias_level = ak4535_set_bias_level, + .suspend_bias_off = true, + + .controls = ak4535_snd_controls, + .num_controls = ARRAY_SIZE(ak4535_snd_controls), .dapm_widgets = ak4535_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets), .dapm_routes = ak4535_audio_map, diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 7afe8f482088..70861c7b1631 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -505,39 +505,7 @@ static struct snd_soc_dai_driver ak4641_dai[] = { }, }; -static int ak4641_suspend(struct snd_soc_codec *codec) -{ - ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int ak4641_resume(struct snd_soc_codec *codec) -{ - ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return 0; -} - -static int ak4641_probe(struct snd_soc_codec *codec) -{ - /* power on device */ - ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int ak4641_remove(struct snd_soc_codec *codec) -{ - ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - - static struct snd_soc_codec_driver soc_codec_dev_ak4641 = { - .probe = ak4641_probe, - .remove = ak4641_remove, - .suspend = ak4641_suspend, - .resume = ak4641_resume, .controls = ak4641_snd_controls, .num_controls = ARRAY_SIZE(ak4641_snd_controls), .dapm_widgets = ak4641_dapm_widgets, @@ -545,6 +513,7 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4641 = { .dapm_routes = ak4641_audio_map, .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map), .set_bias_level = ak4641_set_bias_level, + .suspend_bias_off = true, }; static const struct regmap_config ak4641_regmap = { diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 041712592e29..dde8b49c19ad 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -491,23 +491,7 @@ static int ak4642_resume(struct snd_soc_codec *codec) return 0; } - -static int ak4642_probe(struct snd_soc_codec *codec) -{ - ak4642_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int ak4642_remove(struct snd_soc_codec *codec) -{ - ak4642_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_ak4642 = { - .probe = ak4642_probe, - .remove = ak4642_remove, .resume = ak4642_resume, .set_bias_level = ak4642_set_bias_level, .controls = ak4642_snd_controls, diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index 998fa0c5a0b9..686cacb0e835 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -611,20 +611,7 @@ static struct snd_soc_dai_driver ak4671_dai = { .ops = &ak4671_dai_ops, }; -static int ak4671_probe(struct snd_soc_codec *codec) -{ - return ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY); -} - -static int ak4671_remove(struct snd_soc_codec *codec) -{ - ak4671_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_ak4671 = { - .probe = ak4671_probe, - .remove = ak4671_remove, .set_bias_level = ak4671_set_bias_level, .controls = ak4671_snd_controls, .num_controls = ARRAY_SIZE(ak4671_snd_controls), diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 9d0755aa1d16..bdf8c5ac8ca4 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -866,7 +866,6 @@ static int alc5623_suspend(struct snd_soc_codec *codec) { struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); - alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF); regcache_cache_only(alc5623->regmap, true); return 0; @@ -887,15 +886,6 @@ static int alc5623_resume(struct snd_soc_codec *codec) return ret; } - alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - /* charge alc5623 caps */ - if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { - alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - codec->dapm.bias_level = SND_SOC_BIAS_ON; - alc5623_set_bias_level(codec, codec->dapm.bias_level); - } - return 0; } @@ -906,9 +896,6 @@ static int alc5623_probe(struct snd_soc_codec *codec) alc5623_reset(codec); - /* power on device */ - alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - if (alc5623->add_ctrl) { snd_soc_write(codec, ALC5623_ADD_CTRL_REG, alc5623->add_ctrl); @@ -964,19 +951,12 @@ static int alc5623_probe(struct snd_soc_codec *codec) return 0; } -/* power down chip */ -static int alc5623_remove(struct snd_soc_codec *codec) -{ - alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_device_alc5623 = { .probe = alc5623_probe, - .remove = alc5623_remove, .suspend = alc5623_suspend, .resume = alc5623_resume, .set_bias_level = alc5623_set_bias_level, + .suspend_bias_off = true, }; static const struct regmap_config alc5623_regmap = { diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index 85942ca36cbf..d1fdbc266631 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -1038,23 +1038,15 @@ static struct snd_soc_dai_driver alc5632_dai = { }; #ifdef CONFIG_PM -static int alc5632_suspend(struct snd_soc_codec *codec) -{ - alc5632_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static int alc5632_resume(struct snd_soc_codec *codec) { struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec); regcache_sync(alc5632->regmap); - alc5632_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; } #else -#define alc5632_suspend NULL #define alc5632_resume NULL #endif @@ -1062,9 +1054,6 @@ static int alc5632_probe(struct snd_soc_codec *codec) { struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec); - /* power on device */ - alc5632_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - switch (alc5632->id) { case 0x5c: snd_soc_add_codec_controls(codec, alc5632_vol_snd_controls, @@ -1077,19 +1066,12 @@ static int alc5632_probe(struct snd_soc_codec *codec) return 0; } -/* power down chip */ -static int alc5632_remove(struct snd_soc_codec *codec) -{ - alc5632_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static struct snd_soc_codec_driver soc_codec_device_alc5632 = { .probe = alc5632_probe, - .remove = alc5632_remove, - .suspend = alc5632_suspend, .resume = alc5632_resume, .set_bias_level = alc5632_set_bias_level, + .suspend_bias_off = true, + .controls = alc5632_snd_controls, .num_controls = ARRAY_SIZE(alc5632_snd_controls), .dapm_widgets = alc5632_dapm_widgets, diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 0c05e7a7945f..9550d7433ad0 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -61,6 +61,11 @@ #define ARIZONA_FLL_MIN_OUTDIV 2 #define ARIZONA_FLL_MAX_OUTDIV 7 +#define ARIZONA_FMT_DSP_MODE_A 0 +#define ARIZONA_FMT_DSP_MODE_B 1 +#define ARIZONA_FMT_I2S_MODE 2 +#define ARIZONA_FMT_LEFT_JUSTIFIED_MODE 3 + #define arizona_fll_err(_fll, fmt, ...) \ dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_fll_warn(_fll, fmt, ...) \ @@ -648,7 +653,7 @@ SOC_ENUM_SINGLE_DECL(arizona_in_hpf_cut_enum, EXPORT_SYMBOL_GPL(arizona_in_hpf_cut_enum); static const char * const arizona_in_dmic_osr_text[] = { - "1.536MHz", "3.072MHz", "6.144MHz", + "1.536MHz", "3.072MHz", "6.144MHz", "768kHz", }; const struct soc_enum arizona_in_dmic_osr[] = { @@ -946,10 +951,26 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_A: - mode = 0; + mode = ARIZONA_FMT_DSP_MODE_A; + break; + case SND_SOC_DAIFMT_DSP_B: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) + != SND_SOC_DAIFMT_CBM_CFM) { + arizona_aif_err(dai, "DSP_B not valid in slave mode\n"); + return -EINVAL; + } + mode = ARIZONA_FMT_DSP_MODE_B; break; case SND_SOC_DAIFMT_I2S: - mode = 2; + mode = ARIZONA_FMT_I2S_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) + != SND_SOC_DAIFMT_CBM_CFM) { + arizona_aif_err(dai, "LEFT_J not valid in slave mode\n"); + return -EINVAL; + } + mode = ARIZONA_FMT_LEFT_JUSTIFIED_MODE; break; default: arizona_aif_err(dai, "Unsupported DAI format %d\n", @@ -1164,13 +1185,13 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec, { 0x80, 0x0 }, }; - mutex_lock(&codec->mutex); + mutex_lock(&arizona->dac_comp_lock); dac_comp[1].def = arizona->dac_comp_coeff; if (rate >= 176400) dac_comp[2].def = arizona->dac_comp_enabled; - mutex_unlock(&codec->mutex); + mutex_unlock(&arizona->dac_comp_lock); regmap_multi_reg_write(arizona->regmap, dac_comp, @@ -1298,7 +1319,8 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, /* Force multiple of 2 channels for I2S mode */ val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT); - if ((channels & 1) && (val & ARIZONA_AIF1_FMT_MASK)) { + val &= ARIZONA_AIF1_FMT_MASK; + if ((channels & 1) && (val == ARIZONA_FMT_I2S_MODE)) { arizona_aif_dbg(dai, "Forcing stereo mode\n"); bclk_target /= channels; bclk_target *= channels + 1; diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 537327c7f7f1..8d638e8aa8eb 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -62,14 +62,10 @@ static int cq93vc_mute(struct snd_soc_dai *dai, int mute) static int cq93vc_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { - struct snd_soc_codec *codec = codec_dai->codec; - struct davinci_vc *davinci_vc = codec->dev->platform_data; - switch (freq) { case 22579200: case 27000000: case 33868800: - davinci_vc->cq93vc.sysclk = freq; return 0; } @@ -126,32 +122,6 @@ static struct snd_soc_dai_driver cq93vc_dai = { .ops = &cq93vc_dai_ops, }; -static int cq93vc_resume(struct snd_soc_codec *codec) -{ - cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int cq93vc_probe(struct snd_soc_codec *codec) -{ - struct davinci_vc *davinci_vc = codec->dev->platform_data; - - davinci_vc->cq93vc.codec = codec; - - /* Off, with power on */ - cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} - -static int cq93vc_remove(struct snd_soc_codec *codec) -{ - cq93vc_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - static struct regmap *cq93vc_get_regmap(struct device *dev) { struct davinci_vc *davinci_vc = dev->platform_data; @@ -161,9 +131,6 @@ static struct regmap *cq93vc_get_regmap(struct device *dev) static struct snd_soc_codec_driver soc_codec_dev_cq93vc = { .set_bias_level = cq93vc_set_bias_level, - .probe = cq93vc_probe, - .remove = cq93vc_remove, - .resume = cq93vc_resume, .get_regmap = cq93vc_get_regmap, .controls = cq93vc_snd_controls, .num_controls = ARRAY_SIZE(cq93vc_snd_controls), diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index 4fdd47d700e3..ce6086835ebd 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -32,7 +32,6 @@ #include "cs4265.h" struct cs4265_private { - struct device *dev; struct regmap *regmap; struct gpio_desc *reset_gpio; u8 format; @@ -598,7 +597,6 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client, GFP_KERNEL); if (cs4265 == NULL) return -ENOMEM; - cs4265->dev = &i2c_client->dev; cs4265->regmap = devm_regmap_init_i2c(i2c_client, &cs4265_regmap); if (IS_ERR(cs4265->regmap)) { diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c new file mode 100644 index 000000000000..b264da030340 --- /dev/null +++ b/sound/soc/codecs/cs4271-i2c.c @@ -0,0 +1,62 @@ +/* + * CS4271 I2C audio driver + * + * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru> + * + * 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. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "cs4271.h" + +static int cs4271_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap_config config; + + config = cs4271_regmap_config; + config.reg_bits = 8; + config.val_bits = 8; + + return cs4271_probe(&client->dev, + devm_regmap_init_i2c(client, &config)); +} + +static int cs4271_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id cs4271_i2c_id[] = { + { "cs4271", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id); + +static struct i2c_driver cs4271_i2c_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(cs4271_dt_ids), + }, + .probe = cs4271_i2c_probe, + .remove = cs4271_i2c_remove, + .id_table = cs4271_i2c_id, +}; +module_i2c_driver(cs4271_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS4271 I2C Driver"); +MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs4271-spi.c b/sound/soc/codecs/cs4271-spi.c new file mode 100644 index 000000000000..acd49d86e706 --- /dev/null +++ b/sound/soc/codecs/cs4271-spi.c @@ -0,0 +1,55 @@ +/* + * CS4271 SPI audio driver + * + * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru> + * + * 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. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "cs4271.h" + +static int cs4271_spi_probe(struct spi_device *spi) +{ + struct regmap_config config; + + config = cs4271_regmap_config; + config.reg_bits = 16; + config.val_bits = 8; + config.read_flag_mask = 0x21; + config.write_flag_mask = 0x20; + + return cs4271_probe(&spi->dev, devm_regmap_init_spi(spi, &config)); +} + +static int cs4271_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static struct spi_driver cs4271_spi_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(cs4271_dt_ids), + }, + .probe = cs4271_spi_probe, + .remove = cs4271_spi_remove, +}; +module_spi_driver(cs4271_spi_driver); + +MODULE_DESCRIPTION("ASoC CS4271 SPI Driver"); +MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 93cec52f4733..79a4efcb894c 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -23,8 +23,6 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/gpio.h> -#include <linux/i2c.h> -#include <linux/spi/spi.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_gpio.h> @@ -32,6 +30,7 @@ #include <sound/soc.h> #include <sound/tlv.h> #include <sound/cs4271.h> +#include "cs4271.h" #define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ @@ -527,14 +526,15 @@ static int cs4271_soc_resume(struct snd_soc_codec *codec) #endif /* CONFIG_PM */ #ifdef CONFIG_OF -static const struct of_device_id cs4271_dt_ids[] = { +const struct of_device_id cs4271_dt_ids[] = { { .compatible = "cirrus,cs4271", }, { } }; MODULE_DEVICE_TABLE(of, cs4271_dt_ids); +EXPORT_SYMBOL_GPL(cs4271_dt_ids); #endif -static int cs4271_probe(struct snd_soc_codec *codec) +static int cs4271_codec_probe(struct snd_soc_codec *codec) { struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); struct cs4271_platform_data *cs4271plat = codec->dev->platform_data; @@ -587,7 +587,7 @@ static int cs4271_probe(struct snd_soc_codec *codec) return 0; } -static int cs4271_remove(struct snd_soc_codec *codec) +static int cs4271_codec_remove(struct snd_soc_codec *codec) { struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); @@ -599,8 +599,8 @@ static int cs4271_remove(struct snd_soc_codec *codec) }; static struct snd_soc_codec_driver soc_codec_dev_cs4271 = { - .probe = cs4271_probe, - .remove = cs4271_remove, + .probe = cs4271_codec_probe, + .remove = cs4271_codec_remove, .suspend = cs4271_soc_suspend, .resume = cs4271_soc_resume, @@ -642,14 +642,8 @@ static int cs4271_common_probe(struct device *dev, return 0; } -#if defined(CONFIG_SPI_MASTER) - -static const struct regmap_config cs4271_spi_regmap = { - .reg_bits = 16, - .val_bits = 8, +const struct regmap_config cs4271_regmap_config = { .max_register = CS4271_LASTREG, - .read_flag_mask = 0x21, - .write_flag_mask = 0x20, .reg_defaults = cs4271_reg_defaults, .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults), @@ -657,140 +651,27 @@ static const struct regmap_config cs4271_spi_regmap = { .volatile_reg = cs4271_volatile_reg, }; +EXPORT_SYMBOL_GPL(cs4271_regmap_config); -static int cs4271_spi_probe(struct spi_device *spi) +int cs4271_probe(struct device *dev, struct regmap *regmap) { struct cs4271_private *cs4271; int ret; - ret = cs4271_common_probe(&spi->dev, &cs4271); - if (ret < 0) - return ret; - - spi_set_drvdata(spi, cs4271); - cs4271->regmap = devm_regmap_init_spi(spi, &cs4271_spi_regmap); - if (IS_ERR(cs4271->regmap)) - return PTR_ERR(cs4271->regmap); - - return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271, - &cs4271_dai, 1); -} - -static int cs4271_spi_remove(struct spi_device *spi) -{ - snd_soc_unregister_codec(&spi->dev); - return 0; -} - -static struct spi_driver cs4271_spi_driver = { - .driver = { - .name = "cs4271", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(cs4271_dt_ids), - }, - .probe = cs4271_spi_probe, - .remove = cs4271_spi_remove, -}; -#endif /* defined(CONFIG_SPI_MASTER) */ - -#if IS_ENABLED(CONFIG_I2C) -static const struct i2c_device_id cs4271_i2c_id[] = { - {"cs4271", 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); -static const struct regmap_config cs4271_i2c_regmap = { - .reg_bits = 8, - .val_bits = 8, - .max_register = CS4271_LASTREG, - - .reg_defaults = cs4271_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults), - .cache_type = REGCACHE_RBTREE, - - .volatile_reg = cs4271_volatile_reg, -}; - -static int cs4271_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct cs4271_private *cs4271; - int ret; - - ret = cs4271_common_probe(&client->dev, &cs4271); + ret = cs4271_common_probe(dev, &cs4271); if (ret < 0) return ret; - i2c_set_clientdata(client, cs4271); - cs4271->regmap = devm_regmap_init_i2c(client, &cs4271_i2c_regmap); - if (IS_ERR(cs4271->regmap)) - return PTR_ERR(cs4271->regmap); + dev_set_drvdata(dev, cs4271); + cs4271->regmap = regmap; - return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271, - &cs4271_dai, 1); -} - -static int cs4271_i2c_remove(struct i2c_client *client) -{ - snd_soc_unregister_codec(&client->dev); - return 0; -} - -static struct i2c_driver cs4271_i2c_driver = { - .driver = { - .name = "cs4271", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(cs4271_dt_ids), - }, - .id_table = cs4271_i2c_id, - .probe = cs4271_i2c_probe, - .remove = cs4271_i2c_remove, -}; -#endif /* IS_ENABLED(CONFIG_I2C) */ - -/* - * We only register our serial bus driver here without - * assignment to particular chip. So if any of the below - * fails, there is some problem with I2C or SPI subsystem. - * In most cases this module will be compiled with support - * of only one serial bus. - */ -static int __init cs4271_modinit(void) -{ - int ret; - -#if IS_ENABLED(CONFIG_I2C) - ret = i2c_add_driver(&cs4271_i2c_driver); - if (ret) { - pr_err("Failed to register CS4271 I2C driver: %d\n", ret); - return ret; - } -#endif - -#if defined(CONFIG_SPI_MASTER) - ret = spi_register_driver(&cs4271_spi_driver); - if (ret) { - pr_err("Failed to register CS4271 SPI driver: %d\n", ret); - return ret; - } -#endif - - return 0; -} -module_init(cs4271_modinit); - -static void __exit cs4271_modexit(void) -{ -#if defined(CONFIG_SPI_MASTER) - spi_unregister_driver(&cs4271_spi_driver); -#endif - -#if IS_ENABLED(CONFIG_I2C) - i2c_del_driver(&cs4271_i2c_driver); -#endif + return snd_soc_register_codec(dev, &soc_codec_dev_cs4271, &cs4271_dai, + 1); } -module_exit(cs4271_modexit); +EXPORT_SYMBOL_GPL(cs4271_probe); MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>"); MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver"); diff --git a/sound/soc/codecs/cs4271.h b/sound/soc/codecs/cs4271.h new file mode 100644 index 000000000000..9adad8eefdc9 --- /dev/null +++ b/sound/soc/codecs/cs4271.h @@ -0,0 +1,11 @@ +#ifndef _CS4271_PRIV_H +#define _CS4271_PRIV_H + +#include <linux/regmap.h> + +extern const struct of_device_id cs4271_dt_ids[]; +extern const struct regmap_config cs4271_regmap_config; + +int cs4271_probe(struct device *dev, struct regmap *regmap); + +#endif diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index cee51ae177c1..c40428f25ba5 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -46,6 +46,7 @@ static struct i2c_driver cs42l51_i2c_driver = { .driver = { .name = "cs42l51", .owner = THIS_MODULE, + .of_match_table = cs42l51_of_match, }, .probe = cs42l51_i2c_probe, .remove = cs42l51_i2c_remove, diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 09488d97de60..b3951524339f 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -153,15 +153,17 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = { static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + switch (event) { case SND_SOC_DAPM_PRE_PMD: - snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1, + snd_soc_update_bits(codec, CS42L51_POWER_CTL1, CS42L51_POWER_CTL1_PDN, CS42L51_POWER_CTL1_PDN); break; default: case SND_SOC_DAPM_POST_PMD: - snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1, + snd_soc_update_bits(codec, CS42L51_POWER_CTL1, CS42L51_POWER_CTL1_PDN, 0); break; } @@ -558,11 +560,13 @@ error: } EXPORT_SYMBOL_GPL(cs42l51_probe); -static const struct of_device_id cs42l51_of_match[] = { +const struct of_device_id cs42l51_of_match[] = { { .compatible = "cirrus,cs42l51", }, { } }; MODULE_DEVICE_TABLE(of, cs42l51_of_match); +EXPORT_SYMBOL_GPL(cs42l51_of_match); + MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h index 8c55bf384bc6..0ca805492ac4 100644 --- a/sound/soc/codecs/cs42l51.h +++ b/sound/soc/codecs/cs42l51.h @@ -22,6 +22,7 @@ struct device; extern const struct regmap_config cs42l51_regmap; int cs42l51_probe(struct device *dev, struct regmap *regmap); +extern const struct of_device_id cs42l51_of_match[]; #define CS42L51_CHIP_ID 0x1B #define CS42L51_CHIP_REV_A 0x00 diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 2f8b94683e83..7c55537c69cf 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -584,7 +584,7 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = { static int cs42l73_spklo_spk_amp_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 cs42l73_private *priv = snd_soc_codec_get_drvdata(codec); switch (event) { case SND_SOC_DAPM_POST_PMD: @@ -600,7 +600,7 @@ static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w, static int cs42l73_ear_amp_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 cs42l73_private *priv = snd_soc_codec_get_drvdata(codec); switch (event) { case SND_SOC_DAPM_POST_PMD: @@ -618,7 +618,7 @@ static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w, static int cs42l73_hp_amp_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 cs42l73_private *priv = snd_soc_codec_get_drvdata(codec); switch (event) { case SND_SOC_DAPM_POST_PMD: diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c index aae410d122ee..2d05b5d3a6ce 100644 --- a/sound/soc/codecs/es8328-i2c.c +++ b/sound/soc/codecs/es8328-i2c.c @@ -19,7 +19,7 @@ #include "es8328.h" static const struct i2c_device_id es8328_id[] = { - { "everest,es8328", 0 }, + { "es8328", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, es8328_id); diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index d519294f57c7..34ed9a91f392 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -1311,6 +1311,10 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { {"MIC1 Input", NULL, "MIC1"}, {"MIC2 Input", NULL, "MIC2"}, + {"DMICL", NULL, "DMICL_ENA"}, + {"DMICL", NULL, "DMICR_ENA"}, + {"DMICR", NULL, "DMICL_ENA"}, + {"DMICR", NULL, "DMICR_ENA"}, {"DMICL", NULL, "AHPF"}, {"DMICR", NULL, "AHPF"}, @@ -1368,8 +1372,6 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { {"DMIC Mux", "ADC", "ADCR"}, {"DMIC Mux", "DMIC", "DMICL"}, {"DMIC Mux", "DMIC", "DMICR"}, - {"DMIC Mux", "DMIC", "DMICL_ENA"}, - {"DMIC Mux", "DMIC", "DMICR_ENA"}, {"LBENL Mux", "Normal", "DMIC Mux"}, {"LBENL Mux", "Loopback", "LTENL Mux"}, @@ -1395,8 +1397,8 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { {"STENL Mux", "Sidetone Left", "DMICL"}, {"STENR Mux", "Sidetone Right", "ADCR"}, {"STENR Mux", "Sidetone Right", "DMICR"}, - {"DACL", "NULL", "STENL Mux"}, - {"DACR", "NULL", "STENL Mux"}, + {"DACL", NULL, "STENL Mux"}, + {"DACR", NULL, "STENR Mux"}, {"AIFINL", NULL, "SHDN"}, {"AIFINR", NULL, "SHDN"}, @@ -1941,13 +1943,13 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai, * 0x02 (when master clk is 20MHz to 40MHz).. * 0x03 (when master clk is 40MHz to 60MHz).. */ - if ((freq >= 10000000) && (freq < 20000000)) { + if ((freq >= 10000000) && (freq <= 20000000)) { snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, M98090_PSCLK_DIV1); - } else if ((freq >= 20000000) && (freq < 40000000)) { + } else if ((freq > 20000000) && (freq <= 40000000)) { snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, M98090_PSCLK_DIV2); - } else if ((freq >= 40000000) && (freq < 60000000)) { + } else if ((freq > 40000000) && (freq <= 60000000)) { snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, M98090_PSCLK_DIV4); } else { diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 0ee6797d5083..01f3cc9c780f 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -16,6 +16,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/clk.h> +#include <linux/mutex.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -57,6 +58,7 @@ struct max98095_priv { unsigned int mic2pre; struct snd_soc_jack *headphone_jack; struct snd_soc_jack *mic_jack; + struct mutex lock; }; static const struct reg_default max98095_reg_def[] = { @@ -1803,7 +1805,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL); snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0); - mutex_lock(&codec->mutex); + mutex_lock(&max98095->lock); snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); m98095_eq_band(codec, channel, 0, coef_set->band1); m98095_eq_band(codec, channel, 1, coef_set->band2); @@ -1811,7 +1813,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, m98095_eq_band(codec, channel, 3, coef_set->band4); m98095_eq_band(codec, channel, 4, coef_set->band5); snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0); - mutex_unlock(&codec->mutex); + mutex_unlock(&max98095->lock); /* Restore the original on/off state */ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave); @@ -1957,12 +1959,12 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL); snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0); - mutex_lock(&codec->mutex); + mutex_lock(&max98095->lock); snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); m98095_biquad_band(codec, channel, 0, coef_set->band1); m98095_biquad_band(codec, channel, 1, coef_set->band2); snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0); - mutex_unlock(&codec->mutex); + mutex_unlock(&max98095->lock); /* Restore the original on/off state */ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave); @@ -2395,6 +2397,8 @@ static int max98095_i2c_probe(struct i2c_client *i2c, if (max98095 == NULL) return -ENOMEM; + mutex_init(&max98095->lock); + max98095->regmap = devm_regmap_init_i2c(i2c, &max98095_regmap); if (IS_ERR(max98095->regmap)) { ret = PTR_ERR(max98095->regmap); diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 3fb83bf09768..d16331e0b64d 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -139,6 +139,7 @@ static const struct reg_default rt5645_reg[] = { { 0x76, 0x000a }, { 0x77, 0x0c00 }, { 0x78, 0x0000 }, + { 0x79, 0x0123 }, { 0x80, 0x0000 }, { 0x81, 0x0000 }, { 0x82, 0x0000 }, @@ -334,6 +335,7 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg) case RT5645_DMIC_CTRL2: case RT5645_TDM_CTRL_1: case RT5645_TDM_CTRL_2: + case RT5645_TDM_CTRL_3: case RT5645_GLB_CLK: case RT5645_PLL_CTRL1: case RT5645_PLL_CTRL2: diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index ba9d9b4d4857..9bd8b4f63303 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -100,18 +100,18 @@ static const struct reg_default rt5670_reg[] = { { 0x4c, 0x5380 }, { 0x4f, 0x0073 }, { 0x52, 0x00d3 }, - { 0x53, 0xf0f0 }, + { 0x53, 0xf000 }, { 0x61, 0x0000 }, { 0x62, 0x0001 }, { 0x63, 0x00c3 }, { 0x64, 0x0000 }, - { 0x65, 0x0000 }, + { 0x65, 0x0001 }, { 0x66, 0x0000 }, { 0x6f, 0x8000 }, { 0x70, 0x8000 }, { 0x71, 0x8000 }, { 0x72, 0x8000 }, - { 0x73, 0x1110 }, + { 0x73, 0x7770 }, { 0x74, 0x0e00 }, { 0x75, 0x1505 }, { 0x76, 0x0015 }, @@ -125,21 +125,21 @@ static const struct reg_default rt5670_reg[] = { { 0x83, 0x0000 }, { 0x84, 0x0000 }, { 0x85, 0x0000 }, - { 0x86, 0x0008 }, + { 0x86, 0x0004 }, { 0x87, 0x0000 }, { 0x88, 0x0000 }, { 0x89, 0x0000 }, { 0x8a, 0x0000 }, { 0x8b, 0x0000 }, - { 0x8c, 0x0007 }, + { 0x8c, 0x0003 }, { 0x8d, 0x0000 }, { 0x8e, 0x0004 }, { 0x8f, 0x1100 }, { 0x90, 0x0646 }, { 0x91, 0x0c06 }, { 0x93, 0x0000 }, - { 0x94, 0x0000 }, - { 0x95, 0x0000 }, + { 0x94, 0x1270 }, + { 0x95, 0x1000 }, { 0x97, 0x0000 }, { 0x98, 0x0000 }, { 0x99, 0x0000 }, @@ -150,11 +150,11 @@ static const struct reg_default rt5670_reg[] = { { 0x9e, 0x0400 }, { 0xae, 0x7000 }, { 0xaf, 0x0000 }, - { 0xb0, 0x6000 }, + { 0xb0, 0x7000 }, { 0xb1, 0x0000 }, { 0xb2, 0x0000 }, { 0xb3, 0x001f }, - { 0xb4, 0x2206 }, + { 0xb4, 0x220c }, { 0xb5, 0x1f00 }, { 0xb6, 0x0000 }, { 0xb7, 0x0000 }, @@ -171,25 +171,25 @@ static const struct reg_default rt5670_reg[] = { { 0xcf, 0x1813 }, { 0xd0, 0x0690 }, { 0xd1, 0x1c17 }, - { 0xd3, 0xb320 }, + { 0xd3, 0xa220 }, { 0xd4, 0x0000 }, { 0xd6, 0x0400 }, { 0xd9, 0x0809 }, { 0xda, 0x0000 }, { 0xdb, 0x0001 }, { 0xdc, 0x0049 }, - { 0xdd, 0x0009 }, + { 0xdd, 0x0024 }, { 0xe6, 0x8000 }, { 0xe7, 0x0000 }, - { 0xec, 0xb300 }, + { 0xec, 0xa200 }, { 0xed, 0x0000 }, - { 0xee, 0xb300 }, + { 0xee, 0xa200 }, { 0xef, 0x0000 }, { 0xf8, 0x0000 }, { 0xf9, 0x0000 }, { 0xfa, 0x8010 }, { 0xfb, 0x0033 }, - { 0xfc, 0x0080 }, + { 0xfc, 0x0100 }, }; static bool rt5670_volatile_register(struct device *dev, unsigned int reg) @@ -1877,6 +1877,10 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = { { "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" }, { "DAC1 MIXR", NULL, "DAC Stereo1 Filter" }, + { "DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC MIX", NULL, "DAC1 MIXL" }, { "DAC MIX", NULL, "DAC1 MIXR" }, @@ -1926,14 +1930,10 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = { { "DAC L1", NULL, "DAC L1 Power" }, { "DAC L1", NULL, "Stereo DAC MIXL" }, - { "DAC L1", NULL, "PLL1", is_sys_clk_from_pll }, { "DAC R1", NULL, "DAC R1 Power" }, { "DAC R1", NULL, "Stereo DAC MIXR" }, - { "DAC R1", NULL, "PLL1", is_sys_clk_from_pll }, { "DAC L2", NULL, "Mono DAC MIXL" }, - { "DAC L2", NULL, "PLL1", is_sys_clk_from_pll }, { "DAC R2", NULL, "Mono DAC MIXR" }, - { "DAC R2", NULL, "PLL1", is_sys_clk_from_pll }, { "OUT MIXL", "BST1 Switch", "BST1" }, { "OUT MIXL", "INL Switch", "INL VOL" }, diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 6bb77d76561b..dab9b15304af 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1299,8 +1299,7 @@ static int sgtl5000_probe(struct snd_soc_codec *codec) /* enable small pop, introduce 400ms delay in turning off */ snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL, - SGTL5000_SMALL_POP, - SGTL5000_SMALL_POP); + SGTL5000_SMALL_POP, 1); /* disable short cut detector */ snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0); diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h index 2f8c88931f69..bd7a344bf8c5 100644 --- a/sound/soc/codecs/sgtl5000.h +++ b/sound/soc/codecs/sgtl5000.h @@ -275,7 +275,7 @@ #define SGTL5000_BIAS_CTRL_MASK 0x000e #define SGTL5000_BIAS_CTRL_SHIFT 1 #define SGTL5000_BIAS_CTRL_WIDTH 3 -#define SGTL5000_SMALL_POP 0x0001 +#define SGTL5000_SMALL_POP 0 /* * SGTL5000_CHIP_MIC_CTRL diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index f2de7e049bc6..81a38dd9af1f 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -159,6 +159,13 @@ int _process_sigma_firmware(struct device *dev, goto done; } + if (ssfw_head->version != 1) { + dev_err(dev, + "Failed to load firmware: Invalid version %d. Supported firmware versions: 1\n", + ssfw_head->version); + goto done; + } + crc = crc32(0, fw->data + sizeof(*ssfw_head), fw->size - sizeof(*ssfw_head)); pr_debug("%s: crc=%x\n", __func__, crc); 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/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 145fe5b253d4..93de5dd0a7b9 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -911,12 +911,13 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, } aic31xx->p_div = i; - for (i = 0; aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++) { - if (i == ARRAY_SIZE(aic31xx_divs)) { - dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n", - __func__, freq); - return -EINVAL; - } + for (i = 0; i < ARRAY_SIZE(aic31xx_divs) && + aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++) + ; + if (i == ARRAY_SIZE(aic31xx_divs)) { + dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n", + __func__, freq); + return -EINVAL; } /* set clock on MCLK, BCLK, or GPIO1 as PLL input */ diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index f60234962527..d78fb8dffc8c 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -619,10 +619,10 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, struct arizona *arizona = dev_get_drvdata(codec->dev->parent); uint16_t data; - mutex_lock(&codec->mutex); + mutex_lock(&arizona->dac_comp_lock); data = cpu_to_be16(arizona->dac_comp_coeff); memcpy(ucontrol->value.bytes.data, &data, sizeof(data)); - mutex_unlock(&codec->mutex); + mutex_unlock(&arizona->dac_comp_lock); return 0; } @@ -633,11 +633,11 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct arizona *arizona = dev_get_drvdata(codec->dev->parent); - mutex_lock(&codec->mutex); + mutex_lock(&arizona->dac_comp_lock); memcpy(&arizona->dac_comp_coeff, ucontrol->value.bytes.data, sizeof(arizona->dac_comp_coeff)); arizona->dac_comp_coeff = be16_to_cpu(arizona->dac_comp_coeff); - mutex_unlock(&codec->mutex); + mutex_unlock(&arizona->dac_comp_lock); return 0; } @@ -648,9 +648,9 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct arizona *arizona = dev_get_drvdata(codec->dev->parent); - mutex_lock(&codec->mutex); + mutex_lock(&arizona->dac_comp_lock); ucontrol->value.integer.value[0] = arizona->dac_comp_enabled; - mutex_unlock(&codec->mutex); + mutex_unlock(&arizona->dac_comp_lock); return 0; } @@ -661,9 +661,9 @@ static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct arizona *arizona = dev_get_drvdata(codec->dev->parent); - mutex_lock(&codec->mutex); + mutex_lock(&arizona->dac_comp_lock); arizona->dac_comp_enabled = ucontrol->value.integer.value[0]; - mutex_unlock(&codec->mutex); + mutex_unlock(&arizona->dac_comp_lock); return 0; } @@ -1900,6 +1900,8 @@ static int wm5102_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, wm5102); + mutex_init(&arizona->dac_comp_lock); + wm5102->core.arizona = arizona; wm5102->core.num_inputs = 6; diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index eebb3280bfad..5dae9a6f8076 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -24,6 +24,7 @@ #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> #include <linux/of_device.h> +#include <linux/mutex.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -50,6 +51,8 @@ struct wm8731_priv { int sysclk_type; int playback_fs; bool deemph; + + struct mutex lock; }; @@ -138,7 +141,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, if (deemph > 1) return -EINVAL; - mutex_lock(&codec->mutex); + mutex_lock(&wm8731->lock); if (wm8731->deemph != deemph) { wm8731->deemph = deemph; @@ -146,7 +149,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, ret = 1; } - mutex_unlock(&codec->mutex); + mutex_unlock(&wm8731->lock); return ret; } @@ -685,6 +688,8 @@ static int wm8731_spi_probe(struct spi_device *spi) if (wm8731 == NULL) return -ENOMEM; + mutex_init(&wm8731->lock); + wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); if (IS_ERR(wm8731->regmap)) { ret = PTR_ERR(wm8731->regmap); diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index c038b3e04398..ffbe6df3453a 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -26,6 +26,7 @@ #include <linux/regmap.h> #include <linux/slab.h> #include <linux/irq.h> +#include <linux/mutex.h> #include <sound/core.h> #include <sound/jack.h> #include <sound/pcm.h> @@ -123,6 +124,7 @@ struct wm8903_priv { int sysclk; int irq; + struct mutex lock; int fs; int deemph; @@ -457,7 +459,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, if (deemph > 1) return -EINVAL; - mutex_lock(&codec->mutex); + mutex_lock(&wm8903->lock); if (wm8903->deemph != deemph) { wm8903->deemph = deemph; @@ -465,7 +467,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, ret = 1; } - mutex_unlock(&codec->mutex); + mutex_unlock(&wm8903->lock); return ret; } @@ -2023,6 +2025,8 @@ static int wm8903_i2c_probe(struct i2c_client *i2c, GFP_KERNEL); if (wm8903 == NULL) return -ENOMEM; + + mutex_init(&wm8903->lock); wm8903->dev = &i2c->dev; wm8903->regmap = devm_regmap_init_i2c(i2c, &wm8903_regmap); diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c index 0dada7f0105e..3cbc82b33292 100644 --- a/sound/soc/codecs/wm8958-dsp2.c +++ b/sound/soc/codecs/wm8958-dsp2.c @@ -867,9 +867,9 @@ static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context) struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); if (fw && (wm8958_dsp2_fw(codec, "ENH_EQ", fw, true) == 0)) { - mutex_lock(&codec->mutex); + mutex_lock(&wm8994->fw_lock); wm8994->enh_eq = fw; - mutex_unlock(&codec->mutex); + mutex_unlock(&wm8994->fw_lock); } } @@ -879,9 +879,9 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context) struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); if (fw && (wm8958_dsp2_fw(codec, "MBC+VSS", fw, true) == 0)) { - mutex_lock(&codec->mutex); + mutex_lock(&wm8994->fw_lock); wm8994->mbc_vss = fw; - mutex_unlock(&codec->mutex); + mutex_unlock(&wm8994->fw_lock); } } @@ -891,9 +891,9 @@ static void wm8958_mbc_loaded(const struct firmware *fw, void *context) struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); if (fw && (wm8958_dsp2_fw(codec, "MBC", fw, true) == 0)) { - mutex_lock(&codec->mutex); + mutex_lock(&wm8994->fw_lock); wm8994->mbc = fw; - mutex_unlock(&codec->mutex); + mutex_unlock(&wm8994->fw_lock); } } diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 9077411e62ce..61ca4a7cb6ea 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -26,6 +26,7 @@ #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/workqueue.h> +#include <linux/mutex.h> #include <sound/core.h> #include <sound/jack.h> #include <sound/pcm.h> @@ -67,6 +68,7 @@ struct wm8962_priv { int fll_fref; int fll_fout; + struct mutex dsp2_ena_lock; u16 dsp2_ena; struct delayed_work mic_work; @@ -1570,7 +1572,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) & WM8962_DSP2_ENA; - mutex_lock(&codec->mutex); + mutex_lock(&wm8962->dsp2_ena_lock); if (ucontrol->value.integer.value[0]) wm8962->dsp2_ena |= 1 << shift; @@ -1590,7 +1592,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, } out: - mutex_unlock(&codec->mutex); + mutex_unlock(&wm8962->dsp2_ena_lock); return ret; } @@ -3557,6 +3559,8 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, if (wm8962 == NULL) return -ENOMEM; + mutex_init(&wm8962->dsp2_ena_lock); + i2c_set_clientdata(i2c, wm8962); INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work); diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 1fcb9f3f3097..dbca6e0cc93a 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -4457,6 +4457,8 @@ static int wm8994_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, wm8994); + mutex_init(&wm8994->fw_lock); + wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent); pm_runtime_enable(&pdev->dev); diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 6536f8d45ac6..dd73387b1cc4 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -13,6 +13,7 @@ #include <linux/firmware.h> #include <linux/completion.h> #include <linux/workqueue.h> +#include <linux/mutex.h> #include "wm_hubs.h" @@ -156,6 +157,7 @@ struct wm8994_priv { unsigned int aif1clk_disable:1; unsigned int aif2clk_disable:1; + struct mutex fw_lock; int dsp_active; const struct firmware *cur_fw; const struct firmware *mbc; 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/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index f412a9911a75..720d6e852986 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -21,6 +21,7 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> +#include <linux/vmalloc.h> #include <linux/workqueue.h> #include <sound/core.h> #include <sound/pcm.h> @@ -169,11 +170,12 @@ static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, if (buf == NULL) return NULL; - buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA); + buf->buf = vmalloc(len); if (!buf->buf) { - kfree(buf); + vfree(buf); return NULL; } + memcpy(buf->buf, src, len); if (list) list_add_tail(&buf->list, list); @@ -188,7 +190,7 @@ static void wm_adsp_buf_free(struct list_head *list) struct wm_adsp_buf, list); list_del(&buf->list); - kfree(buf->buf); + vfree(buf->buf); kfree(buf); } } @@ -684,38 +686,24 @@ static int wm_adsp_load(struct wm_adsp *dsp) } if (reg) { - size_t to_write = PAGE_SIZE; - size_t remain = le32_to_cpu(region->len); - const u8 *data = region->data; - - while (remain > 0) { - if (remain < PAGE_SIZE) - to_write = remain; - - buf = wm_adsp_buf_alloc(data, - to_write, - &buf_list); - if (!buf) { - adsp_err(dsp, "Out of memory\n"); - ret = -ENOMEM; - goto out_fw; - } - - ret = regmap_raw_write_async(regmap, reg, - buf->buf, - to_write); - if (ret != 0) { - adsp_err(dsp, - "%s.%d: Failed to write %zd bytes at %d in %s: %d\n", - file, regions, - to_write, offset, - region_name, ret); - goto out_fw; - } + buf = wm_adsp_buf_alloc(region->data, + le32_to_cpu(region->len), + &buf_list); + if (!buf) { + adsp_err(dsp, "Out of memory\n"); + ret = -ENOMEM; + goto out_fw; + } - data += to_write; - reg += to_write / 2; - remain -= to_write; + ret = regmap_raw_write_async(regmap, reg, buf->buf, + le32_to_cpu(region->len)); + if (ret != 0) { + adsp_err(dsp, + "%s.%d: Failed to write %d bytes at %d in %s: %d\n", + file, regions, + le32_to_cpu(region->len), offset, + region_name, ret); + goto out_fw; } } @@ -1065,8 +1053,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) be32_to_cpu(adsp1_alg[i].zm)); region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; + if (!region) { + ret = -ENOMEM; + goto out; + } region->type = WMFW_ADSP1_DM; region->alg = be32_to_cpu(adsp1_alg[i].alg.id); region->base = be32_to_cpu(adsp1_alg[i].dm); @@ -1083,8 +1073,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) } region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; + if (!region) { + ret = -ENOMEM; + goto out; + } region->type = WMFW_ADSP1_ZM; region->alg = be32_to_cpu(adsp1_alg[i].alg.id); region->base = be32_to_cpu(adsp1_alg[i].zm); @@ -1113,8 +1105,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) be32_to_cpu(adsp2_alg[i].zm)); region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; + if (!region) { + ret = -ENOMEM; + goto out; + } region->type = WMFW_ADSP2_XM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].xm); @@ -1131,8 +1125,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) } region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; + if (!region) { + ret = -ENOMEM; + goto out; + } region->type = WMFW_ADSP2_YM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].ym); @@ -1149,8 +1145,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) } region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; + if (!region) { + ret = -ENOMEM; + goto out; + } region->type = WMFW_ADSP2_ZM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].zm); @@ -1355,6 +1353,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) file, blocks, pos - firmware->size); out_fw: + regmap_async_complete(regmap); release_firmware(firmware); wm_adsp_buf_free(&buf_list); out: @@ -1594,13 +1593,6 @@ static void wm_adsp2_boot_work(struct work_struct *work) if (ret != 0) goto err; - ret = regmap_update_bits_async(dsp->regmap, - dsp->base + ADSP2_CONTROL, - ADSP2_CORE_ENA, - ADSP2_CORE_ENA); - if (ret != 0) - goto err; - dsp->running = true; return; @@ -1650,8 +1642,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_START, - ADSP2_START); + ADSP2_CORE_ENA | ADSP2_START, + ADSP2_CORE_ENA | ADSP2_START); if (ret != 0) goto err; break; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 0eed9b1b24e1..0dab382ba147 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -70,6 +70,7 @@ struct davinci_mcasp { void __iomem *base; u32 fifo_base; struct device *dev; + struct snd_pcm_substream *substreams[2]; /* McASP specific data */ int tdm_slots; @@ -80,6 +81,7 @@ struct davinci_mcasp { u8 bclk_div; u16 bclk_lrclk_ratio; int streams; + u32 irq_request[2]; int sysclk_freq; bool bclk_master; @@ -90,6 +92,9 @@ struct davinci_mcasp { bool dat_port; + /* Used for comstraint setting on the second stream */ + u32 channels; + #ifdef CONFIG_PM_SLEEP struct davinci_mcasp_context context; #endif @@ -154,9 +159,16 @@ static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp) static void mcasp_start_rx(struct davinci_mcasp *mcasp) { + if (mcasp->rxnumevt) { /* enable FIFO */ + u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + mcasp_set_bits(mcasp, reg, FIFO_ENABLE); + } + + /* Start clocks */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST); - /* * When ASYNC == 0 the transmit and receive sections operate * synchronously from the transmit clock and frame sync. We need to make @@ -167,74 +179,69 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); } + /* Activate serializer(s) */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR); - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0); - - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0); - + /* Release RX state machine */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); + /* Release Frame Sync generator */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); - if (mcasp_is_synchronous(mcasp)) mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); + + /* enable receive IRQs */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG, + mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]); } static void mcasp_start_tx(struct davinci_mcasp *mcasp) { - u8 offset = 0, i; u32 cnt; + if (mcasp->txnumevt) { /* enable FIFO */ + u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + mcasp_set_bits(mcasp, reg, FIFO_ENABLE); + } + + /* Start clocks */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); + /* Activate serializer(s) */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR); - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0); - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0); - for (i = 0; i < mcasp->num_serializer; i++) { - if (mcasp->serial_dir[i] == TX_MODE) { - offset = i; - break; - } - } - - /* wait for TX ready */ + /* wait for XDATA to be cleared */ cnt = 0; - while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(offset)) & - TXSTATE) && (cnt < 100000)) + while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & + ~XRDATA) && (cnt < 100000)) cnt++; - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0); + /* Release TX state machine */ + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); + /* Release Frame Sync generator */ + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); + + /* enable transmit IRQs */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG, + mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]); } static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream) { - u32 reg; - mcasp->streams++; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (mcasp->txnumevt) { /* enable FIFO */ - reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; - mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); - mcasp_set_bits(mcasp, reg, FIFO_ENABLE); - } + if (stream == SNDRV_PCM_STREAM_PLAYBACK) mcasp_start_tx(mcasp); - } else { - if (mcasp->rxnumevt) { /* enable FIFO */ - reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; - mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); - mcasp_set_bits(mcasp, reg, FIFO_ENABLE); - } + else mcasp_start_rx(mcasp); - } } static void mcasp_stop_rx(struct davinci_mcasp *mcasp) { + /* disable IRQ sources */ + mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG, + mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]); + /* * In synchronous mode stop the TX clocks if no other stream is * running @@ -244,12 +251,22 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0); mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); + + if (mcasp->rxnumevt) { /* disable FIFO */ + u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + } } static void mcasp_stop_tx(struct davinci_mcasp *mcasp) { u32 val = 0; + /* disable IRQ sources */ + mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG, + mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]); + /* * In synchronous mode keep TX clocks running if the capture stream is * still running. @@ -259,27 +276,92 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val); mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); + + if (mcasp->txnumevt) { /* disable FIFO */ + u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + } } static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream) { - u32 reg; - mcasp->streams--; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (mcasp->txnumevt) { /* disable FIFO */ - reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; - mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); - } + if (stream == SNDRV_PCM_STREAM_PLAYBACK) mcasp_stop_tx(mcasp); - } else { - if (mcasp->rxnumevt) { /* disable FIFO */ - reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; - mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); - } + else mcasp_stop_rx(mcasp); +} + +static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data) +{ + struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data; + struct snd_pcm_substream *substream; + u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]; + u32 handled_mask = 0; + u32 stat; + + stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG); + if (stat & XUNDRN & irq_mask) { + dev_warn(mcasp->dev, "Transmit buffer underflow\n"); + handled_mask |= XUNDRN; + + substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream) { + snd_pcm_stream_lock_irq(substream); + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irq(substream); + } } + + if (!handled_mask) + dev_warn(mcasp->dev, "unhandled tx event. txstat: 0x%08x\n", + stat); + + if (stat & XRERR) + handled_mask |= XRERR; + + /* Ack the handled event only */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, handled_mask); + + return IRQ_RETVAL(handled_mask); +} + +static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data) +{ + struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data; + struct snd_pcm_substream *substream; + u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]; + u32 handled_mask = 0; + u32 stat; + + stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG); + if (stat & ROVRN & irq_mask) { + dev_warn(mcasp->dev, "Receive buffer overflow\n"); + handled_mask |= ROVRN; + + substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE]; + if (substream) { + snd_pcm_stream_lock_irq(substream); + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irq(substream); + } + } + + if (!handled_mask) + dev_warn(mcasp->dev, "unhandled rx event. rxstat: 0x%08x\n", + stat); + + if (stat & XRERR) + handled_mask |= XRERR; + + /* Ack the handled event only */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, handled_mask); + + return IRQ_RETVAL(handled_mask); } static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, @@ -500,8 +582,17 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, * both left and right channels), so it has to be divided by number of * tdm-slots (for I2S - divided by 2). */ - if (mcasp->bclk_lrclk_ratio) - word_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots; + if (mcasp->bclk_lrclk_ratio) { + u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots; + + /* + * When we have more bclk then it is needed for the data, we + * need to use the rotation to move the received samples to have + * correct alignment. + */ + rx_rotate = (slot_length - word_length) / 4; + word_length = slot_length; + } /* mapping of the XSSZ bit-field as described in the datasheet */ fmt = (word_length >> 1) - 1; @@ -635,19 +726,29 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, return 0; } -static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream) +static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, + int channels) { int i, active_slots; + int total_slots; + int active_serializers; u32 mask = 0; u32 busel = 0; - if ((mcasp->tdm_slots < 2) || (mcasp->tdm_slots > 32)) { - dev_err(mcasp->dev, "tdm slot %d not supported\n", - mcasp->tdm_slots); - return -EINVAL; - } + total_slots = mcasp->tdm_slots; + + /* + * If more than one serializer is needed, then use them with + * their specified tdm_slots count. Otherwise, one serializer + * can cope with the transaction using as many slots as channels + * in the stream, requires channels symmetry + */ + active_serializers = (channels + total_slots - 1) / total_slots; + if (active_serializers == 1) + active_slots = channels; + else + active_slots = total_slots; - active_slots = (mcasp->tdm_slots > 31) ? 32 : mcasp->tdm_slots; for (i = 0; i < active_slots; i++) mask |= (1 << i); @@ -659,12 +760,12 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream) mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask); mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD); mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, - FSXMOD(mcasp->tdm_slots), FSXMOD(0x1FF)); + FSXMOD(total_slots), FSXMOD(0x1FF)); mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask); mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, - FSRMOD(mcasp->tdm_slots), FSRMOD(0x1FF)); + FSRMOD(total_slots), FSRMOD(0x1FF)); return 0; } @@ -778,7 +879,8 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) ret = mcasp_dit_hw_param(mcasp, params_rate(params)); else - ret = mcasp_i2s_hw_param(mcasp, substream->stream); + ret = mcasp_i2s_hw_param(mcasp, substream->stream, + channels); if (ret) return ret; @@ -826,6 +928,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, davinci_config_channel_size(mcasp, word_length); + if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) + mcasp->channels = channels; + return 0; } @@ -854,7 +959,65 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream, return ret; } +static int davinci_mcasp_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + u32 max_channels = 0; + int i, dir; + + mcasp->substreams[substream->stream] = substream; + + if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) + return 0; + + /* + * Limit the maximum allowed channels for the first stream: + * number of serializers for the direction * tdm slots per serializer + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = TX_MODE; + else + dir = RX_MODE; + + for (i = 0; i < mcasp->num_serializer; i++) { + if (mcasp->serial_dir[i] == dir) + max_channels++; + } + max_channels *= mcasp->tdm_slots; + /* + * If the already active stream has less channels than the calculated + * limnit based on the seirializers * tdm_slots, we need to use that as + * a constraint for the second stream. + * Otherwise (first stream or less allowed channels) we use the + * calculated constraint. + */ + if (mcasp->channels && mcasp->channels < max_channels) + max_channels = mcasp->channels; + + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + 2, max_channels); + return 0; +} + +static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + + mcasp->substreams[substream->stream] = NULL; + + if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) + return; + + if (!cpu_dai->active) + mcasp->channels = 0; +} + static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { + .startup = davinci_mcasp_startup, + .shutdown = davinci_mcasp_shutdown, .trigger = davinci_mcasp_trigger, .hw_params = davinci_mcasp_hw_params, .set_fmt = davinci_mcasp_set_dai_fmt, @@ -971,6 +1134,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = { }, .ops = &davinci_mcasp_dai_ops, + .symmetric_samplebits = 1, }, { .name = "davinci-mcasp.1", @@ -1194,6 +1358,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev) struct resource *mem, *ioarea, *res, *dat; struct davinci_mcasp_pdata *pdata; struct davinci_mcasp *mcasp; + char *irq_name; + int irq; int ret; if (!pdev->dev.platform_data && !pdev->dev.of_node) { @@ -1235,6 +1401,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(&pdev->dev); if (IS_ERR_VALUE(ret)) { dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n"); + pm_runtime_disable(&pdev->dev); return ret; } @@ -1246,7 +1413,21 @@ static int davinci_mcasp_probe(struct platform_device *pdev) } mcasp->op_mode = pdata->op_mode; - mcasp->tdm_slots = pdata->tdm_slots; + /* sanity check for tdm slots parameter */ + if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) { + if (pdata->tdm_slots < 2) { + dev_err(&pdev->dev, "invalid tdm slots: %d\n", + pdata->tdm_slots); + mcasp->tdm_slots = 2; + } else if (pdata->tdm_slots > 32) { + dev_err(&pdev->dev, "invalid tdm slots: %d\n", + pdata->tdm_slots); + mcasp->tdm_slots = 32; + } else { + mcasp->tdm_slots = pdata->tdm_slots; + } + } + mcasp->num_serializer = pdata->num_serializer; #ifdef CONFIG_PM_SLEEP mcasp->context.xrsr_regs = devm_kzalloc(&pdev->dev, @@ -1260,6 +1441,36 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->dev = &pdev->dev; + irq = platform_get_irq_byname(pdev, "rx"); + if (irq >= 0) { + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx\n", + dev_name(&pdev->dev)); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + davinci_mcasp_rx_irq_handler, + IRQF_ONESHOT, irq_name, mcasp); + if (ret) { + dev_err(&pdev->dev, "RX IRQ request failed\n"); + goto err; + } + + mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN; + } + + irq = platform_get_irq_byname(pdev, "tx"); + if (irq >= 0) { + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx\n", + dev_name(&pdev->dev)); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + davinci_mcasp_tx_irq_handler, + IRQF_ONESHOT, irq_name, mcasp); + if (ret) { + dev_err(&pdev->dev, "TX IRQ request failed\n"); + goto err; + } + + mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN; + } + dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); if (dat) mcasp->dat_port = true; diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h index 98fbc451892a..79dc511180bf 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/davinci/davinci-mcasp.h @@ -253,6 +253,13 @@ #define TXFSRST BIT(12) /* Frame Sync Generator Reset */ /* + * DAVINCI_MCASP_TXSTAT_REG - Transmitter Status Register Bits + * DAVINCI_MCASP_RXSTAT_REG - Receiver Status Register Bits + */ +#define XRERR BIT(8) /* Transmit/Receive error */ +#define XRDATA BIT(5) /* Transmit/Receive data ready */ + +/* * DAVINCI_MCASP_AMUTE_REG - Mute Control Register Bits */ #define MUTENA(val) (val) @@ -279,6 +286,16 @@ #define TXDATADMADIS BIT(0) /* + * DAVINCI_MCASP_EVTCTLR_REG - Receiver Interrupt Control Register Bits + */ +#define ROVRN BIT(0) + +/* + * DAVINCI_MCASP_EVTCTLX_REG - Transmitter Interrupt Control Register Bits + */ +#define XUNDRN BIT(0) + +/* * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits */ #define FIFO_ENABLE BIT(16) diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index e961388e6e9c..08f0229f8d68 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -338,31 +338,34 @@ static int dw_i2s_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no i2s resource defined\n"); - return -ENODEV; - } - - if (!devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "i2s region already claimed\n"); - return -EBUSY; - } - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) { dev_warn(&pdev->dev, "kzalloc fail\n"); return -ENOMEM; } - dev->i2s_base = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (!dev->i2s_base) { - dev_err(&pdev->dev, "ioremap fail for i2s_region\n"); + dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL); + if (!dw_i2s_dai) { + dev_err(&pdev->dev, "mem allocation failed for dai driver\n"); return -ENOMEM; } + dw_i2s_dai->ops = &dw_i2s_dai_ops; + dw_i2s_dai->suspend = dw_i2s_suspend; + dw_i2s_dai->resume = dw_i2s_resume; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no i2s resource defined\n"); + return -ENODEV; + } + + dev->i2s_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->i2s_base)) { + dev_err(&pdev->dev, "ioremap fail for i2s_region\n"); + return PTR_ERR(dev->i2s_base); + } + cap = pdata->cap; dev->capability = cap; dev->i2s_clk_cfg = pdata->i2s_clk_cfg; @@ -388,13 +391,6 @@ static int dw_i2s_probe(struct platform_device *pdev) if (ret < 0) goto err_clk_put; - dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL); - if (!dw_i2s_dai) { - dev_err(&pdev->dev, "mem allocation failed for dai driver\n"); - ret = -ENOMEM; - goto err_clk_disable; - } - if (cap & DWC_I2S_PLAY) { dev_dbg(&pdev->dev, " designware: play supported\n"); dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; @@ -411,10 +407,6 @@ static int dw_i2s_probe(struct platform_device *pdev) dw_i2s_dai->capture.rates = pdata->snd_rates; } - dw_i2s_dai->ops = &dw_i2s_dai_ops; - dw_i2s_dai->suspend = dw_i2s_suspend; - dw_i2s_dai->resume = dw_i2s_resume; - dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component, diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 3b145313f93e..9deabdd2b1a2 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -684,12 +684,38 @@ static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg) } } +static struct reg_default fsl_asrc_reg[] = { + { REG_ASRCTR, 0x0000 }, { REG_ASRIER, 0x0000 }, + { REG_ASRCNCR, 0x0000 }, { REG_ASRCFG, 0x0000 }, + { REG_ASRCSR, 0x0000 }, { REG_ASRCDR1, 0x0000 }, + { REG_ASRCDR2, 0x0000 }, { REG_ASRSTR, 0x0000 }, + { REG_ASRRA, 0x0000 }, { REG_ASRRB, 0x0000 }, + { REG_ASRRC, 0x0000 }, { REG_ASRPM1, 0x0000 }, + { REG_ASRPM2, 0x0000 }, { REG_ASRPM3, 0x0000 }, + { REG_ASRPM4, 0x0000 }, { REG_ASRPM5, 0x0000 }, + { REG_ASRTFR1, 0x0000 }, { REG_ASRCCR, 0x0000 }, + { REG_ASRDIA, 0x0000 }, { REG_ASRDOA, 0x0000 }, + { REG_ASRDIB, 0x0000 }, { REG_ASRDOB, 0x0000 }, + { REG_ASRDIC, 0x0000 }, { REG_ASRDOC, 0x0000 }, + { REG_ASRIDRHA, 0x0000 }, { REG_ASRIDRLA, 0x0000 }, + { REG_ASRIDRHB, 0x0000 }, { REG_ASRIDRLB, 0x0000 }, + { REG_ASRIDRHC, 0x0000 }, { REG_ASRIDRLC, 0x0000 }, + { REG_ASR76K, 0x0A47 }, { REG_ASR56K, 0x0DF3 }, + { REG_ASRMCRA, 0x0000 }, { REG_ASRFSTA, 0x0000 }, + { REG_ASRMCRB, 0x0000 }, { REG_ASRFSTB, 0x0000 }, + { REG_ASRMCRC, 0x0000 }, { REG_ASRFSTC, 0x0000 }, + { REG_ASRMCR1A, 0x0000 }, { REG_ASRMCR1B, 0x0000 }, + { REG_ASRMCR1C, 0x0000 }, +}; + static const struct regmap_config fsl_asrc_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = REG_ASRMCR1C, + .reg_defaults = fsl_asrc_reg, + .num_reg_defaults = ARRAY_SIZE(fsl_asrc_reg), .readable_reg = fsl_asrc_readable_reg, .volatile_reg = fsl_asrc_volatile_reg, .writeable_reg = fsl_asrc_writeable_reg, @@ -792,7 +818,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) return -ENOMEM; asrc_priv->pdev = pdev; - strcpy(asrc_priv->name, np->name); + strncpy(asrc_priv->name, np->name, sizeof(asrc_priv->name) - 1); /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 8bcdfda09d7a..ca319d59f843 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -513,10 +513,15 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, u32 width = snd_pcm_format_width(params_format(params)); u32 channels = params_channels(params); u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); + u32 slot_width = width; u32 bclk, mask, val; int ret; - bclk = params_rate(params) * esai_priv->slot_width * esai_priv->slots; + /* Override slot_width if being specifially set */ + if (esai_priv->slot_width) + slot_width = esai_priv->slot_width; + + bclk = params_rate(params) * slot_width * esai_priv->slots; ret = fsl_esai_set_bclk(dai, tx, bclk); if (ret) @@ -538,7 +543,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val); mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0); - val = ESAI_xCR_xSWS(esai_priv->slot_width, width) | (tx ? ESAI_xCR_PADC : 0); + val = ESAI_xCR_xSWS(slot_width, width) | (tx ? ESAI_xCR_PADC : 0); regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val); @@ -734,7 +739,7 @@ static int fsl_esai_probe(struct platform_device *pdev) return -ENOMEM; esai_priv->pdev = pdev; - strcpy(esai_priv->name, np->name); + strncpy(esai_priv->name, np->name, sizeof(esai_priv->name) - 1); /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -780,9 +785,6 @@ static int fsl_esai_probe(struct platform_device *pdev) return ret; } - /* Set a default slot size */ - esai_priv->slot_width = 32; - /* Set a default slot number */ esai_priv->slots = 2; 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/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 33fc5c3abf55..4df867cbb92a 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -691,9 +691,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) } #define HSW_FORMATS \ - (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S8) + (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) static struct snd_soc_dai_driver hsw_dais[] = { { 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/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index f373e37f8305..c74ba37f862c 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -154,8 +154,10 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on) while (val) { regmap_read(i2s->regmap, I2S_CLR, &val); retry--; - if (!retry) + if (!retry) { dev_warn(i2s->dev, "fail to clear\n"); + break; + } } } } diff --git a/sound/soc/s6000/Kconfig b/sound/soc/s6000/Kconfig deleted file mode 100644 index f244a2566f20..000000000000 --- a/sound/soc/s6000/Kconfig +++ /dev/null @@ -1,26 +0,0 @@ -config SND_S6000_SOC - tristate "SoC Audio for the Stretch s6000 family" - depends on XTENSA_VARIANT_S6000 || COMPILE_TEST - depends on HAS_IOMEM - select SND_S6000_SOC_PCM if XTENSA_VARIANT_S6000 - help - Say Y or M if you want to add support for codecs attached to - s6000 family chips. You will also need to select the platform - to support below. - -config SND_S6000_SOC_PCM - tristate - -config SND_S6000_SOC_I2S - tristate - -config SND_S6000_SOC_S6IPCAM - bool "SoC Audio support for Stretch 6105 IP Camera" - depends on SND_S6000_SOC=y - depends on I2C=y - depends on XTENSA_PLATFORM_S6105 || COMPILE_TEST - select SND_S6000_SOC_I2S - select SND_SOC_TLV320AIC3X - help - Say Y if you want to add support for SoC audio on the - Stretch s6105 IP Camera Reference Design. diff --git a/sound/soc/s6000/Makefile b/sound/soc/s6000/Makefile deleted file mode 100644 index 0f0ae2a012aa..000000000000 --- a/sound/soc/s6000/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# s6000 Platform Support -snd-soc-s6000-objs := s6000-pcm.o -snd-soc-s6000-i2s-objs := s6000-i2s.o - -obj-$(CONFIG_SND_S6000_SOC_PCM) += snd-soc-s6000.o -obj-$(CONFIG_SND_S6000_SOC_I2S) += snd-soc-s6000-i2s.o - -# s6105 Machine Support -snd-soc-s6ipcam-objs := s6105-ipcam.o - -obj-$(CONFIG_SND_S6000_SOC_S6IPCAM) += snd-soc-s6ipcam.o diff --git a/sound/soc/s6000/s6000-i2s.c b/sound/soc/s6000/s6000-i2s.c deleted file mode 100644 index 1c8d01166e5b..000000000000 --- a/sound/soc/s6000/s6000-i2s.c +++ /dev/null @@ -1,617 +0,0 @@ -/* - * ALSA SoC I2S Audio Layer for the Stretch S6000 family - * - * Author: Daniel Gloeckner, <dg@emlix.com> - * Copyright: (C) 2009 emlix GmbH <info@emlix.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. - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/delay.h> -#include <linux/clk.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/slab.h> - -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/initval.h> -#include <sound/soc.h> - -#include "s6000-i2s.h" -#include "s6000-pcm.h" - -struct s6000_i2s_dev { - dma_addr_t sifbase; - u8 __iomem *scbbase; - unsigned int wide; - unsigned int channel_in; - unsigned int channel_out; - unsigned int lines_in; - unsigned int lines_out; - struct s6000_pcm_dma_params dma_params; -}; - -#define S6_I2S_INTERRUPT_STATUS 0x00 -#define S6_I2S_INT_OVERRUN 1 -#define S6_I2S_INT_UNDERRUN 2 -#define S6_I2S_INT_ALIGNMENT 4 -#define S6_I2S_INTERRUPT_ENABLE 0x04 -#define S6_I2S_INTERRUPT_RAW 0x08 -#define S6_I2S_INTERRUPT_CLEAR 0x0C -#define S6_I2S_INTERRUPT_SET 0x10 -#define S6_I2S_MODE 0x20 -#define S6_I2S_DUAL 0 -#define S6_I2S_WIDE 1 -#define S6_I2S_TX_DEFAULT 0x24 -#define S6_I2S_DATA_CFG(c) (0x40 + 0x10 * (c)) -#define S6_I2S_IN 0 -#define S6_I2S_OUT 1 -#define S6_I2S_UNUSED 2 -#define S6_I2S_INTERFACE_CFG(c) (0x44 + 0x10 * (c)) -#define S6_I2S_DIV_MASK 0x001fff -#define S6_I2S_16BIT 0x000000 -#define S6_I2S_20BIT 0x002000 -#define S6_I2S_24BIT 0x004000 -#define S6_I2S_32BIT 0x006000 -#define S6_I2S_BITS_MASK 0x006000 -#define S6_I2S_MEM_16BIT 0x000000 -#define S6_I2S_MEM_32BIT 0x008000 -#define S6_I2S_MEM_MASK 0x008000 -#define S6_I2S_CHANNELS_SHIFT 16 -#define S6_I2S_CHANNELS_MASK 0x030000 -#define S6_I2S_SCK_IN 0x000000 -#define S6_I2S_SCK_OUT 0x040000 -#define S6_I2S_SCK_DIR 0x040000 -#define S6_I2S_WS_IN 0x000000 -#define S6_I2S_WS_OUT 0x080000 -#define S6_I2S_WS_DIR 0x080000 -#define S6_I2S_LEFT_FIRST 0x000000 -#define S6_I2S_RIGHT_FIRST 0x100000 -#define S6_I2S_FIRST 0x100000 -#define S6_I2S_CUR_SCK 0x200000 -#define S6_I2S_CUR_WS 0x400000 -#define S6_I2S_ENABLE(c) (0x48 + 0x10 * (c)) -#define S6_I2S_DISABLE_IF 0x02 -#define S6_I2S_ENABLE_IF 0x03 -#define S6_I2S_IS_BUSY 0x04 -#define S6_I2S_DMA_ACTIVE 0x08 -#define S6_I2S_IS_ENABLED 0x10 - -#define S6_I2S_NUM_LINES 4 - -#define S6_I2S_SIF_PORT0 0x0000000 -#define S6_I2S_SIF_PORT1 0x0000080 /* docs say 0x0000010 */ - -static inline void s6_i2s_write_reg(struct s6000_i2s_dev *dev, int reg, u32 val) -{ - writel(val, dev->scbbase + reg); -} - -static inline u32 s6_i2s_read_reg(struct s6000_i2s_dev *dev, int reg) -{ - return readl(dev->scbbase + reg); -} - -static inline void s6_i2s_mod_reg(struct s6000_i2s_dev *dev, int reg, - u32 mask, u32 val) -{ - val ^= s6_i2s_read_reg(dev, reg) & ~mask; - s6_i2s_write_reg(dev, reg, val); -} - -static void s6000_i2s_start_channel(struct s6000_i2s_dev *dev, int channel) -{ - int i, j, cur, prev; - - /* - * Wait for WCLK to toggle 5 times before enabling the channel - * s6000 Family Datasheet 3.6.4: - * "At least two cycles of WS must occur between commands - * to disable or enable the interface" - */ - j = 0; - prev = ~S6_I2S_CUR_WS; - for (i = 1000000; --i && j < 6; ) { - cur = s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(channel)) - & S6_I2S_CUR_WS; - if (prev != cur) { - prev = cur; - j++; - } - } - if (j < 6) - printk(KERN_WARNING "s6000-i2s: timeout waiting for WCLK\n"); - - s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_ENABLE_IF); -} - -static void s6000_i2s_stop_channel(struct s6000_i2s_dev *dev, int channel) -{ - s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_DISABLE_IF); -} - -static void s6000_i2s_start(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); - int channel; - - channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - dev->channel_out : dev->channel_in; - - s6000_i2s_start_channel(dev, channel); -} - -static void s6000_i2s_stop(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); - int channel; - - channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - dev->channel_out : dev->channel_in; - - s6000_i2s_stop_channel(dev, channel); -} - -static int s6000_i2s_trigger(struct snd_pcm_substream *substream, int cmd, - int after) -{ - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ^ !after) - s6000_i2s_start(substream); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (!after) - s6000_i2s_stop(substream); - } - return 0; -} - -static unsigned int s6000_i2s_int_sources(struct s6000_i2s_dev *dev) -{ - unsigned int pending; - pending = s6_i2s_read_reg(dev, S6_I2S_INTERRUPT_RAW); - pending &= S6_I2S_INT_ALIGNMENT | - S6_I2S_INT_UNDERRUN | - S6_I2S_INT_OVERRUN; - s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR, pending); - - return pending; -} - -static unsigned int s6000_i2s_check_xrun(struct snd_soc_dai *cpu_dai) -{ - struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); - unsigned int errors; - unsigned int ret; - - errors = s6000_i2s_int_sources(dev); - if (likely(!errors)) - return 0; - - ret = 0; - if (errors & S6_I2S_INT_ALIGNMENT) - printk(KERN_ERR "s6000-i2s: WCLK misaligned\n"); - if (errors & S6_I2S_INT_UNDERRUN) - ret |= 1 << SNDRV_PCM_STREAM_PLAYBACK; - if (errors & S6_I2S_INT_OVERRUN) - ret |= 1 << SNDRV_PCM_STREAM_CAPTURE; - return ret; -} - -static void s6000_i2s_wait_disabled(struct s6000_i2s_dev *dev) -{ - int channel; - int n = 50; - for (channel = 0; channel < 2; channel++) { - while (--n >= 0) { - int v = s6_i2s_read_reg(dev, S6_I2S_ENABLE(channel)); - if ((v & S6_I2S_IS_ENABLED) - || !(v & (S6_I2S_DMA_ACTIVE | S6_I2S_IS_BUSY))) - break; - udelay(20); - } - } - if (n < 0) - printk(KERN_WARNING "s6000-i2s: timeout disabling interfaces"); -} - -static int s6000_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, - unsigned int fmt) -{ - struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); - u32 w; - - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - w = S6_I2S_SCK_IN | S6_I2S_WS_IN; - break; - case SND_SOC_DAIFMT_CBS_CFM: - w = S6_I2S_SCK_OUT | S6_I2S_WS_IN; - break; - case SND_SOC_DAIFMT_CBM_CFS: - w = S6_I2S_SCK_IN | S6_I2S_WS_OUT; - break; - case SND_SOC_DAIFMT_CBS_CFS: - w = S6_I2S_SCK_OUT | S6_I2S_WS_OUT; - break; - default: - return -EINVAL; - } - - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - w |= S6_I2S_LEFT_FIRST; - break; - case SND_SOC_DAIFMT_NB_IF: - w |= S6_I2S_RIGHT_FIRST; - break; - default: - return -EINVAL; - } - - s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(0), - S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w); - s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(1), - S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w); - - return 0; -} - -static int s6000_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) -{ - struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - - if (!div || (div & 1) || div > (S6_I2S_DIV_MASK + 1) * 2) - return -EINVAL; - - s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(div_id), - S6_I2S_DIV_MASK, div / 2 - 1); - return 0; -} - -static int s6000_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - int interf; - u32 w = 0; - - if (dev->wide) - interf = 0; - else { - w |= (((params_channels(params) - 2) / 2) - << S6_I2S_CHANNELS_SHIFT) & S6_I2S_CHANNELS_MASK; - interf = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - ? dev->channel_out : dev->channel_in; - } - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - w |= S6_I2S_16BIT | S6_I2S_MEM_16BIT; - break; - case SNDRV_PCM_FORMAT_S32_LE: - w |= S6_I2S_32BIT | S6_I2S_MEM_32BIT; - break; - default: - printk(KERN_WARNING "s6000-i2s: unsupported PCM format %x\n", - params_format(params)); - return -EINVAL; - } - - if (s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(interf)) - & S6_I2S_IS_ENABLED) { - printk(KERN_ERR "s6000-i2s: interface already enabled\n"); - return -EBUSY; - } - - s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(interf), - S6_I2S_CHANNELS_MASK|S6_I2S_MEM_MASK|S6_I2S_BITS_MASK, - w); - - return 0; -} - -static int s6000_i2s_dai_probe(struct snd_soc_dai *dai) -{ - struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - struct s6000_snd_platform_data *pdata = dai->dev->platform_data; - - if (!pdata) - return -EINVAL; - - dai->capture_dma_data = &dev->dma_params; - dai->playback_dma_data = &dev->dma_params; - - dev->wide = pdata->wide; - dev->channel_in = pdata->channel_in; - dev->channel_out = pdata->channel_out; - dev->lines_in = pdata->lines_in; - dev->lines_out = pdata->lines_out; - - s6_i2s_write_reg(dev, S6_I2S_MODE, - dev->wide ? S6_I2S_WIDE : S6_I2S_DUAL); - - if (dev->wide) { - int i; - - if (dev->lines_in + dev->lines_out > S6_I2S_NUM_LINES) - return -EINVAL; - - dev->channel_in = 0; - dev->channel_out = 1; - dai->driver->capture.channels_min = 2 * dev->lines_in; - dai->driver->capture.channels_max = dai->driver->capture.channels_min; - dai->driver->playback.channels_min = 2 * dev->lines_out; - dai->driver->playback.channels_max = dai->driver->playback.channels_min; - - for (i = 0; i < dev->lines_out; i++) - s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_OUT); - - for (; i < S6_I2S_NUM_LINES - dev->lines_in; i++) - s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), - S6_I2S_UNUSED); - - for (; i < S6_I2S_NUM_LINES; i++) - s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_IN); - } else { - unsigned int cfg[2] = {S6_I2S_UNUSED, S6_I2S_UNUSED}; - - if (dev->lines_in > 1 || dev->lines_out > 1) - return -EINVAL; - - dai->driver->capture.channels_min = 2 * dev->lines_in; - dai->driver->capture.channels_max = 8 * dev->lines_in; - dai->driver->playback.channels_min = 2 * dev->lines_out; - dai->driver->playback.channels_max = 8 * dev->lines_out; - - if (dev->lines_in) - cfg[dev->channel_in] = S6_I2S_IN; - if (dev->lines_out) - cfg[dev->channel_out] = S6_I2S_OUT; - - s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(0), cfg[0]); - s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(1), cfg[1]); - } - - if (dev->lines_out) { - if (dev->lines_in) { - if (!dev->dma_params.dma_out) - return -ENODEV; - } else { - dev->dma_params.dma_out = dev->dma_params.dma_in; - dev->dma_params.dma_in = 0; - } - } - dev->dma_params.sif_in = dev->sifbase + (dev->channel_in ? - S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0); - dev->dma_params.sif_out = dev->sifbase + (dev->channel_out ? - S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0); - dev->dma_params.same_rate = pdata->same_rate | pdata->wide; - return 0; -} - -#define S6000_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS -#define S6000_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) - -static const struct snd_soc_dai_ops s6000_i2s_dai_ops = { - .set_fmt = s6000_i2s_set_dai_fmt, - .set_clkdiv = s6000_i2s_set_clkdiv, - .hw_params = s6000_i2s_hw_params, -}; - -static struct snd_soc_dai_driver s6000_i2s_dai = { - .probe = s6000_i2s_dai_probe, - .playback = { - .channels_min = 2, - .channels_max = 8, - .formats = S6000_I2S_FORMATS, - .rates = S6000_I2S_RATES, - .rate_min = 0, - .rate_max = 1562500, - }, - .capture = { - .channels_min = 2, - .channels_max = 8, - .formats = S6000_I2S_FORMATS, - .rates = S6000_I2S_RATES, - .rate_min = 0, - .rate_max = 1562500, - }, - .ops = &s6000_i2s_dai_ops, -}; - -static const struct snd_soc_component_driver s6000_i2s_component = { - .name = "s6000-i2s", -}; - -static int s6000_i2s_probe(struct platform_device *pdev) -{ - struct s6000_i2s_dev *dev; - struct resource *scbmem, *sifmem, *region, *dma1, *dma2; - u8 __iomem *mmio; - int ret; - - scbmem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!scbmem) { - dev_err(&pdev->dev, "no mem resource?\n"); - ret = -ENODEV; - goto err_release_none; - } - - region = request_mem_region(scbmem->start, resource_size(scbmem), - pdev->name); - if (!region) { - dev_err(&pdev->dev, "I2S SCB region already claimed\n"); - ret = -EBUSY; - goto err_release_none; - } - - mmio = ioremap(scbmem->start, resource_size(scbmem)); - if (!mmio) { - dev_err(&pdev->dev, "can't ioremap SCB region\n"); - ret = -ENOMEM; - goto err_release_scb; - } - - sifmem = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!sifmem) { - dev_err(&pdev->dev, "no second mem resource?\n"); - ret = -ENODEV; - goto err_release_map; - } - - region = request_mem_region(sifmem->start, resource_size(sifmem), - pdev->name); - if (!region) { - dev_err(&pdev->dev, "I2S SIF region already claimed\n"); - ret = -EBUSY; - goto err_release_map; - } - - dma1 = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dma1) { - dev_err(&pdev->dev, "no dma resource?\n"); - ret = -ENODEV; - goto err_release_sif; - } - - region = request_mem_region(dma1->start, resource_size(dma1), - pdev->name); - if (!region) { - dev_err(&pdev->dev, "I2S DMA region already claimed\n"); - ret = -EBUSY; - goto err_release_sif; - } - - dma2 = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (dma2) { - region = request_mem_region(dma2->start, resource_size(dma2), - pdev->name); - if (!region) { - dev_err(&pdev->dev, - "I2S DMA region already claimed\n"); - ret = -EBUSY; - goto err_release_dma1; - } - } - - dev = kzalloc(sizeof(struct s6000_i2s_dev), GFP_KERNEL); - if (!dev) { - ret = -ENOMEM; - goto err_release_dma2; - } - dev_set_drvdata(&pdev->dev, dev); - - dev->sifbase = sifmem->start; - dev->scbbase = mmio; - - s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0); - s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR, - S6_I2S_INT_ALIGNMENT | - S6_I2S_INT_UNDERRUN | - S6_I2S_INT_OVERRUN); - - s6000_i2s_stop_channel(dev, 0); - s6000_i2s_stop_channel(dev, 1); - s6000_i2s_wait_disabled(dev); - - dev->dma_params.check_xrun = s6000_i2s_check_xrun; - dev->dma_params.trigger = s6000_i2s_trigger; - dev->dma_params.dma_in = dma1->start; - dev->dma_params.dma_out = dma2 ? dma2->start : 0; - dev->dma_params.irq = platform_get_irq(pdev, 0); - if (dev->dma_params.irq < 0) { - dev_err(&pdev->dev, "no irq resource?\n"); - ret = -ENODEV; - goto err_release_dev; - } - - s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, - S6_I2S_INT_ALIGNMENT | - S6_I2S_INT_UNDERRUN | - S6_I2S_INT_OVERRUN); - - ret = snd_soc_register_component(&pdev->dev, &s6000_i2s_component, - &s6000_i2s_dai, 1); - if (ret) - goto err_release_dev; - - return 0; - -err_release_dev: - kfree(dev); -err_release_dma2: - if (dma2) - release_mem_region(dma2->start, resource_size(dma2)); -err_release_dma1: - release_mem_region(dma1->start, resource_size(dma1)); -err_release_sif: - release_mem_region(sifmem->start, resource_size(sifmem)); -err_release_map: - iounmap(mmio); -err_release_scb: - release_mem_region(scbmem->start, resource_size(scbmem)); -err_release_none: - return ret; -} - -static int s6000_i2s_remove(struct platform_device *pdev) -{ - struct s6000_i2s_dev *dev = dev_get_drvdata(&pdev->dev); - struct resource *region; - void __iomem *mmio = dev->scbbase; - - snd_soc_unregister_component(&pdev->dev); - - s6000_i2s_stop_channel(dev, 0); - s6000_i2s_stop_channel(dev, 1); - - s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0); - kfree(dev); - - region = platform_get_resource(pdev, IORESOURCE_DMA, 0); - release_mem_region(region->start, resource_size(region)); - - region = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (region) - release_mem_region(region->start, resource_size(region)); - - region = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(region->start, resource_size(region)); - - iounmap(mmio); - region = platform_get_resource(pdev, IORESOURCE_IO, 0); - release_mem_region(region->start, resource_size(region)); - - return 0; -} - -static struct platform_driver s6000_i2s_driver = { - .probe = s6000_i2s_probe, - .remove = s6000_i2s_remove, - .driver = { - .name = "s6000-i2s", - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(s6000_i2s_driver); - -MODULE_AUTHOR("Daniel Gloeckner"); -MODULE_DESCRIPTION("Stretch s6000 family I2S SoC Interface"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/s6000/s6000-i2s.h b/sound/soc/s6000/s6000-i2s.h deleted file mode 100644 index 86aa1921c89e..000000000000 --- a/sound/soc/s6000/s6000-i2s.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * ALSA SoC I2S Audio Layer for the Stretch s6000 family - * - * Author: Daniel Gloeckner, <dg@emlix.com> - * Copyright: (C) 2009 emlix GmbH <info@emlix.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. - */ - -#ifndef _S6000_I2S_H -#define _S6000_I2S_H - -struct s6000_snd_platform_data { - int lines_in; - int lines_out; - int channel_in; - int channel_out; - int wide; - int same_rate; -}; -#endif diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c deleted file mode 100644 index fb8461e1b1f6..000000000000 --- a/sound/soc/s6000/s6000-pcm.c +++ /dev/null @@ -1,521 +0,0 @@ -/* - * ALSA PCM interface for the Stetch s6000 family - * - * Author: Daniel Gloeckner, <dg@emlix.com> - * Copyright: (C) 2009 emlix GmbH <info@emlix.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. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/dma-mapping.h> -#include <linux/interrupt.h> - -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> - -#include <asm/dma.h> -#include <variant/dmac.h> - -#include "s6000-pcm.h" - -#define S6_PCM_PREALLOCATE_SIZE (96 * 1024) -#define S6_PCM_PREALLOCATE_MAX (2048 * 1024) - -static struct snd_pcm_hardware s6000_pcm_hardware = { - .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_JOINT_DUPLEX), - .buffer_bytes_max = 0x7ffffff0, - .period_bytes_min = 16, - .period_bytes_max = 0xfffff0, - .periods_min = 2, - .periods_max = 1024, /* no limit */ - .fifo_size = 0, -}; - -struct s6000_runtime_data { - spinlock_t lock; - int period; /* current DMA period */ -}; - -static void s6000_pcm_enqueue_dma(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct s6000_runtime_data *prtd = runtime->private_data; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par; - int channel; - unsigned int period_size; - unsigned int dma_offset; - dma_addr_t dma_pos; - dma_addr_t src, dst; - - par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); - - period_size = snd_pcm_lib_period_bytes(substream); - dma_offset = prtd->period * period_size; - dma_pos = runtime->dma_addr + dma_offset; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - src = dma_pos; - dst = par->sif_out; - channel = par->dma_out; - } else { - src = par->sif_in; - dst = dma_pos; - channel = par->dma_in; - } - - if (!s6dmac_channel_enabled(DMA_MASK_DMAC(channel), - DMA_INDEX_CHNL(channel))) - return; - - if (s6dmac_fifo_full(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel))) { - printk(KERN_ERR "s6000-pcm: fifo full\n"); - return; - } - - if (WARN_ON(period_size & 15)) - return; - s6dmac_put_fifo(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel), - src, dst, period_size); - - prtd->period++; - if (unlikely(prtd->period >= runtime->periods)) - prtd->period = 0; -} - -static irqreturn_t s6000_pcm_irq(int irq, void *data) -{ - struct snd_pcm *pcm = data; - struct snd_soc_pcm_runtime *runtime = pcm->private_data; - struct s6000_runtime_data *prtd; - unsigned int has_xrun; - int i, ret = IRQ_NONE; - - for (i = 0; i < 2; ++i) { - struct snd_pcm_substream *substream = pcm->streams[i].substream; - struct s6000_pcm_dma_params *params = - snd_soc_dai_get_dma_data(runtime->cpu_dai, substream); - u32 channel; - unsigned int pending; - - if (substream == SNDRV_PCM_STREAM_PLAYBACK) - channel = params->dma_out; - else - channel = params->dma_in; - - has_xrun = params->check_xrun(runtime->cpu_dai); - - if (!channel) - continue; - - if (unlikely(has_xrun & (1 << i)) && - substream->runtime && - snd_pcm_running(substream)) { - dev_dbg(pcm->dev, "xrun\n"); - snd_pcm_stream_lock(substream); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(substream); - ret = IRQ_HANDLED; - } - - pending = s6dmac_int_sources(DMA_MASK_DMAC(channel), - DMA_INDEX_CHNL(channel)); - - if (pending & 1) { - ret = IRQ_HANDLED; - if (likely(substream->runtime && - snd_pcm_running(substream))) { - snd_pcm_period_elapsed(substream); - dev_dbg(pcm->dev, "period elapsed %x %x\n", - s6dmac_cur_src(DMA_MASK_DMAC(channel), - DMA_INDEX_CHNL(channel)), - s6dmac_cur_dst(DMA_MASK_DMAC(channel), - DMA_INDEX_CHNL(channel))); - prtd = substream->runtime->private_data; - spin_lock(&prtd->lock); - s6000_pcm_enqueue_dma(substream); - spin_unlock(&prtd->lock); - } - } - - if (unlikely(pending & ~7)) { - if (pending & (1 << 3)) - printk(KERN_WARNING - "s6000-pcm: DMA %x Underflow\n", - channel); - if (pending & (1 << 4)) - printk(KERN_WARNING - "s6000-pcm: DMA %x Overflow\n", - channel); - if (pending & 0x1e0) - printk(KERN_WARNING - "s6000-pcm: DMA %x Master Error " - "(mask %x)\n", - channel, pending >> 5); - - } - } - - return ret; -} - -static int s6000_pcm_start(struct snd_pcm_substream *substream) -{ - struct s6000_runtime_data *prtd = substream->runtime->private_data; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par; - unsigned long flags; - int srcinc; - u32 dma; - - par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); - - spin_lock_irqsave(&prtd->lock, flags); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - srcinc = 1; - dma = par->dma_out; - } else { - srcinc = 0; - dma = par->dma_in; - } - s6dmac_enable_chan(DMA_MASK_DMAC(dma), DMA_INDEX_CHNL(dma), - 1 /* priority 1 (0 is max) */, - 0 /* peripheral requests w/o xfer length mode */, - srcinc /* source address increment */, - srcinc^1 /* destination address increment */, - 0 /* chunksize 0 (skip impossible on this dma) */, - 0 /* source skip after chunk (impossible) */, - 0 /* destination skip after chunk (impossible) */, - 4 /* 16 byte burst size */, - -1 /* don't conserve bandwidth */, - 0 /* low watermark irq descriptor threshold */, - 0 /* disable hardware timestamps */, - 1 /* enable channel */); - - s6000_pcm_enqueue_dma(substream); - s6000_pcm_enqueue_dma(substream); - - spin_unlock_irqrestore(&prtd->lock, flags); - - return 0; -} - -static int s6000_pcm_stop(struct snd_pcm_substream *substream) -{ - struct s6000_runtime_data *prtd = substream->runtime->private_data; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par; - unsigned long flags; - u32 channel; - - par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - channel = par->dma_out; - else - channel = par->dma_in; - - s6dmac_set_terminal_count(DMA_MASK_DMAC(channel), - DMA_INDEX_CHNL(channel), 0); - - spin_lock_irqsave(&prtd->lock, flags); - - s6dmac_disable_chan(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel)); - - spin_unlock_irqrestore(&prtd->lock, flags); - - return 0; -} - -static int s6000_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par; - int ret; - - par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); - - ret = par->trigger(substream, cmd, 0); - if (ret < 0) - return ret; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ret = s6000_pcm_start(substream); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = s6000_pcm_stop(substream); - break; - default: - ret = -EINVAL; - } - if (ret < 0) - return ret; - - return par->trigger(substream, cmd, 1); -} - -static int s6000_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct s6000_runtime_data *prtd = substream->runtime->private_data; - - prtd->period = 0; - - return 0; -} - -static snd_pcm_uframes_t s6000_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par; - struct snd_pcm_runtime *runtime = substream->runtime; - struct s6000_runtime_data *prtd = runtime->private_data; - unsigned long flags; - unsigned int offset; - dma_addr_t count; - - par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); - - spin_lock_irqsave(&prtd->lock, flags); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - count = s6dmac_cur_src(DMA_MASK_DMAC(par->dma_out), - DMA_INDEX_CHNL(par->dma_out)); - else - count = s6dmac_cur_dst(DMA_MASK_DMAC(par->dma_in), - DMA_INDEX_CHNL(par->dma_in)); - - count -= runtime->dma_addr; - - spin_unlock_irqrestore(&prtd->lock, flags); - - offset = bytes_to_frames(runtime, count); - if (unlikely(offset >= runtime->buffer_size)) - offset = 0; - - return offset; -} - -static int s6000_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par; - struct snd_pcm_runtime *runtime = substream->runtime; - struct s6000_runtime_data *prtd; - int ret; - - par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); - snd_soc_set_runtime_hwparams(substream, &s6000_pcm_hardware); - - ret = snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 16); - if (ret < 0) - return ret; - ret = snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16); - if (ret < 0) - return ret; - ret = snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); - if (ret < 0) - return ret; - - if (par->same_rate) { - int rate; - spin_lock(&par->lock); /* needed? */ - rate = par->rate; - spin_unlock(&par->lock); - if (rate != -1) { - ret = snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_RATE, - rate, rate); - if (ret < 0) - return ret; - } - } - - prtd = kzalloc(sizeof(struct s6000_runtime_data), GFP_KERNEL); - if (prtd == NULL) - return -ENOMEM; - - spin_lock_init(&prtd->lock); - - runtime->private_data = prtd; - - return 0; -} - -static int s6000_pcm_close(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct s6000_runtime_data *prtd = runtime->private_data; - - kfree(prtd); - - return 0; -} - -static int s6000_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par; - int ret; - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) { - printk(KERN_WARNING "s6000-pcm: allocation of memory failed\n"); - return ret; - } - - par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); - - if (par->same_rate) { - spin_lock(&par->lock); - if (par->rate == -1 || - !(par->in_use & ~(1 << substream->stream))) { - par->rate = params_rate(hw_params); - par->in_use |= 1 << substream->stream; - } else if (params_rate(hw_params) != par->rate) { - snd_pcm_lib_free_pages(substream); - par->in_use &= ~(1 << substream->stream); - ret = -EBUSY; - } - spin_unlock(&par->lock); - } - return ret; -} - -static int s6000_pcm_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct s6000_pcm_dma_params *par = - snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); - - spin_lock(&par->lock); - par->in_use &= ~(1 << substream->stream); - if (!par->in_use) - par->rate = -1; - spin_unlock(&par->lock); - - return snd_pcm_lib_free_pages(substream); -} - -static struct snd_pcm_ops s6000_pcm_ops = { - .open = s6000_pcm_open, - .close = s6000_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = s6000_pcm_hw_params, - .hw_free = s6000_pcm_hw_free, - .trigger = s6000_pcm_trigger, - .prepare = s6000_pcm_prepare, - .pointer = s6000_pcm_pointer, -}; - -static void s6000_pcm_free(struct snd_pcm *pcm) -{ - struct snd_soc_pcm_runtime *runtime = pcm->private_data; - struct s6000_pcm_dma_params *params = - snd_soc_dai_get_dma_data(runtime->cpu_dai, - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream); - - free_irq(params->irq, pcm); - snd_pcm_lib_preallocate_free_for_all(pcm); -} - -static int s6000_pcm_new(struct snd_soc_pcm_runtime *runtime) -{ - struct snd_card *card = runtime->card->snd_card; - struct snd_pcm *pcm = runtime->pcm; - struct s6000_pcm_dma_params *params; - int res; - - params = snd_soc_dai_get_dma_data(runtime->cpu_dai, - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream); - - res = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); - if (res) - return res; - - if (params->dma_in) { - s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_in), - DMA_INDEX_CHNL(params->dma_in)); - s6dmac_int_sources(DMA_MASK_DMAC(params->dma_in), - DMA_INDEX_CHNL(params->dma_in)); - } - - if (params->dma_out) { - s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_out), - DMA_INDEX_CHNL(params->dma_out)); - s6dmac_int_sources(DMA_MASK_DMAC(params->dma_out), - DMA_INDEX_CHNL(params->dma_out)); - } - - res = request_irq(params->irq, s6000_pcm_irq, IRQF_SHARED, - "s6000-audio", pcm); - if (res) { - printk(KERN_ERR "s6000-pcm couldn't get IRQ\n"); - return res; - } - - res = snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_DEV, - card->dev, - S6_PCM_PREALLOCATE_SIZE, - S6_PCM_PREALLOCATE_MAX); - if (res) - printk(KERN_WARNING "s6000-pcm: preallocation failed\n"); - - spin_lock_init(¶ms->lock); - params->in_use = 0; - params->rate = -1; - return 0; -} - -static struct snd_soc_platform_driver s6000_soc_platform = { - .ops = &s6000_pcm_ops, - .pcm_new = s6000_pcm_new, - .pcm_free = s6000_pcm_free, -}; - -static int s6000_soc_platform_probe(struct platform_device *pdev) -{ - return snd_soc_register_platform(&pdev->dev, &s6000_soc_platform); -} - -static int s6000_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -static struct platform_driver s6000_pcm_driver = { - .driver = { - .name = "s6000-pcm-audio", - .owner = THIS_MODULE, - }, - - .probe = s6000_soc_platform_probe, - .remove = s6000_soc_platform_remove, -}; - -module_platform_driver(s6000_pcm_driver); - -MODULE_AUTHOR("Daniel Gloeckner"); -MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/s6000/s6000-pcm.h b/sound/soc/s6000/s6000-pcm.h deleted file mode 100644 index 09d9b883e58b..000000000000 --- a/sound/soc/s6000/s6000-pcm.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * ALSA PCM interface for the Stretch s6000 family - * - * Author: Daniel Gloeckner, <dg@emlix.com> - * Copyright: (C) 2009 emlix GmbH <info@emlix.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. - */ - -#ifndef _S6000_PCM_H -#define _S6000_PCM_H - -struct snd_soc_dai; -struct snd_pcm_substream; - -struct s6000_pcm_dma_params { - unsigned int (*check_xrun)(struct snd_soc_dai *cpu_dai); - int (*trigger)(struct snd_pcm_substream *substream, int cmd, int after); - dma_addr_t sif_in; - dma_addr_t sif_out; - u32 dma_in; - u32 dma_out; - int irq; - int same_rate; - - spinlock_t lock; - int in_use; - int rate; -}; - -#endif diff --git a/sound/soc/s6000/s6105-ipcam.c b/sound/soc/s6000/s6105-ipcam.c deleted file mode 100644 index 3510c01f8a6a..000000000000 --- a/sound/soc/s6000/s6105-ipcam.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * ASoC driver for Stretch s6105 IP camera platform - * - * Author: Daniel Gloeckner, <dg@emlix.com> - * Copyright: (C) 2009 emlix GmbH <info@emlix.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. - */ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/i2c.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/soc.h> - -#include "s6000-pcm.h" -#include "s6000-i2s.h" - -#define S6105_CAM_CODEC_CLOCK 12288000 - -static int s6105_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->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; - - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM | - SND_SOC_DAIFMT_NB_NF); - if (ret < 0) - return ret; - - /* set the codec system clock */ - ret = snd_soc_dai_set_sysclk(codec_dai, 0, S6105_CAM_CODEC_CLOCK, - SND_SOC_CLOCK_OUT); - if (ret < 0) - return ret; - - return 0; -} - -static struct snd_soc_ops s6105_ops = { - .hw_params = s6105_hw_params, -}; - -/* s6105 machine dapm widgets */ -static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { - SND_SOC_DAPM_LINE("Audio Out Differential", NULL), - SND_SOC_DAPM_LINE("Audio Out Stereo", NULL), - SND_SOC_DAPM_LINE("Audio In", NULL), -}; - -/* s6105 machine audio_mapnections to the codec pins */ -static const struct snd_soc_dapm_route audio_map[] = { - /* Audio Out connected to HPLOUT, HPLCOM, HPROUT */ - {"Audio Out Differential", NULL, "HPLOUT"}, - {"Audio Out Differential", NULL, "HPLCOM"}, - {"Audio Out Stereo", NULL, "HPLOUT"}, - {"Audio Out Stereo", NULL, "HPROUT"}, - - /* Audio In connected to LINE1L, LINE1R */ - {"LINE1L", NULL, "Audio In"}, - {"LINE1R", NULL, "Audio In"}, -}; - -static int output_type_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item) { - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, "HPLOUT/HPROUT"); - } else { - strcpy(uinfo->value.enumerated.name, "HPLOUT/HPLCOM"); - } - return 0; -} - -static int output_type_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.enumerated.item[0] = kcontrol->private_value; - return 0; -} - -static int output_type_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_card *card = kcontrol->private_data; - struct snd_soc_dapm_context *dapm = &card->dapm; - unsigned int val = (ucontrol->value.enumerated.item[0] != 0); - char *differential = "Audio Out Differential"; - char *stereo = "Audio Out Stereo"; - - if (kcontrol->private_value == val) - return 0; - kcontrol->private_value = val; - snd_soc_dapm_disable_pin(dapm, val ? differential : stereo); - snd_soc_dapm_sync(dapm); - snd_soc_dapm_enable_pin(dapm, val ? stereo : differential); - snd_soc_dapm_sync(dapm); - - return 1; -} - -static const struct snd_kcontrol_new audio_out_mux = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Output Mux", - .index = 0, - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = output_type_info, - .get = output_type_get, - .put = output_type_put, - .private_value = 1 /* default to stereo */ -}; - -/* Logic for a aic3x as connected on the s6105 ip camera ref design */ -static int s6105_aic3x_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_card *card = rtd->card; - - /* must correspond to audio_out_mux.private_value initializer */ - snd_soc_dapm_disable_pin(&card->dapm, "Audio Out Differential"); - - snd_ctl_add(card->snd_card, snd_ctl_new1(&audio_out_mux, card)); - - return 0; -} - -/* s6105 digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link s6105_dai = { - .name = "TLV320AIC31", - .stream_name = "AIC31", - .cpu_dai_name = "s6000-i2s", - .codec_dai_name = "tlv320aic3x-hifi", - .platform_name = "s6000-pcm-audio", - .codec_name = "tlv320aic3x-codec.0-001a", - .init = s6105_aic3x_init, - .ops = &s6105_ops, -}; - -/* s6105 audio machine driver */ -static struct snd_soc_card snd_soc_card_s6105 = { - .name = "Stretch IP Camera", - .owner = THIS_MODULE, - .dai_link = &s6105_dai, - .num_links = 1, - - .dapm_widgets = aic3x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(aic3x_dapm_widgets), - .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map), - .fully_routed = true, -}; - -static struct s6000_snd_platform_data s6105_snd_data __initdata = { - .wide = 0, - .channel_in = 0, - .channel_out = 1, - .lines_in = 1, - .lines_out = 1, - .same_rate = 1, -}; - -static struct platform_device *s6105_snd_device; - -/* temporary i2c device creation until this can be moved into the machine - * support file. -*/ -static struct i2c_board_info i2c_device[] = { - { I2C_BOARD_INFO("tlv320aic33", 0x18), } -}; - -static int __init s6105_init(void) -{ - int ret; - - i2c_register_board_info(0, i2c_device, ARRAY_SIZE(i2c_device)); - - s6105_snd_device = platform_device_alloc("soc-audio", -1); - if (!s6105_snd_device) - return -ENOMEM; - - platform_set_drvdata(s6105_snd_device, &snd_soc_card_s6105); - platform_device_add_data(s6105_snd_device, &s6105_snd_data, - sizeof(s6105_snd_data)); - - ret = platform_device_add(s6105_snd_device); - if (ret) - platform_device_put(s6105_snd_device); - - return ret; -} - -static void __exit s6105_exit(void) -{ - platform_device_unregister(s6105_snd_device); -} - -module_init(s6105_init); -module_exit(s6105_exit); - -MODULE_AUTHOR("Daniel Gloeckner"); -MODULE_DESCRIPTION("Stretch s6105 IP camera ASoC driver"); -MODULE_LICENSE("GPL"); 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/samsung/snow.c b/sound/soc/samsung/snow.c index 0acf5d0eed53..72118a77dd5b 100644 --- a/sound/soc/samsung/snow.c +++ b/sound/soc/samsung/snow.c @@ -110,6 +110,7 @@ static const struct of_device_id snow_of_match[] = { { .compatible = "google,snow-audio-max98095", }, {}, }; +MODULE_DEVICE_TABLE(of, snow_of_match); static struct platform_driver snow_driver = { .driver = { diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 66fddec9543d..8869971d7884 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -842,12 +842,9 @@ static int fsi_clk_disable(struct device *dev, return -EINVAL; if (1 == clock->count--) { - if (clock->xck) - clk_disable(clock->xck); - if (clock->ick) - clk_disable(clock->ick); - if (clock->div) - clk_disable(clock->div); + clk_disable(clock->xck); + clk_disable(clock->ick); + clk_disable(clock->div); } return 0; @@ -1711,8 +1708,7 @@ static const struct snd_soc_dai_ops fsi_dai_ops = { static struct snd_pcm_hardware fsi_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE, + SNDRV_PCM_INFO_MMAP_VALID, .buffer_bytes_max = 64 * 1024, .period_bytes_min = 32, .period_bytes_max = 8192, 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/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 1922ec57d10a..70042197f9e2 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -886,8 +886,7 @@ static int rsnd_dai_probe(struct platform_device *pdev, static struct snd_pcm_hardware rsnd_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE, + SNDRV_PCM_INFO_MMAP_VALID, .buffer_bytes_max = 64 * 1024, .period_bytes_min = 32, .period_bytes_max = 8192, 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-cache.c b/sound/soc/soc-cache.c index a9f82b5aba9d..07f43356f963 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -15,56 +15,6 @@ #include <linux/export.h> #include <linux/slab.h> -#include <trace/events/asoc.h> - -static bool snd_soc_set_cache_val(void *base, unsigned int idx, - unsigned int val, unsigned int word_size) -{ - switch (word_size) { - case 1: { - u8 *cache = base; - if (cache[idx] == val) - return true; - cache[idx] = val; - break; - } - case 2: { - u16 *cache = base; - if (cache[idx] == val) - return true; - cache[idx] = val; - break; - } - default: - WARN(1, "Invalid word_size %d\n", word_size); - break; - } - return false; -} - -static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, - unsigned int word_size) -{ - if (!base) - return -1; - - switch (word_size) { - case 1: { - const u8 *cache = base; - return cache[idx]; - } - case 2: { - const u16 *cache = base; - return cache[idx]; - } - default: - WARN(1, "Invalid word_size %d\n", word_size); - break; - } - /* unreachable */ - return -1; -} - int snd_soc_cache_init(struct snd_soc_codec *codec) { const struct snd_soc_codec_driver *codec_drv = codec->driver; @@ -75,8 +25,6 @@ int snd_soc_cache_init(struct snd_soc_codec *codec) if (!reg_size) return 0; - mutex_init(&codec->cache_rw_mutex); - dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n", codec->component.name); @@ -103,100 +51,3 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec) codec->reg_cache = NULL; return 0; } - -/** - * snd_soc_cache_read: Fetch the value of a given register from the cache. - * - * @codec: CODEC to configure. - * @reg: The register index. - * @value: The value to be returned. - */ -int snd_soc_cache_read(struct snd_soc_codec *codec, - unsigned int reg, unsigned int *value) -{ - if (!value) - return -EINVAL; - - mutex_lock(&codec->cache_rw_mutex); - if (!ZERO_OR_NULL_PTR(codec->reg_cache)) - *value = snd_soc_get_cache_val(codec->reg_cache, reg, - codec->driver->reg_word_size); - mutex_unlock(&codec->cache_rw_mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_cache_read); - -/** - * snd_soc_cache_write: Set the value of a given register in the cache. - * - * @codec: CODEC to configure. - * @reg: The register index. - * @value: The new register value. - */ -int snd_soc_cache_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int value) -{ - mutex_lock(&codec->cache_rw_mutex); - if (!ZERO_OR_NULL_PTR(codec->reg_cache)) - snd_soc_set_cache_val(codec->reg_cache, reg, value, - codec->driver->reg_word_size); - mutex_unlock(&codec->cache_rw_mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_cache_write); - -static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) -{ - int i; - int ret; - const struct snd_soc_codec_driver *codec_drv; - unsigned int val; - - codec_drv = codec->driver; - for (i = 0; i < codec_drv->reg_cache_size; ++i) { - ret = snd_soc_cache_read(codec, i, &val); - if (ret) - return ret; - if (codec_drv->reg_cache_default) - if (snd_soc_get_cache_val(codec_drv->reg_cache_default, - i, codec_drv->reg_word_size) == val) - continue; - - ret = snd_soc_write(codec, i, val); - if (ret) - return ret; - dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n", - i, val); - } - return 0; -} - -/** - * snd_soc_cache_sync: Sync the register cache with the hardware. - * - * @codec: CODEC to configure. - * - * Any registers that should not be synced should be marked as - * volatile. In general drivers can choose not to use the provided - * syncing functionality if they so require. - */ -int snd_soc_cache_sync(struct snd_soc_codec *codec) -{ - const char *name = "flat"; - int ret; - - if (!codec->cache_sync) - return 0; - - dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n", - codec->component.name); - trace_snd_soc_cache_sync(codec, name, "start"); - ret = snd_soc_flat_cache_sync(codec); - if (!ret) - codec->cache_sync = 0; - trace_snd_soc_cache_sync(codec, name, "end"); - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_cache_sync); diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index cecfab3cc948..590a82f01d0b 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -258,10 +258,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); - else - dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; @@ -456,11 +453,7 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, if (ret < 0) goto out; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); - else - dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); - + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; out: diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4c8f8a23a0e9..11d01e5a2f70 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) @@ -309,9 +296,6 @@ static void soc_init_codec_debugfs(struct snd_soc_component *component) { struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - debugfs_create_bool("cache_sync", 0444, codec->component.debugfs_root, - &codec->cache_sync); - codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, codec->component.debugfs_root, codec, &codec_reg_fops); @@ -499,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 @@ -592,17 +542,12 @@ int snd_soc_suspend(struct device *dev) for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; - struct snd_soc_platform *platform = card->rtd[i].platform; 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); - if (platform->driver->suspend && !platform->suspended) { - platform->driver->suspend(cpu_dai); - platform->suspended = 1; - } } /* close any waiting streams and save state */ @@ -629,8 +574,8 @@ int snd_soc_suspend(struct device *dev) SND_SOC_DAPM_STREAM_SUSPEND); } - /* Recheck all analogue paths too */ - dapm_mark_io_dirty(&card->dapm); + /* Recheck all endpoints too, their state is affected by suspend */ + dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); /* suspend all CODECs */ @@ -656,7 +601,6 @@ int snd_soc_suspend(struct device *dev) if (codec->driver->suspend) codec->driver->suspend(codec); codec->suspended = 1; - codec->cache_sync = 1; if (codec->component.regmap) regcache_mark_dirty(codec->component.regmap); /* deactivate pins to sleep state */ @@ -676,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 */ @@ -712,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); } @@ -775,17 +719,12 @@ static void soc_resume_deferred(struct work_struct *work) for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; - struct snd_soc_platform *platform = card->rtd[i].platform; 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); - if (platform->driver->resume && platform->suspended) { - platform->driver->resume(cpu_dai); - platform->suspended = 0; - } } if (card->resume_post) @@ -796,8 +735,8 @@ static void soc_resume_deferred(struct work_struct *work) /* userspace can access us now we are back as we were before */ snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0); - /* Recheck all analogue paths too */ - dapm_mark_io_dirty(&card->dapm); + /* Recheck all endpoints too, their state is affected by suspend */ + dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); } @@ -805,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) @@ -828,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"); @@ -884,7 +825,7 @@ static struct snd_soc_dai *snd_soc_find_dai( list_for_each_entry(component, &component_list, list) { if (dlc->of_node && component->dev->of_node != dlc->of_node) continue; - if (dlc->name && strcmp(dev_name(component->dev), dlc->name)) + if (dlc->name && strcmp(component->name, dlc->name)) continue; list_for_each_entry(dai, &component->dai_list, list) { if (dlc->dai_name && strcmp(dai->name, dlc->dai_name)) @@ -1251,25 +1192,22 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num, return 0; } -static int soc_probe_codec_dai(struct snd_soc_card *card, - struct snd_soc_dai *codec_dai, - int order) +static int soc_probe_dai(struct snd_soc_dai *dai, int order) { int ret; - if (!codec_dai->probed && codec_dai->driver->probe_order == order) { - if (codec_dai->driver->probe) { - ret = codec_dai->driver->probe(codec_dai); + if (!dai->probed && dai->driver->probe_order == order) { + if (dai->driver->probe) { + ret = dai->driver->probe(dai); if (ret < 0) { - dev_err(codec_dai->dev, - "ASoC: failed to probe CODEC DAI %s: %d\n", - codec_dai->name, ret); + dev_err(dai->dev, + "ASoC: failed to probe DAI %s: %d\n", + dai->name, ret); return ret; } } - /* mark codec_dai as probed and add to card dai list */ - codec_dai->probed = 1; + dai->probed = 1; } return 0; @@ -1319,40 +1257,22 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int i, ret; dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", card->name, num, order); - /* config components */ - cpu_dai->platform = platform; - cpu_dai->card = card; - for (i = 0; i < rtd->num_codecs; i++) - rtd->codec_dais[i]->card = card; - /* set default power off timeout */ rtd->pmdown_time = pmdown_time; - /* probe the cpu_dai */ - if (!cpu_dai->probed && - cpu_dai->driver->probe_order == order) { - if (cpu_dai->driver->probe) { - ret = cpu_dai->driver->probe(cpu_dai); - if (ret < 0) { - dev_err(cpu_dai->dev, - "ASoC: failed to probe CPU DAI %s: %d\n", - cpu_dai->name, ret); - return ret; - } - } - cpu_dai->probed = 1; - } + ret = soc_probe_dai(cpu_dai, order); + if (ret) + return ret; /* probe the CODEC DAI */ for (i = 0; i < rtd->num_codecs; i++) { - ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order); + ret = soc_probe_dai(rtd->codec_dais[i], order); if (ret) return ret; } @@ -1422,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]; @@ -1793,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); @@ -1949,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 @@ -2326,7 +1947,7 @@ EXPORT_SYMBOL_GPL(snd_soc_add_card_controls); int snd_soc_add_dai_controls(struct snd_soc_dai *dai, const struct snd_kcontrol_new *controls, int num_controls) { - struct snd_card *card = dai->card->snd_card; + struct snd_card *card = dai->component->card->snd_card; return snd_soc_add_controls(card, dai->dev, controls, num_controls, NULL, dai); @@ -2334,1020 +1955,6 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai, EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls); /** - * snd_soc_info_enum_double - enumerated double mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about a double enumerated - * mixer control. - * - * Returns 0 for success. - */ -int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = e->shift_l == e->shift_r ? 1 : 2; - uinfo->value.enumerated.items = e->items; - - if (uinfo->value.enumerated.item >= e->items) - uinfo->value.enumerated.item = e->items - 1; - strlcpy(uinfo->value.enumerated.name, - e->texts[uinfo->value.enumerated.item], - sizeof(uinfo->value.enumerated.name)); - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_enum_double); - -/** - * snd_soc_get_enum_double - enumerated double mixer get callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value of a double enumerated mixer. - * - * Returns 0 for success. - */ -int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val, item; - unsigned int reg_val; - int ret; - - ret = snd_soc_component_read(component, e->reg, ®_val); - if (ret) - return ret; - val = (reg_val >> e->shift_l) & e->mask; - item = snd_soc_enum_val_to_item(e, val); - ucontrol->value.enumerated.item[0] = item; - if (e->shift_l != e->shift_r) { - val = (reg_val >> e->shift_l) & e->mask; - item = snd_soc_enum_val_to_item(e, val); - ucontrol->value.enumerated.item[1] = item; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_enum_double); - -/** - * snd_soc_put_enum_double - enumerated double mixer put callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value of a double enumerated mixer. - * - * Returns 0 for success. - */ -int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int *item = ucontrol->value.enumerated.item; - unsigned int val; - unsigned int mask; - - if (item[0] >= e->items) - return -EINVAL; - val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; - mask = e->mask << e->shift_l; - if (e->shift_l != e->shift_r) { - if (item[1] >= e->items) - return -EINVAL; - val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r; - mask |= e->mask << e->shift_r; - } - - return snd_soc_component_update_bits(component, e->reg, mask, val); -} -EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); - -/** - * snd_soc_read_signed - Read a codec register and interprete as signed value - * @component: component - * @reg: Register to read - * @mask: Mask to use after shifting the register value - * @shift: Right shift of register value - * @sign_bit: Bit that describes if a number is negative or not. - * @signed_val: Pointer to where the read value should be stored - * - * This functions reads a codec register. The register value is shifted right - * by 'shift' bits and masked with the given 'mask'. Afterwards it translates - * the given registervalue into a signed integer if sign_bit is non-zero. - * - * Returns 0 on sucess, otherwise an error value - */ -static int snd_soc_read_signed(struct snd_soc_component *component, - unsigned int reg, unsigned int mask, unsigned int shift, - unsigned int sign_bit, int *signed_val) -{ - int ret; - unsigned int val; - - ret = snd_soc_component_read(component, reg, &val); - if (ret < 0) - return ret; - - val = (val >> shift) & mask; - - if (!sign_bit) { - *signed_val = val; - return 0; - } - - /* non-negative number */ - if (!(val & BIT(sign_bit))) { - *signed_val = val; - return 0; - } - - ret = val; - - /* - * The register most probably does not contain a full-sized int. - * Instead we have an arbitrary number of bits in a signed - * representation which has to be translated into a full-sized int. - * This is done by filling up all bits above the sign-bit. - */ - ret |= ~((int)(BIT(sign_bit) - 1)); - - *signed_val = ret; - - return 0; -} - -/** - * snd_soc_info_volsw - single mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about a single mixer control, or a double - * mixer control that spans 2 registers. - * - * Returns 0 for success. - */ -int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int platform_max; - - if (!mc->platform_max) - mc->platform_max = mc->max; - platform_max = mc->platform_max; - - if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume")) - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - else - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - - uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = platform_max - mc->min; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_volsw); - -/** - * snd_soc_get_volsw - single mixer get callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value of a single mixer control, or a double mixer - * control that spans 2 registers. - * - * Returns 0 for success. - */ -int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; - int max = mc->max; - int min = mc->min; - int sign_bit = mc->sign_bit; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int invert = mc->invert; - int val; - int ret; - - if (sign_bit) - mask = BIT(sign_bit + 1) - 1; - - ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val); - if (ret) - return ret; - - ucontrol->value.integer.value[0] = val - min; - if (invert) - ucontrol->value.integer.value[0] = - max - ucontrol->value.integer.value[0]; - - if (snd_soc_volsw_is_stereo(mc)) { - if (reg == reg2) - ret = snd_soc_read_signed(component, reg, mask, rshift, - sign_bit, &val); - else - ret = snd_soc_read_signed(component, reg2, mask, shift, - sign_bit, &val); - if (ret) - return ret; - - ucontrol->value.integer.value[1] = val - min; - if (invert) - ucontrol->value.integer.value[1] = - max - ucontrol->value.integer.value[1]; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_volsw); - -/** - * snd_soc_put_volsw - single mixer put callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value of a single mixer control, or a double mixer - * control that spans 2 registers. - * - * Returns 0 for success. - */ -int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; - int max = mc->max; - int min = mc->min; - unsigned int sign_bit = mc->sign_bit; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int invert = mc->invert; - int err; - bool type_2r = false; - unsigned int val2 = 0; - unsigned int val, val_mask; - - if (sign_bit) - mask = BIT(sign_bit + 1) - 1; - - val = ((ucontrol->value.integer.value[0] + min) & mask); - if (invert) - val = max - val; - val_mask = mask << shift; - val = val << shift; - if (snd_soc_volsw_is_stereo(mc)) { - val2 = ((ucontrol->value.integer.value[1] + min) & mask); - if (invert) - val2 = max - val2; - if (reg == reg2) { - val_mask |= mask << rshift; - val |= val2 << rshift; - } else { - val2 = val2 << shift; - type_2r = true; - } - } - err = snd_soc_component_update_bits(component, reg, val_mask, val); - if (err < 0) - return err; - - if (type_2r) - err = snd_soc_component_update_bits(component, reg2, val_mask, - val2); - - return err; -} -EXPORT_SYMBOL_GPL(snd_soc_put_volsw); - -/** - * snd_soc_get_volsw_sx - single mixer get callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value of a single mixer control, or a double mixer - * control that spans 2 registers. - * - * Returns 0 for success. - */ -int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; - int max = mc->max; - int min = mc->min; - int mask = (1 << (fls(min + max) - 1)) - 1; - unsigned int val; - int ret; - - ret = snd_soc_component_read(component, reg, &val); - if (ret < 0) - return ret; - - ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask; - - if (snd_soc_volsw_is_stereo(mc)) { - ret = snd_soc_component_read(component, reg2, &val); - if (ret < 0) - return ret; - - val = ((val >> rshift) - min) & mask; - ucontrol->value.integer.value[1] = val; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx); - -/** - * snd_soc_put_volsw_sx - double mixer set callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to set the value of a double mixer control that spans 2 registers. - * - * Returns 0 for success. - */ -int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; - int max = mc->max; - int min = mc->min; - int mask = (1 << (fls(min + max) - 1)) - 1; - int err = 0; - unsigned int val, val_mask, val2 = 0; - - val_mask = mask << shift; - val = (ucontrol->value.integer.value[0] + min) & mask; - val = val << shift; - - err = snd_soc_component_update_bits(component, reg, val_mask, val); - if (err < 0) - return err; - - if (snd_soc_volsw_is_stereo(mc)) { - val_mask = mask << rshift; - val2 = (ucontrol->value.integer.value[1] + min) & mask; - val2 = val2 << rshift; - - err = snd_soc_component_update_bits(component, reg2, val_mask, - val2); - } - return err; -} -EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx); - -/** - * snd_soc_info_volsw_s8 - signed mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about a signed mixer control. - * - * Returns 0 for success. - */ -int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int platform_max; - int min = mc->min; - - if (!mc->platform_max) - mc->platform_max = mc->max; - platform_max = mc->platform_max; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = platform_max - min; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8); - -/** - * snd_soc_get_volsw_s8 - signed mixer get callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value of a signed mixer control. - * - * Returns 0 for success. - */ -int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - unsigned int reg = mc->reg; - unsigned int val; - int min = mc->min; - int ret; - - ret = snd_soc_component_read(component, reg, &val); - if (ret) - return ret; - - ucontrol->value.integer.value[0] = - ((signed char)(val & 0xff))-min; - ucontrol->value.integer.value[1] = - ((signed char)((val >> 8) & 0xff))-min; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8); - -/** - * snd_soc_put_volsw_sgn - signed mixer put callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value of a signed mixer control. - * - * Returns 0 for success. - */ -int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - unsigned int reg = mc->reg; - int min = mc->min; - unsigned int val; - - val = (ucontrol->value.integer.value[0]+min) & 0xff; - val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8; - - return snd_soc_component_update_bits(component, reg, 0xffff, val); -} -EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); - -/** - * snd_soc_info_volsw_range - single mixer info callback with range. - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information, within a range, about a single - * mixer control. - * - * returns 0 for success. - */ -int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int platform_max; - int min = mc->min; - - if (!mc->platform_max) - mc->platform_max = mc->max; - platform_max = mc->platform_max; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = platform_max - min; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range); - -/** - * snd_soc_put_volsw_range - single mixer put value callback with range. - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value, within a range, for a single mixer control. - * - * Returns 0 for success. - */ -int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - unsigned int reg = mc->reg; - unsigned int rreg = mc->rreg; - unsigned int shift = mc->shift; - int min = mc->min; - int max = mc->max; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int invert = mc->invert; - unsigned int val, val_mask; - int ret; - - if (invert) - val = (max - ucontrol->value.integer.value[0]) & mask; - else - val = ((ucontrol->value.integer.value[0] + min) & mask); - val_mask = mask << shift; - val = val << shift; - - ret = snd_soc_component_update_bits(component, reg, val_mask, val); - if (ret < 0) - return ret; - - if (snd_soc_volsw_is_stereo(mc)) { - if (invert) - val = (max - ucontrol->value.integer.value[1]) & mask; - else - val = ((ucontrol->value.integer.value[1] + min) & mask); - val_mask = mask << shift; - val = val << shift; - - ret = snd_soc_component_update_bits(component, rreg, val_mask, - val); - } - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range); - -/** - * snd_soc_get_volsw_range - single mixer get callback with range - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value, within a range, of a single mixer control. - * - * Returns 0 for success. - */ -int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int rreg = mc->rreg; - unsigned int shift = mc->shift; - int min = mc->min; - int max = mc->max; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int invert = mc->invert; - unsigned int val; - int ret; - - ret = snd_soc_component_read(component, reg, &val); - if (ret) - return ret; - - ucontrol->value.integer.value[0] = (val >> shift) & mask; - if (invert) - ucontrol->value.integer.value[0] = - max - ucontrol->value.integer.value[0]; - else - ucontrol->value.integer.value[0] = - ucontrol->value.integer.value[0] - min; - - if (snd_soc_volsw_is_stereo(mc)) { - ret = snd_soc_component_read(component, rreg, &val); - if (ret) - return ret; - - ucontrol->value.integer.value[1] = (val >> shift) & mask; - if (invert) - ucontrol->value.integer.value[1] = - max - ucontrol->value.integer.value[1]; - else - ucontrol->value.integer.value[1] = - ucontrol->value.integer.value[1] - min; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); - -/** - * snd_soc_limit_volume - Set new limit to an existing volume control. - * - * @codec: where to look for the control - * @name: Name of the control - * @max: new maximum limit - * - * Return 0 for success, else error. - */ -int snd_soc_limit_volume(struct snd_soc_codec *codec, - const char *name, int max) -{ - struct snd_card *card = codec->component.card->snd_card; - struct snd_kcontrol *kctl; - struct soc_mixer_control *mc; - int found = 0; - int ret = -EINVAL; - - /* Sanity check for name and max */ - if (unlikely(!name || max <= 0)) - return -EINVAL; - - list_for_each_entry(kctl, &card->controls, list) { - if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) { - found = 1; - break; - } - } - if (found) { - mc = (struct soc_mixer_control *)kctl->private_value; - if (max <= mc->max) { - mc->platform_max = max; - ret = 0; - } - } - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_limit_volume); - -int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_bytes *params = (void *)kcontrol->private_value; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = params->num_regs * component->val_bytes; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_bytes_info); - -int snd_soc_bytes_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_bytes *params = (void *)kcontrol->private_value; - int ret; - - if (component->regmap) - ret = regmap_raw_read(component->regmap, params->base, - ucontrol->value.bytes.data, - params->num_regs * component->val_bytes); - else - ret = -EINVAL; - - /* Hide any masked bytes to ensure consistent data reporting */ - if (ret == 0 && params->mask) { - switch (component->val_bytes) { - case 1: - ucontrol->value.bytes.data[0] &= ~params->mask; - break; - case 2: - ((u16 *)(&ucontrol->value.bytes.data))[0] - &= cpu_to_be16(~params->mask); - break; - case 4: - ((u32 *)(&ucontrol->value.bytes.data))[0] - &= cpu_to_be32(~params->mask); - break; - default: - return -EINVAL; - } - } - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_bytes_get); - -int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_bytes *params = (void *)kcontrol->private_value; - int ret, len; - unsigned int val, mask; - void *data; - - if (!component->regmap || !params->num_regs) - return -EINVAL; - - len = params->num_regs * component->val_bytes; - - data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); - if (!data) - return -ENOMEM; - - /* - * If we've got a mask then we need to preserve the register - * bits. We shouldn't modify the incoming data so take a - * copy. - */ - if (params->mask) { - ret = regmap_read(component->regmap, params->base, &val); - if (ret != 0) - goto out; - - val &= params->mask; - - switch (component->val_bytes) { - case 1: - ((u8 *)data)[0] &= ~params->mask; - ((u8 *)data)[0] |= val; - break; - case 2: - mask = ~params->mask; - ret = regmap_parse_val(component->regmap, - &mask, &mask); - if (ret != 0) - goto out; - - ((u16 *)data)[0] &= mask; - - ret = regmap_parse_val(component->regmap, - &val, &val); - if (ret != 0) - goto out; - - ((u16 *)data)[0] |= val; - break; - case 4: - mask = ~params->mask; - ret = regmap_parse_val(component->regmap, - &mask, &mask); - if (ret != 0) - goto out; - - ((u32 *)data)[0] &= mask; - - ret = regmap_parse_val(component->regmap, - &val, &val); - if (ret != 0) - goto out; - - ((u32 *)data)[0] |= val; - break; - default: - ret = -EINVAL; - goto out; - } - } - - ret = regmap_raw_write(component->regmap, params->base, - data, len); - -out: - kfree(data); - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_bytes_put); - -int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *ucontrol) -{ - struct soc_bytes_ext *params = (void *)kcontrol->private_value; - - ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; - ucontrol->count = params->max; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext); - -int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv) -{ - struct soc_bytes_ext *params = (void *)kcontrol->private_value; - unsigned int count = size < params->max ? size : params->max; - int ret = -ENXIO; - - switch (op_flag) { - case SNDRV_CTL_TLV_OP_READ: - if (params->get) - ret = params->get(tlv, count); - break; - case SNDRV_CTL_TLV_OP_WRITE: - if (params->put) - ret = params->put(tlv, count); - break; - } - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback); - -/** - * snd_soc_info_xr_sx - signed multi register info callback - * @kcontrol: mreg control - * @uinfo: control element information - * - * Callback to provide information of a control that can - * span multiple codec registers which together - * forms a single signed value in a MSB/LSB manner. - * - * Returns 0 for success. - */ -int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mreg_control *mc = - (struct soc_mreg_control *)kcontrol->private_value; - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = mc->min; - uinfo->value.integer.max = mc->max; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx); - -/** - * snd_soc_get_xr_sx - signed multi register get callback - * @kcontrol: mreg control - * @ucontrol: control element information - * - * Callback to get the value of a control that can span - * multiple codec registers which together forms a single - * signed value in a MSB/LSB manner. The control supports - * specifying total no of bits used to allow for bitfields - * across the multiple codec registers. - * - * Returns 0 for success. - */ -int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mreg_control *mc = - (struct soc_mreg_control *)kcontrol->private_value; - unsigned int regbase = mc->regbase; - unsigned int regcount = mc->regcount; - unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; - unsigned int regwmask = (1<<regwshift)-1; - unsigned int invert = mc->invert; - unsigned long mask = (1UL<<mc->nbits)-1; - long min = mc->min; - long max = mc->max; - long val = 0; - unsigned int regval; - unsigned int i; - int ret; - - for (i = 0; i < regcount; i++) { - ret = snd_soc_component_read(component, regbase+i, ®val); - if (ret) - return ret; - val |= (regval & regwmask) << (regwshift*(regcount-i-1)); - } - val &= mask; - if (min < 0 && val > max) - val |= ~mask; - if (invert) - val = max - val; - ucontrol->value.integer.value[0] = val; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx); - -/** - * snd_soc_put_xr_sx - signed multi register get callback - * @kcontrol: mreg control - * @ucontrol: control element information - * - * Callback to set the value of a control that can span - * multiple codec registers which together forms a single - * signed value in a MSB/LSB manner. The control supports - * specifying total no of bits used to allow for bitfields - * across the multiple codec registers. - * - * Returns 0 for success. - */ -int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mreg_control *mc = - (struct soc_mreg_control *)kcontrol->private_value; - unsigned int regbase = mc->regbase; - unsigned int regcount = mc->regcount; - unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; - unsigned int regwmask = (1<<regwshift)-1; - unsigned int invert = mc->invert; - unsigned long mask = (1UL<<mc->nbits)-1; - long max = mc->max; - long val = ucontrol->value.integer.value[0]; - unsigned int i, regval, regmask; - int err; - - if (invert) - val = max - val; - val &= mask; - for (i = 0; i < regcount; i++) { - regval = (val >> (regwshift*(regcount-i-1))) & regwmask; - regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask; - err = snd_soc_component_update_bits(component, regbase+i, - regmask, regval); - if (err < 0) - return err; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx); - -/** - * snd_soc_get_strobe - strobe get callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback get the value of a strobe mixer control. - * - * Returns 0 for success. - */ -int snd_soc_get_strobe(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int shift = mc->shift; - unsigned int mask = 1 << shift; - unsigned int invert = mc->invert != 0; - unsigned int val; - int ret; - - ret = snd_soc_component_read(component, reg, &val); - if (ret) - return ret; - - val &= mask; - - if (shift != 0 && val != 0) - val = val >> shift; - ucontrol->value.enumerated.item[0] = val ^ invert; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_strobe); - -/** - * snd_soc_put_strobe - strobe put callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback strobe a register bit to high then low (or the inverse) - * in one pass of a single mixer enum control. - * - * Returns 1 for success. - */ -int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int shift = mc->shift; - unsigned int mask = 1 << shift; - unsigned int invert = mc->invert != 0; - unsigned int strobe = ucontrol->value.enumerated.item[0] != 0; - unsigned int val1 = (strobe ^ invert) ? mask : 0; - unsigned int val2 = (strobe ^ invert) ? 0 : mask; - int err; - - err = snd_soc_component_update_bits(component, reg, mask, val1); - if (err < 0) - return err; - - return snd_soc_component_update_bits(component, reg, mask, val2); -} -EXPORT_SYMBOL_GPL(snd_soc_put_strobe); - -/** * snd_soc_dai_set_sysclk - configure DAI system or master clock. * @dai: DAI * @clk_id: DAI specific clock ID @@ -3996,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); } @@ -4362,7 +3009,6 @@ int snd_soc_register_codec(struct device *dev, codec->dev = dev; codec->driver = codec_drv; codec->component.val_bytes = codec_drv->reg_word_size; - mutex_init(&codec->mutex); #ifdef CONFIG_DEBUG_FS codec->component.init_debugfs = soc_init_codec_debugfs; @@ -4585,7 +3231,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname) { struct device_node *np = card->dev->of_node; - int num_routes; + int num_routes, old_routes; struct snd_soc_dapm_route *routes; int i, ret; @@ -4603,7 +3249,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } - routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes), + old_routes = card->num_dapm_routes; + routes = devm_kzalloc(card->dev, + (old_routes + num_routes) * sizeof(*routes), GFP_KERNEL); if (!routes) { dev_err(card->dev, @@ -4611,9 +3259,11 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } + memcpy(routes, card->dapm_routes, old_routes * sizeof(*routes)); + for (i = 0; i < num_routes; i++) { ret = of_property_read_string_index(np, propname, - 2 * i, &routes[i].sink); + 2 * i, &routes[old_routes + i].sink); if (ret) { dev_err(card->dev, "ASoC: Property '%s' index %d could not be read: %d\n", @@ -4621,7 +3271,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } ret = of_property_read_string_index(np, propname, - (2 * i) + 1, &routes[i].source); + (2 * i) + 1, &routes[old_routes + i].source); if (ret) { dev_err(card->dev, "ASoC: Property '%s' index %d could not be read: %d\n", @@ -4630,7 +3280,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, } } - card->num_dapm_routes = num_routes; + card->num_dapm_routes += num_routes; card->dapm_routes = routes; return 0; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c61cb9cedbcd..c5136bb1f982 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -159,27 +159,135 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) } } -void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm) +/* + * dapm_widget_invalidate_input_paths() - Invalidate the cached number of input + * paths + * @w: The widget for which to invalidate the cached number of input paths + * + * The function resets the cached number of inputs for the specified widget and + * all widgets that can be reached via outgoing paths from the widget. + * + * This function must be called if the number of input paths for a widget might + * have changed. E.g. if the source state of a widget changes or a path is added + * or activated with the widget as the sink. + */ +static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_widget *sink; + struct snd_soc_dapm_path *p; + LIST_HEAD(list); + + dapm_assert_locked(w->dapm); + + if (w->inputs == -1) + return; + + w->inputs = -1; + list_add_tail(&w->work_list, &list); + + list_for_each_entry(w, &list, work_list) { + list_for_each_entry(p, &w->sinks, list_source) { + if (p->is_supply || p->weak || !p->connect) + continue; + sink = p->sink; + if (sink->inputs != -1) { + sink->inputs = -1; + list_add_tail(&sink->work_list, &list); + } + } + } +} + +/* + * dapm_widget_invalidate_output_paths() - Invalidate the cached number of + * output paths + * @w: The widget for which to invalidate the cached number of output paths + * + * Resets the cached number of outputs for the specified widget and all widgets + * that can be reached via incoming paths from the widget. + * + * This function must be called if the number of output paths for a widget might + * have changed. E.g. if the sink state of a widget changes or a path is added + * or activated with the widget as the source. + */ +static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_widget *source; + struct snd_soc_dapm_path *p; + LIST_HEAD(list); + + dapm_assert_locked(w->dapm); + + if (w->outputs == -1) + return; + + w->outputs = -1; + list_add_tail(&w->work_list, &list); + + list_for_each_entry(w, &list, work_list) { + list_for_each_entry(p, &w->sources, list_sink) { + if (p->is_supply || p->weak || !p->connect) + continue; + source = p->source; + if (source->outputs != -1) { + source->outputs = -1; + list_add_tail(&source->work_list, &list); + } + } + } +} + +/* + * dapm_path_invalidate() - Invalidates the cached number of inputs and outputs + * for the widgets connected to a path + * @p: The path to invalidate + * + * Resets the cached number of inputs for the sink of the path and the cached + * number of outputs for the source of the path. + * + * This function must be called when a path is added, removed or the connected + * state changes. + */ +static void dapm_path_invalidate(struct snd_soc_dapm_path *p) +{ + /* + * Weak paths or supply paths do not influence the number of input or + * output paths of their neighbors. + */ + if (p->weak || p->is_supply) + return; + + /* + * The number of connected endpoints is the sum of the number of + * connected endpoints of all neighbors. If a node with 0 connected + * endpoints is either connected or disconnected that sum won't change, + * so there is no need to re-check the path. + */ + if (p->source->inputs != 0) + dapm_widget_invalidate_input_paths(p->sink); + if (p->sink->outputs != 0) + dapm_widget_invalidate_output_paths(p->source); +} + +void dapm_mark_endpoints_dirty(struct snd_soc_card *card) { - struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; mutex_lock(&card->dapm_mutex); list_for_each_entry(w, &card->widgets, list) { - switch (w->id) { - case snd_soc_dapm_input: - case snd_soc_dapm_output: - dapm_mark_dirty(w, "Rechecking inputs and outputs"); - break; - default: - break; + if (w->is_sink || w->is_source) { + dapm_mark_dirty(w, "Rechecking endpoints"); + if (w->is_sink) + dapm_widget_invalidate_output_paths(w); + if (w->is_source) + dapm_widget_invalidate_input_paths(w); } } mutex_unlock(&card->dapm_mutex); } -EXPORT_SYMBOL_GPL(dapm_mark_io_dirty); +EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty); /* create a new dapm widget */ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( @@ -386,8 +494,6 @@ static void dapm_reset(struct snd_soc_card *card) list_for_each_entry(w, &card->widgets, list) { w->new_power = w->power; w->power_checked = false; - w->inputs = -1; - w->outputs = -1; } } @@ -469,10 +575,9 @@ out: /* connect mux widget to its interconnecting audio paths */ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, - struct snd_soc_dapm_path *path, const char *control_name, - const struct snd_kcontrol_new *kcontrol) + struct snd_soc_dapm_path *path, const char *control_name) { + const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0]; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, item; int i; @@ -493,10 +598,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, for (i = 0; i < e->items; i++) { if (!(strcmp(control_name, e->texts[i]))) { - list_add(&path->list, &dapm->card->paths); - list_add(&path->list_sink, &dest->sources); - list_add(&path->list_source, &src->sinks); - path->name = (char*)e->texts[i]; + path->name = e->texts[i]; if (i == item) path->connect = 1; else @@ -509,11 +611,10 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, } /* set up initial codec paths */ -static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, - struct snd_soc_dapm_path *p, int i) +static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i) { struct soc_mixer_control *mc = (struct soc_mixer_control *) - w->kcontrol_news[i].private_value; + p->sink->kcontrol_news[i].private_value; unsigned int reg = mc->reg; unsigned int shift = mc->shift; unsigned int max = mc->max; @@ -522,7 +623,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, unsigned int val; if (reg != SND_SOC_NOPM) { - soc_dapm_read(w->dapm, reg, &val); + soc_dapm_read(p->sink->dapm, reg, &val); val = (val >> shift) & mask; if (invert) val = max - val; @@ -534,19 +635,15 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, /* connect mixer widget to its interconnecting audio paths */ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, struct snd_soc_dapm_path *path, const char *control_name) { int i; /* search for mixer kcontrol */ - for (i = 0; i < dest->num_kcontrols; i++) { - if (!strcmp(control_name, dest->kcontrol_news[i].name)) { - list_add(&path->list, &dapm->card->paths); - list_add(&path->list_sink, &dest->sources); - list_add(&path->list_source, &src->sinks); - path->name = dest->kcontrol_news[i].name; - dapm_set_mixer_path_status(dest, path, i); + for (i = 0; i < path->sink->num_kcontrols; i++) { + if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) { + path->name = path->sink->kcontrol_news[i].name; + dapm_set_mixer_path_status(path, i); return 0; } } @@ -738,8 +835,10 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) if (ret < 0) return ret; - list_for_each_entry(path, &w->sources, list_sink) - dapm_kcontrol_add_path(w->kcontrols[0], path); + list_for_each_entry(path, &w->sources, list_sink) { + if (path->name) + dapm_kcontrol_add_path(w->kcontrols[0], path); + } return 0; } @@ -754,34 +853,6 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w) return 0; } -/* reset 'walked' bit for each dapm path */ -static void dapm_clear_walk_output(struct snd_soc_dapm_context *dapm, - struct list_head *sink) -{ - struct snd_soc_dapm_path *p; - - list_for_each_entry(p, sink, list_source) { - if (p->walked) { - p->walked = 0; - dapm_clear_walk_output(dapm, &p->sink->sinks); - } - } -} - -static void dapm_clear_walk_input(struct snd_soc_dapm_context *dapm, - struct list_head *source) -{ - struct snd_soc_dapm_path *p; - - list_for_each_entry(p, source, list_sink) { - if (p->walked) { - p->walked = 0; - dapm_clear_walk_input(dapm, &p->source->sources); - } - } -} - - /* We implement power down on suspend by checking the power state of * the ALSA card - when we are suspending the ALSA state for the card * is set to D3. @@ -856,61 +927,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, DAPM_UPDATE_STAT(widget, path_checks); - switch (widget->id) { - case snd_soc_dapm_supply: - case snd_soc_dapm_regulator_supply: - case snd_soc_dapm_clock_supply: - case snd_soc_dapm_kcontrol: - return 0; - default: - break; - } - - switch (widget->id) { - case snd_soc_dapm_adc: - case snd_soc_dapm_aif_out: - case snd_soc_dapm_dai_out: - if (widget->active) { - widget->outputs = snd_soc_dapm_suspend_check(widget); - return widget->outputs; - } - default: - break; - } - - if (widget->connected) { - /* connected pin ? */ - if (widget->id == snd_soc_dapm_output && !widget->ext) { - widget->outputs = snd_soc_dapm_suspend_check(widget); - return widget->outputs; - } - - /* connected jack or spk ? */ - if (widget->id == snd_soc_dapm_hp || - widget->id == snd_soc_dapm_spk || - (widget->id == snd_soc_dapm_line && - !list_empty(&widget->sources))) { - widget->outputs = snd_soc_dapm_suspend_check(widget); - return widget->outputs; - } + if (widget->is_sink && widget->connected) { + widget->outputs = snd_soc_dapm_suspend_check(widget); + return widget->outputs; } list_for_each_entry(path, &widget->sinks, list_source) { DAPM_UPDATE_STAT(widget, neighbour_checks); - if (path->weak) + if (path->weak || path->is_supply) continue; if (path->walking) return 1; - if (path->walked) - continue; - trace_snd_soc_dapm_output_path(widget, path); - if (path->sink && path->connect) { - path->walked = 1; + if (path->connect) { path->walking = 1; /* do we need to add this widget to the list ? */ @@ -952,73 +985,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, DAPM_UPDATE_STAT(widget, path_checks); - switch (widget->id) { - case snd_soc_dapm_supply: - case snd_soc_dapm_regulator_supply: - case snd_soc_dapm_clock_supply: - case snd_soc_dapm_kcontrol: - return 0; - default: - break; - } - - /* active stream ? */ - switch (widget->id) { - case snd_soc_dapm_dac: - case snd_soc_dapm_aif_in: - case snd_soc_dapm_dai_in: - if (widget->active) { - widget->inputs = snd_soc_dapm_suspend_check(widget); - return widget->inputs; - } - default: - break; - } - - if (widget->connected) { - /* connected pin ? */ - if (widget->id == snd_soc_dapm_input && !widget->ext) { - widget->inputs = snd_soc_dapm_suspend_check(widget); - return widget->inputs; - } - - /* connected VMID/Bias for lower pops */ - if (widget->id == snd_soc_dapm_vmid) { - widget->inputs = snd_soc_dapm_suspend_check(widget); - return widget->inputs; - } - - /* connected jack ? */ - if (widget->id == snd_soc_dapm_mic || - (widget->id == snd_soc_dapm_line && - !list_empty(&widget->sinks))) { - widget->inputs = snd_soc_dapm_suspend_check(widget); - return widget->inputs; - } - - /* signal generator */ - if (widget->id == snd_soc_dapm_siggen) { - widget->inputs = snd_soc_dapm_suspend_check(widget); - return widget->inputs; - } + if (widget->is_source && widget->connected) { + widget->inputs = snd_soc_dapm_suspend_check(widget); + return widget->inputs; } list_for_each_entry(path, &widget->sources, list_sink) { DAPM_UPDATE_STAT(widget, neighbour_checks); - if (path->weak) + if (path->weak || path->is_supply) continue; if (path->walking) return 1; - if (path->walked) - continue; - trace_snd_soc_dapm_input_path(widget, path); - if (path->source && path->connect) { - path->walked = 1; + if (path->connect) { path->walking = 1; /* do we need to add this widget to the list ? */ @@ -1060,21 +1043,25 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list) { - struct snd_soc_card *card = dai->card; + struct snd_soc_card *card = dai->component->card; + struct snd_soc_dapm_widget *w; int paths; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - dapm_reset(card); - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* + * For is_connected_{output,input}_ep fully discover the graph we need + * to reset the cached number of inputs and outputs. + */ + list_for_each_entry(w, &card->widgets, list) { + w->inputs = -1; + w->outputs = -1; + } + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) paths = is_connected_output_ep(dai->playback_widget, list); - dapm_clear_walk_output(&card->dapm, - &dai->playback_widget->sinks); - } else { + else paths = is_connected_input_ep(dai->capture_widget, list); - dapm_clear_walk_input(&card->dapm, - &dai->capture_widget->sources); - } trace_snd_soc_dapm_connected(paths, stream); mutex_unlock(&card->dapm_mutex); @@ -1163,44 +1150,10 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) DAPM_UPDATE_STAT(w, power_checks); in = is_connected_input_ep(w, NULL); - dapm_clear_walk_input(w->dapm, &w->sources); out = is_connected_output_ep(w, NULL); - dapm_clear_walk_output(w->dapm, &w->sinks); return out != 0 && in != 0; } -/* Check to see if an ADC has power */ -static int dapm_adc_check_power(struct snd_soc_dapm_widget *w) -{ - int in; - - DAPM_UPDATE_STAT(w, power_checks); - - if (w->active) { - in = is_connected_input_ep(w, NULL); - dapm_clear_walk_input(w->dapm, &w->sources); - return in != 0; - } else { - return dapm_generic_check_power(w); - } -} - -/* Check to see if a DAC has power */ -static int dapm_dac_check_power(struct snd_soc_dapm_widget *w) -{ - int out; - - DAPM_UPDATE_STAT(w, power_checks); - - if (w->active) { - out = is_connected_output_ep(w, NULL); - dapm_clear_walk_output(w->dapm, &w->sinks); - return out != 0; - } else { - return dapm_generic_check_power(w); - } -} - /* Check to see if a power supply is needed */ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) { @@ -1219,9 +1172,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) !path->connected(path->source, path->sink)) continue; - if (!path->sink) - continue; - if (dapm_widget_power_check(path->sink)) return 1; } @@ -1636,27 +1586,14 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, /* If we changed our power state perhaps our neigbours changed * also. */ - list_for_each_entry(path, &w->sources, list_sink) { - if (path->source) { - dapm_widget_set_peer_power(path->source, power, + list_for_each_entry(path, &w->sources, list_sink) + dapm_widget_set_peer_power(path->source, power, path->connect); + + /* Supplies can't affect their outputs, only their inputs */ + if (!w->is_supply) { + list_for_each_entry(path, &w->sinks, list_source) + dapm_widget_set_peer_power(path->sink, power, path->connect); - } - } - switch (w->id) { - case snd_soc_dapm_supply: - case snd_soc_dapm_regulator_supply: - case snd_soc_dapm_clock_supply: - case snd_soc_dapm_kcontrol: - /* Supplies can't affect their outputs, only their inputs */ - break; - default: - list_for_each_entry(path, &w->sinks, list_source) { - if (path->sink) { - dapm_widget_set_peer_power(path->sink, power, - path->connect); - } - } - break; } if (power) @@ -1863,10 +1800,14 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (!buf) return -ENOMEM; - in = is_connected_input_ep(w, NULL); - dapm_clear_walk_input(w->dapm, &w->sources); - out = is_connected_output_ep(w, NULL); - dapm_clear_walk_output(w->dapm, &w->sinks); + /* Supply widgets are not handled by is_connected_{input,output}_ep() */ + if (w->is_supply) { + in = 0; + out = 0; + } else { + in = is_connected_input_ep(w, NULL); + out = is_connected_output_ep(w, NULL); + } ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", w->name, w->power ? "On" : "Off", @@ -2011,32 +1952,45 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) #endif +/* + * soc_dapm_connect_path() - Connects or disconnects a path + * @path: The path to update + * @connect: The new connect state of the path. True if the path is connected, + * false if it is disconneted. + * @reason: The reason why the path changed (for debugging only) + */ +static void soc_dapm_connect_path(struct snd_soc_dapm_path *path, + bool connect, const char *reason) +{ + if (path->connect == connect) + return; + + path->connect = connect; + dapm_mark_dirty(path->source, reason); + dapm_mark_dirty(path->sink, reason); + dapm_path_invalidate(path); +} + /* test and update the power status of a mux widget */ static int soc_dapm_mux_update_power(struct snd_soc_card *card, struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) { struct snd_soc_dapm_path *path; int found = 0; + bool connect; lockdep_assert_held(&card->dapm_mutex); /* find dapm widget path assoc with kcontrol */ dapm_kcontrol_for_each_path(path, kcontrol) { - if (!path->name || !e->texts[mux]) - continue; - found = 1; /* we now need to match the string in the enum to the path */ - if (!(strcmp(path->name, e->texts[mux]))) { - path->connect = 1; /* new connection */ - dapm_mark_dirty(path->source, "mux connection"); - } else { - if (path->connect) - dapm_mark_dirty(path->source, - "mux disconnection"); - path->connect = 0; /* old connection must be powered down */ - } - dapm_mark_dirty(path->sink, "mux change"); + if (!(strcmp(path->name, e->texts[mux]))) + connect = true; + else + connect = false; + + soc_dapm_connect_path(path, connect, "mux update"); } if (found) @@ -2075,9 +2029,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, /* find dapm widget path assoc with kcontrol */ dapm_kcontrol_for_each_path(path, kcontrol) { found = 1; - path->connect = connect; - dapm_mark_dirty(path->source, "mixer connection"); - dapm_mark_dirty(path->sink, "mixer update"); + soc_dapm_connect_path(path, connect, "mixer update"); } if (found) @@ -2255,8 +2207,11 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, return -EINVAL; } - if (w->connected != status) + if (w->connected != status) { dapm_mark_dirty(w, "pin configuration"); + dapm_widget_invalidate_input_paths(w); + dapm_widget_invalidate_output_paths(w); + } w->connected = status; if (status == 0) @@ -2309,6 +2264,53 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); +/* + * dapm_update_widget_flags() - Re-compute widget sink and source flags + * @w: The widget for which to update the flags + * + * Some widgets have a dynamic category which depends on which neighbors they + * are connected to. This function update the category for these widgets. + * + * This function must be called whenever a path is added or removed to a widget. + */ +static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p; + + switch (w->id) { + case snd_soc_dapm_input: + w->is_source = 1; + list_for_each_entry(p, &w->sources, list_sink) { + if (p->source->id == snd_soc_dapm_micbias || + p->source->id == snd_soc_dapm_mic || + p->source->id == snd_soc_dapm_line || + p->source->id == snd_soc_dapm_output) { + w->is_source = 0; + break; + } + } + break; + case snd_soc_dapm_output: + w->is_sink = 1; + list_for_each_entry(p, &w->sinks, list_source) { + if (p->sink->id == snd_soc_dapm_spk || + p->sink->id == snd_soc_dapm_hp || + p->sink->id == snd_soc_dapm_line || + p->sink->id == snd_soc_dapm_input) { + w->is_sink = 0; + break; + } + } + break; + case snd_soc_dapm_line: + w->is_sink = !list_empty(&w->sources); + w->is_source = !list_empty(&w->sinks); + break; + default: + break; + } +} + static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, const char *control, @@ -2318,6 +2320,27 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_path *path; int ret; + if (wsink->is_supply && !wsource->is_supply) { + dev_err(dapm->dev, + "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n", + wsource->name, wsink->name); + return -EINVAL; + } + + if (connected && !wsource->is_supply) { + dev_err(dapm->dev, + "connected() callback only supported for supply widgets (%s -> %s)\n", + wsource->name, wsink->name); + return -EINVAL; + } + + if (wsource->is_supply && control) { + dev_err(dapm->dev, + "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n", + wsource->name, control, wsink->name); + return -EINVAL; + } + path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); if (!path) return -ENOMEM; @@ -2330,85 +2353,49 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, INIT_LIST_HEAD(&path->list_source); INIT_LIST_HEAD(&path->list_sink); - /* check for external widgets */ - if (wsink->id == snd_soc_dapm_input) { - if (wsource->id == snd_soc_dapm_micbias || - wsource->id == snd_soc_dapm_mic || - wsource->id == snd_soc_dapm_line || - wsource->id == snd_soc_dapm_output) - wsink->ext = 1; - } - if (wsource->id == snd_soc_dapm_output) { - if (wsink->id == snd_soc_dapm_spk || - wsink->id == snd_soc_dapm_hp || - wsink->id == snd_soc_dapm_line || - wsink->id == snd_soc_dapm_input) - wsource->ext = 1; - } - - dapm_mark_dirty(wsource, "Route added"); - dapm_mark_dirty(wsink, "Route added"); + if (wsource->is_supply || wsink->is_supply) + path->is_supply = 1; /* connect static paths */ if (control == NULL) { - list_add(&path->list, &dapm->card->paths); - list_add(&path->list_sink, &wsink->sources); - list_add(&path->list_source, &wsource->sinks); path->connect = 1; - return 0; - } - - /* connect dynamic paths */ - switch (wsink->id) { - case snd_soc_dapm_adc: - case snd_soc_dapm_dac: - case snd_soc_dapm_pga: - case snd_soc_dapm_out_drv: - case snd_soc_dapm_input: - case snd_soc_dapm_output: - case snd_soc_dapm_siggen: - case snd_soc_dapm_micbias: - case snd_soc_dapm_vmid: - case snd_soc_dapm_pre: - case snd_soc_dapm_post: - case snd_soc_dapm_supply: - case snd_soc_dapm_regulator_supply: - case snd_soc_dapm_clock_supply: - case snd_soc_dapm_aif_in: - case snd_soc_dapm_aif_out: - case snd_soc_dapm_dai_in: - case snd_soc_dapm_dai_out: - case snd_soc_dapm_dai_link: - case snd_soc_dapm_kcontrol: - list_add(&path->list, &dapm->card->paths); - list_add(&path->list_sink, &wsink->sources); - list_add(&path->list_source, &wsource->sinks); - path->connect = 1; - return 0; - case snd_soc_dapm_mux: - ret = dapm_connect_mux(dapm, wsource, wsink, path, control, - &wsink->kcontrol_news[0]); - if (ret != 0) - goto err; - break; - case snd_soc_dapm_switch: - case snd_soc_dapm_mixer: - case snd_soc_dapm_mixer_named_ctl: - ret = dapm_connect_mixer(dapm, wsource, wsink, path, control); - if (ret != 0) + } else { + /* connect dynamic paths */ + switch (wsink->id) { + case snd_soc_dapm_mux: + ret = dapm_connect_mux(dapm, path, control); + if (ret != 0) + goto err; + break; + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + ret = dapm_connect_mixer(dapm, path, control); + if (ret != 0) + goto err; + break; + default: + dev_err(dapm->dev, + "Control not supported for path %s -> [%s] -> %s\n", + wsource->name, control, wsink->name); + ret = -EINVAL; goto err; - break; - case snd_soc_dapm_hp: - case snd_soc_dapm_mic: - case snd_soc_dapm_line: - case snd_soc_dapm_spk: - list_add(&path->list, &dapm->card->paths); - list_add(&path->list_sink, &wsink->sources); - list_add(&path->list_source, &wsource->sinks); - path->connect = 0; - return 0; + } } + list_add(&path->list, &dapm->card->paths); + list_add(&path->list_sink, &wsink->sources); + list_add(&path->list_source, &wsource->sinks); + + dapm_update_widget_flags(wsource); + dapm_update_widget_flags(wsink); + + dapm_mark_dirty(wsource, "Route added"); + dapm_mark_dirty(wsink, "Route added"); + + if (dapm->card->instantiated && path->connect) + dapm_path_invalidate(path); + return 0; err: kfree(path); @@ -2489,6 +2476,7 @@ err: static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route) { + struct snd_soc_dapm_widget *wsource, *wsink; struct snd_soc_dapm_path *path, *p; const char *sink; const char *source; @@ -2526,10 +2514,19 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, } if (path) { - dapm_mark_dirty(path->source, "Route removed"); - dapm_mark_dirty(path->sink, "Route removed"); + wsource = path->source; + wsink = path->sink; + + dapm_mark_dirty(wsource, "Route removed"); + dapm_mark_dirty(wsink, "Route removed"); + if (path->connect) + dapm_path_invalidate(path); dapm_free_path(path); + + /* Update any path related flags */ + dapm_update_widget_flags(wsource); + dapm_update_widget_flags(wsink); } else { dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n", source, sink); @@ -3087,40 +3084,44 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, } switch (w->id) { - case snd_soc_dapm_switch: - case snd_soc_dapm_mixer: - case snd_soc_dapm_mixer_named_ctl: + case snd_soc_dapm_mic: + case snd_soc_dapm_input: + w->is_source = 1; w->power_check = dapm_generic_check_power; break; - case snd_soc_dapm_mux: + case snd_soc_dapm_spk: + case snd_soc_dapm_hp: + case snd_soc_dapm_output: + w->is_sink = 1; w->power_check = dapm_generic_check_power; break; - case snd_soc_dapm_dai_out: - w->power_check = dapm_adc_check_power; - break; - case snd_soc_dapm_dai_in: - w->power_check = dapm_dac_check_power; + case snd_soc_dapm_vmid: + case snd_soc_dapm_siggen: + w->is_source = 1; + w->power_check = dapm_always_on_check_power; break; + case snd_soc_dapm_mux: + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: case snd_soc_dapm_adc: case snd_soc_dapm_aif_out: case snd_soc_dapm_dac: case snd_soc_dapm_aif_in: case snd_soc_dapm_pga: case snd_soc_dapm_out_drv: - case snd_soc_dapm_input: - case snd_soc_dapm_output: case snd_soc_dapm_micbias: - case snd_soc_dapm_spk: - case snd_soc_dapm_hp: - case snd_soc_dapm_mic: case snd_soc_dapm_line: case snd_soc_dapm_dai_link: + case snd_soc_dapm_dai_out: + case snd_soc_dapm_dai_in: w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: case snd_soc_dapm_kcontrol: + w->is_supply = 1; w->power_check = dapm_supply_check_power; break; default: @@ -3137,6 +3138,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, INIT_LIST_HEAD(&w->dirty); list_add(&w->list, &dapm->card->widgets); + w->inputs = -1; + w->outputs = -1; + /* machine layer set ups unconnected pins and insertions */ w->connected = 1; return w; @@ -3484,6 +3488,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: break; } + + if (w->id == snd_soc_dapm_dai_in) { + w->is_source = w->active; + dapm_widget_invalidate_input_paths(w); + } else { + w->is_sink = w->active; + dapm_widget_invalidate_output_paths(w); + } } } @@ -3610,7 +3622,15 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, } dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin); - w->connected = 1; + if (!w->connected) { + /* + * w->force does not affect the number of input or output paths, + * so we only have to recheck if w->connected is changed + */ + dapm_widget_invalidate_input_paths(w); + dapm_widget_invalidate_output_paths(w); + w->connected = 1; + } w->force = 1; dapm_mark_dirty(w, "force enable"); @@ -3788,35 +3808,54 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); +/** + * dapm_is_external_path() - Checks if a path is a external path + * @card: The card the path belongs to + * @path: The path to check + * + * Returns true if the path is either between two different DAPM contexts or + * between two external pins of the same DAPM context. Otherwise returns + * false. + */ +static bool dapm_is_external_path(struct snd_soc_card *card, + struct snd_soc_dapm_path *path) +{ + dev_dbg(card->dev, + "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n", + path->source->name, path->source->id, path->source->dapm, + path->sink->name, path->sink->id, path->sink->dapm); + + /* Connection between two different DAPM contexts */ + if (path->source->dapm != path->sink->dapm) + return true; + + /* Loopback connection from external pin to external pin */ + if (path->sink->id == snd_soc_dapm_input) { + switch (path->source->id) { + case snd_soc_dapm_output: + case snd_soc_dapm_micbias: + return true; + default: + break; + } + } + + return false; +} + static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card, struct snd_soc_dapm_widget *w) { struct snd_soc_dapm_path *p; - list_for_each_entry(p, &card->paths, list) { - if ((p->source == w) || (p->sink == w)) { - dev_dbg(card->dev, - "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n", - p->source->name, p->source->id, p->source->dapm, - p->sink->name, p->sink->id, p->sink->dapm); + list_for_each_entry(p, &w->sources, list_sink) { + if (dapm_is_external_path(card, p)) + return true; + } - /* Connected to something other than the codec */ - if (p->source->dapm != p->sink->dapm) - return true; - /* - * Loopback connection from codec external pin to - * codec external pin - */ - if (p->sink->id == snd_soc_dapm_input) { - switch (p->source->id) { - case snd_soc_dapm_output: - case snd_soc_dapm_micbias: - return true; - default: - break; - } - } - } + list_for_each_entry(p, &w->sinks, list_source) { + if (dapm_is_external_path(card, p)) + return true; } return false; diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index ab47fea997a3..ef1d42d7c6f6 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -116,7 +116,7 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_report); * * @jack: ASoC jack * @count: Number of zones - * @zone: Array of zones + * @zones: Array of zones * * After this function has been called the zones specified in the * array will be associated with the jack. diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c new file mode 100644 index 000000000000..100d92b5b77e --- /dev/null +++ b/sound/soc/soc-ops.c @@ -0,0 +1,952 @@ +/* + * soc-ops.c -- Generic ASoC operations + * + * 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/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/bitops.h> +#include <linux/ctype.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dpcm.h> +#include <sound/initval.h> + +/** + * snd_soc_info_enum_double - enumerated double mixer info callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a double enumerated + * mixer control. + * + * Returns 0 for success. + */ +int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2, + e->items, e->texts); +} +EXPORT_SYMBOL_GPL(snd_soc_info_enum_double); + +/** + * snd_soc_get_enum_double - enumerated double mixer get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value of a double enumerated mixer. + * + * Returns 0 for success. + */ +int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val, item; + unsigned int reg_val; + int ret; + + ret = snd_soc_component_read(component, e->reg, ®_val); + if (ret) + return ret; + val = (reg_val >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + ucontrol->value.enumerated.item[0] = item; + if (e->shift_l != e->shift_r) { + val = (reg_val >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + ucontrol->value.enumerated.item[1] = item; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_enum_double); + +/** + * snd_soc_put_enum_double - enumerated double mixer put callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to set the value of a double enumerated mixer. + * + * Returns 0 for success. + */ +int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val; + unsigned int mask; + + if (item[0] >= e->items) + return -EINVAL; + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + mask = e->mask << e->shift_l; + if (e->shift_l != e->shift_r) { + if (item[1] >= e->items) + return -EINVAL; + val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r; + mask |= e->mask << e->shift_r; + } + + return snd_soc_component_update_bits(component, e->reg, mask, val); +} +EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); + +/** + * snd_soc_read_signed - Read a codec register and interprete as signed value + * @component: component + * @reg: Register to read + * @mask: Mask to use after shifting the register value + * @shift: Right shift of register value + * @sign_bit: Bit that describes if a number is negative or not. + * @signed_val: Pointer to where the read value should be stored + * + * This functions reads a codec register. The register value is shifted right + * by 'shift' bits and masked with the given 'mask'. Afterwards it translates + * the given registervalue into a signed integer if sign_bit is non-zero. + * + * Returns 0 on sucess, otherwise an error value + */ +static int snd_soc_read_signed(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int shift, + unsigned int sign_bit, int *signed_val) +{ + int ret; + unsigned int val; + + ret = snd_soc_component_read(component, reg, &val); + if (ret < 0) + return ret; + + val = (val >> shift) & mask; + + if (!sign_bit) { + *signed_val = val; + return 0; + } + + /* non-negative number */ + if (!(val & BIT(sign_bit))) { + *signed_val = val; + return 0; + } + + ret = val; + + /* + * The register most probably does not contain a full-sized int. + * Instead we have an arbitrary number of bits in a signed + * representation which has to be translated into a full-sized int. + * This is done by filling up all bits above the sign-bit. + */ + ret |= ~((int)(BIT(sign_bit) - 1)); + + *signed_val = ret; + + return 0; +} + +/** + * snd_soc_info_volsw - single mixer info callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a single mixer control, or a double + * mixer control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int platform_max; + + if (!mc->platform_max) + mc->platform_max = mc->max; + platform_max = mc->platform_max; + + if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume")) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + + uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = platform_max - mc->min; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_volsw); + +/** + * snd_soc_get_volsw - single mixer get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value of a single mixer control, or a double mixer + * control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int min = mc->min; + int sign_bit = mc->sign_bit; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + int val; + int ret; + + if (sign_bit) + mask = BIT(sign_bit + 1) - 1; + + ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = val - min; + if (invert) + ucontrol->value.integer.value[0] = + max - ucontrol->value.integer.value[0]; + + if (snd_soc_volsw_is_stereo(mc)) { + if (reg == reg2) + ret = snd_soc_read_signed(component, reg, mask, rshift, + sign_bit, &val); + else + ret = snd_soc_read_signed(component, reg2, mask, shift, + sign_bit, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[1] = val - min; + if (invert) + ucontrol->value.integer.value[1] = + max - ucontrol->value.integer.value[1]; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_volsw); + +/** + * snd_soc_put_volsw - single mixer put callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to set the value of a single mixer control, or a double mixer + * control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int min = mc->min; + unsigned int sign_bit = mc->sign_bit; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + int err; + bool type_2r = false; + unsigned int val2 = 0; + unsigned int val, val_mask; + + if (sign_bit) + mask = BIT(sign_bit + 1) - 1; + + val = ((ucontrol->value.integer.value[0] + min) & mask); + if (invert) + val = max - val; + val_mask = mask << shift; + val = val << shift; + if (snd_soc_volsw_is_stereo(mc)) { + val2 = ((ucontrol->value.integer.value[1] + min) & mask); + if (invert) + val2 = max - val2; + if (reg == reg2) { + val_mask |= mask << rshift; + val |= val2 << rshift; + } else { + val2 = val2 << shift; + type_2r = true; + } + } + err = snd_soc_component_update_bits(component, reg, val_mask, val); + if (err < 0) + return err; + + if (type_2r) + err = snd_soc_component_update_bits(component, reg2, val_mask, + val2); + + return err; +} +EXPORT_SYMBOL_GPL(snd_soc_put_volsw); + +/** + * snd_soc_get_volsw_sx - single mixer get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value of a single mixer control, or a double mixer + * control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int min = mc->min; + int mask = (1 << (fls(min + max) - 1)) - 1; + unsigned int val; + int ret; + + ret = snd_soc_component_read(component, reg, &val); + if (ret < 0) + return ret; + + ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask; + + if (snd_soc_volsw_is_stereo(mc)) { + ret = snd_soc_component_read(component, reg2, &val); + if (ret < 0) + return ret; + + val = ((val >> rshift) - min) & mask; + ucontrol->value.integer.value[1] = val; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx); + +/** + * snd_soc_put_volsw_sx - double mixer set callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to set the value of a double mixer control that spans 2 registers. + * + * Returns 0 for success. + */ +int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int min = mc->min; + int mask = (1 << (fls(min + max) - 1)) - 1; + int err = 0; + unsigned int val, val_mask, val2 = 0; + + val_mask = mask << shift; + val = (ucontrol->value.integer.value[0] + min) & mask; + val = val << shift; + + err = snd_soc_component_update_bits(component, reg, val_mask, val); + if (err < 0) + return err; + + if (snd_soc_volsw_is_stereo(mc)) { + val_mask = mask << rshift; + val2 = (ucontrol->value.integer.value[1] + min) & mask; + val2 = val2 << rshift; + + err = snd_soc_component_update_bits(component, reg2, val_mask, + val2); + } + return err; +} +EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx); + +/** + * snd_soc_info_volsw_range - single mixer info callback with range. + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information, within a range, about a single + * mixer control. + * + * returns 0 for success. + */ +int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int platform_max; + int min = mc->min; + + if (!mc->platform_max) + mc->platform_max = mc->max; + platform_max = mc->platform_max; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = platform_max - min; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range); + +/** + * snd_soc_put_volsw_range - single mixer put value callback with range. + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to set the value, within a range, for a single mixer control. + * + * Returns 0 for success. + */ +int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + unsigned int reg = mc->reg; + unsigned int rreg = mc->rreg; + unsigned int shift = mc->shift; + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val, val_mask; + int ret; + + if (invert) + val = (max - ucontrol->value.integer.value[0]) & mask; + else + val = ((ucontrol->value.integer.value[0] + min) & mask); + val_mask = mask << shift; + val = val << shift; + + ret = snd_soc_component_update_bits(component, reg, val_mask, val); + if (ret < 0) + return ret; + + if (snd_soc_volsw_is_stereo(mc)) { + if (invert) + val = (max - ucontrol->value.integer.value[1]) & mask; + else + val = ((ucontrol->value.integer.value[1] + min) & mask); + val_mask = mask << shift; + val = val << shift; + + ret = snd_soc_component_update_bits(component, rreg, val_mask, + val); + } + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range); + +/** + * snd_soc_get_volsw_range - single mixer get callback with range + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value, within a range, of a single mixer control. + * + * Returns 0 for success. + */ +int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int rreg = mc->rreg; + unsigned int shift = mc->shift; + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val; + int ret; + + ret = snd_soc_component_read(component, reg, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = (val >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = + max - ucontrol->value.integer.value[0]; + else + ucontrol->value.integer.value[0] = + ucontrol->value.integer.value[0] - min; + + if (snd_soc_volsw_is_stereo(mc)) { + ret = snd_soc_component_read(component, rreg, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[1] = (val >> shift) & mask; + if (invert) + ucontrol->value.integer.value[1] = + max - ucontrol->value.integer.value[1]; + else + ucontrol->value.integer.value[1] = + ucontrol->value.integer.value[1] - min; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); + +/** + * snd_soc_limit_volume - Set new limit to an existing volume control. + * + * @codec: where to look for the control + * @name: Name of the control + * @max: new maximum limit + * + * Return 0 for success, else error. + */ +int snd_soc_limit_volume(struct snd_soc_codec *codec, + const char *name, int max) +{ + struct snd_card *card = codec->component.card->snd_card; + struct snd_kcontrol *kctl; + struct soc_mixer_control *mc; + int found = 0; + int ret = -EINVAL; + + /* Sanity check for name and max */ + if (unlikely(!name || max <= 0)) + return -EINVAL; + + list_for_each_entry(kctl, &card->controls, list) { + if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) { + found = 1; + break; + } + } + if (found) { + mc = (struct soc_mixer_control *)kctl->private_value; + if (max <= mc->max) { + mc->platform_max = max; + ret = 0; + } + } + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_limit_volume); + +int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_bytes *params = (void *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = params->num_regs * component->val_bytes; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_bytes_info); + +int snd_soc_bytes_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_bytes *params = (void *)kcontrol->private_value; + int ret; + + if (component->regmap) + ret = regmap_raw_read(component->regmap, params->base, + ucontrol->value.bytes.data, + params->num_regs * component->val_bytes); + else + ret = -EINVAL; + + /* Hide any masked bytes to ensure consistent data reporting */ + if (ret == 0 && params->mask) { + switch (component->val_bytes) { + case 1: + ucontrol->value.bytes.data[0] &= ~params->mask; + break; + case 2: + ((u16 *)(&ucontrol->value.bytes.data))[0] + &= cpu_to_be16(~params->mask); + break; + case 4: + ((u32 *)(&ucontrol->value.bytes.data))[0] + &= cpu_to_be32(~params->mask); + break; + default: + return -EINVAL; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_bytes_get); + +int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_bytes *params = (void *)kcontrol->private_value; + int ret, len; + unsigned int val, mask; + void *data; + + if (!component->regmap || !params->num_regs) + return -EINVAL; + + len = params->num_regs * component->val_bytes; + + data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + + /* + * If we've got a mask then we need to preserve the register + * bits. We shouldn't modify the incoming data so take a + * copy. + */ + if (params->mask) { + ret = regmap_read(component->regmap, params->base, &val); + if (ret != 0) + goto out; + + val &= params->mask; + + switch (component->val_bytes) { + case 1: + ((u8 *)data)[0] &= ~params->mask; + ((u8 *)data)[0] |= val; + break; + case 2: + mask = ~params->mask; + ret = regmap_parse_val(component->regmap, + &mask, &mask); + if (ret != 0) + goto out; + + ((u16 *)data)[0] &= mask; + + ret = regmap_parse_val(component->regmap, + &val, &val); + if (ret != 0) + goto out; + + ((u16 *)data)[0] |= val; + break; + case 4: + mask = ~params->mask; + ret = regmap_parse_val(component->regmap, + &mask, &mask); + if (ret != 0) + goto out; + + ((u32 *)data)[0] &= mask; + + ret = regmap_parse_val(component->regmap, + &val, &val); + if (ret != 0) + goto out; + + ((u32 *)data)[0] |= val; + break; + default: + ret = -EINVAL; + goto out; + } + } + + ret = regmap_raw_write(component->regmap, params->base, + data, len); + +out: + kfree(data); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_bytes_put); + +int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; + ucontrol->count = params->max; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext); + +int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + unsigned int count = size < params->max ? size : params->max; + int ret = -ENXIO; + + switch (op_flag) { + case SNDRV_CTL_TLV_OP_READ: + if (params->get) + ret = params->get(tlv, count); + break; + case SNDRV_CTL_TLV_OP_WRITE: + if (params->put) + ret = params->put(tlv, count); + break; + } + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback); + +/** + * snd_soc_info_xr_sx - signed multi register info callback + * @kcontrol: mreg control + * @uinfo: control element information + * + * Callback to provide information of a control that can + * span multiple codec registers which together + * forms a single signed value in a MSB/LSB manner. + * + * Returns 0 for success. + */ +int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mreg_control *mc = + (struct soc_mreg_control *)kcontrol->private_value; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = mc->min; + uinfo->value.integer.max = mc->max; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx); + +/** + * snd_soc_get_xr_sx - signed multi register get callback + * @kcontrol: mreg control + * @ucontrol: control element information + * + * Callback to get the value of a control that can span + * multiple codec registers which together forms a single + * signed value in a MSB/LSB manner. The control supports + * specifying total no of bits used to allow for bitfields + * across the multiple codec registers. + * + * Returns 0 for success. + */ +int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mreg_control *mc = + (struct soc_mreg_control *)kcontrol->private_value; + unsigned int regbase = mc->regbase; + unsigned int regcount = mc->regcount; + unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; + unsigned int regwmask = (1<<regwshift)-1; + unsigned int invert = mc->invert; + unsigned long mask = (1UL<<mc->nbits)-1; + long min = mc->min; + long max = mc->max; + long val = 0; + unsigned int regval; + unsigned int i; + int ret; + + for (i = 0; i < regcount; i++) { + ret = snd_soc_component_read(component, regbase+i, ®val); + if (ret) + return ret; + val |= (regval & regwmask) << (regwshift*(regcount-i-1)); + } + val &= mask; + if (min < 0 && val > max) + val |= ~mask; + if (invert) + val = max - val; + ucontrol->value.integer.value[0] = val; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx); + +/** + * snd_soc_put_xr_sx - signed multi register get callback + * @kcontrol: mreg control + * @ucontrol: control element information + * + * Callback to set the value of a control that can span + * multiple codec registers which together forms a single + * signed value in a MSB/LSB manner. The control supports + * specifying total no of bits used to allow for bitfields + * across the multiple codec registers. + * + * Returns 0 for success. + */ +int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mreg_control *mc = + (struct soc_mreg_control *)kcontrol->private_value; + unsigned int regbase = mc->regbase; + unsigned int regcount = mc->regcount; + unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; + unsigned int regwmask = (1<<regwshift)-1; + unsigned int invert = mc->invert; + unsigned long mask = (1UL<<mc->nbits)-1; + long max = mc->max; + long val = ucontrol->value.integer.value[0]; + unsigned int i, regval, regmask; + int err; + + if (invert) + val = max - val; + val &= mask; + for (i = 0; i < regcount; i++) { + regval = (val >> (regwshift*(regcount-i-1))) & regwmask; + regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask; + err = snd_soc_component_update_bits(component, regbase+i, + regmask, regval); + if (err < 0) + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx); + +/** + * snd_soc_get_strobe - strobe get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback get the value of a strobe mixer control. + * + * Returns 0 for success. + */ +int snd_soc_get_strobe(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int mask = 1 << shift; + unsigned int invert = mc->invert != 0; + unsigned int val; + int ret; + + ret = snd_soc_component_read(component, reg, &val); + if (ret) + return ret; + + val &= mask; + + if (shift != 0 && val != 0) + val = val >> shift; + ucontrol->value.enumerated.item[0] = val ^ invert; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_strobe); + +/** + * snd_soc_put_strobe - strobe put callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback strobe a register bit to high then low (or the inverse) + * in one pass of a single mixer enum control. + * + * Returns 1 for success. + */ +int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int mask = 1 << shift; + unsigned int invert = mc->invert != 0; + unsigned int strobe = ucontrol->value.enumerated.item[0] != 0; + unsigned int val1 = (strobe ^ invert) ? mask : 0; + unsigned int val2 = (strobe ^ invert) ? 0 : mask; + int err; + + err = snd_soc_component_update_bits(component, reg, mask, val1); + if (err < 0) + return err; + + return snd_soc_component_update_bits(component, reg, mask, val2); +} +EXPORT_SYMBOL_GPL(snd_soc_put_strobe); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 2f4f074d64a4..eb87d96e2cf0 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -654,6 +654,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) codec_dai->rate = 0; } + snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream); + if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); @@ -772,6 +774,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) for (i = 0; i < rtd->num_codecs; i++) snd_soc_dai_digital_mute(rtd->codec_dais[i], 0, substream->stream); + snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream); out: mutex_unlock(&rtd->pcm_mutex); @@ -1522,13 +1525,36 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); } +static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd); + +/* Set FE's runtime_update state; the state is protected via PCM stream lock + * for avoiding the race with trigger callback. + * If the state is unset and a trigger is pending while the previous operation, + * process the pending trigger action here. + */ +static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe, + int stream, enum snd_soc_dpcm_update state) +{ + struct snd_pcm_substream *substream = + snd_soc_dpcm_get_substream(fe, stream); + + snd_pcm_stream_lock_irq(substream); + if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) { + dpcm_fe_dai_do_trigger(substream, + fe->dpcm[stream].trigger_pending - 1); + fe->dpcm[stream].trigger_pending = 0; + } + fe->dpcm[stream].runtime_update = state; + snd_pcm_stream_unlock_irq(substream); +} + static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) { struct snd_soc_pcm_runtime *fe = fe_substream->private_data; struct snd_pcm_runtime *runtime = fe_substream->runtime; int stream = fe_substream->stream, ret = 0; - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); ret = dpcm_be_dai_startup(fe, fe_substream->stream); if (ret < 0) { @@ -1550,13 +1576,13 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) dpcm_set_fe_runtime(fe_substream); snd_pcm_limit_hw_rates(runtime); - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); return 0; unwind: dpcm_be_dai_startup_unwind(fe, fe_substream->stream); be_err: - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); return ret; } @@ -1603,7 +1629,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *fe = substream->private_data; int stream = substream->stream; - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); /* shutdown the BEs */ dpcm_be_dai_shutdown(fe, substream->stream); @@ -1617,7 +1643,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); return 0; } @@ -1641,6 +1667,10 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) continue; + /* do not free hw if this BE is used by other FE */ + if (be->dpcm[stream].users > 1) + continue; + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && @@ -1665,7 +1695,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) int err, stream = substream->stream; mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name); @@ -1680,7 +1710,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) err = dpcm_be_dai_hw_free(fe, stream); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); mutex_unlock(&fe->card->mutex); return 0; @@ -1773,7 +1803,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, int ret, stream = substream->stream; mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); memcpy(&fe->dpcm[substream->stream].hw_params, params, sizeof(struct snd_pcm_hw_params)); @@ -1796,7 +1826,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; out: - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); mutex_unlock(&fe->card->mutex); return ret; } @@ -1910,7 +1940,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, } EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); -static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) +static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *fe = substream->private_data; int stream = substream->stream, ret; @@ -1984,6 +2014,23 @@ out: return ret; } +static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + int stream = substream->stream; + + /* if FE's runtime_update is already set, we're in race; + * process this trigger later at exit + */ + if (fe->dpcm[stream].runtime_update != SND_SOC_DPCM_UPDATE_NO) { + fe->dpcm[stream].trigger_pending = cmd + 1; + return 0; /* delayed, assuming it's successful */ + } + + /* we're alone, let's trigger */ + return dpcm_fe_dai_do_trigger(substream, cmd); +} + int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; @@ -2027,7 +2074,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name); - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); /* there is no point preparing this FE if there are no BEs */ if (list_empty(&fe->dpcm[stream].be_clients)) { @@ -2054,7 +2101,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; out: - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); mutex_unlock(&fe->card->mutex); return ret; @@ -2201,11 +2248,11 @@ static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream) { int ret; - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE); ret = dpcm_run_update_startup(fe, stream); if (ret < 0) dev_err(fe->dev, "ASoC: failed to startup some BEs\n"); - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); return ret; } @@ -2214,11 +2261,11 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream) { int ret; - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE); ret = dpcm_run_update_shutdown(fe, stream); if (ret < 0) dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n"); - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); return ret; } 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 = { diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index cd71fd889d8b..00b7e2d02690 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -292,7 +292,7 @@ static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd) struct snd_card *card = rtd->card->snd_card; struct snd_soc_dai *dai = rtd->cpu_dai; struct snd_pcm *pcm = rtd->pcm; - struct platform_device *pdev = to_platform_device(dai->platform->dev); + struct platform_device *pdev = to_platform_device(rtd->platform->dev); struct txx9aclc_soc_device *dev; struct resource *r; int i; |