diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index a1d07d347c20e30af065d204d94a314d18c408e3..1e492feaa9c66014c26871353d0df601521c87e6 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -78,11 +78,6 @@ static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev, if (ret != 0) return ret; - ret = regmap_update_bits(regmap, ARIZONA_DYNAMIC_FREQUENCY_SCALING_1, - ARIZONA_SUBSYS_MAX_FREQ, val); - if (ret != 0) - return ret; - if (val) return 0; diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index e7d08806f3e92d9c98a7c5bfc4fe84d5aead3f5f..c3152072d682d6550224b56b7a058a58d9cdd70e 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -6,27 +6,22 @@ config SND_ATMEL_SOC the ATMEL SSC interface. You will also need to select the audio interfaces to support below. +if SND_ATMEL_SOC + config SND_ATMEL_SOC_PDC - tristate - depends on SND_ATMEL_SOC + bool config SND_ATMEL_SOC_DMA - tristate - depends on SND_ATMEL_SOC + bool select SND_SOC_GENERIC_DMAENGINE_PCM config SND_ATMEL_SOC_SSC tristate - depends on SND_ATMEL_SOC - help - Say Y or M if you want to add support for codecs the - ATMEL SSC interface. You will also needs to select the individual - machine drivers to support below. config SND_AT91_SOC_SAM9G20_WM8731 tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" depends on ARCH_AT91 || COMPILE_TEST - depends on ATMEL_SSC && SND_ATMEL_SOC && SND_SOC_I2C_AND_SPI + depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI select SND_ATMEL_SOC_PDC select SND_ATMEL_SOC_SSC select SND_SOC_WM8731 @@ -37,7 +32,7 @@ config SND_AT91_SOC_SAM9G20_WM8731 config SND_ATMEL_SOC_WM8904 tristate "Atmel ASoC driver for boards using WM8904 codec" depends on ARCH_AT91 || COMPILE_TEST - depends on ATMEL_SSC && SND_ATMEL_SOC && I2C + depends on ATMEL_SSC && I2C select SND_ATMEL_SOC_SSC select SND_ATMEL_SOC_DMA select SND_SOC_WM8904 @@ -48,10 +43,11 @@ config SND_ATMEL_SOC_WM8904 config SND_AT91_SOC_SAM9X5_WM8731 tristate "SoC Audio support for WM8731-based at91sam9x5 board" depends on ARCH_AT91 || COMPILE_TEST - depends on ATMEL_SSC && SND_ATMEL_SOC && SND_SOC_I2C_AND_SPI + depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI select SND_ATMEL_SOC_SSC select SND_ATMEL_SOC_DMA select SND_SOC_WM8731 help Say Y if you want to add support for audio SoC on an at91sam9x5 based board that is using WM8731 codec. +endif diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index b327e5cc8de352b84a8bffc5260e4d6e6ea5f2df..4fa7ac91f9720508f933af9e9ad6aec60113597a 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -1,10 +1,8 @@ # AT91 Platform Support -snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o -snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o -snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o +snd-soc-atmel-pcm-$(CONFIG_SND_ATMEL_SOC_PDC) := atmel-pcm-pdc.o +snd-soc-atmel-pcm-$(CONFIG_SND_ATMEL_SOC_DMA) += atmel-pcm-dma.o +snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o $(snd-soc-atmel-pcm-y) -obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o -obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o # AT91 Machine Support diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 8de836165cf2ed1978c4846ac56e84bcbae4e6d1..d7469cdd90dc854a9f0db0dd5c666948ecf9bdf3 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -95,8 +95,9 @@ static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = { static const struct snd_soc_dapm_route intercon[] = { - /* speaker connected to LHPOUT */ + /* speaker connected to LHPOUT/RHPOUT */ {"Ext Spk", NULL, "LHPOUT"}, + {"Ext Spk", NULL, "RHPOUT"}, /* mic is connected to Mic Jack, with WM8731 Mic Bias */ {"MICIN", NULL, "Mic Bias"}, @@ -108,9 +109,7 @@ static const struct snd_soc_dapm_route intercon[] = { */ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; printk(KERN_DEBUG @@ -124,10 +123,6 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) return ret; } - /* not connected */ - snd_soc_dapm_nc_pin(dapm, "RLINEIN"); - snd_soc_dapm_nc_pin(dapm, "LLINEIN"); - #ifndef ENABLE_MIC_INPUT snd_soc_dapm_nc_pin(&rtd->card->dapm, "Int Mic"); #endif @@ -158,6 +153,7 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = { .num_dapm_widgets = ARRAY_SIZE(at91sam9g20ek_dapm_widgets), .dapm_routes = intercon, .num_dapm_routes = ARRAY_SIZE(intercon), + .fully_routed = true, }; static int at91sam9g20ek_audio_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c index d203ec9ba5b6cbe2c9d91b64c17858871705ce1f..9bdd15f408c164e9756f52e2ea041df9a0f65293 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[] = { diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 260a652e4a434b2a45c607448ab4cdc5ce30b93d..36d8425707459aec9f35bcdb9009b25fc546ad1b 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; @@ -800,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/arizona.c b/sound/soc/codecs/arizona.c index 0cb2962ddb9e8bf8dc9d2be2d4dd16fe82276bf1..88f6df21ad959e9d0bcf3feac761080114b140e5 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -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) { diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 11ff899b02724fae943d2bc1ce98550b26a62985..84e119a5651533ff1c012c08617384078b254f68 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/wm5102.c b/sound/soc/codecs/wm5102.c index b1537046e9fdb009d9ad015eb2be96cc3086ccdd..d8959e31853d6630763237cc9f8add13e87578bd 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -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"), @@ -1910,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; @@ -1919,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 efcfe180cbbca8a85925b79e886ada253eca73c1..14a7739d6c0960e89f7c698e7bb2b00d3dc7b5cf 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -1696,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/wm8997.c b/sound/soc/codecs/wm8997.c index 52404d7bc79047443fff2fecfdcd362bcfd3d3be..4134dc7e12434cb1a1e43b723eb43a9e23bcee52 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -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, @@ -1127,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/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index d01c2095452f9a0aaa5a2f1e91d2753fd48b1500..b62ffd0c133ea1f02868f774d12f9ecc70a64357 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 @@ static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl) 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 @@ static int wm_adsp_load(struct wm_adsp *dsp) 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 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) 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 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, } 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 a4f6b64deb61bcec9729d5d8355b135b647774c8..0e5f07c35d5053b996a7c916b88fdd8b56ec20c3 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/wmfw.h b/sound/soc/codecs/wmfw.h index ef163360a7459ebd7a8a70af00118cbcdb95717e..7613d60d62ea32d6d639a065b75eb2656c70dae7 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -15,6 +15,17 @@ #include +#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