diff options
author | Mark Brown <broonie@kernel.org> | 2015-06-10 18:31:55 +0100 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2015-06-10 18:31:55 +0100 |
commit | 5ae4f63b5062bc4e329940cc5cecdf0856b85849 (patch) | |
tree | 63f389874f6d330f84a9d36abec2c127bd71066e /sound/soc/codecs | |
parent | fb5ab0e7473d41e2a9db0fcb569faf337d595838 (diff) | |
parent | 11e688862c4c8162119a4ca51c3326555966c8bb (diff) | |
download | op-kernel-dev-5ae4f63b5062bc4e329940cc5cecdf0856b85849.zip op-kernel-dev-5ae4f63b5062bc4e329940cc5cecdf0856b85849.tar.gz |
Merge tag 'asoc-v4.2' into asoc-rt5645
ASoC: Updates for v4.2
The big thing this release has been Liam's addition of topology support
to the core. We've also seen quite a bit of driver work and the
continuation of Lars' refactoring for component support.
- Support for loading ASoC topology maps from firmware, intended to be
used to allow self-describing DSP firmware images to be built which
can map controls added by the DSP to userspace without the kernel
needing to know about individual DSP firmwares.
- Lots of refactoring to avoid direct access to snd_soc_codec where
it's not needed supporting future refactoring.
- Big refactoring and cleanup serieses for the Wolfson ADSP and TI
TAS2552 drivers.
- Support for TI TAS571x power amplifiers.
- Support for Qualcomm APQ8016 and ZTE ZX296702 SoCs.
- Support for x86 systems with RT5650 and Qualcomm Storm.
Diffstat (limited to 'sound/soc/codecs')
131 files changed, 2807 insertions, 1280 deletions
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index a0f2653..38b3dad 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -1140,7 +1140,7 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Enable Audio PLL & Audio section */ data = AUDIO_PLL | AUDIO_SECTION_ON; pm860x_reg_write(pm860x->i2c, REG_MISC2, data); @@ -1156,7 +1156,6 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec, pm860x_set_bits(pm860x->i2c, REG_MISC2, data, 0); break; } - codec->dapm.bias_level = level; return 0; } @@ -1187,16 +1186,16 @@ static struct snd_soc_dai_driver pm860x_dai[] = { .channels_min = 2, .channels_max = 2, .rates = PM860X_RATES, - .formats = SNDRV_PCM_FORMAT_S16_LE | \ - SNDRV_PCM_FORMAT_S18_3LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE, }, .capture = { .stream_name = "PCM Capture", .channels_min = 2, .channels_max = 2, .rates = PM860X_RATES, - .formats = SNDRV_PCM_FORMAT_S16_LE | \ - SNDRV_PCM_FORMAT_S18_3LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE, }, .ops = &pm860x_pcm_dai_ops, }, { @@ -1208,16 +1207,16 @@ static struct snd_soc_dai_driver pm860x_dai[] = { .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FORMAT_S16_LE | \ - SNDRV_PCM_FORMAT_S18_3LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE, }, .capture = { .stream_name = "I2S Capture", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FORMAT_S16_LE | \ - SNDRV_PCM_FORMAT_S18_3LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE, }, .ops = &pm860x_i2s_dai_ops, }, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 061c465..9b36011 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -16,7 +16,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_88PM860X if MFD_88PM860X select SND_SOC_L3 select SND_SOC_AB8500_CODEC if ABX500_CORE - select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS + select SND_SOC_AC97_CODEC select SND_SOC_AD1836 if SPI_MASTER select SND_SOC_AD193X_SPI if SPI_MASTER select SND_SOC_AD193X_I2C if I2C @@ -54,7 +54,7 @@ config SND_SOC_ALL_CODECS 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 + select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI select SND_SOC_DA7213 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C @@ -104,6 +104,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TAS2552 if I2C select SND_SOC_TAS5086 if I2C + select SND_SOC_TAS571X if I2C select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -211,8 +212,9 @@ config SND_SOC_AB8500_CODEC tristate config SND_SOC_AC97_CODEC - tristate + tristate "Build generic ASoC AC97 CODEC driver" select SND_AC97_CODEC + select SND_SOC_AC97_BUS config SND_SOC_AD1836 tristate @@ -611,6 +613,10 @@ config SND_SOC_TAS5086 tristate "Texas Instruments TAS5086 speaker amplifier" depends on I2C +config SND_SOC_TAS571X + tristate "Texas Instruments TAS5711/TAS5717/TAS5719 power amplifiers" + depends on I2C + config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index abe2d7e..3dcf5ac 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -106,6 +106,7 @@ snd-soc-sta350-objs := sta350.o snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o +snd-soc-tas571x-objs := tas571x.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -288,6 +289,7 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o +obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 88ca9cb..c7d243d 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -1209,6 +1209,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); struct device *dev = codec->dev; bool apply_fir, apply_iir; @@ -1234,15 +1235,14 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, apply_fir = req == ANC_APPLY_FIR || req == ANC_APPLY_FIR_IIR; apply_iir = req == ANC_APPLY_IIR || req == ANC_APPLY_FIR_IIR; - status = snd_soc_dapm_force_enable_pin(&codec->dapm, - "ANC Configure Input"); + status = snd_soc_dapm_force_enable_pin(dapm, "ANC Configure Input"); if (status < 0) { dev_err(dev, "%s: ERROR: Failed to enable power (status = %d)!\n", __func__, status); goto cleanup; } - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); anc_configure(codec, apply_fir, apply_iir); @@ -1259,8 +1259,8 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, drvdata->anc_status = ANC_IIR_CONFIGURED; } - status = snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input"); - snd_soc_dapm_sync(&codec->dapm); + status = snd_soc_dapm_disable_pin(dapm, "ANC Configure Input"); + snd_soc_dapm_sync(dapm); cleanup: mutex_unlock(&drvdata->ctrl_lock); @@ -1947,6 +1947,7 @@ static int ab8500_audio_init_audioblock(struct snd_soc_codec *codec) static int ab8500_audio_setup_mics(struct snd_soc_codec *codec, struct amic_settings *amics) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); u8 value8; unsigned int value; int status; @@ -1973,15 +1974,15 @@ static int ab8500_audio_setup_mics(struct snd_soc_codec *codec, dev_dbg(codec->dev, "%s: Mic 1a regulator: %s\n", __func__, amic_micbias_str(amics->mic1a_micbias)); route = &ab8500_dapm_routes_mic1a_vamicx[amics->mic1a_micbias]; - status = snd_soc_dapm_add_routes(&codec->dapm, route, 1); + status = snd_soc_dapm_add_routes(dapm, route, 1); dev_dbg(codec->dev, "%s: Mic 1b regulator: %s\n", __func__, amic_micbias_str(amics->mic1b_micbias)); route = &ab8500_dapm_routes_mic1b_vamicx[amics->mic1b_micbias]; - status |= snd_soc_dapm_add_routes(&codec->dapm, route, 1); + status |= snd_soc_dapm_add_routes(dapm, route, 1); dev_dbg(codec->dev, "%s: Mic 2 regulator: %s\n", __func__, amic_micbias_str(amics->mic2_micbias)); route = &ab8500_dapm_routes_mic2_vamicx[amics->mic2_micbias]; - status |= snd_soc_dapm_add_routes(&codec->dapm, route, 1); + status |= snd_soc_dapm_add_routes(dapm, route, 1); if (status < 0) { dev_err(codec->dev, "%s: Failed to add AMic-regulator DAPM-routes (%d).\n", @@ -2461,6 +2462,7 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np, static int ab8500_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct device *dev = codec->dev; struct device_node *np = dev->of_node; struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev); @@ -2541,7 +2543,7 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec) &ab8500_filter_controls[AB8500_FILTER_SID_FIR].private_value; drvdata->sid_fir_values = (long *)fc->value; - (void)snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input"); + snd_soc_dapm_disable_pin(dapm, "ANC Configure Input"); mutex_init(&drvdata->ctrl_lock); diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index d0ac723..5b3224c 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -44,10 +44,6 @@ static int ac97_prepare(struct snd_pcm_substream *substream, return snd_ac97_set_rate(ac97, reg, substream->runtime->rate); } -#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\ - SNDRV_PCM_RATE_48000) - static const struct snd_soc_dai_ops ac97_dai_ops = { .prepare = ac97_prepare, }; @@ -58,13 +54,13 @@ static struct snd_soc_dai_driver ac97_dai = { .stream_name = "AC97 Playback", .channels_min = 1, .channels_max = 2, - .rates = STD_AC97_RATES, + .rates = SNDRV_PCM_RATE_KNOT, .formats = SND_SOC_STD_AC97_FMTS,}, .capture = { .stream_name = "AC97 Capture", .channels_min = 1, .channels_max = 2, - .rates = STD_AC97_RATES, + .rates = SNDRV_PCM_RATE_KNOT, .formats = SND_SOC_STD_AC97_FMTS,}, .ops = &ac97_dai_ops, }; diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 685998d..95f0bec 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -251,7 +251,7 @@ static int ad1836_resume(struct snd_soc_codec *codec) static int ad1836_probe(struct snd_soc_codec *codec) { struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int num_dacs, num_adcs; int ret = 0; int i; diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 783dcb5..a431602 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -1444,7 +1444,6 @@ static int adau1373_set_bias_level(struct snd_soc_codec *codec, ADAU1373_PWDN_CTRL3_PWR_EN, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index d4e219b..ff7f846 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -16,6 +16,7 @@ #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/of_device.h> +#include <linux/regulator/consumer.h> #include <linux/regmap.h> #include <sound/core.h> #include <sound/pcm.h> @@ -101,6 +102,10 @@ #define ADAU1701_FIRMWARE "adau1701.bin" +static const char * const supply_names[] = { + "dvdd", "avdd" +}; + struct adau1701 { int gpio_nreset; int gpio_pll_mode[2]; @@ -112,6 +117,7 @@ struct adau1701 { u8 pin_config[12]; struct sigmadsp *sigmadsp; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; }; static const struct snd_kcontrol_new adau1701_controls[] = { @@ -565,7 +571,6 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -669,6 +674,13 @@ static int adau1701_probe(struct snd_soc_codec *codec) if (ret) return ret; + ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + if (ret < 0) { + dev_err(codec->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + /* * Let the pll_clkdiv variable default to something that won't happen * at runtime. That way, we can postpone the firmware download from @@ -680,7 +692,7 @@ static int adau1701_probe(struct snd_soc_codec *codec) /* initalize with pre-configured pll mode settings */ ret = adau1701_reset(codec, adau1701->pll_clkdiv, 0); if (ret < 0) - return ret; + goto exit_regulators_disable; /* set up pin config */ val = 0; @@ -696,10 +708,60 @@ static int adau1701_probe(struct snd_soc_codec *codec) regmap_write(adau1701->regmap, ADAU1701_PINCONF_1, val); return 0; + +exit_regulators_disable: + + regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies); + return ret; } +static int adau1701_remove(struct snd_soc_codec *codec) +{ + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + + if (gpio_is_valid(adau1701->gpio_nreset)) + gpio_set_value_cansleep(adau1701->gpio_nreset, 0); + + regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies); + + return 0; +} + +#ifdef CONFIG_PM +static int adau1701_suspend(struct snd_soc_codec *codec) +{ + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + + regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + + return 0; +} + +static int adau1701_resume(struct snd_soc_codec *codec) +{ + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + if (ret < 0) { + dev_err(codec->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + return adau1701_reset(codec, adau1701->pll_clkdiv, 0); +} +#else +#define adau1701_resume NULL +#define adau1701_suspend NULL +#endif /* CONFIG_PM */ + static struct snd_soc_codec_driver adau1701_codec_drv = { .probe = adau1701_probe, + .remove = adau1701_remove, + .resume = adau1701_resume, + .suspend = adau1701_suspend, .set_bias_level = adau1701_set_bias_level, .idle_bias_off = true, @@ -730,32 +792,58 @@ static int adau1701_i2c_probe(struct i2c_client *client, struct device *dev = &client->dev; int gpio_nreset = -EINVAL; int gpio_pll_mode[2] = { -EINVAL, -EINVAL }; - int ret; + int ret, i; adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL); if (!adau1701) return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + adau1701->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + if (ret < 0) { + dev_err(dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + adau1701->client = client; adau1701->regmap = devm_regmap_init(dev, NULL, client, &adau1701_regmap); - if (IS_ERR(adau1701->regmap)) - return PTR_ERR(adau1701->regmap); + if (IS_ERR(adau1701->regmap)) { + ret = PTR_ERR(adau1701->regmap); + goto exit_regulators_disable; + } + if (dev->of_node) { gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0); - if (gpio_nreset < 0 && gpio_nreset != -ENOENT) - return gpio_nreset; + if (gpio_nreset < 0 && gpio_nreset != -ENOENT) { + ret = gpio_nreset; + goto exit_regulators_disable; + } gpio_pll_mode[0] = of_get_named_gpio(dev->of_node, "adi,pll-mode-gpios", 0); - if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT) - return gpio_pll_mode[0]; + if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT) { + ret = gpio_pll_mode[0]; + goto exit_regulators_disable; + } gpio_pll_mode[1] = of_get_named_gpio(dev->of_node, "adi,pll-mode-gpios", 1); - if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT) - return gpio_pll_mode[1]; + if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT) { + ret = gpio_pll_mode[1]; + goto exit_regulators_disable; + } of_property_read_u32(dev->of_node, "adi,pll-clkdiv", &adau1701->pll_clkdiv); @@ -769,7 +857,7 @@ static int adau1701_i2c_probe(struct i2c_client *client, ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW, "ADAU1701 Reset"); if (ret < 0) - return ret; + goto exit_regulators_disable; } if (gpio_is_valid(gpio_pll_mode[0]) && @@ -778,13 +866,13 @@ static int adau1701_i2c_probe(struct i2c_client *client, GPIOF_OUT_INIT_LOW, "ADAU1701 PLL mode 0"); if (ret < 0) - return ret; + goto exit_regulators_disable; ret = devm_gpio_request_one(dev, gpio_pll_mode[1], GPIOF_OUT_INIT_LOW, "ADAU1701 PLL mode 1"); if (ret < 0) - return ret; + goto exit_regulators_disable; } adau1701->gpio_nreset = gpio_nreset; @@ -795,11 +883,17 @@ static int adau1701_i2c_probe(struct i2c_client *client, adau1701->sigmadsp = devm_sigmadsp_init_i2c(client, &adau1701_sigmadsp_ops, ADAU1701_FIRMWARE); - if (IS_ERR(adau1701->sigmadsp)) - return PTR_ERR(adau1701->sigmadsp); + if (IS_ERR(adau1701->sigmadsp)) { + ret = PTR_ERR(adau1701->sigmadsp); + goto exit_regulators_disable; + } ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv, &adau1701_dai, 1); + +exit_regulators_disable: + + regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies); return ret; } diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index a1baeee..2f12477 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -466,7 +466,6 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -483,6 +482,7 @@ static enum adau1761_output_mode adau1761_get_lineout_mode( static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau1761_platform_data *pdata = codec->dev->platform_data; struct adau *adau = snd_soc_codec_get_drvdata(codec); enum adau1761_digmic_jackdet_pin_mode mode; @@ -515,21 +515,18 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec) if (ret) return ret; case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: /* fallthrough */ - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau1761_no_dmic_routes, + ret = snd_soc_dapm_add_routes(dapm, adau1761_no_dmic_routes, ARRAY_SIZE(adau1761_no_dmic_routes)); if (ret) return ret; break; case ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC: - ret = snd_soc_dapm_new_controls(&codec->dapm, - adau1761_dmic_widgets, + ret = snd_soc_dapm_new_controls(dapm, adau1761_dmic_widgets, ARRAY_SIZE(adau1761_dmic_widgets)); if (ret) return ret; - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau1761_dmic_routes, + ret = snd_soc_dapm_add_routes(dapm, adau1761_dmic_routes, ARRAY_SIZE(adau1761_dmic_routes)); if (ret) return ret; @@ -547,6 +544,7 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec) static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau *adau = snd_soc_codec_get_drvdata(codec); struct adau1761_platform_data *pdata = codec->dev->platform_data; enum adau1761_output_mode mode; @@ -577,12 +575,12 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec) } if (mode == ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS) { - ret = snd_soc_dapm_new_controls(&codec->dapm, + ret = snd_soc_dapm_new_controls(dapm, adau1761_capless_dapm_widgets, ARRAY_SIZE(adau1761_capless_dapm_widgets)); if (ret) return ret; - ret = snd_soc_dapm_add_routes(&codec->dapm, + ret = snd_soc_dapm_add_routes(dapm, adau1761_capless_dapm_routes, ARRAY_SIZE(adau1761_capless_dapm_routes)); } else { @@ -590,12 +588,12 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec) ARRAY_SIZE(adau1761_mono_controls)); if (ret) return ret; - ret = snd_soc_dapm_new_controls(&codec->dapm, + ret = snd_soc_dapm_new_controls(dapm, adau1761_mono_dapm_widgets, ARRAY_SIZE(adau1761_mono_dapm_widgets)); if (ret) return ret; - ret = snd_soc_dapm_add_routes(&codec->dapm, + ret = snd_soc_dapm_add_routes(dapm, adau1761_mono_dapm_routes, ARRAY_SIZE(adau1761_mono_dapm_routes)); } @@ -640,6 +638,7 @@ static bool adau1761_readable_register(struct device *dev, unsigned int reg) static int adau1761_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau1761_platform_data *pdata = codec->dev->platform_data; struct adau *adau = snd_soc_codec_get_drvdata(codec); int ret; @@ -692,14 +691,12 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec) return ret; if (adau->type == ADAU1761) { - ret = snd_soc_dapm_new_controls(&codec->dapm, - adau1761_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, adau1761_dapm_widgets, ARRAY_SIZE(adau1761_dapm_widgets)); if (ret) return ret; - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau1761_dapm_routes, + ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_routes, ARRAY_SIZE(adau1761_dapm_routes)); if (ret) return ret; diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c index 35581f4..fde9068 100644 --- a/sound/soc/codecs/adau1781.c +++ b/sound/soc/codecs/adau1781.c @@ -339,7 +339,6 @@ static int adau1781_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -383,6 +382,7 @@ static int adau1781_set_input_mode(struct adau *adau, unsigned int reg, static int adau1781_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev); struct adau *adau = snd_soc_codec_get_drvdata(codec); int ret; @@ -403,19 +403,17 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec) } if (pdata && pdata->use_dmic) { - ret = snd_soc_dapm_new_controls(&codec->dapm, + ret = snd_soc_dapm_new_controls(dapm, adau1781_dmic_dapm_widgets, ARRAY_SIZE(adau1781_dmic_dapm_widgets)); if (ret) return ret; - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau1781_dmic_dapm_routes, + ret = snd_soc_dapm_add_routes(dapm, adau1781_dmic_dapm_routes, ARRAY_SIZE(adau1781_dmic_dapm_routes)); if (ret) return ret; } else { - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau1781_adc_dapm_routes, + ret = snd_soc_dapm_add_routes(dapm, adau1781_adc_dapm_routes, ARRAY_SIZE(adau1781_adc_dapm_routes)); if (ret) return ret; diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index fa2e690..fcf05b2 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -155,6 +155,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau *adau = snd_soc_codec_get_drvdata(codec); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct snd_soc_dapm_update update; @@ -188,7 +189,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol, update.reg = reg; update.val = val; - snd_soc_dapm_mux_update_power(&codec->dapm, kcontrol, + snd_soc_dapm_mux_update_power(dapm, kcontrol, ucontrol->value.enumerated.item[0], e, &update); } @@ -444,8 +445,8 @@ static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id, static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(dai->codec); struct adau *adau = snd_soc_codec_get_drvdata(dai->codec); - struct snd_soc_dapm_context *dapm = &dai->codec->dapm; switch (clk_id) { case ADAU17X1_CLK_SRC_MCLK: @@ -804,6 +805,7 @@ EXPORT_SYMBOL_GPL(adau17x1_setup_firmware); int adau17x1_add_widgets(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau *adau = snd_soc_codec_get_drvdata(codec); int ret; @@ -811,14 +813,13 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec) ARRAY_SIZE(adau17x1_controls)); if (ret) return ret; - ret = snd_soc_dapm_new_controls(&codec->dapm, adau17x1_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, adau17x1_dapm_widgets, ARRAY_SIZE(adau17x1_dapm_widgets)); if (ret) return ret; if (adau17x1_has_dsp(adau)) { - ret = snd_soc_dapm_new_controls(&codec->dapm, - adau17x1_dsp_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, adau17x1_dsp_dapm_widgets, ARRAY_SIZE(adau17x1_dsp_dapm_widgets)); if (ret) return ret; @@ -840,21 +841,20 @@ EXPORT_SYMBOL_GPL(adau17x1_add_widgets); int adau17x1_add_routes(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau *adau = snd_soc_codec_get_drvdata(codec); int ret; - ret = snd_soc_dapm_add_routes(&codec->dapm, adau17x1_dapm_routes, + ret = snd_soc_dapm_add_routes(dapm, adau17x1_dapm_routes, ARRAY_SIZE(adau17x1_dapm_routes)); if (ret) return ret; if (adau17x1_has_dsp(adau)) { - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau17x1_dsp_dapm_routes, + ret = snd_soc_dapm_add_routes(dapm, adau17x1_dsp_dapm_routes, ARRAY_SIZE(adau17x1_dsp_dapm_routes)); } else { - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau17x1_no_dsp_dapm_routes, + ret = snd_soc_dapm_add_routes(dapm, adau17x1_no_dsp_dapm_routes, ARRAY_SIZE(adau17x1_no_dsp_dapm_routes)); } return ret; diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c index 7ad8e15..9bdd15f 100644 --- a/sound/soc/codecs/adau1977.c +++ b/sound/soc/codecs/adau1977.c @@ -202,7 +202,7 @@ static const struct snd_soc_dapm_route adau1977_dapm_routes[] = { ADAU1977_REG_DC_HPF_CAL, (x) - 1, 1, 0) #define ADAU1977_DC_SUB_SWITCH(x) \ - SOC_SINGLE("ADC" #x " DC Substraction Capture Switch", \ + SOC_SINGLE("ADC" #x " DC Subtraction Capture Switch", \ ADAU1977_REG_DC_HPF_CAL, (x) + 3, 1, 0) static const struct snd_kcontrol_new adau1977_snd_controls[] = { @@ -485,7 +485,7 @@ static int adau1977_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) ret = adau1977_power_enable(adau1977); break; case SND_SOC_BIAS_OFF: @@ -493,12 +493,7 @@ static int adau1977_set_bias_level(struct snd_soc_codec *codec, break; } - if (ret) - return ret; - - codec->dapm.bias_level = level; - - return 0; + return ret; } static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, @@ -853,12 +848,13 @@ static int adau1977_set_sysclk(struct snd_soc_codec *codec, static int adau1977_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec); int ret; switch (adau1977->type) { case ADAU1977: - ret = snd_soc_dapm_new_controls(&codec->dapm, + ret = snd_soc_dapm_new_controls(dapm, adau1977_micbias_dapm_widgets, ARRAY_SIZE(adau1977_micbias_dapm_widgets)); if (ret < 0) diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 4373ada..36d8425 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -539,7 +539,7 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec, unsigned int freq, int dir) { struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); if (dir == SND_SOC_CLOCK_IN) { switch (clk_id) { @@ -622,6 +622,7 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec, static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); unsigned int pll_ctrl1 = 0; unsigned int pll_ctrl2 = 0; @@ -687,7 +688,7 @@ static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id, adav80x->pll_src = source; - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); } return 0; @@ -714,7 +715,6 @@ static int adav80x_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -801,11 +801,12 @@ static struct snd_soc_dai_driver adav80x_dais[] = { static int adav80x_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); /* Force PLLs on for SYSCLK output */ - snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1"); - snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2"); + snd_soc_dapm_force_enable_pin(dapm, "PLL1"); + snd_soc_dapm_force_enable_pin(dapm, "PLL2"); /* Power down S/PDIF receiver, since it is currently not supported */ regmap_write(adav80x->regmap, ADAV80X_PLL_OUTE, 0x20); diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 9130d91..8670861 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -341,7 +341,6 @@ static int ak4535_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 81b54a2..2d0ff45 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -412,7 +412,7 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0x20); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { if (pdata && gpio_is_valid(pdata->gpio_power)) gpio_set_value(pdata->gpio_power, 1); mdelay(1); @@ -439,7 +439,6 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec, regcache_mark_dirty(ak4641->regmap); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 13585e8..7c0f6552 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -482,7 +482,6 @@ static int ak4642_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, PW_MGMT1, PMVCM, PMVCM); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index 2a58b1d..0e59063 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -577,7 +577,6 @@ static int ak4671_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 0e35799..0fc24e0 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -826,7 +826,6 @@ static int alc5623_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1, 0); break; } - codec->dapm.bias_level = level; return 0; } @@ -894,7 +893,7 @@ static int alc5623_resume(struct snd_soc_codec *codec) static int alc5623_probe(struct snd_soc_codec *codec) { struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); alc5623_reset(codec); diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index db3283a..607a63b 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -1000,7 +1000,6 @@ static int alc5632_set_bias_level(struct snd_soc_codec *codec, ALC5632_PWR_MANAG_ADD1_MASK, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index eff4b4d..88f6df2 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -208,11 +208,12 @@ static const struct snd_soc_dapm_widget arizona_spkr = int arizona_init_spk(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona *arizona = priv->arizona; int ret; - ret = snd_soc_dapm_new_controls(&codec->dapm, &arizona_spkl, 1); + ret = snd_soc_dapm_new_controls(dapm, &arizona_spkl, 1); if (ret != 0) return ret; @@ -220,8 +221,7 @@ int arizona_init_spk(struct snd_soc_codec *codec) case WM8997: break; default: - ret = snd_soc_dapm_new_controls(&codec->dapm, - &arizona_spkr, 1); + ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1); if (ret != 0) return ret; break; @@ -258,13 +258,14 @@ static const struct snd_soc_dapm_route arizona_mono_routes[] = { int arizona_init_mono(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona *arizona = priv->arizona; int i; for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) { if (arizona->pdata.out_mono[i]) - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, &arizona_mono_routes[i], 1); } @@ -274,6 +275,7 @@ EXPORT_SYMBOL_GPL(arizona_init_mono); int arizona_init_gpio(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona *arizona = priv->arizona; int i; @@ -281,23 +283,21 @@ int arizona_init_gpio(struct snd_soc_codec *codec) switch (arizona->type) { case WM5110: case WM8280: - snd_soc_dapm_disable_pin(&codec->dapm, "DRC2 Signal Activity"); + snd_soc_dapm_disable_pin(dapm, "DRC2 Signal Activity"); break; default: break; } - snd_soc_dapm_disable_pin(&codec->dapm, "DRC1 Signal Activity"); + snd_soc_dapm_disable_pin(dapm, "DRC1 Signal Activity"); for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) { case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT: - snd_soc_dapm_enable_pin(&codec->dapm, - "DRC1 Signal Activity"); + snd_soc_dapm_enable_pin(dapm, "DRC1 Signal Activity"); break; case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT: - snd_soc_dapm_enable_pin(&codec->dapm, - "DRC2 Signal Activity"); + snd_soc_dapm_enable_pin(dapm, "DRC2 Signal Activity"); break; default: break; @@ -851,6 +851,134 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w, } EXPORT_SYMBOL_GPL(arizona_hp_ev); +static int arizona_dvfs_enable(struct snd_soc_codec *codec) +{ + const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + int ret; + + ret = regulator_set_voltage(arizona->dcvdd, 1800000, 1800000); + if (ret) { + dev_err(codec->dev, "Failed to boost DCVDD: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_DYNAMIC_FREQUENCY_SCALING_1, + ARIZONA_SUBSYS_MAX_FREQ, + ARIZONA_SUBSYS_MAX_FREQ); + if (ret) { + dev_err(codec->dev, "Failed to enable subsys max: %d\n", ret); + regulator_set_voltage(arizona->dcvdd, 1200000, 1800000); + return ret; + } + + return 0; +} + +static int arizona_dvfs_disable(struct snd_soc_codec *codec) +{ + const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + int ret; + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_DYNAMIC_FREQUENCY_SCALING_1, + ARIZONA_SUBSYS_MAX_FREQ, 0); + if (ret) { + dev_err(codec->dev, "Failed to disable subsys max: %d\n", ret); + return ret; + } + + ret = regulator_set_voltage(arizona->dcvdd, 1200000, 1800000); + if (ret) { + dev_err(codec->dev, "Failed to unboost DCVDD: %d\n", ret); + return ret; + } + + return 0; +} + +int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags) +{ + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + mutex_lock(&priv->dvfs_lock); + + if (!priv->dvfs_cached && !priv->dvfs_reqs) { + ret = arizona_dvfs_enable(codec); + if (ret) + goto err; + } + + priv->dvfs_reqs |= flags; +err: + mutex_unlock(&priv->dvfs_lock); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_dvfs_up); + +int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags) +{ + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + unsigned int old_reqs; + int ret = 0; + + mutex_lock(&priv->dvfs_lock); + + old_reqs = priv->dvfs_reqs; + priv->dvfs_reqs &= ~flags; + + if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs) + ret = arizona_dvfs_disable(codec); + + mutex_unlock(&priv->dvfs_lock); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_dvfs_down); + +int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + mutex_lock(&priv->dvfs_lock); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (priv->dvfs_reqs) + ret = arizona_dvfs_enable(codec); + + priv->dvfs_cached = false; + break; + case SND_SOC_DAPM_PRE_PMD: + /* We must ensure DVFS is disabled before the codec goes into + * suspend so that we are never in an illegal state of DVFS + * enabled without enough DCVDD + */ + priv->dvfs_cached = true; + + if (priv->dvfs_reqs) + ret = arizona_dvfs_disable(codec); + break; + default: + break; + } + + mutex_unlock(&priv->dvfs_lock); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev); + +void arizona_init_dvfs(struct arizona_priv *priv) +{ + mutex_init(&priv->dvfs_lock); +} +EXPORT_SYMBOL_GPL(arizona_init_dvfs); + static unsigned int arizona_sysclk_48k_rates[] = { 6144000, 12288000, @@ -1266,7 +1394,7 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream, struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; int base = dai->driver->base; - int i, sr_val; + int i, sr_val, ret; /* * We will need to be more flexible than this in future, @@ -1282,6 +1410,23 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream, } sr_val = i; + switch (priv->arizona->type) { + case WM5102: + case WM8997: + if (arizona_sr_vals[sr_val] >= 88200) + ret = arizona_dvfs_up(codec, ARIZONA_DVFS_SR1_RQ); + else + ret = arizona_dvfs_down(codec, ARIZONA_DVFS_SR1_RQ); + + if (ret) { + arizona_aif_err(dai, "Failed to change DVFS %d\n", ret); + return ret; + } + break; + default: + break; + } + switch (dai_priv->clk) { case ARIZONA_CLK_SYSCLK: switch (priv->arizona->type) { @@ -1474,6 +1619,7 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = dai->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; struct snd_soc_dapm_route routes[2]; @@ -1504,15 +1650,15 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai, routes[0].source = arizona_dai_clk_str(dai_priv->clk); routes[1].source = arizona_dai_clk_str(dai_priv->clk); - snd_soc_dapm_del_routes(&codec->dapm, routes, ARRAY_SIZE(routes)); + snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes)); routes[0].source = arizona_dai_clk_str(clk_id); routes[1].source = arizona_dai_clk_str(clk_id); - snd_soc_dapm_add_routes(&codec->dapm, routes, ARRAY_SIZE(routes)); + snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes)); dai_priv->clk = clk_id; - return snd_soc_dapm_sync(&codec->dapm); + return snd_soc_dapm_sync(dapm); } static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate) diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 11ff899..84e119a 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -60,6 +60,9 @@ #define ARIZONA_MAX_DAI 6 #define ARIZONA_MAX_ADSP 4 +#define ARIZONA_DVFS_SR1_RQ 0x001 +#define ARIZONA_DVFS_ADSP1_RQ 0x100 + struct arizona; struct wm_adsp; @@ -84,6 +87,10 @@ struct arizona_priv { unsigned int spk_ena:2; unsigned int spk_ena_pending:1; + + unsigned int dvfs_reqs; + struct mutex dvfs_lock; + bool dvfs_cached; }; #define ARIZONA_NUM_MIXER_INPUTS 103 @@ -107,8 +114,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; arizona_mixer_tlv) #define ARIZONA_MUX_ENUM_DECL(name, reg) \ - SOC_VALUE_ENUM_SINGLE_DECL(name, reg, 0, 0xff, \ - arizona_mixer_texts, arizona_mixer_values) + SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL( \ + name, reg, 0, 0xff, arizona_mixer_texts, arizona_mixer_values) #define ARIZONA_MUX_CTL_DECL(name) \ const struct snd_kcontrol_new name##_mux = \ @@ -245,6 +252,12 @@ struct arizona_fll { char clock_ok_name[ARIZONA_FLL_NAME_LEN]; }; +extern int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags); +extern int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags); +extern int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +extern void arizona_init_dvfs(struct arizona_priv *priv); + extern int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, int ok_irq, struct arizona_fll *fll); extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source, diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index e7238b8..b084ad1 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -63,7 +63,7 @@ static int bt_sco_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id bt_sco_driver_ids[] = { +static const struct platform_device_id bt_sco_driver_ids[] = { { .name = "dfbmcs320", }, @@ -74,9 +74,18 @@ static struct platform_device_id bt_sco_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids); +#if defined(CONFIG_OF) +static const struct of_device_id bt_sco_codec_of_match[] = { + { .compatible = "delta,dfbmcs320", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match); +#endif + static struct platform_driver bt_sco_driver = { .driver = { .name = "bt-sco", + .of_match_table = of_match_ptr(bt_sco_codec_of_match), }, .probe = bt_sco_probe, .remove = bt_sco_remove, diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index d6dedd4..1c895a5 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -92,7 +92,6 @@ static int cq93vc_set_bias_level(struct snd_soc_codec *codec, DAVINCI_VC_REG12_POWER_ALL_OFF); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index 60598b2..8f40025 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -13,7 +13,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/version.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index cac48dd..d7ec4756 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -503,7 +503,6 @@ static int cs4265_set_bias_level(struct snd_soc_codec *codec, CS4265_PWRCTL_PDN); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 1589e7a..4de52c9 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -897,7 +897,7 @@ static int cs42l52_set_bias_level(struct snd_soc_codec *codec, CS42L52_PWRCTL1_PDN_CODEC, 0); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_cache_only(cs42l52->regmap, false); regcache_sync(cs42l52->regmap); } @@ -908,7 +908,6 @@ static int cs42l52_set_bias_level(struct snd_soc_codec *codec, regcache_cache_only(cs42l52->regmap, true); break; } - codec->dapm.bias_level = level; return 0; } @@ -956,7 +955,7 @@ static void cs42l52_beep_work(struct work_struct *work) struct cs42l52_private *cs42l52 = container_of(work, struct cs42l52_private, beep_work); struct snd_soc_codec *codec = cs42l52->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int i; int val = 0; int best = 0; diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index cbc654f..1e11ba4 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -953,7 +953,7 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec, CS42L56_PDN_ALL_MASK, 0); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_cache_only(cs42l56->regmap, false); regcache_sync(cs42l56->regmap); ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies), @@ -978,7 +978,6 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec, cs42l56->supplies); break; } - codec->dapm.bias_level = level; return 0; } @@ -1026,7 +1025,7 @@ static void cs42l56_beep_work(struct work_struct *work) struct cs42l56_private *cs42l56 = container_of(work, struct cs42l56_private, beep_work); struct snd_soc_codec *codec = cs42l56->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int i; int val = 0; int best = 0; diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 8ecedba..b7853b9 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -1208,7 +1208,7 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_cache_only(cs42l73->regmap, false); regcache_sync(cs42l73->regmap); } @@ -1228,7 +1228,6 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, CS42L73_DMMCC, CS42L73_MCLKDIS, 1); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c index 670ebfe..e1d4686 100644 --- a/sound/soc/codecs/cs42xx8.c +++ b/sound/soc/codecs/cs42xx8.c @@ -380,7 +380,7 @@ EXPORT_SYMBOL_GPL(cs42xx8_regmap_config); static int cs42xx8_codec_probe(struct snd_soc_codec *codec) { struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); switch (cs42xx8->drvdata->num_adcs) { case 3: diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index 0f334bc..d6f4abb 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -333,7 +333,7 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_PREPARE: - if (codec->dapm.bias_level != SND_SOC_BIAS_STANDBY) + if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_STANDBY) break; if (IS_ERR(cx20442->por)) err = PTR_ERR(cx20442->por); @@ -341,7 +341,7 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec, err = regulator_enable(cx20442->por); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level != SND_SOC_BIAS_PREPARE) + if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_PREPARE) break; if (IS_ERR(cx20442->por)) err = PTR_ERR(cx20442->por); @@ -351,8 +351,6 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec, default: break; } - if (!err) - codec->dapm.bias_level = level; return err; } diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 9ec577f..238e48a 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1374,7 +1374,7 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Enable VMID reference & master bias */ snd_soc_update_bits(codec, DA7213_REFERENCES, DA7213_VMID_EN | DA7213_BIAS_EN, @@ -1387,7 +1387,6 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec, DA7213_VMID_EN | DA7213_BIAS_EN, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index 911c26c..2075236 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -1432,7 +1432,7 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Init Codec */ snd_soc_write(codec, DA732X_REG_REF1, DA732X_VMID_FASTCHG); @@ -1502,8 +1502,6 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index ad19cc5..66bb446 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -1364,7 +1364,7 @@ static int da9055_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Enable VMID reference & master bias */ snd_soc_update_bits(codec, DA9055_REFERENCES, DA9055_VMID_EN | DA9055_BIAS_EN, @@ -1377,7 +1377,6 @@ static int da9055_set_bias_level(struct snd_soc_codec *codec, DA9055_VMID_EN | DA9055_BIAS_EN, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index c5f35a0..6a09101 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -536,7 +536,7 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_update_bits(codec, ES8328_CONTROL1, ES8328_CONTROL1_VMIDSEL_MASK | ES8328_CONTROL1_ENREF, @@ -566,7 +566,6 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c index 3a89ce6..ebd9028 100644 --- a/sound/soc/codecs/isabelle.c +++ b/sound/soc/codecs/isabelle.c @@ -909,8 +909,6 @@ static int isabelle_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index 933f447..9363fdb 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -258,7 +258,7 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: /* The only way to clear the suspend flag is to reset the codec */ - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) jz4740_codec_wakeup(regmap); mask = JZ4740_CODEC_1_VREF_DISABLE | @@ -281,8 +281,6 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c index a924bb9..99ffc49 100644 --- a/sound/soc/codecs/lm4857.c +++ b/sound/soc/codecs/lm4857.c @@ -23,11 +23,6 @@ #include <sound/soc.h> #include <sound/tlv.h> -struct lm4857 { - struct regmap *regmap; - uint8_t mode; -}; - static const struct reg_default lm4857_default_regs[] = { { 0x0, 0x00 }, { 0x1, 0x00 }, @@ -46,66 +41,33 @@ static const struct reg_default lm4857_default_regs[] = { #define LM4857_WAKEUP 5 #define LM4857_EPGAIN 4 -static int lm4857_get_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); - - ucontrol->value.integer.value[0] = lm4857->mode; - - return 0; -} - -static int lm4857_set_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); - uint8_t value = ucontrol->value.integer.value[0]; - - lm4857->mode = value; - - if (codec->dapm.bias_level == SND_SOC_BIAS_ON) - regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, value + 6); - - return 1; -} - -static int lm4857_set_bias_level(struct snd_soc_codec *codec, - enum snd_soc_bias_level level) -{ - struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); - - switch (level) { - case SND_SOC_BIAS_ON: - regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, - lm4857->mode + 6); - break; - case SND_SOC_BIAS_STANDBY: - regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, 0); - break; - default: - break; - } - - codec->dapm.bias_level = level; - - return 0; -} +static const unsigned int lm4857_mode_values[] = { + 0, + 6, + 7, + 8, + 9, +}; -static const char *lm4857_mode[] = { +static const char * const lm4857_mode_texts[] = { + "Off", "Earpiece", "Loudspeaker", "Loudspeaker + Headphone", "Headphone", }; -static SOC_ENUM_SINGLE_EXT_DECL(lm4857_mode_enum, lm4857_mode); +static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(lm4857_mode_enum, + LM4857_CTRL, 0, 0xf, lm4857_mode_texts, lm4857_mode_values); + +static const struct snd_kcontrol_new lm4857_mode_ctrl = + SOC_DAPM_ENUM("Mode", lm4857_mode_enum); static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = { SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_DEMUX("Mode", SND_SOC_NOPM, 0, 0, &lm4857_mode_ctrl), + SND_SOC_DAPM_OUTPUT("LS"), SND_SOC_DAPM_OUTPUT("HP"), SND_SOC_DAPM_OUTPUT("EP"), @@ -127,24 +89,18 @@ static const struct snd_kcontrol_new lm4857_controls[] = { LM4857_WAKEUP, 1, 0), SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL, LM4857_EPGAIN, 1, 0), - - SOC_ENUM_EXT("Mode", lm4857_mode_enum, - lm4857_get_mode, lm4857_set_mode), }; -/* There is a demux between the input signal and the output signals. - * Currently there is no easy way to model it in ASoC and since it does not make - * much of a difference in practice simply connect the input direclty to the - * outputs. */ static const struct snd_soc_dapm_route lm4857_routes[] = { - {"LS", NULL, "IN"}, - {"HP", NULL, "IN"}, - {"EP", NULL, "IN"}, + { "Mode", NULL, "IN" }, + { "LS", "Loudspeaker", "Mode" }, + { "LS", "Loudspeaker + Headphone", "Mode" }, + { "HP", "Headphone", "Mode" }, + { "HP", "Loudspeaker + Headphone", "Mode" }, + { "EP", "Earpiece", "Mode" }, }; -static struct snd_soc_codec_driver soc_codec_dev_lm4857 = { - .set_bias_level = lm4857_set_bias_level, - +static struct snd_soc_component_driver lm4857_component_driver = { .controls = lm4857_controls, .num_controls = ARRAY_SIZE(lm4857_controls), .dapm_widgets = lm4857_dapm_widgets, @@ -167,25 +123,14 @@ static const struct regmap_config lm4857_regmap_config = { static int lm4857_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - struct lm4857 *lm4857; - - lm4857 = devm_kzalloc(&i2c->dev, sizeof(*lm4857), GFP_KERNEL); - if (!lm4857) - return -ENOMEM; - - i2c_set_clientdata(i2c, lm4857); - - lm4857->regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config); - if (IS_ERR(lm4857->regmap)) - return PTR_ERR(lm4857->regmap); + struct regmap *regmap; - return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0); -} + regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); -static int lm4857_i2c_remove(struct i2c_client *i2c) -{ - snd_soc_unregister_codec(&i2c->dev); - return 0; + return devm_snd_soc_register_component(&i2c->dev, + &lm4857_component_driver, NULL, 0); } static const struct i2c_device_id lm4857_i2c_id[] = { @@ -200,7 +145,6 @@ static struct i2c_driver lm4857_i2c_driver = { .owner = THIS_MODULE, }, .probe = lm4857_i2c_probe, - .remove = lm4857_i2c_remove, .id_table = lm4857_i2c_id, }; diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index c4dfde9..6600aa0 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -1271,7 +1271,7 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) regcache_sync(lm49453->regmap); snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG, @@ -1284,8 +1284,6 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 805b3f8..d0f4534 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1571,7 +1571,7 @@ static int max98088_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) regcache_sync(max98088->regmap); snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN, @@ -1584,7 +1584,6 @@ static int max98088_set_bias_level(struct snd_soc_codec *codec, regcache_mark_dirty(max98088->regmap); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 3e33ef2..679f0a0 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -1500,7 +1500,7 @@ static const struct snd_soc_dapm_route max98091_dapm_routes[] = { static int max98090_add_widgets(struct snd_soc_codec *codec) { struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); snd_soc_add_codec_controls(codec, max98090_snd_controls, ARRAY_SIZE(max98090_snd_controls)); @@ -1798,16 +1798,17 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec, * away from ON. Disable the clock in that case, otherwise * enable it. */ - if (!IS_ERR(max98090->mclk)) { - if (codec->dapm.bias_level == SND_SOC_BIAS_ON) - clk_disable_unprepare(max98090->mclk); - else - clk_prepare_enable(max98090->mclk); - } + if (IS_ERR(max98090->mclk)) + break; + + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) + clk_disable_unprepare(max98090->mclk); + else + clk_prepare_enable(max98090->mclk); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regcache_sync(max98090->regmap); if (ret != 0) { dev_err(codec->dev, @@ -1824,7 +1825,6 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec, regcache_mark_dirty(max98090->regmap); break; } - codec->dapm.bias_level = level; return 0; } @@ -2187,7 +2187,6 @@ static void max98090_jack_work(struct work_struct *work) struct max98090_priv, jack_work.work); struct snd_soc_codec *codec = max98090->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; int status = 0; int reg; @@ -2266,8 +2265,6 @@ static void max98090_jack_work(struct work_struct *work) snd_soc_jack_report(max98090->jack, status, SND_JACK_HEADSET | SND_JACK_BTN_0); - - snd_soc_dapm_sync(dapm); } static irqreturn_t max98090_interrupt(int irq, void *data) @@ -2422,6 +2419,8 @@ static int max98090_probe(struct snd_soc_codec *codec) struct max98090_cdata *cdata; enum max98090_type devtype; int ret = 0; + int err; + unsigned int micbias; dev_dbg(codec->dev, "max98090_probe\n"); @@ -2506,8 +2505,17 @@ static int max98090_probe(struct snd_soc_codec *codec) snd_soc_write(codec, M98090_REG_BIAS_CONTROL, M98090_VCM_MODE_MASK); + err = device_property_read_u32(codec->dev, "maxim,micbias", &micbias); + if (err) { + micbias = M98090_MBVSEL_2V8; + dev_info(codec->dev, "use default 2.8v micbias\n"); + } else if (micbias < M98090_MBVSEL_2V2 || micbias > M98090_MBVSEL_2V8) { + dev_err(codec->dev, "micbias out of range 0x%x\n", micbias); + micbias = M98090_MBVSEL_2V8; + } + snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE, - M98090_MBVSEL_MASK, M98090_MBVSEL_2V8); + M98090_MBVSEL_MASK, micbias); max98090_add_widgets(codec); diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 8fba0c3..9a46d3d 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1650,16 +1650,17 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec, * away from ON. Disable the clock in that case, otherwise * enable it. */ - if (!IS_ERR(max98095->mclk)) { - if (codec->dapm.bias_level == SND_SOC_BIAS_ON) - clk_disable_unprepare(max98095->mclk); - else - clk_prepare_enable(max98095->mclk); - } + if (IS_ERR(max98095->mclk)) + break; + + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) + clk_disable_unprepare(max98095->mclk); + else + clk_prepare_enable(max98095->mclk); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regcache_sync(max98095->regmap); if (ret != 0) { @@ -1678,7 +1679,6 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec, regcache_mark_dirty(max98095->regmap); break; } - codec->dapm.bias_level = level; return 0; } @@ -2198,7 +2198,7 @@ static int max98095_suspend(struct snd_soc_codec *codec) if (max98095->headphone_jack || max98095->mic_jack) max98095_jack_detect_disable(codec); - max98095_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -2208,7 +2208,7 @@ static int max98095_resume(struct snd_soc_codec *codec) struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); struct i2c_client *client = to_i2c_client(codec->dev); - max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); if (max98095->headphone_jack || max98095->mic_jack) { max98095_jack_detect_enable(codec); @@ -2301,8 +2301,8 @@ static int max98095_probe(struct snd_soc_codec *codec) /* register an audio interrupt */ ret = request_threaded_irq(client->irq, NULL, max98095_report_jack, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - "max98095", codec); + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "max98095", codec); if (ret) { dev_err(codec->dev, "Failed to request IRQ: %d\n", ret); goto err_access; diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c index bf3e933..3a2fda0 100644 --- a/sound/soc/codecs/max98357a.c +++ b/sound/soc/codecs/max98357a.c @@ -60,13 +60,12 @@ static int max98357a_codec_probe(struct snd_soc_codec *codec) { struct gpio_desc *sdmode; - sdmode = devm_gpiod_get(codec->dev, "sdmode"); + sdmode = devm_gpiod_get(codec->dev, "sdmode", GPIOD_OUT_LOW); if (IS_ERR(sdmode)) { dev_err(codec->dev, "%s() unable to get sdmode GPIO: %ld\n", __func__, PTR_ERR(sdmode)); return PTR_ERR(sdmode); } - gpiod_direction_output(sdmode, 0); snd_soc_codec_set_drvdata(codec, sdmode); return 0; diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index 10f8e47..481d58f1 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -252,7 +252,7 @@ static int max9850_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regcache_sync(max9850->regmap); if (ret) { dev_err(codec->dev, @@ -264,7 +264,6 @@ static int max9850_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_OFF: break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c index 9b5a17d..aad6642 100644 --- a/sound/soc/codecs/max98925.c +++ b/sound/soc/codecs/max98925.c @@ -346,7 +346,7 @@ static int max98925_dai_set_fmt(struct snd_soc_dai *codec_dai, } regmap_update_bits(max98925->regmap, MAX98925_FORMAT, - M98925_DAI_BCI_MASK, invert); + M98925_DAI_BCI_MASK | M98925_DAI_WCI_MASK, invert); return 0; } diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index 2ffb9a0..3d44fc5 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -623,14 +623,14 @@ static int mc13783_probe(struct snd_soc_codec *codec) AUDIO_SSI_SEL, 0); else mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC, - 0, AUDIO_SSI_SEL); + AUDIO_SSI_SEL, AUDIO_SSI_SEL); if (priv->dac_ssi_port == MC13783_SSI1_PORT) mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC, AUDIO_SSI_SEL, 0); else mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC, - 0, AUDIO_SSI_SEL); + AUDIO_SSI_SEL, AUDIO_SSI_SEL); return 0; } diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 711f550..62dda24 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -523,7 +523,7 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: /* VMID ON */ - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG, ML26124_VMID, ML26124_VMID); msleep(500); @@ -536,7 +536,6 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec, ML26124_VMID, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index e12764d..de16429 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -242,7 +242,7 @@ static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_STANDBY: break; @@ -270,7 +270,7 @@ static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_STANDBY: break; @@ -298,7 +298,7 @@ static int pcm512x_overclock_dac_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_STANDBY: break; @@ -641,8 +641,6 @@ static int pcm512x_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 0fcda35..c6cca06 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -301,6 +301,7 @@ static int rt286_support_power_controls[] = { static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) { + struct snd_soc_dapm_context *dapm; unsigned int val, buf; *hp = false; @@ -308,6 +309,9 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) if (!rt286->codec) return -EINVAL; + + dapm = snd_soc_codec_get_dapm(rt286->codec); + if (rt286->pdata.cbj_en) { regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf); *hp = buf & 0x80000000; @@ -316,14 +320,11 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) regmap_update_bits(rt286->regmap, RT286_DC_GAIN, 0x200, 0x200); - snd_soc_dapm_force_enable_pin(&rt286->codec->dapm, - "HV"); - snd_soc_dapm_force_enable_pin(&rt286->codec->dapm, - "VREF"); + snd_soc_dapm_force_enable_pin(dapm, "HV"); + snd_soc_dapm_force_enable_pin(dapm, "VREF"); /* power LDO1 */ - snd_soc_dapm_force_enable_pin(&rt286->codec->dapm, - "LDO1"); - snd_soc_dapm_sync(&rt286->codec->dapm); + snd_soc_dapm_force_enable_pin(dapm, "LDO1"); + snd_soc_dapm_sync(dapm); regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24); msleep(50); @@ -360,11 +361,11 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) *mic = buf & 0x80000000; } - snd_soc_dapm_disable_pin(&rt286->codec->dapm, "HV"); - snd_soc_dapm_disable_pin(&rt286->codec->dapm, "VREF"); + snd_soc_dapm_disable_pin(dapm, "HV"); + snd_soc_dapm_disable_pin(dapm, "VREF"); if (!*hp) - snd_soc_dapm_disable_pin(&rt286->codec->dapm, "LDO1"); - snd_soc_dapm_sync(&rt286->codec->dapm); + snd_soc_dapm_disable_pin(dapm, "LDO1"); + snd_soc_dapm_sync(dapm); return 0; } @@ -391,6 +392,7 @@ static void rt286_jack_detect_work(struct work_struct *work) int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); rt286->jack = jack; @@ -398,7 +400,7 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) if (jack) { /* enable IRQ */ if (rt286->jack->status & SND_JACK_HEADPHONE) - snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO1"); + snd_soc_dapm_force_enable_pin(dapm, "LDO1"); regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2); /* Send an initial empty report */ snd_soc_jack_report(rt286->jack, rt286->jack->status, @@ -406,9 +408,9 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) } else { /* disable IRQ */ regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x0); - snd_soc_dapm_disable_pin(&codec->dapm, "LDO1"); + snd_soc_dapm_disable_pin(dapm, "LDO1"); } - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); return 0; } @@ -985,7 +987,7 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec, { switch (level) { case SND_SOC_BIAS_PREPARE: - if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { + if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) { snd_soc_write(codec, RT286_SET_AUDIO_POWER, AC_PWRST_D0); snd_soc_update_bits(codec, @@ -1012,7 +1014,6 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index 2c10d77..058167c 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1546,7 +1546,7 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3, RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS, RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS); @@ -1569,7 +1569,6 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } @@ -1615,7 +1614,7 @@ static int rt5631_probe(struct snd_soc_codec *codec) RT5631_DMIC_R_CH_LATCH_RISING); } - codec->dapm.bias_level = SND_SOC_BIAS_STANDBY; + snd_soc_codec_init_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; } diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 178e55d..f40752a 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1870,7 +1870,7 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec, { switch (level) { case SND_SOC_BIAS_STANDBY: - if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) { + if (SND_SOC_BIAS_OFF == snd_soc_codec_get_bias_level(codec)) { snd_soc_update_bits(codec, RT5640_PWR_ANLG1, RT5640_PWR_VREF1 | RT5640_PWR_MB | RT5640_PWR_BG | RT5640_PWR_VREF2, @@ -1902,7 +1902,6 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } @@ -1935,11 +1934,12 @@ EXPORT_SYMBOL_GPL(rt5640_dmic_enable); static int rt5640_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); rt5640->codec = codec; - rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301); snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030); @@ -1951,18 +1951,18 @@ static int rt5640_probe(struct snd_soc_codec *codec) snd_soc_add_codec_controls(codec, rt5640_specific_snd_controls, ARRAY_SIZE(rt5640_specific_snd_controls)); - snd_soc_dapm_new_controls(&codec->dapm, + snd_soc_dapm_new_controls(dapm, rt5640_specific_dapm_widgets, ARRAY_SIZE(rt5640_specific_dapm_widgets)); - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, rt5640_specific_dapm_routes, ARRAY_SIZE(rt5640_specific_dapm_routes)); break; case RT5640_ID_5639: - snd_soc_dapm_new_controls(&codec->dapm, + snd_soc_dapm_new_controls(dapm, rt5639_specific_dapm_widgets, ARRAY_SIZE(rt5639_specific_dapm_widgets)); - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, rt5639_specific_dapm_routes, ARRAY_SIZE(rt5639_specific_dapm_routes)); break; @@ -1991,7 +1991,7 @@ static int rt5640_suspend(struct snd_soc_codec *codec) { struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); - rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); rt5640_reset(codec); regcache_cache_only(rt5640->regmap, true); regcache_mark_dirty(rt5640->regmap); diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 1e207cc..a949b77 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -443,9 +443,9 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), /* Headphone Output Volume */ - SOC_DOUBLE("HP Channel Switch", RT5645_HP_VOL, + SOC_DOUBLE("Headphone Channel Switch", RT5645_HP_VOL, RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1), - SOC_DOUBLE_TLV("HP Playback Volume", RT5645_HP_VOL, + SOC_DOUBLE_TLV("Headphone Playback Volume", RT5645_HP_VOL, RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), /* OUTPUT Control */ @@ -2681,7 +2681,6 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } @@ -3069,7 +3068,7 @@ static int rt5645_probe(struct snd_soc_codec *codec) break; } - rt5645_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); /* for JD function */ if (rt5645->pdata.jd_mode) { @@ -3424,6 +3423,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, } } + INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work); + if (rt5645->i2c->irq) { ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING @@ -3442,8 +3443,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n"); } - INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work); - return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645, rt5645_dai, ARRAY_SIZE(rt5645_dai)); } diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index 9f4c7be..a3506e1 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -1571,7 +1571,7 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec, { switch (level) { case SND_SOC_BIAS_PREPARE: - if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { + if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) { snd_soc_update_bits(codec, RT5651_PWR_ANLG1, RT5651_PWR_VREF1 | RT5651_PWR_MB | RT5651_PWR_BG | RT5651_PWR_VREF2, @@ -1604,7 +1604,6 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } @@ -1625,7 +1624,7 @@ static int rt5651_probe(struct snd_soc_codec *codec) RT5651_PWR_FV1 | RT5651_PWR_FV2, RT5651_PWR_FV1 | RT5651_PWR_FV2); - rt5651_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index cc7f84a..840dd6d 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -416,12 +416,12 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg) static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert) { int val; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); if (jack_insert) { - snd_soc_dapm_force_enable_pin(&codec->dapm, - "Mic Det Power"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0); snd_soc_update_bits(codec, RT5670_CJ_CTRL2, RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD, @@ -447,15 +447,15 @@ static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert) } else { snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4); rt5670->jack_type = SND_JACK_HEADPHONE; - snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); } } else { snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x0); snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4); rt5670->jack_type = 0; - snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); } return rt5670->jack_type; @@ -2603,7 +2603,7 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_PREPARE: - if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { + if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) { snd_soc_update_bits(codec, RT5670_PWR_ANLG1, RT5670_PWR_VREF1 | RT5670_PWR_MB | RT5670_PWR_BG | RT5670_PWR_VREF2, @@ -2647,30 +2647,30 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } static int rt5670_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); switch (snd_soc_read(codec, RT5670_RESET) & RT5670_ID_MASK) { case RT5670_ID_5670: case RT5670_ID_5671: - snd_soc_dapm_new_controls(&codec->dapm, + snd_soc_dapm_new_controls(dapm, rt5670_specific_dapm_widgets, ARRAY_SIZE(rt5670_specific_dapm_widgets)); - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, rt5670_specific_dapm_routes, ARRAY_SIZE(rt5670_specific_dapm_routes)); break; case RT5670_ID_5672: - snd_soc_dapm_new_controls(&codec->dapm, + snd_soc_dapm_new_controls(dapm, rt5672_specific_dapm_widgets, ARRAY_SIZE(rt5672_specific_dapm_widgets)); - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, rt5672_specific_dapm_routes, ARRAY_SIZE(rt5672_specific_dapm_routes)); break; diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index af18258..31d969a 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -62,6 +62,9 @@ static const struct reg_default init_list[] = { {RT5677_PR_BASE + 0x1e, 0x0000}, {RT5677_PR_BASE + 0x12, 0x0eaa}, {RT5677_PR_BASE + 0x14, 0x018a}, + {RT5677_PR_BASE + 0x15, 0x0490}, + {RT5677_PR_BASE + 0x38, 0x0f71}, + {RT5677_PR_BASE + 0x39, 0x0f71}, }; #define RT5677_INIT_REG_LEN ARRAY_SIZE(init_list) @@ -817,7 +820,7 @@ static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol, rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0]; - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) rt5677_set_dsp_vad(codec, rt5677->dsp_vad_en); return 0; @@ -914,7 +917,7 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); - int idx = rl6231_calc_dmic_clk(rt5677->sysclk); + int idx = rl6231_calc_dmic_clk(rt5677->lrck[RT5677_AIF1] << 8); if (idx < 0) dev_err(codec->dev, "Failed to set DMIC clock\n"); @@ -1057,6 +1060,7 @@ int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec, unsigned int asrc5_mask = 0, asrc5_value = 0; unsigned int asrc6_mask = 0, asrc6_value = 0; unsigned int asrc7_mask = 0, asrc7_value = 0; + unsigned int asrc8_mask = 0, asrc8_value = 0; switch (clk_src) { case RT5677_CLK_SEL_SYS: @@ -1193,10 +1197,108 @@ int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec, regmap_update_bits(rt5677->regmap, RT5677_ASRC_7, asrc7_mask, asrc7_value); + /* ASRC 8 */ + if (filter_mask & RT5677_I2S1_SOURCE) { + asrc8_mask |= RT5677_I2S1_CLK_SEL_MASK; + asrc8_value = (asrc8_value & ~RT5677_I2S1_CLK_SEL_MASK) + | ((clk_src - 1) << RT5677_I2S1_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_I2S2_SOURCE) { + asrc8_mask |= RT5677_I2S2_CLK_SEL_MASK; + asrc8_value = (asrc8_value & ~RT5677_I2S2_CLK_SEL_MASK) + | ((clk_src - 1) << RT5677_I2S2_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_I2S3_SOURCE) { + asrc8_mask |= RT5677_I2S3_CLK_SEL_MASK; + asrc8_value = (asrc8_value & ~RT5677_I2S3_CLK_SEL_MASK) + | ((clk_src - 1) << RT5677_I2S3_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_I2S4_SOURCE) { + asrc8_mask |= RT5677_I2S4_CLK_SEL_MASK; + asrc8_value = (asrc8_value & ~RT5677_I2S4_CLK_SEL_MASK) + | ((clk_src - 1) << RT5677_I2S4_CLK_SEL_SFT); + } + + if (asrc8_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_8, asrc8_mask, + asrc8_value); + return 0; } EXPORT_SYMBOL_GPL(rt5677_sel_asrc_clk_src); +static int rt5677_dmic_use_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + unsigned int asrc_setting; + + switch (source->shift) { + case 11: + regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_STO1_CLK_SEL_MASK) >> + RT5677_AD_STO1_CLK_SEL_SFT; + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + break; + + case 10: + regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_STO2_CLK_SEL_MASK) >> + RT5677_AD_STO2_CLK_SEL_SFT; + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + break; + + case 9: + regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_STO3_CLK_SEL_MASK) >> + RT5677_AD_STO3_CLK_SEL_SFT; + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + break; + + case 8: + regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_STO4_CLK_SEL_MASK) >> + RT5677_AD_STO4_CLK_SEL_SFT; + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + break; + + case 7: + regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_MONOL_CLK_SEL_MASK) >> + RT5677_AD_MONOL_CLK_SEL_SFT; + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + break; + + case 6: + regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_MONOR_CLK_SEL_MASK) >> + RT5677_AD_MONOR_CLK_SEL_SFT; + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + break; + + default: + break; + } + + return 0; +} + /* Digital Mixer */ static const struct snd_kcontrol_new rt5677_sto1_adc_l_mix[] = { SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO1_ADC_MIXER, @@ -2476,7 +2578,7 @@ static int rt5677_vref_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: - if (codec->dapm.bias_level != SND_SOC_BIAS_ON && + if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON && !rt5677->is_vref_slow) { mdelay(20); regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, @@ -3054,12 +3156,12 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { }; static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { - { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", can_use_asrc }, - { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", can_use_asrc }, - { "Stereo3 DMIC Mux", NULL, "DMIC STO3 ASRC", can_use_asrc }, - { "Stereo4 DMIC Mux", NULL, "DMIC STO4 ASRC", can_use_asrc }, - { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", can_use_asrc }, - { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", can_use_asrc }, + { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", rt5677_dmic_use_asrc }, + { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", rt5677_dmic_use_asrc }, + { "Stereo3 DMIC Mux", NULL, "DMIC STO3 ASRC", rt5677_dmic_use_asrc }, + { "Stereo4 DMIC Mux", NULL, "DMIC STO4 ASRC", rt5677_dmic_use_asrc }, + { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", rt5677_dmic_use_asrc }, + { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", rt5677_dmic_use_asrc }, { "I2S1", NULL, "I2S1 ASRC", can_use_asrc}, { "I2S2", NULL, "I2S2 ASRC", can_use_asrc}, { "I2S3", NULL, "I2S3 ASRC", can_use_asrc}, @@ -4350,7 +4452,7 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) { rt5677_set_dsp_vad(codec, false); regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, @@ -4392,7 +4494,6 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } @@ -4603,22 +4704,23 @@ static void rt5677_free_gpio(struct i2c_client *i2c) static int rt5677_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); int i; rt5677->codec = codec; if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) { - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, rt5677_dmic2_clk_2, ARRAY_SIZE(rt5677_dmic2_clk_2)); } else { /*use dmic1 clock by default*/ - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, rt5677_dmic2_clk_1, ARRAY_SIZE(rt5677_dmic2_clk_1)); } - rt5677_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020); regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00); @@ -4664,6 +4766,8 @@ static int rt5677_remove(struct snd_soc_codec *codec) regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec); if (gpio_is_valid(rt5677->pow_ldo2)) gpio_set_value_cansleep(rt5677->pow_ldo2, 0); + if (gpio_is_valid(rt5677->reset_pin)) + gpio_set_value_cansleep(rt5677->reset_pin, 0); return 0; } @@ -4679,6 +4783,8 @@ static int rt5677_suspend(struct snd_soc_codec *codec) if (gpio_is_valid(rt5677->pow_ldo2)) gpio_set_value_cansleep(rt5677->pow_ldo2, 0); + if (gpio_is_valid(rt5677->reset_pin)) + gpio_set_value_cansleep(rt5677->reset_pin, 0); } return 0; @@ -4689,10 +4795,13 @@ static int rt5677_resume(struct snd_soc_codec *codec) struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); if (!rt5677->dsp_vad_en) { - if (gpio_is_valid(rt5677->pow_ldo2)) { + if (gpio_is_valid(rt5677->pow_ldo2)) gpio_set_value_cansleep(rt5677->pow_ldo2, 1); + if (gpio_is_valid(rt5677->reset_pin)) + gpio_set_value_cansleep(rt5677->reset_pin, 1); + if (gpio_is_valid(rt5677->pow_ldo2) || + gpio_is_valid(rt5677->reset_pin)) msleep(10); - } regcache_cache_only(rt5677->regmap, false); regcache_sync(rt5677->regmap); @@ -4930,6 +5039,8 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np) rt5677->pow_ldo2 = of_get_named_gpio(np, "realtek,pow-ldo2-gpio", 0); + rt5677->reset_pin = of_get_named_gpio(np, + "realtek,reset-gpio", 0); /* * POW_LDO2 is optional (it may be statically tied on the board). @@ -4940,6 +5051,9 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np) if (!gpio_is_valid(rt5677->pow_ldo2) && (rt5677->pow_ldo2 != -ENOENT)) return rt5677->pow_ldo2; + if (!gpio_is_valid(rt5677->reset_pin) && + (rt5677->reset_pin != -ENOENT)) + return rt5677->reset_pin; of_property_read_u8_array(np, "realtek,gpio-config", rt5677->pdata.gpio_config, RT5677_GPIO_NUM); @@ -5041,6 +5155,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, } } else { rt5677->pow_ldo2 = -EINVAL; + rt5677->reset_pin = -EINVAL; } if (gpio_is_valid(rt5677->pow_ldo2)) { @@ -5052,6 +5167,21 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, rt5677->pow_ldo2, ret); return ret; } + } + + if (gpio_is_valid(rt5677->reset_pin)) { + ret = devm_gpio_request_one(&i2c->dev, rt5677->reset_pin, + GPIOF_OUT_INIT_HIGH, + "RT5677 RESET"); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request RESET %d: %d\n", + rt5677->reset_pin, ret); + return ret; + } + } + + if (gpio_is_valid(rt5677->pow_ldo2) || + gpio_is_valid(rt5677->reset_pin)) { /* Wait a while until I2C bus becomes available. The datasheet * does not specify the exact we should wait but startup * sequence mentiones at least a few milliseconds. diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 9dceb41..7eca38a 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1446,6 +1446,16 @@ #define RT5677_DSP_OB_4_7_CLK_SEL_MASK (0xf << 8) #define RT5677_DSP_OB_4_7_CLK_SEL_SFT 8 +/* ASRC Control 8 (0x8a) */ +#define RT5677_I2S1_CLK_SEL_MASK (0xf << 12) +#define RT5677_I2S1_CLK_SEL_SFT 12 +#define RT5677_I2S2_CLK_SEL_MASK (0xf << 8) +#define RT5677_I2S2_CLK_SEL_SFT 8 +#define RT5677_I2S3_CLK_SEL_MASK (0xf << 4) +#define RT5677_I2S3_CLK_SEL_SFT 4 +#define RT5677_I2S4_CLK_SEL_MASK (0xf) +#define RT5677_I2S4_CLK_SEL_SFT 0 + /* VAD Function Control 4 (0x9f) */ #define RT5677_VAD_SRC_MASK (0x7 << 8) #define RT5677_VAD_SRC_SFT 8 @@ -1744,6 +1754,10 @@ enum { RT5677_AD_MONO_R_FILTER = (0x1 << 12), RT5677_DSP_OB_0_3_FILTER = (0x1 << 13), RT5677_DSP_OB_4_7_FILTER = (0x1 << 14), + RT5677_I2S1_SOURCE = (0x1 << 15), + RT5677_I2S2_SOURCE = (0x1 << 16), + RT5677_I2S3_SOURCE = (0x1 << 17), + RT5677_I2S4_SOURCE = (0x1 << 18), }; struct rt5677_priv { @@ -1762,6 +1776,7 @@ struct rt5677_priv { int pll_in; int pll_out; int pow_ldo2; /* POW_LDO2 pin */ + int reset_pin; /* RESET pin */ enum rt5677_type type; #ifdef CONFIG_GPIOLIB struct gpio_chip gpio_chip; diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 3593a14..e673f6c 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -948,7 +948,7 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable( ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); @@ -979,7 +979,6 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -1092,6 +1091,19 @@ static bool sgtl5000_readable(struct device *dev, unsigned int reg) } /* + * This precalculated table contains all (vag_val * 100 / lo_calcntrl) results + * to select an appropriate lo_vol_* in SGTL5000_CHIP_LINE_OUT_VOL + * The calculatation was done for all possible register values which + * is the array index and the following formula: 10^((idx−15)/40) * 100 + */ +static const u8 vol_quot_table[] = { + 42, 45, 47, 50, 53, 56, 60, 63, + 67, 71, 75, 79, 84, 89, 94, 100, + 106, 112, 119, 126, 133, 141, 150, 158, + 168, 178, 188, 200, 211, 224, 237, 251 +}; + +/* * sgtl5000 has 3 internal power supplies: * 1. VAG, normally set to vdda/2 * 2. charge pump, set to different value @@ -1111,6 +1123,10 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) u16 ana_pwr; u16 lreg_ctrl; int vag; + int lo_vag; + int vol_quot; + int lo_vol; + size_t i; struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); vdda = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer); @@ -1198,23 +1214,45 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) SGTL5000_ANA_GND_MASK, vag << SGTL5000_ANA_GND_SHIFT); /* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */ - vag = vddio / 2; - if (vag <= SGTL5000_LINE_OUT_GND_BASE) - vag = 0; - else if (vag >= SGTL5000_LINE_OUT_GND_BASE + + lo_vag = vddio / 2; + if (lo_vag <= SGTL5000_LINE_OUT_GND_BASE) + lo_vag = 0; + else if (lo_vag >= SGTL5000_LINE_OUT_GND_BASE + SGTL5000_LINE_OUT_GND_STP * SGTL5000_LINE_OUT_GND_MAX) - vag = SGTL5000_LINE_OUT_GND_MAX; + lo_vag = SGTL5000_LINE_OUT_GND_MAX; else - vag = (vag - SGTL5000_LINE_OUT_GND_BASE) / + lo_vag = (lo_vag - SGTL5000_LINE_OUT_GND_BASE) / SGTL5000_LINE_OUT_GND_STP; snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL, SGTL5000_LINE_OUT_CURRENT_MASK | SGTL5000_LINE_OUT_GND_MASK, - vag << SGTL5000_LINE_OUT_GND_SHIFT | + lo_vag << SGTL5000_LINE_OUT_GND_SHIFT | SGTL5000_LINE_OUT_CURRENT_360u << SGTL5000_LINE_OUT_CURRENT_SHIFT); + /* + * Set lineout output level in range (0..31) + * the same value is used for right and left channel + * + * Searching for a suitable index solving this formula: + * idx = 40 * log10(vag_val / lo_cagcntrl) + 15 + */ + vol_quot = (vag * 100) / lo_vag; + lo_vol = 0; + for (i = 0; i < ARRAY_SIZE(vol_quot_table); i++) { + if (vol_quot >= vol_quot_table[i]) + lo_vol = i; + else + break; + } + + snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_VOL, + SGTL5000_LINE_OUT_VOL_RIGHT_MASK | + SGTL5000_LINE_OUT_VOL_LEFT_MASK, + lo_vol << SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT | + lo_vol << SGTL5000_LINE_OUT_VOL_LEFT_SHIFT); + return 0; } diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c index 0a8e43c..29cb442 100644 --- a/sound/soc/codecs/sirf-audio-codec.c +++ b/sound/soc/codecs/sirf-audio-codec.c @@ -395,7 +395,7 @@ struct snd_soc_dai_driver sirf_audio_codec_dai = { static int sirf_audio_codec_probe(struct snd_soc_codec *codec) { - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); pm_runtime_enable(codec->dev); diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index 7947c0e..3a7de01 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -194,7 +194,7 @@ static int sn95031_set_vaud_bias(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) { pr_debug("vaud_bias powering up pll\n"); /* power up the pll */ snd_soc_write(codec, SN95031_AUDPLLCTRL, BIT(5)); @@ -205,17 +205,22 @@ static int sn95031_set_vaud_bias(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + switch (snd_soc_codec_get_bias_level(codec)) { + case SND_SOC_BIAS_OFF: pr_debug("vaud_bias power up rail\n"); /* power up the rail */ snd_soc_write(codec, SN95031_VAUD, BIT(2)|BIT(1)|BIT(0)); msleep(1); - } else if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) { + break; + case SND_SOC_BIAS_PREPARE: /* turn off pcm */ pr_debug("vaud_bias power dn pcm\n"); snd_soc_update_bits(codec, SN95031_PCM2C2, BIT(0), 0); snd_soc_write(codec, SN95031_AUDPLLCTRL, 0); + break; + default: + break; } break; @@ -226,7 +231,6 @@ static int sn95031_set_vaud_bias(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index 67ea55a..f30de76 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -510,7 +510,7 @@ static int ssm2518_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) ret = ssm2518_set_power(ssm2518, true); break; case SND_SOC_BIAS_OFF: @@ -518,12 +518,7 @@ static int ssm2518_set_bias_level(struct snd_soc_codec *codec, break; } - if (ret) - return ret; - - codec->dapm.bias_level = level; - - return 0; + return ret; } static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 314eaec..69a773a 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -473,7 +473,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -524,8 +523,8 @@ static int ssm2602_resume(struct snd_soc_codec *codec) static int ssm2602_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; regmap_update_bits(ssm2602->regmap, SSM2602_LOUT1V, @@ -549,7 +548,7 @@ static int ssm2602_codec_probe(struct snd_soc_codec *codec) static int ssm2604_codec_probe(struct snd_soc_codec *codec) { - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int ret; ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets, diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index a984485..938d2cb 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -353,7 +353,7 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) ret = ssm4567_set_power(ssm4567, true); break; case SND_SOC_BIAS_OFF: @@ -361,12 +361,7 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec, break; } - if (ret) - return ret; - - codec->dapm.bias_level = level; - - return 0; + return ret; } static const struct snd_soc_dai_ops ssm4567_dai_ops = { diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 007a0e3..60eff36 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -819,7 +819,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); if (ret != 0) { @@ -854,7 +854,6 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec, sta32x->supplies); break; } - codec->dapm.bias_level = level; return 0; } @@ -970,7 +969,7 @@ static int sta32x_probe(struct snd_soc_codec *codec) if (sta32x->pdata->needs_esd_watchdog) INIT_DELAYED_WORK(&sta32x->watchdog_work, sta32x_watchdog); - sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Bias level configuration will have done an extra enable */ regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); @@ -1096,16 +1095,10 @@ static int sta32x_i2c_probe(struct i2c_client *i2c, #endif /* GPIOs */ - sta32x->gpiod_nreset = devm_gpiod_get(dev, "reset"); - if (IS_ERR(sta32x->gpiod_nreset)) { - ret = PTR_ERR(sta32x->gpiod_nreset); - if (ret != -ENOENT && ret != -ENOSYS) - return ret; - - sta32x->gpiod_nreset = NULL; - } else { - gpiod_direction_output(sta32x->gpiod_nreset, 0); - } + sta32x->gpiod_nreset = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(sta32x->gpiod_nreset)) + return PTR_ERR(sta32x->gpiod_nreset); /* regulators */ for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++) diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index 669e322..bd819a3 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -853,7 +853,7 @@ static int sta350_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable( ARRAY_SIZE(sta350->supplies), sta350->supplies); @@ -890,7 +890,6 @@ static int sta350_set_bias_level(struct snd_soc_codec *codec, sta350->supplies); break; } - codec->dapm.bias_level = level; return 0; } @@ -1037,7 +1036,7 @@ static int sta350_probe(struct snd_soc_codec *codec) sta350->coef_shadow[60] = 0x400000; sta350->coef_shadow[61] = 0x400000; - sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Bias level configuration will have done an extra enable */ regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies); @@ -1218,8 +1217,8 @@ static int sta350_i2c_probe(struct i2c_client *i2c, if (IS_ERR(sta350->gpiod_nreset)) return PTR_ERR(sta350->gpiod_nreset); - sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down", - GPIOD_OUT_LOW); + sta350->gpiod_power_down = devm_gpiod_get_optional(dev, "power-down", + GPIOD_OUT_LOW); if (IS_ERR(sta350->gpiod_power_down)) return PTR_ERR(sta350->gpiod_power_down); diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index b0f436d..4f70378 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -165,7 +165,7 @@ static int sta529_set_bias_level(struct snd_soc_codec *codec, enum FFX_CLK_ENB); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) regcache_sync(sta529->regmap); snd_soc_update_bits(codec, STA529_FFXCFG0, POWER_CNTLMSAK, POWER_STDBY); @@ -179,12 +179,6 @@ static int sta529_set_bias_level(struct snd_soc_codec *codec, enum break; } - /* - * store the label for powers down audio subsystem for suspend.This is - * used by soc core layer - */ - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index 6464caf..ed4cca7 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -236,7 +236,6 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec, stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff); break; } - codec->dapm.bias_level = level; return 0; } @@ -321,7 +320,7 @@ static struct snd_soc_dai_driver stac9766_dai[] = { .channels_max = 2, .rates = SNDRV_PCM_RATE_32000 | \ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE, + .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE, }, /* alsa ops */ .ops = &stac9766_dai_ops_digital, diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index dfb4ff5..891e2c5 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -34,6 +34,7 @@ #include <sound/soc-dapm.h> #include <sound/tlv.h> #include <sound/tas2552-plat.h> +#include <dt-bindings/sound/tas2552.h> #include "tas2552.h" @@ -45,7 +46,7 @@ static struct reg_default tas2552_reg_defs[] = { {TAS2552_PDM_CFG, 0x01}, {TAS2552_PGA_GAIN, 0x00}, {TAS2552_BOOST_PT_CTRL, 0x0f}, - {TAS2552_RESERVED_0D, 0x00}, + {TAS2552_RESERVED_0D, 0xbe}, {TAS2552_LIMIT_RATE_HYS, 0x08}, {TAS2552_CFG_2, 0xef}, {TAS2552_SER_CTRL_1, 0x00}, @@ -75,20 +76,45 @@ struct tas2552_data { struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES]; struct gpio_desc *enable_gpio; unsigned char regs[TAS2552_VBAT_DATA]; - unsigned int mclk; -}; + unsigned int pll_clkin; + unsigned int pdm_clk; -/* Input mux controls */ -static const char *tas2552_input_texts[] = { - "Digital", "Analog" + unsigned int dai_fmt; + unsigned int tdm_delay; }; +static int tas2552_post_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_POST_PMU: + snd_soc_write(codec, TAS2552_RESERVED_0D, 0xc0); + snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5), + (1 << 5)); + snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 0); + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS, 0); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS, + TAS2552_SWS); + snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 1); + snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5), 0); + snd_soc_write(codec, TAS2552_RESERVED_0D, 0xbe); + break; + } + return 0; +} + +/* Input mux controls */ +static const char * const tas2552_input_texts[] = { + "Digital", "Analog" }; static SOC_ENUM_SINGLE_DECL(tas2552_input_mux_enum, TAS2552_CFG_3, 7, tas2552_input_texts); -static const struct snd_kcontrol_new tas2552_input_mux_control[] = { - SOC_DAPM_ENUM("Input selection", tas2552_input_mux_enum) -}; +static const struct snd_kcontrol_new tas2552_input_mux_control = + SOC_DAPM_ENUM("Route", tas2552_input_mux_enum); static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] = { @@ -96,12 +122,13 @@ static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] = /* MUX Controls */ SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0, - tas2552_input_mux_control), + &tas2552_input_mux_control), SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0), + SND_SOC_DAPM_POST("Post Event", tas2552_post_event), SND_SOC_DAPM_OUTPUT("OUT") }; @@ -118,15 +145,16 @@ static const struct snd_soc_dapm_route tas2552_audio_map[] = { #ifdef CONFIG_PM static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown) { - u8 cfg1_reg; + u8 cfg1_reg = 0; + + if (!tas_data->codec) + return; if (sw_shutdown) - cfg1_reg = 0; - else - cfg1_reg = TAS2552_SWS_MASK; + cfg1_reg = TAS2552_SWS; - snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, - TAS2552_SWS_MASK, cfg1_reg); + snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, TAS2552_SWS, + cfg1_reg); } #endif @@ -138,15 +166,92 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream, struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); int sample_rate, pll_clk; int d; + int cpf; u8 p, j; + u8 ser_ctrl1_reg, wclk_rate; - if (!tas2552->mclk) + switch (params_width(params)) { + case 16: + ser_ctrl1_reg = TAS2552_WORDLENGTH_16BIT; + cpf = 32 + tas2552->tdm_delay; + break; + case 20: + ser_ctrl1_reg = TAS2552_WORDLENGTH_20BIT; + cpf = 64 + tas2552->tdm_delay; + break; + case 24: + ser_ctrl1_reg = TAS2552_WORDLENGTH_24BIT; + cpf = 64 + tas2552->tdm_delay; + break; + case 32: + ser_ctrl1_reg = TAS2552_WORDLENGTH_32BIT; + cpf = 64 + tas2552->tdm_delay; + break; + default: + dev_err(codec->dev, "Not supported sample size: %d\n", + params_width(params)); + return -EINVAL; + } + + if (cpf <= 32) + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_32; + else if (cpf <= 64) + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_64; + else if (cpf <= 128) + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_128; + else + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_256; + + snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, + TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK, + ser_ctrl1_reg); + + switch (params_rate(params)) { + case 8000: + wclk_rate = TAS2552_WCLK_FREQ_8KHZ; + break; + case 11025: + case 12000: + wclk_rate = TAS2552_WCLK_FREQ_11_12KHZ; + break; + case 16000: + wclk_rate = TAS2552_WCLK_FREQ_16KHZ; + break; + case 22050: + case 24000: + wclk_rate = TAS2552_WCLK_FREQ_22_24KHZ; + break; + case 32000: + wclk_rate = TAS2552_WCLK_FREQ_32KHZ; + break; + case 44100: + case 48000: + wclk_rate = TAS2552_WCLK_FREQ_44_48KHZ; + break; + case 88200: + case 96000: + wclk_rate = TAS2552_WCLK_FREQ_88_96KHZ; + break; + case 176400: + case 192000: + wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ; + break; + default: + dev_err(codec->dev, "Not supported sample rate: %d\n", + params_rate(params)); + return -EINVAL; + } + + snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK, + wclk_rate); + + if (!tas2552->pll_clkin) return -EINVAL; snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); - if (tas2552->mclk == TAS2552_245MHZ_CLK || - tas2552->mclk == TAS2552_225MHZ_CLK) { + if (tas2552->pll_clkin == TAS2552_245MHZ_CLK || + tas2552->pll_clkin == TAS2552_225MHZ_CLK) { /* By pass the PLL configuration */ snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2, TAS2552_PLL_BYPASS_MASK, @@ -170,8 +275,8 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - j = (pll_clk * 2 * (1 << p)) / tas2552->mclk; - d = (pll_clk * 2 * (1 << p)) % tas2552->mclk; + j = (pll_clk * 2 * (1 << p)) / tas2552->pll_clkin; + d = (pll_clk * 2 * (1 << p)) % tas2552->pll_clkin; snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1, TAS2552_PLL_J_MASK, j); @@ -185,56 +290,74 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream, return 0; } +#define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \ + TAS2552_WCLKDIR | \ + TAS2552_DATAFORMAT_MASK) +static int tas2552_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); + int delay = 0; + + /* TDM slot selection only valid in DSP_A/_B mode */ + if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A) + delay += (tas2552->tdm_delay + 1); + else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B) + delay += tas2552->tdm_delay; + + /* Configure data delay */ + snd_soc_write(codec, TAS2552_SER_CTRL_2, delay); + + return 0; +} + static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_codec *codec = dai->codec; + struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); u8 serial_format; - u8 serial_control_mask; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: serial_format = 0x00; break; case SND_SOC_DAIFMT_CBS_CFM: - serial_format = TAS2552_WORD_CLK_MASK; + serial_format = TAS2552_WCLKDIR; break; case SND_SOC_DAIFMT_CBM_CFS: - serial_format = TAS2552_BIT_CLK_MASK; + serial_format = TAS2552_BCLKDIR; break; case SND_SOC_DAIFMT_CBM_CFM: - serial_format = (TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK); + serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR); break; default: dev_vdbg(codec->dev, "DAI Format master is not found\n"); return -EINVAL; } - serial_control_mask = TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK; - - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - serial_format &= TAS2552_DAIFMT_I2S_MASK; + switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | + SND_SOC_DAIFMT_INV_MASK)) { + case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): break; - case SND_SOC_DAIFMT_DSP_A: - serial_format |= TAS2552_DAIFMT_DSP; + case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF): + case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF): + serial_format |= TAS2552_DATAFORMAT_DSP; break; - case SND_SOC_DAIFMT_RIGHT_J: - serial_format |= TAS2552_DAIFMT_RIGHT_J; + case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF): + serial_format |= TAS2552_DATAFORMAT_RIGHT_J; break; - case SND_SOC_DAIFMT_LEFT_J: - serial_format |= TAS2552_DAIFMT_LEFT_J; + case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF): + serial_format |= TAS2552_DATAFORMAT_LEFT_J; break; default: dev_vdbg(codec->dev, "DAI Format is not found\n"); return -EINVAL; } + tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; - if (fmt & SND_SOC_DAIFMT_FORMAT_MASK) - serial_control_mask |= TAS2552_DATA_FORMAT_MASK; - - snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, serial_control_mask, - serial_format); - + snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK, + serial_format); return 0; } @@ -243,23 +366,75 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, { struct snd_soc_codec *codec = dai->codec; struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); + u8 reg, mask, val; + + switch (clk_id) { + case TAS2552_PLL_CLKIN_MCLK: + case TAS2552_PLL_CLKIN_BCLK: + case TAS2552_PLL_CLKIN_IVCLKIN: + case TAS2552_PLL_CLKIN_1_8_FIXED: + mask = TAS2552_PLL_SRC_MASK; + val = (clk_id << 3) & mask; /* bit 4:5 in the register */ + reg = TAS2552_CFG_1; + tas2552->pll_clkin = freq; + break; + case TAS2552_PDM_CLK_PLL: + case TAS2552_PDM_CLK_IVCLKIN: + case TAS2552_PDM_CLK_BCLK: + case TAS2552_PDM_CLK_MCLK: + mask = TAS2552_PDM_CLK_SEL_MASK; + val = (clk_id >> 1) & mask; /* bit 0:1 in the register */ + reg = TAS2552_PDM_CFG; + tas2552->pdm_clk = freq; + break; + default: + dev_err(codec->dev, "Invalid clk id: %d\n", clk_id); + return -EINVAL; + } + + snd_soc_update_bits(codec, reg, mask, val); + + return 0; +} + +static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); + unsigned int lsb; + + if (unlikely(!tx_mask)) { + dev_err(codec->dev, "tx masks need to be non 0\n"); + return -EINVAL; + } + + /* TDM based on DSP mode requires slots to be adjacent */ + lsb = __ffs(tx_mask); + if ((lsb + 1) != __fls(tx_mask)) { + dev_err(codec->dev, "Invalid mask, slots must be adjacent\n"); + return -EINVAL; + } + + tas2552->tdm_delay = lsb * slot_width; - tas2552->mclk = freq; + /* DOUT in high-impedance on inactive bit clocks */ + snd_soc_update_bits(codec, TAS2552_DOUT, + TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE); return 0; } static int tas2552_mute(struct snd_soc_dai *dai, int mute) { - u8 cfg1_reg; + u8 cfg1_reg = 0; struct snd_soc_codec *codec = dai->codec; if (mute) - cfg1_reg = TAS2552_MUTE_MASK; - else - cfg1_reg = ~TAS2552_MUTE_MASK; + cfg1_reg |= TAS2552_MUTE; - snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK, cfg1_reg); + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg); return 0; } @@ -269,7 +444,7 @@ static int tas2552_runtime_suspend(struct device *dev) { struct tas2552_data *tas2552 = dev_get_drvdata(dev); - tas2552_sw_shutdown(tas2552, 0); + tas2552_sw_shutdown(tas2552, 1); regcache_cache_only(tas2552->regmap, true); regcache_mark_dirty(tas2552->regmap); @@ -287,7 +462,7 @@ static int tas2552_runtime_resume(struct device *dev) if (tas2552->enable_gpio) gpiod_set_value(tas2552->enable_gpio, 1); - tas2552_sw_shutdown(tas2552, 1); + tas2552_sw_shutdown(tas2552, 0); regcache_cache_only(tas2552->regmap, false); regcache_sync(tas2552->regmap); @@ -303,8 +478,10 @@ static const struct dev_pm_ops tas2552_pm = { static struct snd_soc_dai_ops tas2552_speaker_dai_ops = { .hw_params = tas2552_hw_params, + .prepare = tas2552_prepare, .set_sysclk = tas2552_set_dai_sysclk, .set_fmt = tas2552_set_dai_fmt, + .set_tdm_slot = tas2552_set_dai_tdm_slot, .digital_mute = tas2552_mute, }; @@ -330,16 +507,11 @@ static struct snd_soc_dai_driver tas2552_dai[] = { /* * DAC digital volumes. From -7 to 24 dB in 1 dB steps */ -static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24); +static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0); static const struct snd_kcontrol_new tas2552_snd_controls[] = { SOC_SINGLE_TLV("Speaker Driver Playback Volume", - TAS2552_PGA_GAIN, 0, 0x1f, 1, dac_tlv), - SOC_DAPM_SINGLE("Playback AMP", SND_SOC_NOPM, 0, 1, 0), -}; - -static const struct reg_default tas2552_init_regs[] = { - { TAS2552_RESERVED_0D, 0xc0 }, + TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv), }; static int tas2552_codec_probe(struct snd_soc_codec *codec) @@ -368,31 +540,19 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec) goto probe_fail; } - snd_soc_write(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK | - TAS2552_PLL_SRC_BCLK); + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE); snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL | - TAS2552_DIN_SRC_SEL_AVG_L_R | TAS2552_88_96KHZ); + TAS2552_DIN_SRC_SEL_AVG_L_R); snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I); snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8); - snd_soc_write(codec, TAS2552_PDM_CFG, TAS2552_PDM_BCLK_SEL); snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 | TAS2552_APT_THRESH_2_1_7); - ret = regmap_register_patch(tas2552->regmap, tas2552_init_regs, - ARRAY_SIZE(tas2552_init_regs)); - if (ret != 0) { - dev_err(codec->dev, "Failed to write init registers: %d\n", - ret); - goto patch_fail; - } - snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN | TAS2552_LIM_EN); return 0; -patch_fail: - pm_runtime_put(codec->dev); probe_fail: if (tas2552->enable_gpio) gpiod_set_value(tas2552->enable_gpio, 0); @@ -454,6 +614,8 @@ static struct snd_soc_codec_driver soc_codec_dev_tas2552 = { .remove = tas2552_codec_remove, .suspend = tas2552_suspend, .resume = tas2552_resume, + .ignore_pmdown_time = true, + .controls = tas2552_snd_controls, .num_controls = ARRAY_SIZE(tas2552_snd_controls), .dapm_widgets = tas2552_dapm_widgets, @@ -486,8 +648,12 @@ static int tas2552_probe(struct i2c_client *client, return -ENOMEM; data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(data->enable_gpio)) - return PTR_ERR(data->enable_gpio); + if (IS_ERR(data->enable_gpio)) { + if (PTR_ERR(data->enable_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + data->enable_gpio = NULL;; + } data->tas2552_client = client; data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config); diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h index 6cea8f3..bbb8204 100644 --- a/sound/soc/codecs/tas2552.h +++ b/sound/soc/codecs/tas2552.h @@ -45,10 +45,14 @@ #define TAS2552_MAX_REG 0x20 /* CFG1 Register Masks */ -#define TAS2552_MUTE_MASK (1 << 2) -#define TAS2552_SWS_MASK (1 << 1) -#define TAS2552_WCLK_MASK 0x07 -#define TAS2552_CLASSD_EN_MASK (1 << 7) +#define TAS2552_DEV_RESET (1 << 0) +#define TAS2552_SWS (1 << 1) +#define TAS2552_MUTE (1 << 2) +#define TAS2552_PLL_SRC_MCLK (0x0 << 4) +#define TAS2552_PLL_SRC_BCLK (0x1 << 4) +#define TAS2552_PLL_SRC_IVCLKIN (0x2 << 4) +#define TAS2552_PLL_SRC_1_8_FIXED (0x3 << 4) +#define TAS2552_PLL_SRC_MASK TAS2552_PLL_SRC_1_8_FIXED /* CFG2 Register Masks */ #define TAS2552_CLASSD_EN (1 << 7) @@ -59,38 +63,44 @@ #define TAS2552_IVSENSE_EN (1 << 1) /* CFG3 Register Masks */ -#define TAS2552_WORD_CLK_MASK (1 << 7) -#define TAS2552_BIT_CLK_MASK (1 << 6) -#define TAS2552_DATA_FORMAT_MASK (0x11 << 2) - -#define TAS2552_DAIFMT_I2S_MASK 0xf3 -#define TAS2552_DAIFMT_DSP (1 << 3) -#define TAS2552_DAIFMT_RIGHT_J (1 << 4) -#define TAS2552_DAIFMT_LEFT_J (0x11 << 3) - -#define TAS2552_PLL_SRC_MCLK 0x00 -#define TAS2552_PLL_SRC_BCLK (1 << 3) -#define TAS2552_PLL_SRC_IVCLKIN (1 << 4) -#define TAS2552_PLL_SRC_1_8_FIXED (0x11 << 3) - -#define TAS2552_DIN_SRC_SEL_MUTED 0x00 -#define TAS2552_DIN_SRC_SEL_LEFT (1 << 4) -#define TAS2552_DIN_SRC_SEL_RIGHT (1 << 5) -#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x11 << 4) - +#define TAS2552_WCLK_FREQ_8KHZ (0x0 << 0) +#define TAS2552_WCLK_FREQ_11_12KHZ (0x1 << 0) +#define TAS2552_WCLK_FREQ_16KHZ (0x2 << 0) +#define TAS2552_WCLK_FREQ_22_24KHZ (0x3 << 0) +#define TAS2552_WCLK_FREQ_32KHZ (0x4 << 0) +#define TAS2552_WCLK_FREQ_44_48KHZ (0x5 << 0) +#define TAS2552_WCLK_FREQ_88_96KHZ (0x6 << 0) +#define TAS2552_WCLK_FREQ_176_192KHZ (0x7 << 0) +#define TAS2552_WCLK_FREQ_MASK TAS2552_WCLK_FREQ_176_192KHZ +#define TAS2552_DIN_SRC_SEL_MUTED (0x0 << 3) +#define TAS2552_DIN_SRC_SEL_LEFT (0x1 << 3) +#define TAS2552_DIN_SRC_SEL_RIGHT (0x2 << 3) +#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x3 << 3) #define TAS2552_PDM_IN_SEL (1 << 5) #define TAS2552_I2S_OUT_SEL (1 << 6) -#define TAS2552_ANALOG_IN_SEL (1 << 7) - -/* CFG3 WCLK Dividers */ -#define TAS2552_8KHZ 0x00 -#define TAS2552_11_12KHZ (1 << 1) -#define TAS2552_16KHZ (1 << 2) -#define TAS2552_22_24KHZ (1 << 3) -#define TAS2552_32KHZ (1 << 4) -#define TAS2552_44_48KHZ (1 << 5) -#define TAS2552_88_96KHZ (1 << 6) -#define TAS2552_176_192KHZ (1 << 7) +#define TAS2552_ANALOG_IN_SEL (1 << 7) + +/* DOUT Register Masks */ +#define TAS2552_SDOUT_TRISTATE (1 << 2) + +/* Serial Interface Control Register Masks */ +#define TAS2552_WORDLENGTH_16BIT (0x0 << 0) +#define TAS2552_WORDLENGTH_20BIT (0x1 << 0) +#define TAS2552_WORDLENGTH_24BIT (0x2 << 0) +#define TAS2552_WORDLENGTH_32BIT (0x3 << 0) +#define TAS2552_WORDLENGTH_MASK TAS2552_WORDLENGTH_32BIT +#define TAS2552_DATAFORMAT_I2S (0x0 << 2) +#define TAS2552_DATAFORMAT_DSP (0x1 << 2) +#define TAS2552_DATAFORMAT_RIGHT_J (0x2 << 2) +#define TAS2552_DATAFORMAT_LEFT_J (0x3 << 2) +#define TAS2552_DATAFORMAT_MASK TAS2552_DATAFORMAT_LEFT_J +#define TAS2552_CLKSPERFRAME_32 (0x0 << 4) +#define TAS2552_CLKSPERFRAME_64 (0x1 << 4) +#define TAS2552_CLKSPERFRAME_128 (0x2 << 4) +#define TAS2552_CLKSPERFRAME_256 (0x3 << 4) +#define TAS2552_CLKSPERFRAME_MASK TAS2552_CLKSPERFRAME_256 +#define TAS2552_BCLKDIR (1 << 6) +#define TAS2552_WCLKDIR (1 << 7) /* OUTPUT_DATA register */ #define TAS2552_PDM_DATA_I 0x00 @@ -99,12 +109,12 @@ #define TAS2552_PDM_DATA_V_I (0x11 << 6) /* PDM CFG Register */ -#define TAS2552_PDM_DATA_ES_RISE 0x4 - -#define TAS2552_PDM_PLL_CLK_SEL 0x00 -#define TAS2552_PDM_IV_CLK_SEL (1 << 1) -#define TAS2552_PDM_BCLK_SEL (1 << 2) -#define TAS2552_PDM_MCLK_SEL (1 << 3) +#define TAS2552_PDM_CLK_SEL_PLL (0x0 << 0) +#define TAS2552_PDM_CLK_SEL_IVCLKIN (0x1 << 0) +#define TAS2552_PDM_CLK_SEL_BCLK (0x2 << 0) +#define TAS2552_PDM_CLK_SEL_MCLK (0x3 << 0) +#define TAS2552_PDM_CLK_SEL_MASK TAS2552_PDM_CLK_SEL_MCLK +#define TAS2552_PDM_DATA_ES (1 << 2) /* Boost pass-through register */ #define TAS2552_APT_DELAY_50 0x00 diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c new file mode 100644 index 0000000..85bcc37 --- /dev/null +++ b/sound/soc/codecs/tas571x.c @@ -0,0 +1,514 @@ +/* + * TAS571x amplifier audio driver + * + * Copyright (C) 2015 Google, Inc. + * Copyright (c) 2013 Daniel Mack <zonque@gmail.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/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/stddef.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "tas571x.h" + +#define TAS571X_MAX_SUPPLIES 6 + +struct tas571x_chip { + const char *const *supply_names; + int num_supply_names; + const struct snd_kcontrol_new *controls; + int num_controls; + const struct regmap_config *regmap_config; + int vol_reg_size; +}; + +struct tas571x_private { + const struct tas571x_chip *chip; + struct regmap *regmap; + struct regulator_bulk_data supplies[TAS571X_MAX_SUPPLIES]; + struct clk *mclk; + unsigned int format; + struct gpio_desc *reset_gpio; + struct gpio_desc *pdn_gpio; + struct snd_soc_codec_driver codec_driver; +}; + +static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg) +{ + switch (reg) { + case TAS571X_MVOL_REG: + case TAS571X_CH1_VOL_REG: + case TAS571X_CH2_VOL_REG: + return priv->chip->vol_reg_size; + default: + return 1; + } +} + +static int tas571x_reg_write(void *context, unsigned int reg, + unsigned int value) +{ + struct i2c_client *client = context; + struct tas571x_private *priv = i2c_get_clientdata(client); + unsigned int i, size; + uint8_t buf[5]; + int ret; + + size = tas571x_register_size(priv, reg); + buf[0] = reg; + + for (i = size; i >= 1; --i) { + buf[i] = value; + value >>= 8; + } + + ret = i2c_master_send(client, buf, size + 1); + if (ret == size + 1) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static int tas571x_reg_read(void *context, unsigned int reg, + unsigned int *value) +{ + struct i2c_client *client = context; + struct tas571x_private *priv = i2c_get_clientdata(client); + uint8_t send_buf, recv_buf[4]; + struct i2c_msg msgs[2]; + unsigned int size; + unsigned int i; + int ret; + + size = tas571x_register_size(priv, reg); + send_buf = reg; + + msgs[0].addr = client->addr; + msgs[0].len = sizeof(send_buf); + msgs[0].buf = &send_buf; + msgs[0].flags = 0; + + msgs[1].addr = client->addr; + msgs[1].len = size; + msgs[1].buf = recv_buf; + msgs[1].flags = I2C_M_RD; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + else if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *value = 0; + + for (i = 0; i < size; i++) { + *value <<= 8; + *value |= recv_buf[i]; + } + + return 0; +} + +static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) +{ + struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); + + priv->format = format; + + return 0; +} + +static int tas571x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); + u32 val; + + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + val = 0x00; + break; + case SND_SOC_DAIFMT_I2S: + val = 0x03; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = 0x06; + break; + default: + return -EINVAL; + } + + if (params_width(params) >= 24) + val += 2; + else if (params_width(params) >= 20) + val += 1; + + return regmap_update_bits(priv->regmap, TAS571X_SDI_REG, + TAS571X_SDI_FMT_MASK, val); +} + +static int tas571x_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct tas571x_private *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (!IS_ERR(priv->mclk)) { + ret = clk_prepare_enable(priv->mclk); + if (ret) { + dev_err(codec->dev, + "Failed to enable master clock: %d\n", + ret); + return ret; + } + } + + gpiod_set_value(priv->pdn_gpio, 0); + usleep_range(5000, 6000); + + regcache_cache_only(priv->regmap, false); + ret = regcache_sync(priv->regmap); + if (ret) + return ret; + } + break; + case SND_SOC_BIAS_OFF: + regcache_cache_only(priv->regmap, true); + gpiod_set_value(priv->pdn_gpio, 1); + + if (!IS_ERR(priv->mclk)) + clk_disable_unprepare(priv->mclk); + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops tas571x_dai_ops = { + .set_fmt = tas571x_set_dai_fmt, + .hw_params = tas571x_hw_params, +}; + +static const char *const tas5711_supply_names[] = { + "AVDD", + "DVDD", + "PVDD_A", + "PVDD_B", + "PVDD_C", + "PVDD_D", +}; + +static const DECLARE_TLV_DB_SCALE(tas5711_volume_tlv, -10350, 50, 1); + +static const struct snd_kcontrol_new tas5711_controls[] = { + SOC_SINGLE_TLV("Master Volume", + TAS571X_MVOL_REG, + 0, 0xff, 1, tas5711_volume_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", + TAS571X_CH1_VOL_REG, + TAS571X_CH2_VOL_REG, + 0, 0xff, 1, tas5711_volume_tlv), + SOC_DOUBLE("Speaker Switch", + TAS571X_SOFT_MUTE_REG, + TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, + 1, 1), +}; + +static const struct reg_default tas5711_reg_defaults[] = { + { 0x04, 0x05 }, + { 0x05, 0x40 }, + { 0x06, 0x00 }, + { 0x07, 0xff }, + { 0x08, 0x30 }, + { 0x09, 0x30 }, + { 0x1b, 0x82 }, +}; + +static const struct regmap_config tas5711_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5711_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct tas571x_chip tas5711_chip = { + .supply_names = tas5711_supply_names, + .num_supply_names = ARRAY_SIZE(tas5711_supply_names), + .controls = tas5711_controls, + .num_controls = ARRAY_SIZE(tas5711_controls), + .regmap_config = &tas5711_regmap_config, + .vol_reg_size = 1, +}; + +static const char *const tas5717_supply_names[] = { + "AVDD", + "DVDD", + "HPVDD", + "PVDD_AB", + "PVDD_CD", +}; + +static const DECLARE_TLV_DB_SCALE(tas5717_volume_tlv, -10375, 25, 0); + +static const struct snd_kcontrol_new tas5717_controls[] = { + /* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */ + SOC_SINGLE_TLV("Master Volume", + TAS571X_MVOL_REG, 1, 0x1ff, 1, + tas5717_volume_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", + TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG, + 1, 0x1ff, 1, tas5717_volume_tlv), + SOC_DOUBLE("Speaker Switch", + TAS571X_SOFT_MUTE_REG, + TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, + 1, 1), +}; + +static const struct reg_default tas5717_reg_defaults[] = { + { 0x04, 0x05 }, + { 0x05, 0x40 }, + { 0x06, 0x00 }, + { 0x07, 0x03ff }, + { 0x08, 0x00c0 }, + { 0x09, 0x00c0 }, + { 0x1b, 0x82 }, +}; + +static const struct regmap_config tas5717_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5717_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +/* This entry is reused for tas5719 as the software interface is identical. */ +static const struct tas571x_chip tas5717_chip = { + .supply_names = tas5717_supply_names, + .num_supply_names = ARRAY_SIZE(tas5717_supply_names), + .controls = tas5717_controls, + .num_controls = ARRAY_SIZE(tas5717_controls), + .regmap_config = &tas5717_regmap_config, + .vol_reg_size = 2, +}; + +static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("OUT_A"), + SND_SOC_DAPM_OUTPUT("OUT_B"), + SND_SOC_DAPM_OUTPUT("OUT_C"), + SND_SOC_DAPM_OUTPUT("OUT_D"), +}; + +static const struct snd_soc_dapm_route tas571x_dapm_routes[] = { + { "DACL", NULL, "Playback" }, + { "DACR", NULL, "Playback" }, + + { "OUT_A", NULL, "DACL" }, + { "OUT_B", NULL, "DACL" }, + { "OUT_C", NULL, "DACR" }, + { "OUT_D", NULL, "DACR" }, +}; + +static const struct snd_soc_codec_driver tas571x_codec = { + .set_bias_level = tas571x_set_bias_level, + .idle_bias_off = true, + + .dapm_widgets = tas571x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets), + .dapm_routes = tas571x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes), +}; + +static struct snd_soc_dai_driver tas571x_dai = { + .name = "tas571x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tas571x_dai_ops, +}; + +static const struct of_device_id tas571x_of_match[]; + +static int tas571x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tas571x_private *priv; + struct device *dev = &client->dev; + const struct of_device_id *of_id; + int i, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + i2c_set_clientdata(client, priv); + + of_id = of_match_device(tas571x_of_match, dev); + if (!of_id) { + dev_err(dev, "Unknown device type\n"); + return -EINVAL; + } + priv->chip = of_id->data; + + priv->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) { + dev_err(dev, "Failed to request mclk: %ld\n", + PTR_ERR(priv->mclk)); + return PTR_ERR(priv->mclk); + } + + BUG_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES); + for (i = 0; i < priv->chip->num_supply_names; i++) + priv->supplies[i].supply = priv->chip->supply_names[i]; + + ret = devm_regulator_bulk_get(dev, priv->chip->num_supply_names, + priv->supplies); + if (ret) { + dev_err(dev, "Failed to get supplies: %d\n", ret); + return ret; + } + ret = regulator_bulk_enable(priv->chip->num_supply_names, + priv->supplies); + if (ret) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + priv->regmap = devm_regmap_init(dev, NULL, client, + priv->chip->regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW); + if (IS_ERR(priv->pdn_gpio)) { + dev_err(dev, "error requesting pdn_gpio: %ld\n", + PTR_ERR(priv->pdn_gpio)); + return PTR_ERR(priv->pdn_gpio); + } + + priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->reset_gpio)) { + dev_err(dev, "error requesting reset_gpio: %ld\n", + PTR_ERR(priv->reset_gpio)); + return PTR_ERR(priv->reset_gpio); + } else if (priv->reset_gpio) { + /* pulse the active low reset line for ~100us */ + usleep_range(100, 200); + gpiod_set_value(priv->reset_gpio, 0); + usleep_range(12000, 20000); + } + + ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0); + if (ret) + return ret; + + ret = regmap_update_bits(priv->regmap, TAS571X_SYS_CTRL_2_REG, + TAS571X_SYS_CTRL_2_SDN_MASK, 0); + if (ret) + return ret; + + memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver)); + priv->codec_driver.controls = priv->chip->controls; + priv->codec_driver.num_controls = priv->chip->num_controls; + + if (priv->chip->vol_reg_size == 2) { + /* + * The master volume defaults to 0x3ff (mute), but we ignore + * (zero) the LSB because the hardware step size is 0.125 dB + * and TLV_DB_SCALE_ITEM has a resolution of 0.01 dB. + */ + ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0); + if (ret) + return ret; + } + + regcache_cache_only(priv->regmap, true); + gpiod_set_value(priv->pdn_gpio, 1); + + return snd_soc_register_codec(&client->dev, &priv->codec_driver, + &tas571x_dai, 1); +} + +static int tas571x_i2c_remove(struct i2c_client *client) +{ + struct tas571x_private *priv = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies); + + return 0; +} + +static const struct of_device_id tas571x_of_match[] = { + { .compatible = "ti,tas5711", .data = &tas5711_chip, }, + { .compatible = "ti,tas5717", .data = &tas5717_chip, }, + { .compatible = "ti,tas5719", .data = &tas5717_chip, }, + { } +}; +MODULE_DEVICE_TABLE(of, tas571x_of_match); + +static const struct i2c_device_id tas571x_i2c_id[] = { + { "tas5711", 0 }, + { "tas5717", 0 }, + { "tas5719", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); + +static struct i2c_driver tas571x_i2c_driver = { + .driver = { + .name = "tas571x", + .of_match_table = of_match_ptr(tas571x_of_match), + }, + .probe = tas571x_i2c_probe, + .remove = tas571x_i2c_remove, + .id_table = tas571x_i2c_id, +}; +module_i2c_driver(tas571x_i2c_driver); + +MODULE_DESCRIPTION("ASoC TAS571x driver"); +MODULE_AUTHOR("Kevin Cernekee <cernekee@chromium.org>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h new file mode 100644 index 0000000..0aee471 --- /dev/null +++ b/sound/soc/codecs/tas571x.h @@ -0,0 +1,33 @@ +/* + * TAS571x amplifier audio driver + * + * Copyright (C) 2015 Google, Inc. + * + * 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. + */ + +#ifndef _TAS571X_H +#define _TAS571X_H + +/* device registers */ +#define TAS571X_SDI_REG 0x04 +#define TAS571X_SDI_FMT_MASK 0x0f + +#define TAS571X_SYS_CTRL_2_REG 0x05 +#define TAS571X_SYS_CTRL_2_SDN_MASK 0x40 + +#define TAS571X_SOFT_MUTE_REG 0x06 +#define TAS571X_SOFT_MUTE_CH1_SHIFT 0 +#define TAS571X_SOFT_MUTE_CH2_SHIFT 1 +#define TAS571X_SOFT_MUTE_CH3_SHIFT 2 + +#define TAS571X_MVOL_REG 0x07 +#define TAS571X_CH1_VOL_REG 0x08 +#define TAS571X_CH2_VOL_REG 0x09 + +#define TAS571X_OSC_TRIM_REG 0x1b + +#endif /* _TAS571X_H */ diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c index 16f1b71..aab0af6 100644 --- a/sound/soc/codecs/tfa9879.c +++ b/sound/soc/codecs/tfa9879.c @@ -280,8 +280,8 @@ static int tfa9879_i2c_probe(struct i2c_client *i2c, int i; tfa9879 = devm_kzalloc(&i2c->dev, sizeof(*tfa9879), GFP_KERNEL); - if (IS_ERR(tfa9879)) - return PTR_ERR(tfa9879); + if (!tfa9879) + return -ENOMEM; i2c_set_clientdata(i2c, tfa9879); diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index cc17e7e..cd8c02b 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -506,7 +506,6 @@ static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, TLV320AIC23_PWR, 0x1ff); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index c86dd9a..c4c960f 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -646,7 +646,7 @@ static int aic31xx_add_controls(struct snd_soc_codec *codec) static int aic31xx_add_widgets(struct snd_soc_codec *codec) { - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); int ret = 0; @@ -1027,17 +1027,17 @@ static int aic31xx_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { dev_dbg(codec->dev, "## %s: %d -> %d\n", __func__, - codec->dapm.bias_level, level); + snd_soc_codec_get_bias_level(codec), level); switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) aic31xx_clk_on(codec); break; case SND_SOC_BIAS_STANDBY: - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_OFF: aic31xx_power_on(codec); break; @@ -1049,11 +1049,10 @@ static int aic31xx_set_bias_level(struct snd_soc_codec *codec, } break; case SND_SOC_BIAS_OFF: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) aic31xx_power_off(codec); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 015467e..ad6cb90 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -564,7 +564,6 @@ static int aic32x4_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_OFF: break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 51c4713..a7cf19b 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -147,6 +147,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -179,7 +180,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, update.mask = mask; update.val = val; - snd_soc_dapm_mixer_update_power(&codec->dapm, kcontrol, connect, + snd_soc_dapm_mixer_update_power(dapm, kcontrol, connect, &update); } @@ -979,7 +980,7 @@ static const struct snd_soc_dapm_route intercon_3007[] = { static int aic3x_add_widgets(struct snd_soc_codec *codec) { struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); switch (aic3x->model) { case AIC3X_MODEL_3X: @@ -1384,7 +1385,7 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY && + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY && aic3x->master) { /* enable pll */ snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, @@ -1394,7 +1395,7 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: if (!aic3x->power) aic3x_set_power(codec, 1); - if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE && + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE && aic3x->master) { /* disable pll */ snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, @@ -1406,7 +1407,6 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, aic3x_set_power(codec, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 4e3e607..d67a311 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -633,7 +633,7 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Coming from OFF, switch on the codec */ ret = dac33_hard_power(codec, 1); if (ret != 0) @@ -644,14 +644,13 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_OFF: /* Do not power off, when the codec is already off */ - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) return 0; ret = dac33_hard_power(codec, 0); if (ret != 0) return ret; break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 9fd80ac..12232d7 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -254,12 +254,13 @@ static const struct regmap_config ts3a227e_regmap_config = { .num_reg_defaults = ARRAY_SIZE(ts3a227e_reg_defaults), }; -static int ts3a227e_parse_dt(struct ts3a227e *ts3a227e, struct device_node *np) +static int ts3a227e_parse_device_property(struct ts3a227e *ts3a227e, + struct device *dev) { u32 micbias; int err; - err = of_property_read_u32(np, "ti,micbias", &micbias); + err = device_property_read_u32(dev, "ti,micbias", &micbias); if (!err) { regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_SETTING_3, MICBIAS_SETTING_MASK, @@ -287,12 +288,10 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c, if (IS_ERR(ts3a227e->regmap)) return PTR_ERR(ts3a227e->regmap); - if (dev->of_node) { - ret = ts3a227e_parse_dt(ts3a227e, dev->of_node); - if (ret) { - dev_err(dev, "Failed to parse device tree: %d\n", ret); - return ret; - } + ret = ts3a227e_parse_device_property(ts3a227e, dev); + if (ret) { + dev_err(dev, "Failed to parse device property: %d\n", ret); + return ret; } ret = devm_request_threaded_irq(dev, i2c->irq, NULL, ts3a227e_interrupt, diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index d04693e..90f5f04 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -1588,14 +1588,13 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) twl4030_codec_enable(codec, 1); break; case SND_SOC_BIAS_OFF: twl4030_codec_enable(codec, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index aeec27b..4cad892 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -533,7 +533,7 @@ static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol, int twl6040_get_dl1_gain(struct snd_soc_codec *codec) { - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); if (snd_soc_dapm_get_pin_status(dapm, "EP")) return -1; /* -1dB */ @@ -853,8 +853,6 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } @@ -1123,14 +1121,15 @@ static int twl6040_probe(struct snd_soc_codec *codec) mutex_init(&priv->mutex); ret = request_threaded_irq(priv->plug_irq, NULL, - twl6040_audio_handler, IRQF_NO_SUSPEND, + twl6040_audio_handler, + IRQF_NO_SUSPEND | IRQF_ONESHOT, "twl6040_irq_plug", codec); if (ret) { dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret); return ret; } - twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); twl6040_init_chip(codec); return 0; diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index f883308..913edf2 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -350,7 +350,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec, pd->power(0); break; } - codec->dapm.bias_level = level; return 0; } @@ -478,6 +477,7 @@ static struct snd_soc_dai_driver uda134x_dai = { static int uda134x_soc_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct uda134x_priv *uda134x; struct uda134x_platform_data *pd = codec->component.card->dev->platform_data; const struct snd_soc_dapm_widget *widgets; @@ -526,7 +526,7 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec) num_widgets = ARRAY_SIZE(uda1340_dapm_widgets); } - ret = snd_soc_dapm_new_controls(&codec->dapm, widgets, num_widgets); + ret = snd_soc_dapm_new_controls(dapm, widgets, num_widgets); if (ret) { printk(KERN_ERR "%s failed to register dapm controls: %d", __func__, ret); diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index dc7778b..6e159f5 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -437,7 +437,7 @@ static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai, if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) return -EINVAL; - uda1380_write(codec, UDA1380_IFACE, iface); + uda1380_write_reg_cache(codec, UDA1380_IFACE, iface); return 0; } @@ -590,9 +590,6 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec, int reg; struct uda1380_platform_data *pdata = codec->dev->platform_data; - if (codec->dapm.bias_level == level) - return 0; - switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: @@ -600,7 +597,7 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec, uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { if (gpio_is_valid(pdata->gpio_power)) { gpio_set_value(pdata->gpio_power, 1); mdelay(1); @@ -623,7 +620,6 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec, for (reg = UDA1380_MVOL; reg < UDA1380_CACHEREGNUM; reg++) set_bit(reg - 0x10, &uda1380_cache_dirty); } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index f37989e..6560a66 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -751,13 +751,13 @@ static int wm0010_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) wm0010_boot(codec); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) { mutex_lock(&wm0010->lock); wm0010_halt(codec); mutex_unlock(&wm0010->lock); @@ -767,8 +767,6 @@ static int wm0010_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c index 8011f75..048f005 100644 --- a/sound/soc/codecs/wm1250-ev1.c +++ b/sound/soc/codecs/wm1250-ev1.c @@ -61,8 +61,6 @@ static int wm1250_ev1_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 9674037..4c10cd8 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2101,7 +2101,7 @@ static void wm5100_micd_irq(struct wm5100_priv *wm5100) int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) { struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); if (jack) { wm5100->jack = jack; @@ -2336,6 +2336,7 @@ static void wm5100_free_gpio(struct i2c_client *i2c) static int wm5100_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct i2c_client *i2c = to_i2c_client(codec->dev); struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec); int ret, i; @@ -2353,8 +2354,7 @@ static int wm5100_probe(struct snd_soc_codec *codec) /* TODO: check if we're symmetric */ if (i2c->irq) - snd_soc_dapm_new_controls(&codec->dapm, - wm5100_dapm_widgets_noirq, + snd_soc_dapm_new_controls(dapm, wm5100_dapm_widgets_noirq, ARRAY_SIZE(wm5100_dapm_widgets_noirq)); if (wm5100->pdata.hp_pol) { @@ -2570,11 +2570,13 @@ static int wm5100_i2c_probe(struct i2c_client *i2c, if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) ret = request_threaded_irq(i2c->irq, NULL, - wm5100_edge_irq, irq_flags, + wm5100_edge_irq, + irq_flags | IRQF_ONESHOT, "wm5100", wm5100); else ret = request_threaded_irq(i2c->irq, NULL, wm5100_irq, - irq_flags, "wm5100", + irq_flags | IRQF_ONESHOT, + "wm5100", wm5100); if (ret != 0) { diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 0c6d1bc..d8959e3 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -42,7 +42,7 @@ struct wm5102_priv { static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); -static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0); +static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0); static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); static const struct wm_adsp_region wm5102_dsp1_regions[] = { @@ -605,12 +605,56 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, regmap_write_async(regmap, patch[i].reg, patch[i].def); break; + case SND_SOC_DAPM_PRE_PMD: + break; + default: + return 0; + } + + return arizona_dvfs_sysclk_ev(w, kcontrol, event); +} + +static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + unsigned int v; + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v); + if (ret != 0) { + dev_err(codec->dev, + "Failed to read SYSCLK state: %d\n", ret); + return -EIO; + } + + v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT; + + if (v >= 3) { + ret = arizona_dvfs_up(codec, ARIZONA_DVFS_ADSP1_RQ); + if (ret) { + dev_err(codec->dev, + "Failed to raise DVFS: %d\n", ret); + return ret; + } + } + break; + + case SND_SOC_DAPM_POST_PMD: + ret = arizona_dvfs_down(codec, ARIZONA_DVFS_ADSP1_RQ); + if (ret) + dev_warn(codec->dev, + "Failed to lower DVFS: %d\n", ret); + break; default: break; } - return 0; + return wm_adsp2_early_event(w, kcontrol, event); } static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, @@ -1036,7 +1080,8 @@ static const struct snd_kcontrol_new wm5102_aec_loopback_mux = static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, - 0, wm5102_sysclk_ev, SND_SOC_DAPM_POST_PMU), + 0, wm5102_sysclk_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, @@ -1367,7 +1412,7 @@ ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), -WM_ADSP2("DSP1", 0), +WM_ADSP2_E("DSP1", 0, wm5102_adsp_power_ev), SND_SOC_DAPM_OUTPUT("HPOUT1L"), SND_SOC_DAPM_OUTPUT("HPOUT1R"), @@ -1827,6 +1872,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = { static int wm5102_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); int ret; @@ -1837,9 +1883,9 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) arizona_init_spk(codec); arizona_init_gpio(codec); - snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); + snd_soc_dapm_disable_pin(dapm, "HAPTICS"); - priv->core.arizona->dapm = &codec->dapm; + priv->core.arizona->dapm = dapm; return 0; } @@ -1909,6 +1955,8 @@ static int wm5102_probe(struct platform_device *pdev) wm5102->core.arizona = arizona; wm5102->core.num_inputs = 6; + arizona_init_dvfs(&wm5102->core); + wm5102->core.adsp[0].part = "wm5102"; wm5102->core.adsp[0].num = 1; wm5102->core.adsp[0].type = WMFW_ADSP2; @@ -1918,7 +1966,7 @@ static int wm5102_probe(struct platform_device *pdev) wm5102->core.adsp[0].mem = wm5102_dsp1_regions; wm5102->core.adsp[0].num_mems = ARRAY_SIZE(wm5102_dsp1_regions); - ret = wm_adsp2_init(&wm5102->core.adsp[0], true); + ret = wm_adsp2_init(&wm5102->core.adsp[0]); if (ret != 0) return ret; diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index fbaeddb..14a7739 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -167,7 +167,7 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); -static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0); +static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0); static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); #define WM5110_NG_SRC(name, base) \ @@ -1598,10 +1598,11 @@ static struct snd_soc_dai_driver wm5110_dai[] = { static int wm5110_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec); int ret; - priv->core.arizona->dapm = &codec->dapm; + priv->core.arizona->dapm = dapm; arizona_init_spk(codec); arizona_init_gpio(codec); @@ -1611,9 +1612,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) if (ret != 0) return ret; - snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); - - priv->core.arizona->dapm = &codec->dapm; + snd_soc_dapm_disable_pin(dapm, "HAPTICS"); return 0; } @@ -1697,7 +1696,7 @@ static int wm5110_probe(struct platform_device *pdev) wm5110->core.adsp[i].num_mems = ARRAY_SIZE(wm5110_dsp1_regions); - ret = wm_adsp2_init(&wm5110->core.adsp[i], false); + ret = wm_adsp2_init(&wm5110->core.adsp[i]); if (ret != 0) return ret; } diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index c65e5a7..41c62c1 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1102,7 +1102,7 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); if (ret != 0) @@ -1235,7 +1235,6 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec, priv->supplies); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index b0d84e5..d755508 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -1145,7 +1145,7 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(power), &power[0]); if (ret != 0) { @@ -1232,7 +1232,6 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 8736ad0..dac5beb 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -519,7 +519,7 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN; - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_sync(wm8510->regmap); /* Initial cap charge at VMID 5k */ @@ -538,7 +538,6 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index b1cc94f..8c5b9df 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -308,7 +308,7 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies); if (ret != 0) { @@ -344,7 +344,6 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec, wm8523->supplies); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 0a887c5..759a792 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -795,7 +795,7 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Power up and get individual control of the DACs */ snd_soc_update_bits(codec, WM8580_PWRDN1, WM8580_PWRDN1_PWDN | @@ -812,7 +812,6 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec, WM8580_PWRDN1_PWDN, WM8580_PWRDN1_PWDN); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 121e46d..cc8251f 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -310,7 +310,7 @@ static int wm8711_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) regcache_sync(wm8711->regmap); snd_soc_write(codec, WM8711_PWR, reg | 0x0040); @@ -320,7 +320,6 @@ static int wm8711_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8711_PWR, 0xffff); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 55c7fb4..f1a173e 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -170,7 +170,7 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Power everything up... */ reg = snd_soc_read(codec, WM8728_DACCTL); snd_soc_write(codec, WM8728_DACCTL, reg & ~0x4); @@ -185,7 +185,6 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8728_DACCTL, reg | 0x4); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 2245b6a..915ea11 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -387,6 +387,7 @@ static int wm8731_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 snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); switch (clk_id) { @@ -421,7 +422,7 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, wm8731->sysclk = freq; - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); return 0; } @@ -501,7 +502,7 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); if (ret != 0) @@ -523,7 +524,6 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, regcache_mark_dirty(wm8731->regmap); break; } - codec->dapm.bias_level = level; return 0; } @@ -599,7 +599,7 @@ static int wm8731_probe(struct snd_soc_codec *codec) goto err_regulator_enable; } - wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Latch the update bits */ snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0); diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index ada9ac1..6ad606f 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -469,7 +469,7 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); if (ret != 0) { @@ -483,7 +483,8 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec, /* Fast VMID ramp at 2*2.5k */ snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL, - WM8737_VMIDSEL_MASK, 0x4); + WM8737_VMIDSEL_MASK, + 2 << WM8737_VMIDSEL_SHIFT); /* Bring VMID up */ snd_soc_update_bits(codec, WM8737_POWER_MANAGEMENT, @@ -497,7 +498,8 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec, /* VMID at 2*300k */ snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL, - WM8737_VMIDSEL_MASK, 2); + WM8737_VMIDSEL_MASK, + 1 << WM8737_VMIDSEL_SHIFT); break; @@ -510,7 +512,6 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -560,7 +561,7 @@ static int wm8737_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8737_RIGHT_PGA_VOLUME, WM8737_RVU, WM8737_RVU); - wm8737_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Bias level configuration will have done an extra enable */ regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 9e71c76..09ff01f 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -41,6 +41,7 @@ static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = { /* codec private data */ struct wm8741_priv { + struct wm8741_platform_data pdata; struct regmap *regmap; struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES]; unsigned int sysclk; @@ -87,13 +88,27 @@ static int wm8741_reset(struct snd_soc_codec *codec) static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0); static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0); -static const struct snd_kcontrol_new wm8741_snd_controls[] = { +static const struct snd_kcontrol_new wm8741_snd_controls_stereo[] = { SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine), SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv), }; +static const struct snd_kcontrol_new wm8741_snd_controls_mono_left[] = { +SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, + 1, 255, 1, dac_tlv_fine), +SOC_SINGLE_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, + 0, 511, 1, dac_tlv), +}; + +static const struct snd_kcontrol_new wm8741_snd_controls_mono_right[] = { +SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACRLSB_ATTENUATION, + 1, 255, 1, dac_tlv_fine), +SOC_SINGLE_TLV("Playback Volume", WM8741_DACRMSB_ATTENUATION, + 0, 511, 1, dac_tlv), +}; + static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = { SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0), @@ -398,7 +413,7 @@ static struct snd_soc_dai_driver wm8741_dai = { .name = "wm8741", .playback = { .stream_name = "Playback", - .channels_min = 2, /* Mono modes not yet supported */ + .channels_min = 2, .channels_max = 2, .rates = WM8741_RATES, .formats = WM8741_FORMATS, @@ -416,6 +431,65 @@ static int wm8741_resume(struct snd_soc_codec *codec) #define wm8741_resume NULL #endif +static int wm8741_configure(struct snd_soc_codec *codec) +{ + struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); + + /* Configure differential mode */ + switch (wm8741->pdata.diff_mode) { + case WM8741_DIFF_MODE_STEREO: + case WM8741_DIFF_MODE_STEREO_REVERSED: + case WM8741_DIFF_MODE_MONO_LEFT: + case WM8741_DIFF_MODE_MONO_RIGHT: + snd_soc_update_bits(codec, WM8741_MODE_CONTROL_2, + WM8741_DIFF_MASK, + wm8741->pdata.diff_mode << WM8741_DIFF_SHIFT); + break; + default: + return -EINVAL; + } + + /* Change some default settings - latch VU */ + snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION, + WM8741_UPDATELL, WM8741_UPDATELL); + snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION, + WM8741_UPDATELM, WM8741_UPDATELM); + snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, + WM8741_UPDATERL, WM8741_UPDATERL); + snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION, + WM8741_UPDATERM, WM8741_UPDATERM); + + return 0; +} + +static int wm8741_add_controls(struct snd_soc_codec *codec) +{ + struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); + + switch (wm8741->pdata.diff_mode) { + case WM8741_DIFF_MODE_STEREO: + case WM8741_DIFF_MODE_STEREO_REVERSED: + snd_soc_add_codec_controls(codec, + wm8741_snd_controls_stereo, + ARRAY_SIZE(wm8741_snd_controls_stereo)); + break; + case WM8741_DIFF_MODE_MONO_LEFT: + snd_soc_add_codec_controls(codec, + wm8741_snd_controls_mono_left, + ARRAY_SIZE(wm8741_snd_controls_mono_left)); + break; + case WM8741_DIFF_MODE_MONO_RIGHT: + snd_soc_add_codec_controls(codec, + wm8741_snd_controls_mono_right, + ARRAY_SIZE(wm8741_snd_controls_mono_right)); + break; + default: + return -EINVAL; + } + + return 0; +} + static int wm8741_probe(struct snd_soc_codec *codec) { struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); @@ -434,15 +508,17 @@ static int wm8741_probe(struct snd_soc_codec *codec) goto err_enable; } - /* Change some default settings - latch VU */ - snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION, - WM8741_UPDATELL, WM8741_UPDATELL); - snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION, - WM8741_UPDATELM, WM8741_UPDATELM); - snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, - WM8741_UPDATERL, WM8741_UPDATERL); - snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION, - WM8741_UPDATERM, WM8741_UPDATERM); + ret = wm8741_configure(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to change default settings\n"); + goto err_enable; + } + + ret = wm8741_add_controls(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to add controls\n"); + goto err_enable; + } dev_dbg(codec->dev, "Successful registration\n"); return ret; @@ -467,8 +543,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8741 = { .remove = wm8741_remove, .resume = wm8741_resume, - .controls = wm8741_snd_controls, - .num_controls = ARRAY_SIZE(wm8741_snd_controls), .dapm_widgets = wm8741_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets), .dapm_routes = wm8741_dapm_routes, @@ -493,6 +567,23 @@ static const struct regmap_config wm8741_regmap = { .readable_reg = wm8741_readable, }; +static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741) +{ + const struct wm8741_platform_data *pdata = dev_get_platdata(dev); + u32 diff_mode; + + if (dev->of_node) { + if (of_property_read_u32(dev->of_node, "diff-mode", &diff_mode) + >= 0) + wm8741->pdata.diff_mode = diff_mode; + } else { + if (pdata != NULL) + memcpy(&wm8741->pdata, pdata, sizeof(wm8741->pdata)); + } + + return 0; +} + #if IS_ENABLED(CONFIG_I2C) static int wm8741_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) @@ -522,6 +613,12 @@ static int wm8741_i2c_probe(struct i2c_client *i2c, return ret; } + ret = wm8741_set_pdata(&i2c->dev, wm8741); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to set pdata: %d\n", ret); + return ret; + } + i2c_set_clientdata(i2c, wm8741); ret = snd_soc_register_codec(&i2c->dev, @@ -582,6 +679,12 @@ static int wm8741_spi_probe(struct spi_device *spi) return ret; } + ret = wm8741_set_pdata(&spi->dev, wm8741); + if (ret != 0) { + dev_err(&spi->dev, "Failed to set pdata: %d\n", ret); + return ret; + } + spi_set_drvdata(spi, wm8741); ret = snd_soc_register_codec(&spi->dev, diff --git a/sound/soc/codecs/wm8741.h b/sound/soc/codecs/wm8741.h index 56c1b1d..c8835f6 100644 --- a/sound/soc/codecs/wm8741.h +++ b/sound/soc/codecs/wm8741.h @@ -194,6 +194,12 @@ #define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */ #define WM8741_DITHER_WIDTH 2 /* DITHER - [1:0] */ +/* DIFF field values */ +#define WM8741_DIFF_MODE_STEREO 0 /* stereo normal */ +#define WM8741_DIFF_MODE_STEREO_REVERSED 2 /* stereo reversed */ +#define WM8741_DIFF_MODE_MONO_LEFT 1 /* mono left */ +#define WM8741_DIFF_MODE_MONO_RIGHT 3 /* mono right */ + /* * R32 (0x20) - ADDITONAL_CONTROL_1 */ @@ -208,4 +214,8 @@ #define WM8741_SYSCLK 0 +struct wm8741_platform_data { + u32 diff_mode; /* Differential Output Mode */ +}; + #endif diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index eb0a164..56d89b0 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -634,7 +634,7 @@ static int wm8750_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_cache_sync(codec); /* Set VMID to 5k */ @@ -651,7 +651,6 @@ static int wm8750_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8750_PWR1, 0x0001); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index c50a595..feb2997a 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1352,7 +1352,7 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec, flush_delayed_work(&wm8753->charge_work); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* set vmid to 5k for quick power up */ snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x01c1); schedule_delayed_work(&wm8753->charge_work, @@ -1367,7 +1367,6 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8753_PWR1, 0x0001); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c index 53e977d..66c1f15 100644 --- a/sound/soc/codecs/wm8770.c +++ b/sound/soc/codecs/wm8770.c @@ -510,7 +510,7 @@ static int wm8770_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies), wm8770->supplies); if (ret) { @@ -534,7 +534,6 @@ static int wm8770_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index c13050b..ece9b44 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -344,7 +344,7 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_sync(wm8776->regmap); /* Disable the global powerdown; DAPM does the rest */ @@ -357,7 +357,6 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 1e403f6..c195c2e 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -162,7 +162,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val = ucontrol->value.enumerated.item[0] << e->shift_l; unsigned int mask = 1 << e->shift_l; diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 2eb986c..f3759ec 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -998,8 +998,8 @@ static int wm8900_digital_mute(struct snd_soc_dai *codec_dai, int mute) SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) #define WM8900_PCM_FORMATS \ - (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ - SNDRV_PCM_FORMAT_S24_LE) + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) static const struct snd_soc_dai_ops wm8900_dai_ops = { .hw_params = wm8900_hw_params, @@ -1049,7 +1049,7 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: /* Charge capacitors if initial power up */ - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* STARTUP_BIAS_ENA on */ snd_soc_write(codec, WM8900_REG_POWER1, WM8900_REG_POWER1_STARTUP_BIAS_ENA); @@ -1117,7 +1117,6 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec, WM8900_REG_POWER2_SYSCLK_ENA); break; } - codec->dapm.bias_level = level; return 0; } @@ -1138,7 +1137,7 @@ static int wm8900_suspend(struct snd_soc_codec *codec) wm8900->fll_out = fll_out; wm8900->fll_in = fll_in; - wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -1156,7 +1155,7 @@ static int wm8900_resume(struct snd_soc_codec *codec) return ret; } - wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Restart the FLL? */ if (wm8900->fll_out) { @@ -1189,7 +1188,7 @@ static int wm8900_probe(struct snd_soc_codec *codec) wm8900_reset(codec); /* Turn the chip on */ - wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Latch the volume update bits */ snd_soc_update_bits(codec, WM8900_REG_LINVOL, 0x100, 0x100); diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 04b04f8..b5322c1 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1105,7 +1105,7 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, WM8903_POBCTRL | WM8903_ISEL_MASK | WM8903_STARTUP_BIAS_ENA | @@ -1200,8 +1200,6 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h index db94931..0bb4a64 100644 --- a/sound/soc/codecs/wm8903.h +++ b/sound/soc/codecs/wm8903.h @@ -172,7 +172,7 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec, #define WM8903_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */ #define WM8903_VMID_RES_50K 2 -#define WM8903_VMID_RES_250K 3 +#define WM8903_VMID_RES_250K 4 #define WM8903_VMID_RES_5K 6 /* diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 215e93c..265a4a5 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1168,7 +1168,7 @@ static const struct snd_soc_dapm_route wm8912_intercon[] = { static int wm8904_add_widgets(struct snd_soc_codec *codec) { struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); snd_soc_dapm_new_controls(dapm, wm8904_core_dapm_widgets, ARRAY_SIZE(wm8904_core_dapm_widgets)); @@ -1852,7 +1852,7 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); if (ret != 0) { @@ -1907,7 +1907,6 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec, clk_disable_unprepare(wm8904->mclk); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index e4142b4..98ef0ba 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -492,7 +492,7 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec, ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regcache_sync(wm8940->regmap); if (ret < 0) { dev_err(codec->dev, "Failed to sync cache: %d\n", ret); @@ -510,8 +510,6 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return ret; } @@ -707,7 +705,7 @@ static int wm8940_probe(struct snd_soc_codec *codec) return ret; } - wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); ret = snd_soc_write(codec, WM8940_POWER1, 0x180); if (ret < 0) diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 00bec91..2d591c2 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -298,7 +298,7 @@ static int wm8955_configure_clocking(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8955_PLL_CONTROL_2, WM8955_K_17_9_MASK, (pll.k >> 9) & WM8955_K_17_9_MASK); - snd_soc_update_bits(codec, WM8955_PLL_CONTROL_2, + snd_soc_update_bits(codec, WM8955_PLL_CONTROL_3, WM8955_K_8_0_MASK, pll.k & WM8955_K_8_0_MASK); if (pll.k) @@ -785,7 +785,7 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies); if (ret != 0) { @@ -838,7 +838,6 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec, wm8955->supplies); break; } - codec->dapm.bias_level = level; return 0; } @@ -929,7 +928,7 @@ static int wm8955_probe(struct snd_soc_codec *codec) WM8955_DMEN, WM8955_DMEN); } - wm8955_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Bias level configuration will have done an extra enable */ regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies); diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 3035d98..761418f 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -127,6 +127,8 @@ struct wm8960_priv { struct snd_soc_dapm_widget *out3; bool deemph; int playback_fs; + int bclk; + int sysclk; struct wm8960_data pdata; }; @@ -395,7 +397,7 @@ static const struct snd_soc_dapm_route audio_paths[] = { { "Right Input Mixer", "Boost Switch", "Right Boost Mixer", }, { "Right Input Mixer", NULL, "RINPUT1", }, /* Really Boost Switch */ { "Right Input Mixer", NULL, "RINPUT2" }, - { "Right Input Mixer", NULL, "LINPUT3" }, + { "Right Input Mixer", NULL, "RINPUT3" }, { "Left ADC", NULL, "Left Input Mixer" }, { "Right ADC", NULL, "Right Input Mixer" }, @@ -445,7 +447,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); struct wm8960_data *pdata = &wm8960->pdata; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct snd_soc_dapm_widget *w; snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, @@ -476,7 +478,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) * and save the result. */ list_for_each_entry(w, &codec->component.card->widgets, list) { - if (w->dapm != &codec->dapm) + if (w->dapm != dapm) continue; if (strcmp(w->name, "LOUT1 PGA") == 0) wm8960->lout1 = w; @@ -563,6 +565,72 @@ static struct { { 8000, 5 }, }; +/* Multiply 256 for internal 256 div */ +static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 }; + +/* Multiply 10 to eliminate decimials */ +static const int bclk_divs[] = { + 10, 15, 20, 30, 40, 55, 60, 80, 110, + 120, 160, 220, 240, 320, 320, 320 +}; + +static void wm8960_configure_clocking(struct snd_soc_codec *codec, + bool tx, int lrclk) +{ + struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); + u16 iface2 = snd_soc_read(codec, WM8960_IFACE2); + u32 sysclk; + int i, j; + + if (!(iface1 & (1<<6))) { + dev_dbg(codec->dev, + "Codec is slave mode, no need to configure clock\n"); + return; + } + + if (!wm8960->sysclk) { + dev_dbg(codec->dev, "No SYSCLK configured\n"); + return; + } + + if (!wm8960->bclk || !lrclk) { + dev_dbg(codec->dev, "No audio clocks configured\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) { + if (wm8960->sysclk == lrclk * dac_divs[i]) { + for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) { + sysclk = wm8960->bclk * bclk_divs[j] / 10; + if (wm8960->sysclk == sysclk) + break; + } + if(j != ARRAY_SIZE(bclk_divs)) + break; + } + } + + if (i == ARRAY_SIZE(dac_divs)) { + dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk); + return; + } + + /* + * configure frame clock. If ADCLRC configure as GPIO pin, DACLRC + * pin is used as a frame clock for ADCs and DACs. + */ + if (iface2 & (1<<6)) + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); + else if (tx) + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); + else if (!tx) + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, i << 6); + + /* configure bit clock */ + snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, j); +} + static int wm8960_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -570,8 +638,13 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int i; + wm8960->bclk = snd_soc_params_to_bclk(params); + if (params_channels(params) == 1) + wm8960->bclk *= 2; + /* bit size */ switch (params_width(params)) { case 16: @@ -582,6 +655,12 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, case 24: iface |= 0x0008; break; + case 32: + /* right justify mode does not support 32 word length */ + if ((iface & 0x3) != 0) { + iface |= 0x000c; + break; + } default: dev_err(codec->dev, "unsupported width %d\n", params_width(params)); @@ -602,6 +681,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, /* set iface */ snd_soc_write(codec, WM8960_IFACE1, iface); + + wm8960_configure_clocking(codec, tx, params_rate(params)); + return 0; } @@ -627,7 +709,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_STANDBY: if (!IS_ERR(wm8960->mclk)) { ret = clk_prepare_enable(wm8960->mclk); @@ -655,7 +737,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_sync(wm8960->regmap); /* Enable anti-pop features */ @@ -691,8 +773,6 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } @@ -707,7 +787,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_STANDBY: /* Enable anti pop mode */ snd_soc_update_bits(codec, WM8960_APOP1, @@ -778,7 +858,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_PREPARE: /* Disable HP discharge */ snd_soc_update_bits(codec, WM8960_APOP2, @@ -802,8 +882,6 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } @@ -950,11 +1028,35 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec, return wm8960->set_bias_level(codec, level); } +static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case WM8960_SYSCLK_MCLK: + snd_soc_update_bits(codec, WM8960_CLOCK1, + 0x1, WM8960_SYSCLK_MCLK); + break; + case WM8960_SYSCLK_PLL: + snd_soc_update_bits(codec, WM8960_CLOCK1, + 0x1, WM8960_SYSCLK_PLL); + break; + default: + return -EINVAL; + } + + wm8960->sysclk = freq; + + return 0; +} + #define WM8960_RATES SNDRV_PCM_RATE_8000_48000 #define WM8960_FORMATS \ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE) + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops wm8960_dai_ops = { .hw_params = wm8960_hw_params, @@ -962,6 +1064,7 @@ static const struct snd_soc_dai_ops wm8960_dai_ops = { .set_fmt = wm8960_set_dai_fmt, .set_clkdiv = wm8960_set_dai_clkdiv, .set_pll = wm8960_set_dai_pll, + .set_sysclk = wm8960_set_dai_sysclk, }; static struct snd_soc_dai_driver wm8960_dai = { diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index 95e2c1b..a057662 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -758,7 +758,7 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) { /* Enable bias generation */ reg = snd_soc_read(codec, WM8961_ANTI_POP); reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN; @@ -773,7 +773,7 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) { /* VREF off */ reg = snd_soc_read(codec, WM8961_PWR_MGMT_1); reg &= ~WM8961_VREF; @@ -795,8 +795,6 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 118b003..c5748fd 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2361,7 +2361,7 @@ static int wm8962_add_widgets(struct snd_soc_codec *codec) { struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); struct wm8962_pdata *pdata = &wm8962->pdata; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); snd_soc_add_codec_controls(codec, wm8962_snd_controls, ARRAY_SIZE(wm8962_snd_controls)); @@ -2446,13 +2446,13 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec) * So we here provisionally enable it and then disable it afterward * if current bias_level hasn't reached SND_SOC_BIAS_ON. */ - if (codec->dapm.bias_level != SND_SOC_BIAS_ON) + if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON) snd_soc_update_bits(codec, WM8962_CLOCKING2, WM8962_SYSCLK_ENA_MASK, WM8962_SYSCLK_ENA); dspclk = snd_soc_read(codec, WM8962_CLOCKING1); - if (codec->dapm.bias_level != SND_SOC_BIAS_ON) + if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON) snd_soc_update_bits(codec, WM8962_CLOCKING2, WM8962_SYSCLK_ENA_MASK, 0); @@ -2510,9 +2510,6 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec) static int wm8962_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - if (level == codec->dapm.bias_level) - return 0; - switch (level) { case SND_SOC_BIAS_ON: break; @@ -2530,7 +2527,7 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, WM8962_PWR_MGMT_1, WM8962_VMID_SEL_MASK, 0x100); - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) msleep(100); break; @@ -2538,7 +2535,6 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -2614,7 +2610,7 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, dev_dbg(codec->dev, "hw_params set BCLK %dHz LRCLK %dHz\n", wm8962->bclk, wm8962->lrclk); - if (codec->dapm.bias_level == SND_SOC_BIAS_ON) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) wm8962_configure_bclk(codec); return 0; @@ -3118,7 +3114,7 @@ static irqreturn_t wm8962_irq(int irq, void *data) int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) { struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int irq_mask, enable; wm8962->jack = jack; @@ -3164,7 +3160,7 @@ static void wm8962_beep_work(struct work_struct *work) struct wm8962_priv *wm8962 = container_of(work, struct wm8962_priv, beep_work); struct snd_soc_codec *codec = wm8962->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int i; int reg = 0; int best = 0; @@ -3415,6 +3411,7 @@ static void wm8962_free_gpio(struct snd_soc_codec *codec) static int wm8962_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int ret; struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); int i; @@ -3462,7 +3459,7 @@ static int wm8962_probe(struct snd_soc_codec *codec) } if (!dmicclk || !dmicdat) { dev_dbg(codec->dev, "DMIC not in use, disabling\n"); - snd_soc_dapm_nc_pin(&codec->dapm, "DMICDAT"); + snd_soc_dapm_nc_pin(dapm, "DMICDAT"); } if (dmicclk != dmicdat) dev_warn(codec->dev, "DMIC GPIOs partially configured\n"); diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index f9cbabd..b51184c 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -577,7 +577,7 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec, flush_delayed_work(&wm8971->charge_work); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_cache_sync(codec); /* charge output caps - set vmid to 5k for quick power up */ snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x01c0); @@ -594,7 +594,6 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8971_PWR1, 0x0001); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index ff0e464..33b16a7 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -514,7 +514,7 @@ static int wm8974_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN; - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_sync(dev_get_regmap(codec->dev, NULL)); /* Initial cap charge at VMID 5k */ @@ -533,7 +533,6 @@ static int wm8974_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index cf70329..cfc8cdf 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -868,7 +868,7 @@ static int wm8978_set_bias_level(struct snd_soc_codec *codec, /* bit 3: enable bias, bit 2: enable I/O tie off buffer */ power1 |= 0xc; - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Initial cap charge at VMID 5k */ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1 | 0x3); @@ -888,7 +888,6 @@ static int wm8978_set_bias_level(struct snd_soc_codec *codec, dev_dbg(codec->dev, "%s: %d, %x\n", __func__, level, power1); - codec->dapm.bias_level = level; return 0; } @@ -928,7 +927,7 @@ static int wm8978_suspend(struct snd_soc_codec *codec) { struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec); - wm8978_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); /* Also switch PLL off */ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, 0); @@ -944,7 +943,7 @@ static int wm8978_resume(struct snd_soc_codec *codec) /* Sync reg_cache with the hardware */ regcache_sync(wm8978->regmap); - wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); if (wm8978->f_pllout) /* Switch PLL on */ diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index 5d1cf08..2fdd2c6 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -915,7 +915,7 @@ static int wm8983_set_bias_level(struct snd_soc_codec *codec, 1 << WM8983_VMIDSEL_SHIFT); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regcache_sync(wm8983->regmap); if (ret < 0) { dev_err(codec->dev, "Failed to sync cache: %d\n", ret); @@ -963,7 +963,6 @@ static int wm8983_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index 0b3b54c..8a85f50 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -897,7 +897,7 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec, 1 << WM8985_VMIDSEL_SHIFT); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8985->supplies), wm8985->supplies); if (ret) { @@ -957,7 +957,6 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 24968aa..f13a995 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -738,7 +738,7 @@ static int wm8988_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_sync(wm8988->regmap); /* VREF, VMID=2x5k */ @@ -756,7 +756,6 @@ static int wm8988_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8988_PWR1, 0x0000); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index c93bffc..1993fd2 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -1124,7 +1124,7 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regcache_sync(wm8990->regmap); if (ret < 0) { dev_err(codec->dev, "Failed to sync cache: %d\n", ret); @@ -1227,7 +1227,6 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -1281,7 +1280,7 @@ static int wm8990_probe(struct snd_soc_codec *codec) wm8990_reset(codec); /* charge output caps */ - wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); snd_soc_update_bits(codec, WM8990_AUDIO_INTERFACE_4, WM8990_ALRCGPIO1, WM8990_ALRCGPIO1); diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index 49df0dc..44a6777 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -1131,7 +1131,7 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_sync(wm8991->regmap); /* Enable all output discharge bits */ snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE | @@ -1224,7 +1224,6 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 2e70a27..8a8db86 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -992,7 +992,7 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies); if (ret != 0) @@ -1065,8 +1065,6 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } @@ -1485,7 +1483,7 @@ static struct snd_soc_dai_driver wm8993_dai = { static int wm8993_probe(struct snd_soc_codec *codec) { struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); wm8993->hubs_data.hp_startup_mode = 1; wm8993->hubs_data.dcs_codes_l = -2; @@ -1539,7 +1537,7 @@ static int wm8993_probe(struct snd_soc_codec *codec) * VMID as an output and can disable it. */ if (wm8993->pdata.lineout1_diff && wm8993->pdata.lineout2_diff) - codec->dapm.idle_bias_off = 1; + dapm->idle_bias_off = 1; return 0; @@ -1563,7 +1561,7 @@ static int wm8993_suspend(struct snd_soc_codec *codec) wm8993->fll_fout = fll_fout; wm8993->fll_fref = fll_fref; - wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -1573,7 +1571,7 @@ static int wm8993_resume(struct snd_soc_codec *codec) struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec); int ret; - wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Restart the FLL? */ if (wm8993->fll_fout) { diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 4fbc768..962e1d3 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -212,6 +212,7 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif) static int configure_clock(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int change, new; @@ -239,7 +240,7 @@ static int configure_clock(struct snd_soc_codec *codec) change = snd_soc_update_bits(codec, WM8994_CLOCKING_1, WM8994_SYSCLK_SRC, new); if (change) - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); wm8958_micd_set_rate(codec); @@ -2492,12 +2493,12 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, break; } - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) active_reference(codec); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { switch (control->type) { case WM8958: if (control->revision == 0) { @@ -2521,7 +2522,7 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, WM8994_LINEOUT2_DISCH); } - if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) active_dereference(codec); /* MICBIAS into bypass mode on newer devices */ @@ -2541,20 +2542,18 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_OFF: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) wm8994->cur_fw = NULL; break; } - codec->dapm.bias_level = level; - return 0; } int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); switch (mode) { case WM8994_VMID_NORMAL: @@ -2754,7 +2753,7 @@ static struct { }; static int fs_ratios[] = { - 64, 128, 192, 256, 348, 512, 768, 1024, 1408, 1536 + 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536 }; static int bclk_divs[] = { @@ -3163,7 +3162,7 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec) i + 1, ret); } - wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -3356,6 +3355,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int micbias) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994_micdet *micdet; struct wm8994 *control = wm8994->wm8994; @@ -3370,20 +3370,16 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, case 1: micdet = &wm8994->micdet[0]; if (jack) - ret = snd_soc_dapm_force_enable_pin(&codec->dapm, - "MICBIAS1"); + ret = snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); else - ret = snd_soc_dapm_disable_pin(&codec->dapm, - "MICBIAS1"); + ret = snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); break; case 2: micdet = &wm8994->micdet[1]; if (jack) - ret = snd_soc_dapm_force_enable_pin(&codec->dapm, - "MICBIAS1"); + ret = snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); else - ret = snd_soc_dapm_disable_pin(&codec->dapm, - "MICBIAS1"); + ret = snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); break; default: dev_warn(codec->dev, "Invalid MICBIAS %d\n", micbias); @@ -3415,7 +3411,7 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, WM8994_MIC2_DET_DB_MASK | WM8994_MIC2_SHRT_DB_MASK, WM8994_MIC1_DET_DB | WM8994_MIC1_SHRT_DB); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); return 0; } @@ -3505,6 +3501,7 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data) /* Should be called with accdet_lock held */ static void wm1811_micd_stop(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); if (!wm8994->jackdet) @@ -3515,8 +3512,7 @@ static void wm1811_micd_stop(struct snd_soc_codec *codec) wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_JACK); if (wm8994->wm8994->pdata.jd_ext_cap) - snd_soc_dapm_disable_pin(&codec->dapm, - "MICBIAS2"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS2"); } static void wm8958_button_det(struct snd_soc_codec *codec, u16 status) @@ -3625,14 +3621,14 @@ static void wm1811_mic_work(struct work_struct *work) mic_work.work); struct wm8994 *control = wm8994->wm8994; struct snd_soc_codec *codec = wm8994->hubs.codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); pm_runtime_get_sync(codec->dev); /* If required for an external cap force MICBIAS on */ if (control->pdata.jd_ext_cap) { - snd_soc_dapm_force_enable_pin(&codec->dapm, - "MICBIAS2"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS2"); + snd_soc_dapm_sync(dapm); } mutex_lock(&wm8994->accdet_lock); @@ -3664,6 +3660,7 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) struct wm8994_priv *wm8994 = data; struct wm8994 *control = wm8994->wm8994; struct snd_soc_codec *codec = wm8994->hubs.codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int reg, delay; bool present; @@ -3724,7 +3721,7 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) /* Turn off MICBIAS if it was on for an external cap */ if (control->pdata.jd_ext_cap && !present) - snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS2"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS2"); if (present) snd_soc_jack_report(wm8994->micdet[0].jack, @@ -3770,6 +3767,7 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, wm1811_micdet_cb det_cb, void *det_cb_data, wm1811_mic_id_cb id_cb, void *id_cb_data) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; u16 micd_lvl_sel; @@ -3783,8 +3781,8 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, } if (jack) { - snd_soc_dapm_force_enable_pin(&codec->dapm, "CLK_SYS"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_force_enable_pin(dapm, "CLK_SYS"); + snd_soc_dapm_sync(dapm); wm8994->micdet[0].jack = jack; @@ -3819,7 +3817,7 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, snd_soc_update_bits(codec, WM8958_MIC_DETECT_2, WM8958_MICD_LVL_SEL_MASK, micd_lvl_sel); - WARN_ON(codec->dapm.bias_level > SND_SOC_BIAS_STANDBY); + WARN_ON(snd_soc_codec_get_bias_level(codec) > SND_SOC_BIAS_STANDBY); /* * If we can use jack detection start off with that, @@ -3846,8 +3844,8 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0); wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_NONE); - snd_soc_dapm_disable_pin(&codec->dapm, "CLK_SYS"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_disable_pin(dapm, "CLK_SYS"); + snd_soc_dapm_sync(dapm); } return 0; @@ -3985,9 +3983,9 @@ static irqreturn_t wm8994_temp_shut(int irq, void *data) static int wm8994_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8994 *control = dev_get_drvdata(codec->dev->parent); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; unsigned int reg; int ret, i; @@ -4018,7 +4016,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->micdet_irq = control->pdata.micdet_irq; /* By default use idle_bias_off, will override for WM8994 */ - codec->dapm.idle_bias_off = 1; + dapm->idle_bias_off = 1; /* Set revision-specific configuration */ switch (control->type) { @@ -4026,7 +4024,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) /* Single ended line outputs should have VMID on. */ if (!control->pdata.lineout1_diff || !control->pdata.lineout2_diff) - codec->dapm.idle_bias_off = 0; + dapm->idle_bias_off = 0; switch (control->revision) { case 2: @@ -4086,7 +4084,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) if (wm8994->micdet_irq) ret = request_threaded_irq(wm8994->micdet_irq, NULL, wm8994_mic_irq, - IRQF_TRIGGER_RISING, + IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "Mic1 detect", wm8994); else @@ -4134,7 +4133,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) if (wm8994->micdet_irq) { ret = request_threaded_irq(wm8994->micdet_irq, NULL, wm8958_mic_irq, - IRQF_TRIGGER_RISING, + IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "Mic detect", wm8994); if (ret != 0) diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 66103c2..687c4dd 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -721,6 +721,7 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif) static int configure_clock(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8995_priv *wm8995; int change, new; @@ -751,7 +752,7 @@ static int configure_clock(struct snd_soc_codec *codec) if (!change) return 0; - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); return 0; } @@ -1965,7 +1966,7 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); if (ret) @@ -1990,7 +1991,6 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 308748a..3dd063f 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -1590,7 +1590,7 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); if (ret != 0) { @@ -1628,8 +1628,6 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } @@ -2247,7 +2245,7 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, wm8996_polarity_fn polarity_cb) { struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); wm8996->jack = jack; wm8996->detecting = true; @@ -2292,6 +2290,7 @@ EXPORT_SYMBOL_GPL(wm8996_detect); static void wm8996_hpdet_irq(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); int val, reg, report; @@ -2345,12 +2344,14 @@ out: snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, WM8996_MICD_ENA); - snd_soc_dapm_disable_pin(&codec->dapm, "Bandgap"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_disable_pin(dapm, "Bandgap"); + snd_soc_dapm_sync(dapm); } static void wm8996_hpdet_start(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + /* Unclamp the output, we can't measure while we're shorting it */ snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1, WM8996_HPOUT1L_RMV_SHORT | @@ -2359,8 +2360,8 @@ static void wm8996_hpdet_start(struct snd_soc_codec *codec) WM8996_HPOUT1R_RMV_SHORT); /* We need bandgap for HPDET */ - snd_soc_dapm_force_enable_pin(&codec->dapm, "Bandgap"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_force_enable_pin(dapm, "Bandgap"); + snd_soc_dapm_sync(dapm); /* Go into headphone detect left mode */ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, 0); @@ -2646,10 +2647,12 @@ static int wm8996_probe(struct snd_soc_codec *codec) if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) ret = request_threaded_irq(i2c->irq, NULL, wm8996_edge_irq, - irq_flags, "wm8996", codec); + irq_flags | IRQF_ONESHOT, + "wm8996", codec); else ret = request_threaded_irq(i2c->irq, NULL, wm8996_irq, - irq_flags, "wm8996", codec); + irq_flags | IRQF_ONESHOT, + "wm8996", codec); if (ret == 0) { /* Unmask the interrupt */ diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index a4d1177..4134dc7 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -40,7 +40,7 @@ struct wm8997_priv { static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); -static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0); +static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0); static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); static const struct reg_default wm8997_sysclk_reva_patch[] = { @@ -106,11 +106,13 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w, regmap_write_async(regmap, patch[i].reg, patch[i].def); break; - default: + case SND_SOC_DAPM_PRE_PMD: break; + default: + return 0; } - return 0; + return arizona_dvfs_sysclk_ev(w, kcontrol, event); } static const char *wm8997_osr_text[] = { @@ -409,7 +411,8 @@ static const struct snd_kcontrol_new wm8997_aec_loopback_mux = static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, - 0, wm8997_sysclk_ev, SND_SOC_DAPM_POST_PMU), + 0, wm8997_sysclk_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, @@ -1055,13 +1058,14 @@ static struct snd_soc_dai_driver wm8997_dai[] = { static int wm8997_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec); arizona_init_spk(codec); - snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); + snd_soc_dapm_disable_pin(dapm, "HAPTICS"); - priv->core.arizona->dapm = &codec->dapm; + priv->core.arizona->dapm = dapm; return 0; } @@ -1126,6 +1130,8 @@ static int wm8997_probe(struct platform_device *pdev) wm8997->core.arizona = arizona; wm8997->core.num_inputs = 4; + arizona_init_dvfs(&wm8997->core); + for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++) wm8997->fll[i].vco_mult = 1; diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 13a3f33..8a8b1c0 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -838,7 +838,7 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: /* Initial cold start */ - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_cache_only(wm9081->regmap, false); regcache_sync(wm9081->regmap); @@ -898,8 +898,6 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index 60d243c..13d23fc 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -425,7 +425,7 @@ static const struct snd_soc_dapm_route audio_map_in2_diff[] = { static int wm9090_add_controls(struct snd_soc_codec *codec) { struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int i; snd_soc_dapm_new_controls(dapm, wm9090_dapm_widgets, @@ -496,7 +496,7 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Restore the register cache */ regcache_sync(wm9090->regmap); } @@ -515,8 +515,6 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 98c9525..1fda104 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -610,7 +610,6 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec, ac97_write(codec, AC97_POWERDOWN, 0xffff); break; } - codec->dapm.bias_level = level; return 0; } @@ -646,7 +645,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec) if (ret < 0) return ret; - wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); if (ret == 0) { /* Sync reg_cache with the hardware after cold reset */ diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 7955295..89cd2d6 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1054,8 +1054,8 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream, SNDRV_PCM_RATE_48000) #define WM9713_PCM_FORMATS \ - (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ - SNDRV_PCM_FORMAT_S24_LE) + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) static const struct snd_soc_dai_ops wm9713_dai_ops_hifi = { .prepare = ac97_hifi_prepare, @@ -1171,7 +1171,6 @@ static int wm9713_set_bias_level(struct snd_soc_codec *codec, ac97_write(codec, AC97_POWERDOWN, 0xffff); break; } - codec->dapm.bias_level = level; return 0; } @@ -1201,7 +1200,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) if (ret < 0) return ret; - wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* do we need to re-start the PLL ? */ if (wm9713->pll_in) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index d01c209..b62ffd0 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -121,6 +121,11 @@ #define ADSP2_WDMA_CONFIG_2 0x31 #define ADSP2_RDMA_CONFIG_1 0x34 +#define ADSP2_SCRATCH0 0x40 +#define ADSP2_SCRATCH1 0x41 +#define ADSP2_SCRATCH2 0x42 +#define ADSP2_SCRATCH3 0x43 + /* * ADSP2 Control */ @@ -229,16 +234,18 @@ struct wm_coeff_ctl_ops { struct wm_coeff_ctl { const char *name; - struct wm_adsp_alg_region region; + const char *fw_name; + struct wm_adsp_alg_region alg_region; struct wm_coeff_ctl_ops ops; - struct wm_adsp *adsp; - void *private; + struct wm_adsp *dsp; unsigned int enabled:1; struct list_head list; void *cache; + unsigned int offset; size_t len; unsigned int set:1; struct snd_kcontrol *kcontrol; + unsigned int flags; }; static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, @@ -246,9 +253,9 @@ static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); - ucontrol->value.integer.value[0] = adsp[e->shift_l].fw; + ucontrol->value.integer.value[0] = dsp[e->shift_l].fw; return 0; } @@ -258,18 +265,18 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); - if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw) + if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw) return 0; if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) return -EINVAL; - if (adsp[e->shift_l].running) + if (dsp[e->shift_l].running) return -EBUSY; - adsp[e->shift_l].fw = ucontrol->value.integer.value[0]; + dsp[e->shift_l].fw = ucontrol->value.integer.value[0]; return 0; } @@ -340,28 +347,47 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, return NULL; } -static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, +static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, unsigned int offset) { - if (WARN_ON(!region)) + if (WARN_ON(!mem)) return offset; - switch (region->type) { + switch (mem->type) { case WMFW_ADSP1_PM: - return region->base + (offset * 3); + return mem->base + (offset * 3); case WMFW_ADSP1_DM: - return region->base + (offset * 2); + return mem->base + (offset * 2); case WMFW_ADSP2_XM: - return region->base + (offset * 2); + return mem->base + (offset * 2); case WMFW_ADSP2_YM: - return region->base + (offset * 2); + return mem->base + (offset * 2); case WMFW_ADSP1_ZM: - return region->base + (offset * 2); + return mem->base + (offset * 2); default: WARN(1, "Unknown memory region type"); return offset; } } +static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) +{ + u16 scratch[4]; + int ret; + + ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0, + scratch, sizeof(scratch)); + if (ret) { + adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret); + return; + } + + adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", + be16_to_cpu(scratch[0]), + be16_to_cpu(scratch[1]), + be16_to_cpu(scratch[2]), + be16_to_cpu(scratch[3])); +} + static int wm_coeff_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -372,40 +398,39 @@ static int wm_coeff_info(struct snd_kcontrol *kcontrol, return 0; } -static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, +static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, const void *buf, size_t len) { - struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; - struct wm_adsp_alg_region *region = &ctl->region; + struct wm_adsp_alg_region *alg_region = &ctl->alg_region; const struct wm_adsp_region *mem; - struct wm_adsp *adsp = ctl->adsp; + struct wm_adsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg; - mem = wm_adsp_find_region(adsp, region->type); + mem = wm_adsp_find_region(dsp, alg_region->type); if (!mem) { - adsp_err(adsp, "No base for region %x\n", - region->type); + adsp_err(dsp, "No base for region %x\n", + alg_region->type); return -EINVAL; } - reg = ctl->region.base; + reg = ctl->alg_region.base + ctl->offset; reg = wm_adsp_region_to_reg(mem, reg); scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA); if (!scratch) return -ENOMEM; - ret = regmap_raw_write(adsp->regmap, reg, scratch, + ret = regmap_raw_write(dsp->regmap, reg, scratch, ctl->len); if (ret) { - adsp_err(adsp, "Failed to write %zu bytes to %x: %d\n", + adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", ctl->len, reg, ret); kfree(scratch); return ret; } - adsp_dbg(adsp, "Wrote %zu bytes to %x\n", ctl->len, reg); + adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg); kfree(scratch); @@ -424,42 +449,41 @@ static int wm_coeff_put(struct snd_kcontrol *kcontrol, if (!ctl->enabled) return 0; - return wm_coeff_write_control(kcontrol, p, ctl->len); + return wm_coeff_write_control(ctl, p, ctl->len); } -static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, +static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, void *buf, size_t len) { - struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; - struct wm_adsp_alg_region *region = &ctl->region; + struct wm_adsp_alg_region *alg_region = &ctl->alg_region; const struct wm_adsp_region *mem; - struct wm_adsp *adsp = ctl->adsp; + struct wm_adsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg; - mem = wm_adsp_find_region(adsp, region->type); + mem = wm_adsp_find_region(dsp, alg_region->type); if (!mem) { - adsp_err(adsp, "No base for region %x\n", - region->type); + adsp_err(dsp, "No base for region %x\n", + alg_region->type); return -EINVAL; } - reg = ctl->region.base; + reg = ctl->alg_region.base + ctl->offset; reg = wm_adsp_region_to_reg(mem, reg); scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA); if (!scratch) return -ENOMEM; - ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len); + ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len); if (ret) { - adsp_err(adsp, "Failed to read %zu bytes from %x: %d\n", + adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", ctl->len, reg, ret); kfree(scratch); return ret; } - adsp_dbg(adsp, "Read %zu bytes from %x\n", ctl->len, reg); + adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg); memcpy(buf, scratch, ctl->len); kfree(scratch); @@ -473,17 +497,25 @@ static int wm_coeff_get(struct snd_kcontrol *kcontrol, struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; char *p = ucontrol->value.bytes.data; + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { + if (ctl->enabled) + return wm_coeff_read_control(ctl, p, ctl->len); + else + return -EPERM; + } + memcpy(p, ctl->cache, ctl->len); + return 0; } struct wmfw_ctl_work { - struct wm_adsp *adsp; + struct wm_adsp *dsp; struct wm_coeff_ctl *ctl; struct work_struct work; }; -static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl) +static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) { struct snd_kcontrol_new *kcontrol; int ret; @@ -502,17 +534,25 @@ static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl) kcontrol->put = wm_coeff_put; kcontrol->private_value = (unsigned long)ctl; - ret = snd_soc_add_card_controls(adsp->card, + if (ctl->flags) { + if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE) + kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + if (ctl->flags & WMFW_CTL_FLAG_READABLE) + kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ; + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) + kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; + } + + ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); if (ret < 0) goto err_kcontrol; kfree(kcontrol); - ctl->kcontrol = snd_soc_card_get_kcontrol(adsp->card, + ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name); - list_add(&ctl->list, &adsp->ctl_list); return 0; err_kcontrol: @@ -520,6 +560,358 @@ err_kcontrol: return ret; } +static int wm_coeff_init_control_caches(struct wm_adsp *dsp) +{ + struct wm_coeff_ctl *ctl; + int ret; + + list_for_each_entry(ctl, &dsp->ctl_list, list) { + if (!ctl->enabled || ctl->set) + continue; + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) + continue; + + ret = wm_coeff_read_control(ctl, + ctl->cache, + ctl->len); + if (ret < 0) + return ret; + } + + return 0; +} + +static int wm_coeff_sync_controls(struct wm_adsp *dsp) +{ + struct wm_coeff_ctl *ctl; + int ret; + + list_for_each_entry(ctl, &dsp->ctl_list, list) { + if (!ctl->enabled) + continue; + if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { + ret = wm_coeff_write_control(ctl, + ctl->cache, + ctl->len); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static void wm_adsp_ctl_work(struct work_struct *work) +{ + struct wmfw_ctl_work *ctl_work = container_of(work, + struct wmfw_ctl_work, + work); + + wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); + kfree(ctl_work); +} + +static int wm_adsp_create_control(struct wm_adsp *dsp, + const struct wm_adsp_alg_region *alg_region, + unsigned int offset, unsigned int len, + const char *subname, unsigned int subname_len, + unsigned int flags) +{ + struct wm_coeff_ctl *ctl; + struct wmfw_ctl_work *ctl_work; + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + char *region_name; + int ret; + + if (flags & WMFW_CTL_FLAG_SYS) + return 0; + + switch (alg_region->type) { + case WMFW_ADSP1_PM: + region_name = "PM"; + break; + case WMFW_ADSP1_DM: + region_name = "DM"; + break; + case WMFW_ADSP2_XM: + region_name = "XM"; + break; + case WMFW_ADSP2_YM: + region_name = "YM"; + break; + case WMFW_ADSP1_ZM: + region_name = "ZM"; + break; + default: + adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); + return -EINVAL; + } + + switch (dsp->fw_ver) { + case 0: + case 1: + snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x", + dsp->num, region_name, alg_region->alg); + break; + default: + ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "DSP%d%c %.12s %x", dsp->num, *region_name, + wm_adsp_fw_text[dsp->fw], alg_region->alg); + + /* Truncate the subname from the start if it is too long */ + if (subname) { + int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; + int skip = 0; + + if (subname_len > avail) + skip = subname_len - avail; + + snprintf(name + ret, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s", + subname_len - skip, subname + skip); + } + break; + } + + list_for_each_entry(ctl, &dsp->ctl_list, + list) { + if (!strcmp(ctl->name, name)) { + if (!ctl->enabled) + ctl->enabled = 1; + return 0; + } + } + + ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); + if (!ctl) + return -ENOMEM; + ctl->fw_name = wm_adsp_fw_text[dsp->fw]; + ctl->alg_region = *alg_region; + ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); + if (!ctl->name) { + ret = -ENOMEM; + goto err_ctl; + } + ctl->enabled = 1; + ctl->set = 0; + ctl->ops.xget = wm_coeff_get; + ctl->ops.xput = wm_coeff_put; + ctl->dsp = dsp; + + ctl->flags = flags; + ctl->offset = offset; + if (len > 512) { + adsp_warn(dsp, "Truncating control %s from %d\n", + ctl->name, len); + len = 512; + } + ctl->len = len; + ctl->cache = kzalloc(ctl->len, GFP_KERNEL); + if (!ctl->cache) { + ret = -ENOMEM; + goto err_ctl_name; + } + + list_add(&ctl->list, &dsp->ctl_list); + + ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); + if (!ctl_work) { + ret = -ENOMEM; + goto err_ctl_cache; + } + + ctl_work->dsp = dsp; + ctl_work->ctl = ctl; + INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); + schedule_work(&ctl_work->work); + + return 0; + +err_ctl_cache: + kfree(ctl->cache); +err_ctl_name: + kfree(ctl->name); +err_ctl: + kfree(ctl); + + return ret; +} + +struct wm_coeff_parsed_alg { + int id; + const u8 *name; + int name_len; + int ncoeff; +}; + +struct wm_coeff_parsed_coeff { + int offset; + int mem_type; + const u8 *name; + int name_len; + int ctl_type; + int flags; + int len; +}; + +static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) +{ + int length; + + switch (bytes) { + case 1: + length = **pos; + break; + case 2: + length = le16_to_cpu(*((__le16 *)*pos)); + break; + default: + return 0; + } + + if (str) + *str = *pos + bytes; + + *pos += ((length + bytes) + 3) & ~0x03; + + return length; +} + +static int wm_coeff_parse_int(int bytes, const u8 **pos) +{ + int val = 0; + + switch (bytes) { + case 2: + val = le16_to_cpu(*((__le16 *)*pos)); + break; + case 4: + val = le32_to_cpu(*((__le32 *)*pos)); + break; + default: + break; + } + + *pos += bytes; + + return val; +} + +static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, + struct wm_coeff_parsed_alg *blk) +{ + const struct wmfw_adsp_alg_data *raw; + + switch (dsp->fw_ver) { + case 0: + case 1: + raw = (const struct wmfw_adsp_alg_data *)*data; + *data = raw->data; + + blk->id = le32_to_cpu(raw->id); + blk->name = raw->name; + blk->name_len = strlen(raw->name); + blk->ncoeff = le32_to_cpu(raw->ncoeff); + break; + default: + blk->id = wm_coeff_parse_int(sizeof(raw->id), data); + blk->name_len = wm_coeff_parse_string(sizeof(u8), data, + &blk->name); + wm_coeff_parse_string(sizeof(u16), data, NULL); + blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); + break; + } + + adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); + adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); + adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); +} + +static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, + struct wm_coeff_parsed_coeff *blk) +{ + const struct wmfw_adsp_coeff_data *raw; + const u8 *tmp; + int length; + + switch (dsp->fw_ver) { + case 0: + case 1: + raw = (const struct wmfw_adsp_coeff_data *)*data; + *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); + + blk->offset = le16_to_cpu(raw->hdr.offset); + blk->mem_type = le16_to_cpu(raw->hdr.type); + blk->name = raw->name; + blk->name_len = strlen(raw->name); + blk->ctl_type = le16_to_cpu(raw->ctl_type); + blk->flags = le16_to_cpu(raw->flags); + blk->len = le32_to_cpu(raw->len); + break; + default: + tmp = *data; + blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); + blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); + length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); + blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, + &blk->name); + wm_coeff_parse_string(sizeof(u8), &tmp, NULL); + wm_coeff_parse_string(sizeof(u16), &tmp, NULL); + blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp); + blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); + blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); + + *data = *data + sizeof(raw->hdr) + length; + break; + } + + adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); + adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); + adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); + adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); + adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); + adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); +} + +static int wm_adsp_parse_coeff(struct wm_adsp *dsp, + const struct wmfw_region *region) +{ + struct wm_adsp_alg_region alg_region = {}; + struct wm_coeff_parsed_alg alg_blk; + struct wm_coeff_parsed_coeff coeff_blk; + const u8 *data = region->data; + int i, ret; + + wm_coeff_parse_alg(dsp, &data, &alg_blk); + for (i = 0; i < alg_blk.ncoeff; i++) { + wm_coeff_parse_coeff(dsp, &data, &coeff_blk); + + switch (coeff_blk.ctl_type) { + case SNDRV_CTL_ELEM_TYPE_BYTES: + break; + default: + adsp_err(dsp, "Unknown control type: %d\n", + coeff_blk.ctl_type); + return -EINVAL; + } + + alg_region.type = coeff_blk.mem_type; + alg_region.alg = alg_blk.id; + + ret = wm_adsp_create_control(dsp, &alg_region, + coeff_blk.offset, + coeff_blk.len, + coeff_blk.name, + coeff_blk.name_len, + coeff_blk.flags); + if (ret < 0) + adsp_err(dsp, "Failed to create control: %.*s, %d\n", + coeff_blk.name_len, coeff_blk.name, ret); + } + + return 0; +} + static int wm_adsp_load(struct wm_adsp *dsp) { LIST_HEAD(buf_list); @@ -568,12 +960,22 @@ static int wm_adsp_load(struct wm_adsp *dsp) goto out_fw; } - if (header->ver != 0) { + switch (header->ver) { + case 0: + adsp_warn(dsp, "%s: Depreciated file format %d\n", + file, header->ver); + break; + case 1: + case 2: + break; + default: adsp_err(dsp, "%s: unknown file format %d\n", file, header->ver); goto out_fw; } + adsp_info(dsp, "Firmware version: %d\n", header->ver); + dsp->fw_ver = header->ver; if (header->core != dsp->type) { adsp_err(dsp, "%s: invalid core %d != %d\n", @@ -638,6 +1040,12 @@ static int wm_adsp_load(struct wm_adsp *dsp) text = kzalloc(le32_to_cpu(region->len) + 1, GFP_KERNEL); break; + case WMFW_ALGORITHM_DATA: + region_name = "Algorithm"; + ret = wm_adsp_parse_coeff(dsp, region); + if (ret != 0) + goto out_fw; + break; case WMFW_INFO_TEXT: region_name = "Information"; text = kzalloc(le32_to_cpu(region->len) + 1, @@ -730,444 +1138,316 @@ out: return ret; } -static int wm_coeff_init_control_caches(struct wm_adsp *adsp) +static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, + const struct wm_adsp_alg_region *alg_region) { struct wm_coeff_ctl *ctl; - int ret; - list_for_each_entry(ctl, &adsp->ctl_list, list) { - if (!ctl->enabled || ctl->set) - continue; - ret = wm_coeff_read_control(ctl->kcontrol, - ctl->cache, - ctl->len); - if (ret < 0) - return ret; + list_for_each_entry(ctl, &dsp->ctl_list, list) { + if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && + alg_region->alg == ctl->alg_region.alg && + alg_region->type == ctl->alg_region.type) { + ctl->alg_region.base = alg_region->base; + } } - - return 0; } -static int wm_coeff_sync_controls(struct wm_adsp *adsp) +static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, + unsigned int pos, unsigned int len) { - struct wm_coeff_ctl *ctl; + void *alg; int ret; + __be32 val; - list_for_each_entry(ctl, &adsp->ctl_list, list) { - if (!ctl->enabled) - continue; - if (ctl->set) { - ret = wm_coeff_write_control(ctl->kcontrol, - ctl->cache, - ctl->len); - if (ret < 0) - return ret; - } + if (n_algs == 0) { + adsp_err(dsp, "No algorithms\n"); + return ERR_PTR(-EINVAL); } - return 0; -} - -static void wm_adsp_ctl_work(struct work_struct *work) -{ - struct wmfw_ctl_work *ctl_work = container_of(work, - struct wmfw_ctl_work, - work); - - wmfw_add_ctl(ctl_work->adsp, ctl_work->ctl); - kfree(ctl_work); -} - -static int wm_adsp_create_control(struct wm_adsp *dsp, - const struct wm_adsp_alg_region *region) - -{ - struct wm_coeff_ctl *ctl; - struct wmfw_ctl_work *ctl_work; - char *name; - char *region_name; - int ret; - - name = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!name) - return -ENOMEM; + if (n_algs > 1024) { + adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); + return ERR_PTR(-EINVAL); + } - switch (region->type) { - case WMFW_ADSP1_PM: - region_name = "PM"; - break; - case WMFW_ADSP1_DM: - region_name = "DM"; - break; - case WMFW_ADSP2_XM: - region_name = "XM"; - break; - case WMFW_ADSP2_YM: - region_name = "YM"; - break; - case WMFW_ADSP1_ZM: - region_name = "ZM"; - break; - default: - ret = -EINVAL; - goto err_name; + /* Read the terminator first to validate the length */ + ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm list end: %d\n", + ret); + return ERR_PTR(ret); } - snprintf(name, PAGE_SIZE, "DSP%d %s %x", - dsp->num, region_name, region->alg); + if (be32_to_cpu(val) != 0xbedead) + adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", + pos + len, be32_to_cpu(val)); - list_for_each_entry(ctl, &dsp->ctl_list, - list) { - if (!strcmp(ctl->name, name)) { - if (!ctl->enabled) - ctl->enabled = 1; - goto found; - } - } + alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA); + if (!alg) + return ERR_PTR(-ENOMEM); - ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); - if (!ctl) { - ret = -ENOMEM; - goto err_name; - } - ctl->region = *region; - ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); - if (!ctl->name) { - ret = -ENOMEM; - goto err_ctl; + ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm list: %d\n", + ret); + kfree(alg); + return ERR_PTR(ret); } - ctl->enabled = 1; - ctl->set = 0; - ctl->ops.xget = wm_coeff_get; - ctl->ops.xput = wm_coeff_put; - ctl->adsp = dsp; - ctl->len = region->len; - ctl->cache = kzalloc(ctl->len, GFP_KERNEL); - if (!ctl->cache) { - ret = -ENOMEM; - goto err_ctl_name; - } + return alg; +} - ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); - if (!ctl_work) { - ret = -ENOMEM; - goto err_ctl_cache; - } +static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, + int type, __be32 id, + __be32 base) +{ + struct wm_adsp_alg_region *alg_region; - ctl_work->adsp = dsp; - ctl_work->ctl = ctl; - INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); - schedule_work(&ctl_work->work); + alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); + if (!alg_region) + return ERR_PTR(-ENOMEM); -found: - kfree(name); + alg_region->type = type; + alg_region->alg = be32_to_cpu(id); + alg_region->base = be32_to_cpu(base); - return 0; + list_add_tail(&alg_region->list, &dsp->alg_regions); -err_ctl_cache: - kfree(ctl->cache); -err_ctl_name: - kfree(ctl->name); -err_ctl: - kfree(ctl); -err_name: - kfree(name); - return ret; + if (dsp->fw_ver > 0) + wm_adsp_ctl_fixup_base(dsp, alg_region); + + return alg_region; } -static int wm_adsp_setup_algs(struct wm_adsp *dsp) +static int wm_adsp1_setup_algs(struct wm_adsp *dsp) { - struct regmap *regmap = dsp->regmap; struct wmfw_adsp1_id_hdr adsp1_id; - struct wmfw_adsp2_id_hdr adsp2_id; struct wmfw_adsp1_alg_hdr *adsp1_alg; - struct wmfw_adsp2_alg_hdr *adsp2_alg; - void *alg, *buf; - struct wm_adsp_alg_region *region; + struct wm_adsp_alg_region *alg_region; const struct wm_adsp_region *mem; - unsigned int pos, term; - size_t algs, buf_size; - __be32 val; + unsigned int pos, len; + size_t n_algs; int i, ret; - switch (dsp->type) { - case WMFW_ADSP1: - mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); - break; - case WMFW_ADSP2: - mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); - break; - default: - mem = NULL; - break; - } - + mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); if (WARN_ON(!mem)) return -EINVAL; - switch (dsp->type) { - case WMFW_ADSP1: - ret = regmap_raw_read(regmap, mem->base, &adsp1_id, - sizeof(adsp1_id)); - if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm info: %d\n", - ret); - return ret; - } - - buf = &adsp1_id; - buf_size = sizeof(adsp1_id); - - algs = be32_to_cpu(adsp1_id.algs); - dsp->fw_id = be32_to_cpu(adsp1_id.fw.id); - adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", - dsp->fw_id, - (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, - (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, - be32_to_cpu(adsp1_id.fw.ver) & 0xff, - algs); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP1_ZM; - region->alg = be32_to_cpu(adsp1_id.fw.id); - region->base = be32_to_cpu(adsp1_id.zm); - list_add_tail(®ion->list, &dsp->alg_regions); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP1_DM; - region->alg = be32_to_cpu(adsp1_id.fw.id); - region->base = be32_to_cpu(adsp1_id.dm); - list_add_tail(®ion->list, &dsp->alg_regions); - - pos = sizeof(adsp1_id) / 2; - term = pos + ((sizeof(*adsp1_alg) * algs) / 2); - break; - - case WMFW_ADSP2: - ret = regmap_raw_read(regmap, mem->base, &adsp2_id, - sizeof(adsp2_id)); - if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm info: %d\n", - ret); - return ret; - } - - buf = &adsp2_id; - buf_size = sizeof(adsp2_id); - - algs = be32_to_cpu(adsp2_id.algs); - dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); - adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", - dsp->fw_id, - (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16, - (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8, - be32_to_cpu(adsp2_id.fw.ver) & 0xff, - algs); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP2_XM; - region->alg = be32_to_cpu(adsp2_id.fw.id); - region->base = be32_to_cpu(adsp2_id.xm); - list_add_tail(®ion->list, &dsp->alg_regions); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP2_YM; - region->alg = be32_to_cpu(adsp2_id.fw.id); - region->base = be32_to_cpu(adsp2_id.ym); - list_add_tail(®ion->list, &dsp->alg_regions); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP2_ZM; - region->alg = be32_to_cpu(adsp2_id.fw.id); - region->base = be32_to_cpu(adsp2_id.zm); - list_add_tail(®ion->list, &dsp->alg_regions); - - pos = sizeof(adsp2_id) / 2; - term = pos + ((sizeof(*adsp2_alg) * algs) / 2); - break; - - default: - WARN(1, "Unknown DSP type"); - return -EINVAL; - } - - if (algs == 0) { - adsp_err(dsp, "No algorithms\n"); - return -EINVAL; - } - - if (algs > 1024) { - adsp_err(dsp, "Algorithm count %zx excessive\n", algs); - print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET, - buf, buf_size); - return -EINVAL; - } - - /* Read the terminator first to validate the length */ - ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val)); + ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, + sizeof(adsp1_id)); if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm list end: %d\n", - ret); + adsp_err(dsp, "Failed to read algorithm info: %d\n", + ret); return ret; } - if (be32_to_cpu(val) != 0xbedead) - adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", - term, be32_to_cpu(val)); - - alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA); - if (!alg) - return -ENOMEM; - - ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2); - if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm list: %d\n", - ret); - goto out; - } - - adsp1_alg = alg; - adsp2_alg = alg; - - for (i = 0; i < algs; i++) { - switch (dsp->type) { - case WMFW_ADSP1: - adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", - i, be32_to_cpu(adsp1_alg[i].alg.id), - (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, - (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, - be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, - be32_to_cpu(adsp1_alg[i].dm), - be32_to_cpu(adsp1_alg[i].zm)); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - 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); - region->len = 0; - list_add_tail(®ion->list, &dsp->alg_regions); - if (i + 1 < algs) { - region->len = be32_to_cpu(adsp1_alg[i + 1].dm); - region->len -= be32_to_cpu(adsp1_alg[i].dm); - region->len *= 4; - wm_adsp_create_control(dsp, region); + n_algs = be32_to_cpu(adsp1_id.n_algs); + dsp->fw_id = be32_to_cpu(adsp1_id.fw.id); + adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", + dsp->fw_id, + (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, + be32_to_cpu(adsp1_id.fw.ver) & 0xff, + n_algs); + + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, + adsp1_id.fw.id, adsp1_id.zm); + if (IS_ERR(alg_region)) + return PTR_ERR(alg_region); + + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, + adsp1_id.fw.id, adsp1_id.dm); + if (IS_ERR(alg_region)) + return PTR_ERR(alg_region); + + pos = sizeof(adsp1_id) / 2; + len = (sizeof(*adsp1_alg) * n_algs) / 2; + + adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); + if (IS_ERR(adsp1_alg)) + return PTR_ERR(adsp1_alg); + + for (i = 0; i < n_algs; i++) { + adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", + i, be32_to_cpu(adsp1_alg[i].alg.id), + (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, + be32_to_cpu(adsp1_alg[i].dm), + be32_to_cpu(adsp1_alg[i].zm)); + + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, + adsp1_alg[i].alg.id, + adsp1_alg[i].dm); + if (IS_ERR(alg_region)) { + ret = PTR_ERR(alg_region); + goto out; + } + if (dsp->fw_ver == 0) { + if (i + 1 < n_algs) { + len = be32_to_cpu(adsp1_alg[i + 1].dm); + len -= be32_to_cpu(adsp1_alg[i].dm); + len *= 4; + wm_adsp_create_control(dsp, alg_region, 0, + len, NULL, 0, 0); } else { adsp_warn(dsp, "Missing length info for region DM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); } + } - region = kzalloc(sizeof(*region), GFP_KERNEL); - 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); - region->len = 0; - list_add_tail(®ion->list, &dsp->alg_regions); - if (i + 1 < algs) { - region->len = be32_to_cpu(adsp1_alg[i + 1].zm); - region->len -= be32_to_cpu(adsp1_alg[i].zm); - region->len *= 4; - wm_adsp_create_control(dsp, region); + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, + adsp1_alg[i].alg.id, + adsp1_alg[i].zm); + if (IS_ERR(alg_region)) { + ret = PTR_ERR(alg_region); + goto out; + } + if (dsp->fw_ver == 0) { + if (i + 1 < n_algs) { + len = be32_to_cpu(adsp1_alg[i + 1].zm); + len -= be32_to_cpu(adsp1_alg[i].zm); + len *= 4; + wm_adsp_create_control(dsp, alg_region, 0, + len, NULL, 0, 0); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); } - break; + } + } - case WMFW_ADSP2: - adsp_info(dsp, - "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", - i, be32_to_cpu(adsp2_alg[i].alg.id), - (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, - (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, - be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, - be32_to_cpu(adsp2_alg[i].xm), - be32_to_cpu(adsp2_alg[i].ym), - be32_to_cpu(adsp2_alg[i].zm)); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - 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); - region->len = 0; - list_add_tail(®ion->list, &dsp->alg_regions); - if (i + 1 < algs) { - region->len = be32_to_cpu(adsp2_alg[i + 1].xm); - region->len -= be32_to_cpu(adsp2_alg[i].xm); - region->len *= 4; - wm_adsp_create_control(dsp, region); +out: + kfree(adsp1_alg); + return ret; +} + +static int wm_adsp2_setup_algs(struct wm_adsp *dsp) +{ + struct wmfw_adsp2_id_hdr adsp2_id; + struct wmfw_adsp2_alg_hdr *adsp2_alg; + struct wm_adsp_alg_region *alg_region; + const struct wm_adsp_region *mem; + unsigned int pos, len; + size_t n_algs; + int i, ret; + + mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); + if (WARN_ON(!mem)) + return -EINVAL; + + ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, + sizeof(adsp2_id)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm info: %d\n", + ret); + return ret; + } + + n_algs = be32_to_cpu(adsp2_id.n_algs); + dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); + adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", + dsp->fw_id, + (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8, + be32_to_cpu(adsp2_id.fw.ver) & 0xff, + n_algs); + + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, + adsp2_id.fw.id, adsp2_id.xm); + if (IS_ERR(alg_region)) + return PTR_ERR(alg_region); + + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, + adsp2_id.fw.id, adsp2_id.ym); + if (IS_ERR(alg_region)) + return PTR_ERR(alg_region); + + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, + adsp2_id.fw.id, adsp2_id.zm); + if (IS_ERR(alg_region)) + return PTR_ERR(alg_region); + + pos = sizeof(adsp2_id) / 2; + len = (sizeof(*adsp2_alg) * n_algs) / 2; + + adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); + if (IS_ERR(adsp2_alg)) + return PTR_ERR(adsp2_alg); + + for (i = 0; i < n_algs; i++) { + adsp_info(dsp, + "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", + i, be32_to_cpu(adsp2_alg[i].alg.id), + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, + be32_to_cpu(adsp2_alg[i].xm), + be32_to_cpu(adsp2_alg[i].ym), + be32_to_cpu(adsp2_alg[i].zm)); + + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, + adsp2_alg[i].alg.id, + adsp2_alg[i].xm); + if (IS_ERR(alg_region)) { + ret = PTR_ERR(alg_region); + goto out; + } + if (dsp->fw_ver == 0) { + if (i + 1 < n_algs) { + len = be32_to_cpu(adsp2_alg[i + 1].xm); + len -= be32_to_cpu(adsp2_alg[i].xm); + len *= 4; + wm_adsp_create_control(dsp, alg_region, 0, + len, NULL, 0, 0); } else { adsp_warn(dsp, "Missing length info for region XM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); } + } - region = kzalloc(sizeof(*region), GFP_KERNEL); - 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); - region->len = 0; - list_add_tail(®ion->list, &dsp->alg_regions); - if (i + 1 < algs) { - region->len = be32_to_cpu(adsp2_alg[i + 1].ym); - region->len -= be32_to_cpu(adsp2_alg[i].ym); - region->len *= 4; - wm_adsp_create_control(dsp, region); + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, + adsp2_alg[i].alg.id, + adsp2_alg[i].ym); + if (IS_ERR(alg_region)) { + ret = PTR_ERR(alg_region); + goto out; + } + if (dsp->fw_ver == 0) { + if (i + 1 < n_algs) { + len = be32_to_cpu(adsp2_alg[i + 1].ym); + len -= be32_to_cpu(adsp2_alg[i].ym); + len *= 4; + wm_adsp_create_control(dsp, alg_region, 0, + len, NULL, 0, 0); } else { adsp_warn(dsp, "Missing length info for region YM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); } + } - region = kzalloc(sizeof(*region), GFP_KERNEL); - 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); - region->len = 0; - list_add_tail(®ion->list, &dsp->alg_regions); - if (i + 1 < algs) { - region->len = be32_to_cpu(adsp2_alg[i + 1].zm); - region->len -= be32_to_cpu(adsp2_alg[i].zm); - region->len *= 4; - wm_adsp_create_control(dsp, region); + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, + adsp2_alg[i].alg.id, + adsp2_alg[i].zm); + if (IS_ERR(alg_region)) { + ret = PTR_ERR(alg_region); + goto out; + } + if (dsp->fw_ver == 0) { + if (i + 1 < n_algs) { + len = be32_to_cpu(adsp2_alg[i + 1].zm); + len -= be32_to_cpu(adsp2_alg[i].zm); + len *= 4; + wm_adsp_create_control(dsp, alg_region, 0, + len, NULL, 0, 0); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); } - break; } } out: - kfree(alg); + kfree(adsp2_alg); return ret; } @@ -1354,9 +1634,9 @@ out: return ret; } -int wm_adsp1_init(struct wm_adsp *adsp) +int wm_adsp1_init(struct wm_adsp *dsp) { - INIT_LIST_HEAD(&adsp->alg_regions); + INIT_LIST_HEAD(&dsp->alg_regions); return 0; } @@ -1410,7 +1690,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; - ret = wm_adsp_setup_algs(dsp); + ret = wm_adsp1_setup_algs(dsp); if (ret != 0) goto err; @@ -1531,35 +1811,6 @@ static void wm_adsp2_boot_work(struct work_struct *work) return; } - if (dsp->dvfs) { - ret = regmap_read(dsp->regmap, - dsp->base + ADSP2_CLOCKING, &val); - if (ret != 0) { - adsp_err(dsp, "Failed to read clocking: %d\n", ret); - return; - } - - if ((val & ADSP2_CLK_SEL_MASK) >= 3) { - ret = regulator_enable(dsp->dvfs); - if (ret != 0) { - adsp_err(dsp, - "Failed to enable supply: %d\n", - ret); - return; - } - - ret = regulator_set_voltage(dsp->dvfs, - 1800000, - 1800000); - if (ret != 0) { - adsp_err(dsp, - "Failed to raise supply: %d\n", - ret); - return; - } - } - } - ret = wm_adsp2_ena(dsp); if (ret != 0) return; @@ -1568,7 +1819,7 @@ static void wm_adsp2_boot_work(struct work_struct *work) if (ret != 0) goto err; - ret = wm_adsp_setup_algs(dsp); + ret = wm_adsp2_setup_algs(dsp); if (ret != 0) goto err; @@ -1642,6 +1893,9 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_PRE_PMD: + /* Log firmware state, it can be useful for analysis */ + wm_adsp2_show_fw_status(dsp); + dsp->running = false; regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, @@ -1653,21 +1907,6 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); - if (dsp->dvfs) { - ret = regulator_set_voltage(dsp->dvfs, 1200000, - 1800000); - if (ret != 0) - adsp_warn(dsp, - "Failed to lower supply: %d\n", - ret); - - ret = regulator_disable(dsp->dvfs); - if (ret != 0) - adsp_err(dsp, - "Failed to enable supply: %d\n", - ret); - } - list_for_each_entry(ctl, &dsp->ctl_list, list) ctl->enabled = 0; @@ -1694,7 +1933,7 @@ err: } EXPORT_SYMBOL_GPL(wm_adsp2_event); -int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) +int wm_adsp2_init(struct wm_adsp *dsp) { int ret; @@ -1702,43 +1941,16 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) * Disable the DSP memory by default when in reset for a small * power saving. */ - ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL, + ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_MEM_ENA, 0); if (ret != 0) { - adsp_err(adsp, "Failed to clear memory retention: %d\n", ret); + adsp_err(dsp, "Failed to clear memory retention: %d\n", ret); return ret; } - INIT_LIST_HEAD(&adsp->alg_regions); - INIT_LIST_HEAD(&adsp->ctl_list); - INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work); - - if (dvfs) { - adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); - if (IS_ERR(adsp->dvfs)) { - ret = PTR_ERR(adsp->dvfs); - adsp_err(adsp, "Failed to get DCVDD: %d\n", ret); - return ret; - } - - ret = regulator_enable(adsp->dvfs); - if (ret != 0) { - adsp_err(adsp, "Failed to enable DCVDD: %d\n", ret); - return ret; - } - - ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); - if (ret != 0) { - adsp_err(adsp, "Failed to initialise DVFS: %d\n", ret); - return ret; - } - - ret = regulator_disable(adsp->dvfs); - if (ret != 0) { - adsp_err(adsp, "Failed to disable DCVDD: %d\n", ret); - return ret; - } - } + INIT_LIST_HEAD(&dsp->alg_regions); + INIT_LIST_HEAD(&dsp->ctl_list); + INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); return 0; } diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index a4f6b64..0e5f07c 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -18,8 +18,6 @@ #include "wmfw.h" -struct regulator; - struct wm_adsp_region { int type; unsigned int base; @@ -30,7 +28,6 @@ struct wm_adsp_alg_region { unsigned int alg; int type; unsigned int base; - size_t len; }; struct wm_adsp { @@ -54,10 +51,9 @@ struct wm_adsp { int num_mems; int fw; + int fw_ver; bool running; - struct regulator *dvfs; - struct list_head ctl_list; struct work_struct boot_work; @@ -67,19 +63,22 @@ struct wm_adsp { SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \ wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) -#define WM_ADSP2(wname, num) \ +#define WM_ADSP2_E(wname, num, event_fn) \ { .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \ - .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_early_event, \ - .event_flags = SND_SOC_DAPM_PRE_PMU }, \ + .reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \ + .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }, \ { .id = snd_soc_dapm_out_drv, .name = wname, \ .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } +#define WM_ADSP2(wname, num) \ + WM_ADSP2_E(wname, num, wm_adsp2_early_event) + extern const struct snd_kcontrol_new wm_adsp1_fw_controls[]; extern const struct snd_kcontrol_new wm_adsp2_fw_controls[]; -int wm_adsp1_init(struct wm_adsp *adsp); -int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs); +int wm_adsp1_init(struct wm_adsp *dsp); +int wm_adsp2_init(struct wm_adsp *dsp); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 8366e19..fd86bd1 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -1116,7 +1116,7 @@ static const struct snd_soc_dapm_route lineout2_se_routes[] = { int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec) { - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); /* Latch volume update bits & default ZC on */ snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_1_2_VOLUME, @@ -1160,7 +1160,7 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec, int lineout1_diff, int lineout2_diff) { struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); hubs->codec = codec; diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index ef16336..7613d60 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -15,6 +15,17 @@ #include <linux/types.h> +#define WMFW_MAX_ALG_NAME 256 +#define WMFW_MAX_ALG_DESCR_NAME 256 + +#define WMFW_MAX_COEFF_NAME 256 +#define WMFW_MAX_COEFF_DESCR_NAME 256 + +#define WMFW_CTL_FLAG_SYS 0x8000 +#define WMFW_CTL_FLAG_VOLATILE 0x0004 +#define WMFW_CTL_FLAG_WRITEABLE 0x0002 +#define WMFW_CTL_FLAG_READABLE 0x0001 + struct wmfw_header { char magic[4]; __le32 len; @@ -61,7 +72,7 @@ struct wmfw_adsp1_id_hdr { struct wmfw_id_hdr fw; __be32 zm; __be32 dm; - __be32 algs; + __be32 n_algs; } __packed; struct wmfw_adsp2_id_hdr { @@ -69,7 +80,7 @@ struct wmfw_adsp2_id_hdr { __be32 zm; __be32 xm; __be32 ym; - __be32 algs; + __be32 n_algs; } __packed; struct wmfw_alg_hdr { @@ -90,6 +101,28 @@ struct wmfw_adsp2_alg_hdr { __be32 ym; } __packed; +struct wmfw_adsp_alg_data { + __le32 id; + u8 name[WMFW_MAX_ALG_NAME]; + u8 descr[WMFW_MAX_ALG_DESCR_NAME]; + __le32 ncoeff; + u8 data[]; +} __packed; + +struct wmfw_adsp_coeff_data { + struct { + __le16 offset; + __le16 type; + __le32 size; + } hdr; + u8 name[WMFW_MAX_COEFF_NAME]; + u8 descr[WMFW_MAX_COEFF_DESCR_NAME]; + __le16 ctl_type; + __le16 flags; + __le32 len; + u8 data[]; +} __packed; + struct wmfw_coeff_hdr { u8 magic[4]; __le32 len; @@ -117,9 +150,10 @@ struct wmfw_coeff_item { #define WMFW_ADSP1 1 #define WMFW_ADSP2 2 -#define WMFW_ABSOLUTE 0xf0 -#define WMFW_NAME_TEXT 0xfe -#define WMFW_INFO_TEXT 0xff +#define WMFW_ABSOLUTE 0xf0 +#define WMFW_ALGORITHM_DATA 0xf2 +#define WMFW_NAME_TEXT 0xfe +#define WMFW_INFO_TEXT 0xff #define WMFW_ADSP1_PM 2 #define WMFW_ADSP1_DM 3 |