diff options
Diffstat (limited to 'sound')
34 files changed, 1344 insertions, 438 deletions
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index d1b691b..3fdd87f 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -1,6 +1,6 @@ config SND_ATMEL_SOC tristate "SoC Audio for the Atmel System-on-Chip" - depends on ARCH_AT91 + depends on HAS_IOMEM help Say Y or M if you want to add support for codecs attached to the ATMEL SSC interface. You will also need @@ -24,7 +24,7 @@ config SND_ATMEL_SOC_SSC config SND_AT91_SOC_SAM9G20_WM8731 tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" - depends on ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS + depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS select SND_ATMEL_SOC_PDC select SND_ATMEL_SOC_SSC select SND_SOC_WM8731 @@ -34,7 +34,7 @@ config SND_AT91_SOC_SAM9G20_WM8731 config SND_AT91_SOC_AFEB9260 tristate "SoC Audio support for AFEB9260 board" - depends on ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC + depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC select SND_ATMEL_SOC_PDC select SND_ATMEL_SOC_SSC select SND_SOC_TLV320AIC23 diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c index 6a293c7..054ea4d 100644 --- a/sound/soc/atmel/atmel-pcm-pdc.c +++ b/sound/soc/atmel/atmel-pcm-pdc.c @@ -159,7 +159,7 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, pr_debug("atmel-pcm: " "hw_params: DMA for %s initialized " - "(dma_bytes=%u, period_size=%u)\n", + "(dma_bytes=%zu, period_size=%zu)\n", prtd->params->name, runtime->dma_bytes, prtd->period_size); @@ -201,7 +201,7 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream, int ret = 0; pr_debug("atmel-pcm:buffer_size = %ld," - "dma_area = %p, dma_bytes = %u\n", + "dma_area = %p, dma_bytes = %zu\n", rtd->buffer_size, rtd->dma_area, rtd->dma_bytes); switch (cmd) { diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c index e99f181..3109db7 100644 --- a/sound/soc/atmel/atmel-pcm.c +++ b/sound/soc/atmel/atmel-pcm.c @@ -49,7 +49,7 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, buf->private_data = NULL; buf->area = dma_alloc_coherent(pcm->card->dev, size, &buf->addr, GFP_KERNEL); - pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%d\n", + pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n", (void *)buf->area, (void *)buf->addr, size); if (!buf->area) diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h index bb45d20..12ae814 100644 --- a/sound/soc/atmel/atmel-pcm.h +++ b/sound/soc/atmel/atmel-pcm.h @@ -88,7 +88,8 @@ void atmel_pcm_free(struct snd_pcm *pcm); int atmel_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma); -#ifdef CONFIG_SND_ATMEL_SOC_PDC +#if defined(CONFIG_SND_ATMEL_SOC_PDC) || \ + defined(CONFIG_SND_ATMEL_SOC_PDC_MODULE) int atmel_pcm_pdc_platform_register(struct device *dev); void atmel_pcm_pdc_platform_unregister(struct device *dev); #else @@ -101,7 +102,8 @@ static inline void atmel_pcm_pdc_platform_unregister(struct device *dev) } #endif -#ifdef CONFIG_SND_ATMEL_SOC_DMA +#if defined(CONFIG_SND_ATMEL_SOC_DMA) || \ + defined(CONFIG_SND_ATMEL_SOC_DMA_MODULE) int atmel_pcm_dma_platform_register(struct device *dev); void atmel_pcm_dma_platform_unregister(struct device *dev); #else diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 1c76634..e13580d 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -42,8 +42,6 @@ #include <sound/initval.h> #include <sound/soc.h> -#include <mach/hardware.h> - #include "atmel-pcm.h" #include "atmel_ssc_dai.h" @@ -679,15 +677,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) # define atmel_ssc_resume NULL #endif /* CONFIG_PM */ -static int atmel_ssc_probe(struct snd_soc_dai *dai) -{ - struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; - - snd_soc_dai_set_drvdata(dai, ssc_p); - - return 0; -} - #define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000) #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ @@ -703,7 +692,6 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { }; static struct snd_soc_dai_driver atmel_ssc_dai = { - .probe = atmel_ssc_probe, .suspend = atmel_ssc_suspend, .resume = atmel_ssc_resume, .playback = { @@ -790,8 +778,8 @@ void atmel_ssc_put_audio(int ssc_id) { struct ssc_device *ssc = ssc_info[ssc_id].ssc; - ssc_free(ssc); asoc_ssc_exit(&ssc->pdev->dev); + ssc_free(ssc); } EXPORT_SYMBOL_GPL(atmel_ssc_put_audio); diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index da97629..2d6fbd0 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -305,10 +305,10 @@ static int at91sam9g20ek_audio_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); - atmel_ssc_put_audio(0); - snd_soc_unregister_card(card); - clk_put(mclk); + clk_disable(mclk); mclk = NULL; + snd_soc_unregister_card(card); + atmel_ssc_put_audio(0); return 0; } diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 1f0cdab..2d03787 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -26,6 +26,7 @@ #include <linux/delay.h> #include <linux/i2c.h> #include <linux/slab.h> +#include <linux/of_device.h> #include <linux/module.h> #include <sound/soc.h> #include <sound/initval.h> @@ -513,12 +514,31 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4648 = { }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static struct of_device_id ak4642_of_match[]; static int ak4642_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct device_node *np = i2c->dev.of_node; + const struct snd_soc_codec_driver *driver; + + driver = NULL; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(ak4642_of_match, &i2c->dev); + if (of_id) + driver = of_id->data; + } else { + driver = (struct snd_soc_codec_driver *)id->driver_data; + } + + if (!driver) { + dev_err(&i2c->dev, "no driver\n"); + return -EINVAL; + } + return snd_soc_register_codec(&i2c->dev, - (struct snd_soc_codec_driver *)id->driver_data, - &ak4642_dai, 1); + driver, &ak4642_dai, 1); } static int ak4642_i2c_remove(struct i2c_client *client) @@ -527,6 +547,14 @@ static int ak4642_i2c_remove(struct i2c_client *client) return 0; } +static struct of_device_id ak4642_of_match[] = { + { .compatible = "asahi-kasei,ak4642", .data = &soc_codec_dev_ak4642}, + { .compatible = "asahi-kasei,ak4643", .data = &soc_codec_dev_ak4642}, + { .compatible = "asahi-kasei,ak4648", .data = &soc_codec_dev_ak4648}, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4642_of_match); + static const struct i2c_device_id ak4642_i2c_id[] = { { "ak4642", (kernel_ulong_t)&soc_codec_dev_ak4642 }, { "ak4643", (kernel_ulong_t)&soc_codec_dev_ak4642 }, @@ -539,6 +567,7 @@ static struct i2c_driver ak4642_i2c_driver = { .driver = { .name = "ak4642-codec", .owner = THIS_MODULE, + .of_match_table = ak4642_of_match, }, .probe = ak4642_i2c_probe, .remove = ak4642_i2c_remove, diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index ef62c43..d824c98 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -56,14 +56,14 @@ #define arizona_fll_warn(_fll, fmt, ...) \ dev_warn(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_fll_dbg(_fll, fmt, ...) \ - dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) + dev_dbg(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_aif_err(_dai, fmt, ...) \ dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) #define arizona_aif_warn(_dai, fmt, ...) \ dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) #define arizona_aif_dbg(_dai, fmt, ...) \ - dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) + dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "None", @@ -141,6 +141,30 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "ASRC1R", "ASRC2L", "ASRC2R", + "ISRC1INT1", + "ISRC1INT2", + "ISRC1INT3", + "ISRC1INT4", + "ISRC1DEC1", + "ISRC1DEC2", + "ISRC1DEC3", + "ISRC1DEC4", + "ISRC2INT1", + "ISRC2INT2", + "ISRC2INT3", + "ISRC2INT4", + "ISRC2DEC1", + "ISRC2DEC2", + "ISRC2DEC3", + "ISRC2DEC4", + "ISRC3INT1", + "ISRC3INT2", + "ISRC3INT3", + "ISRC3INT4", + "ISRC3DEC1", + "ISRC3DEC2", + "ISRC3DEC3", + "ISRC3DEC4", }; EXPORT_SYMBOL_GPL(arizona_mixer_texts); @@ -220,6 +244,30 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { 0x91, 0x92, 0x93, + 0xa0, /* ISRC1INT1 */ + 0xa1, + 0xa2, + 0xa3, + 0xa4, /* ISRC1DEC1 */ + 0xa5, + 0xa6, + 0xa7, + 0xa8, /* ISRC2DEC1 */ + 0xa9, + 0xaa, + 0xab, + 0xac, /* ISRC2INT1 */ + 0xad, + 0xae, + 0xaf, + 0xb0, /* ISRC3DEC1 */ + 0xb1, + 0xb2, + 0xb3, + 0xb4, /* ISRC3INT1 */ + 0xb5, + 0xb6, + 0xb7, }; EXPORT_SYMBOL_GPL(arizona_mixer_values); @@ -275,6 +323,15 @@ const struct soc_enum arizona_lhpf4_mode = arizona_lhpf_mode_text); EXPORT_SYMBOL_GPL(arizona_lhpf4_mode); +static const char *arizona_ng_hold_text[] = { + "30ms", "120ms", "250ms", "500ms", +}; + +const struct soc_enum arizona_ng_hold = + SOC_ENUM_SINGLE(ARIZONA_NOISE_GATE_CONTROL, ARIZONA_NGATE_HOLD_SHIFT, + 4, arizona_ng_hold_text); +EXPORT_SYMBOL_GPL(arizona_ng_hold); + int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -417,6 +474,10 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, case 147456000: val |= 6 << ARIZONA_SYSCLK_FREQ_SHIFT; break; + case 0: + dev_dbg(arizona->dev, "%s cleared\n", name); + *clk = freq; + return 0; default: return -EINVAL; } @@ -635,6 +696,9 @@ static int arizona_startup(struct snd_pcm_substream *substream, return 0; } + if (base_rate == 0) + return 0; + if (base_rate % 8000) constraint = &arizona_44k1_constraint; else @@ -645,25 +709,81 @@ static int arizona_startup(struct snd_pcm_substream *substream, constraint); } +static int arizona_hw_params_rate(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + 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; + + /* + * We will need to be more flexible than this in future, + * currently we use a single sample rate for SYSCLK. + */ + for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) + if (arizona_sr_vals[i] == params_rate(params)) + break; + if (i == ARRAY_SIZE(arizona_sr_vals)) { + arizona_aif_err(dai, "Unsupported sample rate %dHz\n", + params_rate(params)); + return -EINVAL; + } + sr_val = i; + + switch (dai_priv->clk) { + case ARIZONA_CLK_SYSCLK: + snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, + ARIZONA_SAMPLE_RATE_1_MASK, sr_val); + if (base) + snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, 0); + break; + case ARIZONA_CLK_ASYNCCLK: + snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, + ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); + if (base) + snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, + 8 << ARIZONA_AIF1_RATE_SHIFT); + break; + default: + arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); + return -EINVAL; + } + + return 0; +} + static int arizona_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + struct arizona *arizona = priv->arizona; int base = dai->driver->base; const int *rates; - int i; - int bclk, lrclk, wl, frame, sr_val; + int i, ret; + int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1]; + int bclk, lrclk, wl, frame, bclk_target; if (params_rate(params) % 8000) rates = &arizona_44k1_bclk_rates[0]; else rates = &arizona_48k_bclk_rates[0]; + bclk_target = snd_soc_params_to_bclk(params); + if (chan_limit && chan_limit < params_channels(params)) { + arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit); + bclk_target /= params_channels(params); + bclk_target *= chan_limit; + } + for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { - if (rates[i] >= snd_soc_params_to_bclk(params) && + if (rates[i] >= bclk_target && rates[i] % params_rate(params) == 0) { bclk = i; break; @@ -675,16 +795,6 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) - if (arizona_sr_vals[i] == params_rate(params)) - break; - if (i == ARRAY_SIZE(arizona_sr_vals)) { - arizona_aif_err(dai, "Unsupported sample rate %dHz\n", - params_rate(params)); - return -EINVAL; - } - sr_val = i; - lrclk = rates[bclk] / params_rate(params); arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", @@ -693,28 +803,9 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, wl = snd_pcm_format_width(params_format(params)); frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl; - /* - * We will need to be more flexible than this in future, - * currently we use a single sample rate for SYSCLK. - */ - switch (dai_priv->clk) { - case ARIZONA_CLK_SYSCLK: - snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, - ARIZONA_SAMPLE_RATE_1_MASK, sr_val); - snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, - ARIZONA_AIF1_RATE_MASK, 0); - break; - case ARIZONA_CLK_ASYNCCLK: - snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, - ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); - snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, - ARIZONA_AIF1_RATE_MASK, - 8 << ARIZONA_AIF1_RATE_SHIFT); - break; - default: - arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); - return -EINVAL; - } + ret = arizona_hw_params_rate(substream, params, dai); + if (ret != 0) + return ret; snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); @@ -789,11 +880,27 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai, return snd_soc_dapm_sync(&codec->dapm); } +static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_codec *codec = dai->codec; + int base = dai->driver->base; + unsigned int reg; + + if (tristate) + reg = ARIZONA_AIF1_TRI; + else + reg = 0; + + return snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_TRI, reg); +} + const struct snd_soc_dai_ops arizona_dai_ops = { .startup = arizona_startup, .set_fmt = arizona_set_fmt, .hw_params = arizona_hw_params, .set_sysclk = arizona_dai_set_sysclk, + .set_tristate = arizona_set_tristate, }; EXPORT_SYMBOL_GPL(arizona_dai_ops); @@ -807,17 +914,6 @@ int arizona_init_dai(struct arizona_priv *priv, int id) } EXPORT_SYMBOL_GPL(arizona_init_dai); -static irqreturn_t arizona_fll_lock(int irq, void *data) -{ - struct arizona_fll *fll = data; - - arizona_fll_dbg(fll, "Lock status changed\n"); - - complete(&fll->lock); - - return IRQ_HANDLED; -} - static irqreturn_t arizona_fll_clock_ok(int irq, void *data) { struct arizona_fll *fll = data; @@ -910,7 +1006,7 @@ static int arizona_calc_fll(struct arizona_fll *fll, cfg->n = target / (ratio * Fref); - if (target % Fref) { + if (target % (ratio * Fref)) { gcd_fll = gcd(target, ratio * Fref); arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll); @@ -922,6 +1018,15 @@ static int arizona_calc_fll(struct arizona_fll *fll, cfg->lambda = 0; } + /* Round down to 16bit range with cost of accuracy lost. + * Denominator must be bigger than numerator so we only + * take care of it. + */ + while (cfg->lambda >= (1 << 16)) { + cfg->theta >>= 1; + cfg->lambda >>= 1; + } + arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", cfg->n, cfg->theta, cfg->lambda); arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", @@ -1057,7 +1162,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, { int ret; - init_completion(&fll->lock); init_completion(&fll->ok); fll->id = id; @@ -1068,13 +1172,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name), "FLL%d clock OK", id); - ret = arizona_request_irq(arizona, lock_irq, fll->lock_name, - arizona_fll_lock, fll); - if (ret != 0) { - dev_err(arizona->dev, "Failed to get FLL%d lock IRQ: %d\n", - id, ret); - } - ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name, arizona_fll_clock_ok, fll); if (ret != 0) { @@ -1089,6 +1186,40 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, } EXPORT_SYMBOL_GPL(arizona_init_fll); +/** + * arizona_set_output_mode - Set the mode of the specified output + * + * @codec: Device to configure + * @output: Output number + * @diff: True to set the output to differential mode + * + * Some systems use external analogue switches to connect more + * analogue devices to the CODEC than are supported by the device. In + * some systems this requires changing the switched output from single + * ended to differential mode dynamically at runtime, an operation + * supported using this function. + * + * Most systems have a single static configuration and should use + * platform data instead. + */ +int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff) +{ + unsigned int reg, val; + + if (output < 1 || output > 6) + return -EINVAL; + + reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8; + + if (diff) + val = ARIZONA_OUT1_MONO; + else + val = 0; + + return snd_soc_update_bits(codec, reg, ARIZONA_OUT1_MONO, val); +} +EXPORT_SYMBOL_GPL(arizona_set_output_mode); + MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 4deebeb..116372c 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -66,7 +66,7 @@ struct arizona_priv { struct arizona_dai_priv dai[ARIZONA_MAX_DAI]; }; -#define ARIZONA_NUM_MIXER_INPUTS 75 +#define ARIZONA_NUM_MIXER_INPUTS 99 extern const unsigned int arizona_mixer_tlv[]; extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; @@ -176,6 +176,8 @@ extern const struct soc_enum arizona_lhpf2_mode; extern const struct soc_enum arizona_lhpf3_mode; extern const struct soc_enum arizona_lhpf4_mode; +extern const struct soc_enum arizona_ng_hold; + extern int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -195,7 +197,6 @@ struct arizona_fll { int id; unsigned int base; unsigned int vco_mult; - struct completion lock; struct completion ok; unsigned int fref; unsigned int fout; @@ -211,4 +212,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source, extern int arizona_init_dai(struct arizona_priv *priv, int dai); +int arizona_set_output_mode(struct snd_soc_codec *codec, int output, + bool diff); + #endif diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index ac8742a..2415a41 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -167,6 +167,8 @@ struct cs4271_private { int gpio_nreset; /* GPIO that disable serial bus, if any */ int gpio_disable; + /* enable soft reset workaround */ + bool enable_soft_reset; }; /* @@ -325,6 +327,33 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream, int i, ret; unsigned int ratio, val; + if (cs4271->enable_soft_reset) { + /* + * Put the codec in soft reset and back again in case it's not + * currently streaming data. This way of bringing the codec in + * sync to the current clocks is not explicitly documented in + * the data sheet, but it seems to work fine, and in contrast + * to a read hardware reset, we don't have to sync back all + * registers every time. + */ + + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + !dai->capture_active) || + (substream->stream == SNDRV_PCM_STREAM_CAPTURE && + !dai->playback_active)) { + ret = snd_soc_update_bits(codec, CS4271_MODE2, + CS4271_MODE2_PDN, + CS4271_MODE2_PDN); + if (ret < 0) + return ret; + + ret = snd_soc_update_bits(codec, CS4271_MODE2, + CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + } + } + cs4271->rate = params_rate(params); /* Configure DAC */ @@ -484,6 +513,10 @@ static int cs4271_probe(struct snd_soc_codec *codec) if (of_get_property(codec->dev->of_node, "cirrus,amutec-eq-bmutec", NULL)) amutec_eq_bmutec = true; + + if (of_get_property(codec->dev->of_node, + "cirrus,enable-soft-reset", NULL)) + cs4271->enable_soft_reset = true; } #endif @@ -492,6 +525,7 @@ static int cs4271_probe(struct snd_soc_codec *codec) gpio_nreset = cs4271plat->gpio_nreset; amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec; + cs4271->enable_soft_reset = cs4271plat->enable_soft_reset; } if (gpio_nreset >= 0) diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 9811a54..0f6f481 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1038,7 +1038,7 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec) struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); int ret; - cs42l52->beep = input_allocate_device(); + cs42l52->beep = devm_input_allocate_device(codec->dev); if (!cs42l52->beep) { dev_err(codec->dev, "Failed to allocate beep device\n"); return; @@ -1059,7 +1059,6 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec) ret = input_register_device(cs42l52->beep); if (ret != 0) { - input_free_device(cs42l52->beep); cs42l52->beep = NULL; dev_err(codec->dev, "Failed to register beep device\n"); } @@ -1076,7 +1075,6 @@ static void cs42l52_free_beep(struct snd_soc_codec *codec) struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); device_remove_file(codec->dev, &dev_attr_beep); - input_unregister_device(cs42l52->beep); cancel_work_sync(&cs42l52->beep_work); cs42l52->beep = NULL; diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 5708a97..4989143 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1210,13 +1210,13 @@ static struct snd_soc_dai_driver aic3x_dai = { .name = "tlv320aic3x-hifi", .playback = { .stream_name = "Playback", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = AIC3X_RATES, .formats = AIC3X_FORMATS,}, .capture = { .stream_name = "Capture", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = AIC3X_RATES, .formats = AIC3X_FORMATS,}, diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 12bcae6..eb96b87 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -76,6 +76,8 @@ struct wm2000_priv { int anc_download_size; char *anc_download; + + struct mutex lock; }; static int wm2000_write(struct i2c_client *i2c, unsigned int reg, @@ -599,13 +601,20 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); int anc_active = ucontrol->value.enumerated.item[0]; + int ret; if (anc_active > 1) return -EINVAL; + mutex_lock(&wm2000->lock); + wm2000->anc_active = anc_active; - return wm2000_anc_set_mode(wm2000); + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; } static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, @@ -625,13 +634,20 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); int val = ucontrol->value.enumerated.item[0]; + int ret; if (val > 1) return -EINVAL; + mutex_lock(&wm2000->lock); + wm2000->spk_ena = val; - return wm2000_anc_set_mode(wm2000); + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; } static const struct snd_kcontrol_new wm2000_controls[] = { @@ -648,6 +664,9 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = w->codec; struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); + int ret; + + mutex_lock(&wm2000->lock); if (SND_SOC_DAPM_EVENT_ON(event)) wm2000->anc_eng_ena = 1; @@ -655,7 +674,11 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, if (SND_SOC_DAPM_EVENT_OFF(event)) wm2000->anc_eng_ena = 0; - return wm2000_anc_set_mode(wm2000); + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; } static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = { @@ -782,6 +805,8 @@ static int wm2000_i2c_probe(struct i2c_client *i2c, return -ENOMEM; } + mutex_init(&wm2000->lock); + dev_set_drvdata(&i2c->dev, wm2000); wm2000->regmap = devm_regmap_init_i2c(i2c, &wm2000_regmap); diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index d8c65f5..d5371e0 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1126,9 +1126,9 @@ SOC_DOUBLE_R_TLV("IN3 Volume", WM2200_IN3L_CONTROL, WM2200_IN3R_CONTROL, SOC_DOUBLE_R("IN1 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L, WM2200_ADC_DIGITAL_VOLUME_1R, WM2200_IN1L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L, +SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_2L, WM2200_ADC_DIGITAL_VOLUME_2R, WM2200_IN2L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L, +SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_3L, WM2200_ADC_DIGITAL_VOLUME_3R, WM2200_IN3L_MUTE_SHIFT, 1, 1), SOC_DOUBLE_R_TLV("IN1 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_1L, diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 1440b3f..5e85b64 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -45,6 +45,7 @@ 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(ng_tlv, -10200, 600, 0); static const struct wm_adsp_region wm5102_dsp1_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x100000 }, @@ -603,6 +604,17 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, return 0; } +#define WM5102_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0) + static const struct snd_kcontrol_new wm5102_snd_controls[] = { SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, ARIZONA_IN1_OSR_SHIFT, 1, 0), @@ -611,32 +623,44 @@ SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL, SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL, ARIZONA_IN3_OSR_SHIFT, 1, 0), -SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL, - ARIZONA_IN1R_CONTROL, - ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, - ARIZONA_IN2R_CONTROL, - ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL, - ARIZONA_IN3R_CONTROL, - ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), - -SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1), - -SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_MUTE_SHIFT, 1, 1), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), @@ -774,6 +798,22 @@ SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM5102_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +WM5102_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +WM5102_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L), +WM5102_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R), +WM5102_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L), +WM5102_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM5102_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM5102_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L), +WM5102_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R), + ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), @@ -880,6 +920,18 @@ ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); + ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE); @@ -1002,6 +1054,26 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, @@ -1138,6 +1210,18 @@ ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + WM_ADSP2("DSP1", 0), SND_SOC_DAPM_OUTPUT("HPOUT1L"), @@ -1193,6 +1277,14 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"), { name, "ASRC1R", "ASRC1R" }, \ { name, "ASRC2L", "ASRC2L" }, \ { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ { name, "DSP1.1", "DSP1" }, \ { name, "DSP1.2", "DSP1" }, \ { name, "DSP1.3", "DSP1" }, \ @@ -1289,6 +1381,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "ASRC2L", NULL, "ASRC2L Input" }, { "ASRC2R", NULL, "ASRC2R Input" }, + { "ISRC1DEC1", NULL, "ISRC1DEC1 Input" }, + { "ISRC1DEC2", NULL, "ISRC1DEC2 Input" }, + + { "ISRC1INT1", NULL, "ISRC1INT1 Input" }, + { "ISRC1INT2", NULL, "ISRC1INT2 Input" }, + + { "ISRC2DEC1", NULL, "ISRC2DEC1 Input" }, + { "ISRC2DEC2", NULL, "ISRC2DEC2 Input" }, + + { "ISRC2INT1", NULL, "ISRC2INT1 Input" }, + { "ISRC2INT2", NULL, "ISRC2INT2 Input" }, + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"), @@ -1336,6 +1440,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { ARIZONA_MUX_ROUTES("ASRC2L"), ARIZONA_MUX_ROUTES("ASRC2R"), + ARIZONA_MUX_ROUTES("ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2"), + + ARIZONA_MUX_ROUTES("ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2"), + ARIZONA_DSP_ROUTES("DSP1"), { "AEC Loopback", "HPOUT1L", "OUT1L" }, @@ -1463,6 +1579,10 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) if (ret != 0) return ret; + ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 1); + if (ret != 0) + return ret; + snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); priv->core.arizona->dapm = &codec->dapm; diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 7a09096..2319937 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -41,6 +41,21 @@ 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(ng_tlv, -10200, 600, 0); + +#define WM5110_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0) static const struct snd_kcontrol_new wm5110_snd_controls[] = { SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, @@ -52,37 +67,52 @@ SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL, SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL, ARIZONA_IN4_OSR_SHIFT, 1, 0), -SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL, - ARIZONA_IN1R_CONTROL, - ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, - ARIZONA_IN2R_CONTROL, - ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL, - ARIZONA_IN3R_CONTROL, - ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), - -SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN4 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L, - ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_MUTE_SHIFT, 1, 1), - -SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN4 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L, - ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN4L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L, + ARIZONA_IN4L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN4R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4R, + ARIZONA_IN4R_MUTE_SHIFT, 1, 1), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN4L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L, + ARIZONA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R, + ARIZONA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), @@ -263,6 +293,25 @@ SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT, SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM5110_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +WM5110_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +WM5110_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L), +WM5110_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R), +WM5110_NG_SRC("HPOUT3L", ARIZONA_NOISE_GATE_SELECT_3L), +WM5110_NG_SRC("HPOUT3R", ARIZONA_NOISE_GATE_SELECT_3R), +WM5110_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM5110_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM5110_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L), +WM5110_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R), +WM5110_NG_SRC("SPKDAT2L", ARIZONA_NOISE_GATE_SELECT_6L), +WM5110_NG_SRC("SPKDAT2R", ARIZONA_NOISE_GATE_SELECT_6R), + ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index b6b6548..f3f7e75 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -15,6 +15,7 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/firmware.h> +#include <linux/list.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> @@ -103,9 +104,19 @@ #define ADSP1_START_SHIFT 0 /* DSP1_START */ #define ADSP1_START_WIDTH 1 /* DSP1_START */ -#define ADSP2_CONTROL 0 -#define ADSP2_CLOCKING 1 -#define ADSP2_STATUS1 4 +/* + * ADSP1 Control 31 + */ +#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ +#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ +#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ + +#define ADSP2_CONTROL 0x0 +#define ADSP2_CLOCKING 0x1 +#define ADSP2_STATUS1 0x4 +#define ADSP2_WDMA_CONFIG_1 0x30 +#define ADSP2_WDMA_CONFIG_2 0x31 +#define ADSP2_RDMA_CONFIG_1 0x34 /* * ADSP2 Control @@ -143,6 +154,109 @@ #define ADSP2_RAM_RDY_SHIFT 0 #define ADSP2_RAM_RDY_WIDTH 1 +struct wm_adsp_buf { + struct list_head list; + void *buf; +}; + +static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, + struct list_head *list) +{ + struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); + + if (buf == NULL) + return NULL; + + buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA); + if (!buf->buf) { + kfree(buf); + return NULL; + } + + if (list) + list_add_tail(&buf->list, list); + + return buf; +} + +static void wm_adsp_buf_free(struct list_head *list) +{ + while (!list_empty(list)) { + struct wm_adsp_buf *buf = list_first_entry(list, + struct wm_adsp_buf, + list); + list_del(&buf->list); + kfree(buf->buf); + kfree(buf); + } +} + +#define WM_ADSP_NUM_FW 4 + +static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { + "MBC/VSS", "Tx", "Tx Speaker", "Rx ANC" +}; + +static struct { + const char *file; +} wm_adsp_fw[WM_ADSP_NUM_FW] = { + { .file = "mbc-vss" }, + { .file = "tx" }, + { .file = "tx-spk" }, + { .file = "rx-anc" }, +}; + +static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = adsp[e->shift_l].fw; + + return 0; +} + +static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw) + return 0; + + if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) + return -EINVAL; + + if (adsp[e->shift_l].running) + return -EBUSY; + + adsp[e->shift_l].fw = ucontrol->value.integer.value[0]; + + return 0; +} + +static const struct soc_enum wm_adsp_fw_enum[] = { + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), +}; + +const struct snd_kcontrol_new wm_adsp_fw_controls[] = { + SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], + wm_adsp_fw_get, wm_adsp_fw_put), +}; +EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, int type) @@ -156,8 +270,29 @@ 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, + unsigned int offset) +{ + switch (region->type) { + case WMFW_ADSP1_PM: + return region->base + (offset * 3); + case WMFW_ADSP1_DM: + return region->base + (offset * 2); + case WMFW_ADSP2_XM: + return region->base + (offset * 2); + case WMFW_ADSP2_YM: + return region->base + (offset * 2); + case WMFW_ADSP1_ZM: + return region->base + (offset * 2); + default: + WARN_ON(NULL != "Unknown memory region type"); + return offset; + } +} + static int wm_adsp_load(struct wm_adsp *dsp) { + LIST_HEAD(buf_list); const struct firmware *firmware; struct regmap *regmap = dsp->regmap; unsigned int pos = 0; @@ -169,7 +304,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) const struct wm_adsp_region *mem; const char *region_name; char *file, *text; - void *buf; + struct wm_adsp_buf *buf; unsigned int reg; int regions = 0; int ret, offset, type, sizes; @@ -178,7 +313,8 @@ static int wm_adsp_load(struct wm_adsp *dsp) if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num); + snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, + wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; ret = request_firmware(&firmware, file, dsp->dev); @@ -283,27 +419,27 @@ static int wm_adsp_load(struct wm_adsp *dsp) case WMFW_ADSP1_PM: BUG_ON(!mem); region_name = "PM"; - reg = mem->base + (offset * 3); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP1_DM: BUG_ON(!mem); region_name = "DM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP2_XM: BUG_ON(!mem); region_name = "XM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP2_YM: BUG_ON(!mem); region_name = "YM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP1_ZM: BUG_ON(!mem); region_name = "ZM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; default: adsp_warn(dsp, @@ -323,18 +459,16 @@ static int wm_adsp_load(struct wm_adsp *dsp) } if (reg) { - buf = kmemdup(region->data, le32_to_cpu(region->len), - GFP_KERNEL | GFP_DMA); + buf = wm_adsp_buf_alloc(region->data, + le32_to_cpu(region->len), + &buf_list); if (!buf) { adsp_err(dsp, "Out of memory\n"); return -ENOMEM; } - ret = regmap_raw_write(regmap, reg, buf, - le32_to_cpu(region->len)); - - kfree(buf); - + ret = regmap_raw_write_async(regmap, reg, buf->buf, + le32_to_cpu(region->len)); if (ret != 0) { adsp_err(dsp, "%s.%d: Failed to write %d bytes at %d in %s: %d\n", @@ -348,12 +482,20 @@ static int wm_adsp_load(struct wm_adsp *dsp) pos += le32_to_cpu(region->len) + sizeof(*region); regions++; } - + + ret = regmap_async_complete(regmap); + if (ret != 0) { + adsp_err(dsp, "Failed to complete async write: %d\n", ret); + goto out_fw; + } + if (pos > firmware->size) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, regions, pos - firmware->size); out_fw: + regmap_async_complete(regmap); + wm_adsp_buf_free(&buf_list); release_firmware(firmware); out: kfree(file); @@ -361,22 +503,222 @@ out: return ret; } +static int wm_adsp_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; + const struct wm_adsp_region *mem; + unsigned int pos, term; + size_t algs, buf_size; + __be32 val; + 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; + } + + if (mem == NULL) { + BUG_ON(mem != NULL); + 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); + adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", + be32_to_cpu(adsp1_id.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); + + 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); + adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", + be32_to_cpu(adsp2_id.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); + + pos = sizeof(adsp2_id) / 2; + term = pos + ((sizeof(*adsp2_alg) * algs) / 2); + break; + + default: + BUG_ON(NULL == "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)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm list end: %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) + return -ENOMEM; + region->type = WMFW_ADSP1_DM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].dm); + list_add_tail(®ion->list, &dsp->alg_regions); + + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP1_ZM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].zm); + list_add_tail(®ion->list, &dsp->alg_regions); + 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) + return -ENOMEM; + region->type = WMFW_ADSP2_XM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].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_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].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_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].zm); + list_add_tail(®ion->list, &dsp->alg_regions); + break; + } + } + +out: + kfree(alg); + return ret; +} + static int wm_adsp_load_coeff(struct wm_adsp *dsp) { + LIST_HEAD(buf_list); struct regmap *regmap = dsp->regmap; struct wmfw_coeff_hdr *hdr; struct wmfw_coeff_item *blk; const struct firmware *firmware; + const struct wm_adsp_region *mem; + struct wm_adsp_alg_region *alg_region; const char *region_name; int ret, pos, blocks, type, offset, reg; char *file; - void *buf; + struct wm_adsp_buf *buf; + int tmp; file = kzalloc(PAGE_SIZE, GFP_KERNEL); if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num); + snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, + wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; ret = request_firmware(&firmware, file, dsp->dev); @@ -399,6 +741,16 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) goto out_fw; } + switch (be32_to_cpu(hdr->rev) & 0xff) { + case 1: + break; + default: + adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", + file, be32_to_cpu(hdr->rev) & 0xff); + ret = -EINVAL; + goto out_fw; + } + adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, (le32_to_cpu(hdr->ver) >> 16) & 0xff, (le32_to_cpu(hdr->ver) >> 8) & 0xff, @@ -411,8 +763,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) pos - firmware->size > sizeof(*blk)) { blk = (void*)(&firmware->data[pos]); - type = be32_to_cpu(blk->type) & 0xff; - offset = le32_to_cpu(blk->offset) & 0xffffff; + type = le16_to_cpu(blk->type); + offset = le16_to_cpu(blk->offset); adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", file, blocks, le32_to_cpu(blk->id), @@ -425,52 +777,105 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) reg = 0; region_name = "Unknown"; switch (type) { - case WMFW_NAME_TEXT: - case WMFW_INFO_TEXT: + case (WMFW_NAME_TEXT << 8): + case (WMFW_INFO_TEXT << 8): break; - case WMFW_ABSOLUTE: + case (WMFW_ABSOLUTE << 8): region_name = "register"; reg = offset; break; + + case WMFW_ADSP1_DM: + case WMFW_ADSP1_ZM: + case WMFW_ADSP2_XM: + case WMFW_ADSP2_YM: + adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", + file, blocks, le32_to_cpu(blk->len), + type, le32_to_cpu(blk->id)); + + mem = wm_adsp_find_region(dsp, type); + if (!mem) { + adsp_err(dsp, "No base for region %x\n", type); + break; + } + + reg = 0; + list_for_each_entry(alg_region, + &dsp->alg_regions, list) { + if (le32_to_cpu(blk->id) == alg_region->alg && + type == alg_region->type) { + reg = alg_region->base; + reg = wm_adsp_region_to_reg(mem, + reg); + reg += offset; + } + } + + if (reg == 0) + adsp_err(dsp, "No %x for algorithm %x\n", + type, le32_to_cpu(blk->id)); + break; + default: - adsp_err(dsp, "Unknown region type %x\n", type); + adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", + file, blocks, type, pos); break; } if (reg) { - buf = kmemdup(blk->data, le32_to_cpu(blk->len), - GFP_KERNEL | GFP_DMA); + buf = wm_adsp_buf_alloc(blk->data, + le32_to_cpu(blk->len), + &buf_list); if (!buf) { adsp_err(dsp, "Out of memory\n"); return -ENOMEM; } - ret = regmap_raw_write(regmap, reg, blk->data, - le32_to_cpu(blk->len)); + adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", + file, blocks, le32_to_cpu(blk->len), + reg); + ret = regmap_raw_write_async(regmap, reg, buf->buf, + le32_to_cpu(blk->len)); if (ret != 0) { adsp_err(dsp, "%s.%d: Failed to write to %x in %s\n", file, blocks, reg, region_name); } - - kfree(buf); } - pos += le32_to_cpu(blk->len) + sizeof(*blk); + tmp = le32_to_cpu(blk->len) % 4; + if (tmp) + pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk); + else + pos += le32_to_cpu(blk->len) + sizeof(*blk); + blocks++; } + ret = regmap_async_complete(regmap); + if (ret != 0) + adsp_err(dsp, "Failed to complete async write: %d\n", ret); + if (pos > firmware->size) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, blocks, pos - firmware->size); out_fw: release_firmware(firmware); + wm_adsp_buf_free(&buf_list); out: kfree(file); return 0; } +int wm_adsp1_init(struct wm_adsp *adsp) +{ + INIT_LIST_HEAD(&adsp->alg_regions); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp1_init); + int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -479,16 +884,46 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; int ret; + int val; switch (event) { case SND_SOC_DAPM_POST_PMU: regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_SYS_ENA, ADSP1_SYS_ENA); + /* + * For simplicity set the DSP clock rate to be the + * SYSCLK rate rather than making it configurable. + */ + if(dsp->sysclk_reg) { + ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); + if (ret != 0) { + adsp_err(dsp, "Failed to read SYSCLK state: %d\n", + ret); + return ret; + } + + val = (val & dsp->sysclk_mask) + >> dsp->sysclk_shift; + + ret = regmap_update_bits(dsp->regmap, + dsp->base + ADSP1_CONTROL_31, + ADSP1_CLK_SEL_MASK, val); + if (ret != 0) { + adsp_err(dsp, "Failed to set clock rate: %d\n", + ret); + return ret; + } + } + ret = wm_adsp_load(dsp); if (ret != 0) goto err; + ret = wm_adsp_setup_algs(dsp); + if (ret != 0) + goto err; + ret = wm_adsp_load_coeff(dsp); if (ret != 0) goto err; @@ -560,6 +995,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = w->codec; struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; + struct wm_adsp_alg_region *alg_region; unsigned int val; int ret; @@ -625,6 +1061,10 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; + ret = wm_adsp_setup_algs(dsp); + if (ret != 0) + goto err; + ret = wm_adsp_load_coeff(dsp); if (ret != 0) goto err; @@ -635,13 +1075,22 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ADSP2_CORE_ENA | ADSP2_START); if (ret != 0) goto err; + + dsp->running = true; break; case SND_SOC_DAPM_PRE_PMD: + dsp->running = false; + regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); + /* Make sure DMAs are quiesced */ + regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); + 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); @@ -656,6 +1105,14 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, "Failed to enable supply: %d\n", ret); } + + while (!list_empty(&dsp->alg_regions)) { + alg_region = list_first_entry(&dsp->alg_regions, + struct wm_adsp_alg_region, + list); + list_del(&alg_region->list); + kfree(alg_region); + } break; default: @@ -685,6 +1142,8 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) return ret; } + INIT_LIST_HEAD(&adsp->alg_regions); + if (dvfs) { adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); if (IS_ERR(adsp->dvfs)) { diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index ffd29a4..cb8871a 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -25,6 +25,13 @@ struct wm_adsp_region { unsigned int base; }; +struct wm_adsp_alg_region { + struct list_head list; + unsigned int alg; + int type; + unsigned int base; +}; + struct wm_adsp { const char *part; int num; @@ -33,10 +40,18 @@ struct wm_adsp { struct regmap *regmap; int base; + int sysclk_reg; + int sysclk_mask; + int sysclk_shift; + + struct list_head alg_regions; const struct wm_adsp_region *mem; int num_mems; + int fw; + bool running; + struct regulator *dvfs; }; @@ -50,6 +65,9 @@ struct wm_adsp { .shift = num, .event = wm_adsp2_event, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } +extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; + +int wm_adsp1_init(struct wm_adsp *adsp); int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index 5632ded..ef16336 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -93,15 +93,20 @@ struct wmfw_adsp2_alg_hdr { struct wmfw_coeff_hdr { u8 magic[4]; __le32 len; - __le32 ver; + union { + __be32 rev; + __le32 ver; + }; + union { + __be32 core; + __le32 core_ver; + }; u8 data[]; } __packed; struct wmfw_coeff_item { - union { - __be32 type; - __le32 offset; - }; + __le16 offset; + __le16 type; __le32 id; __le32 ver; __le32 sr; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 55e2bf6..9321e5c 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -626,7 +626,7 @@ static int davinci_config_channel_size(struct davinci_audio_dev *dev, int word_length) { u32 fmt; - u32 rotate = (32 - word_length) / 4; + u32 rotate = (word_length / 4) & 0x7; u32 mask = (1ULL << word_length) - 1; /* diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 1aa5130..deb30d5 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -210,15 +210,19 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, switch (config->chan_nr) { case EIGHT_CHANNEL_SUPPORT: ch_reg = 3; + break; case SIX_CHANNEL_SUPPORT: ch_reg = 2; + break; case FOUR_CHANNEL_SUPPORT: ch_reg = 1; + break; case TWO_CHANNEL_SUPPORT: ch_reg = 0; break; default: dev_err(dev->dev, "channel not supported\n"); + return -EINVAL; } i2s_disable_channels(dev, substream->stream); diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index a210c8d..3b98159 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -108,13 +108,18 @@ if SND_IMX_SOC config SND_SOC_IMX_SSI tristate -config SND_SOC_IMX_PCM_FIQ +config SND_SOC_IMX_PCM tristate + +config SND_SOC_IMX_PCM_FIQ + bool select FIQ + select SND_SOC_IMX_PCM config SND_SOC_IMX_PCM_DMA - tristate + bool select SND_SOC_DMAENGINE_PCM + select SND_SOC_IMX_PCM config SND_SOC_IMX_AUDMUX tristate diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index ec14579..afd3479 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -41,10 +41,7 @@ endif obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o -obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += snd-soc-imx-pcm-fiq.o -snd-soc-imx-pcm-fiq-y := imx-pcm-fiq.o imx-pcm.o -obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += snd-soc-imx-pcm-dma.o -snd-soc-imx-pcm-dma-y := imx-pcm-dma.o imx-pcm.o +obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o # i.MX Machine Support snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index bf363d8..500f8ce 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -154,26 +154,7 @@ static struct snd_soc_platform_driver imx_soc_platform_mx2 = { .pcm_free = imx_pcm_free, }; -static int imx_soc_platform_probe(struct platform_device *pdev) +int imx_pcm_dma_init(struct platform_device *pdev) { return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); } - -static int imx_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -static struct platform_driver imx_pcm_driver = { - .driver = { - .name = "imx-pcm-audio", - .owner = THIS_MODULE, - }, - .probe = imx_soc_platform_probe, - .remove = imx_soc_platform_remove, -}; - -module_platform_driver(imx_pcm_driver); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:imx-pcm-audio"); diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 5ec362a..920f945 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -281,7 +281,7 @@ static struct snd_soc_platform_driver imx_soc_platform_fiq = { .pcm_free = imx_pcm_fiq_free, }; -static int imx_soc_platform_probe(struct platform_device *pdev) +int imx_pcm_fiq_init(struct platform_device *pdev) { struct imx_ssi *ssi = platform_get_drvdata(pdev); int ret; @@ -314,23 +314,3 @@ failed_register: return ret; } - -static int imx_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -static struct platform_driver imx_pcm_driver = { - .driver = { - .name = "imx-fiq-pcm-audio", - .owner = THIS_MODULE, - }, - - .probe = imx_soc_platform_probe, - .remove = imx_soc_platform_remove, -}; - -module_platform_driver(imx_pcm_driver); - -MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/imx-pcm.c b/sound/soc/fsl/imx-pcm.c index 0c9f188..0d0625b 100644 --- a/sound/soc/fsl/imx-pcm.c +++ b/sound/soc/fsl/imx-pcm.c @@ -31,6 +31,7 @@ int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, runtime->dma_bytes); return ret; } +EXPORT_SYMBOL_GPL(snd_imx_pcm_mmap); static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) { @@ -79,6 +80,7 @@ int imx_pcm_new(struct snd_soc_pcm_runtime *rtd) out: return ret; } +EXPORT_SYMBOL_GPL(imx_pcm_new); void imx_pcm_free(struct snd_pcm *pcm) { @@ -100,6 +102,39 @@ void imx_pcm_free(struct snd_pcm *pcm) buf->area = NULL; } } +EXPORT_SYMBOL_GPL(imx_pcm_free); + +static int imx_pcm_probe(struct platform_device *pdev) +{ + if (strcmp(pdev->id_entry->name, "imx-fiq-pcm-audio") == 0) + return imx_pcm_fiq_init(pdev); + + return imx_pcm_dma_init(pdev); +} + +static int imx_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static struct platform_device_id imx_pcm_devtype[] = { + { .name = "imx-pcm-audio", }, + { .name = "imx-fiq-pcm-audio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, imx_pcm_devtype); + +static struct platform_driver imx_pcm_driver = { + .driver = { + .name = "imx-pcm", + .owner = THIS_MODULE, + }, + .id_table = imx_pcm_devtype, + .probe = imx_pcm_probe, + .remove = imx_pcm_remove, +}; +module_platform_driver(imx_pcm_driver); MODULE_DESCRIPTION("Freescale i.MX PCM driver"); MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index 83c0ed7..5ae13a1 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -30,4 +30,22 @@ int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, int imx_pcm_new(struct snd_soc_pcm_runtime *rtd); void imx_pcm_free(struct snd_pcm *pcm); +#ifdef CONFIG_SND_SOC_IMX_PCM_DMA +int imx_pcm_dma_init(struct platform_device *pdev); +#else +static inline int imx_pcm_dma_init(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif + +#ifdef CONFIG_SND_SOC_IMX_PCM_FIQ +int imx_pcm_fiq_init(struct platform_device *pdev); +#else +static inline int imx_pcm_fiq_init(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif + #endif /* _IMX_PCM_H */ diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index ee10e87..13f6dd1 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -469,7 +469,7 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev) { int ret = 0; - ret = s3c_i2sv2_register_dai(&pdev->dev, -1, &s3c2412_i2s_dai); + ret = snd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai); if (ret) { pr_err("failed to register the dai\n"); return ret; diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index a606d0f..c724026 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -16,6 +16,8 @@ #include <linux/dma-mapping.h> #include <linux/pm_runtime.h> #include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/scatterlist.h> #include <linux/sh_dma.h> #include <linux/slab.h> @@ -131,8 +133,6 @@ #define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) -typedef int (*set_rate_func)(struct device *dev, int rate, int enable); - /* * bus options * @@ -244,8 +244,7 @@ struct fsi_clk { struct clk *ick; struct clk *div; int (*set_rate)(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate); + struct fsi_priv *fsi); unsigned long rate; unsigned int count; @@ -254,7 +253,6 @@ struct fsi_clk { struct fsi_priv { void __iomem *base; struct fsi_master *master; - struct sh_fsi_port_info *info; struct fsi_stream playback; struct fsi_stream capture; @@ -270,8 +268,6 @@ struct fsi_priv { int enable_stream:1; int bit_clk_inv:1; int lr_clk_inv:1; - - long rate; }; struct fsi_stream_handler { @@ -303,7 +299,7 @@ struct fsi_master { int irq; struct fsi_priv fsia; struct fsi_priv fsib; - struct fsi_core *core; + const struct fsi_core *core; spinlock_t lock; }; @@ -431,22 +427,6 @@ static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) return fsi_get_priv_frm_dai(fsi_get_dai(substream)); } -static set_rate_func fsi_get_info_set_rate(struct fsi_priv *fsi) -{ - if (!fsi->info) - return NULL; - - return fsi->info->set_rate; -} - -static u32 fsi_get_info_flags(struct fsi_priv *fsi) -{ - if (!fsi->info) - return 0; - - return fsi->info->flags; -} - static u32 fsi_get_port_shift(struct fsi_priv *fsi, struct fsi_stream *io) { int is_play = fsi_stream_is_play(fsi, io); @@ -757,8 +737,7 @@ static int fsi_clk_init(struct device *dev, int ick, int div, int (*set_rate)(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate)) + struct fsi_priv *fsi)) { struct fsi_clk *clock = &fsi->clock; int is_porta = fsi_is_port_a(fsi); @@ -829,8 +808,7 @@ static int fsi_clk_is_valid(struct fsi_priv *fsi) } static int fsi_clk_enable(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate) + struct fsi_priv *fsi) { struct fsi_clk *clock = &fsi->clock; int ret = -EINVAL; @@ -839,7 +817,7 @@ static int fsi_clk_enable(struct device *dev, return ret; if (0 == clock->count) { - ret = clock->set_rate(dev, fsi, rate); + ret = clock->set_rate(dev, fsi); if (ret < 0) { fsi_clk_invalid(fsi); return ret; @@ -946,11 +924,11 @@ static int fsi_clk_set_ackbpf(struct device *dev, } static int fsi_clk_set_rate_external(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate) + struct fsi_priv *fsi) { struct clk *xck = fsi->clock.xck; struct clk *ick = fsi->clock.ick; + unsigned long rate = fsi->clock.rate; unsigned long xrate; int ackmd, bpfmd; int ret = 0; @@ -978,11 +956,11 @@ static int fsi_clk_set_rate_external(struct device *dev, } static int fsi_clk_set_rate_cpg(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate) + struct fsi_priv *fsi) { struct clk *ick = fsi->clock.ick; struct clk *div = fsi->clock.div; + unsigned long rate = fsi->clock.rate; unsigned long target = 0; /* 12288000 or 11289600 */ unsigned long actual, cout; unsigned long diff, min; @@ -1063,85 +1041,6 @@ static int fsi_clk_set_rate_cpg(struct device *dev, return ret; } -static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, - long rate, int enable) -{ - set_rate_func set_rate = fsi_get_info_set_rate(fsi); - int ret; - - /* - * CAUTION - * - * set_rate will be deleted - */ - if (!set_rate) { - if (enable) - return fsi_clk_enable(dev, fsi, rate); - else - return fsi_clk_disable(dev, fsi); - } - - ret = set_rate(dev, rate, enable); - if (ret < 0) /* error */ - return ret; - - if (!enable) - return 0; - - if (ret > 0) { - u32 data = 0; - - switch (ret & SH_FSI_ACKMD_MASK) { - default: - /* FALL THROUGH */ - case SH_FSI_ACKMD_512: - data |= (0x0 << 12); - break; - case SH_FSI_ACKMD_256: - data |= (0x1 << 12); - break; - case SH_FSI_ACKMD_128: - data |= (0x2 << 12); - break; - case SH_FSI_ACKMD_64: - data |= (0x3 << 12); - break; - case SH_FSI_ACKMD_32: - data |= (0x4 << 12); - break; - } - - switch (ret & SH_FSI_BPFMD_MASK) { - default: - /* FALL THROUGH */ - case SH_FSI_BPFMD_32: - data |= (0x0 << 8); - break; - case SH_FSI_BPFMD_64: - data |= (0x1 << 8); - break; - case SH_FSI_BPFMD_128: - data |= (0x2 << 8); - break; - case SH_FSI_BPFMD_256: - data |= (0x3 << 8); - break; - case SH_FSI_BPFMD_512: - data |= (0x4 << 8); - break; - case SH_FSI_BPFMD_16: - data |= (0x7 << 8); - break; - } - - fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data); - udelay(10); - ret = 0; - } - - return ret; -} - /* * pio data transfer handler */ @@ -1637,7 +1536,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev) { - u32 flags = fsi_get_info_flags(fsi); u32 data = 0; /* clock setting */ @@ -1654,19 +1552,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi, data |= (1 << 4); if (fsi_is_clk_master(fsi)) data <<= 8; - /* FIXME - * - * SH_FSI_xxx_INV style will be removed - */ - if (SH_FSI_LRM_INV & flags) - data |= 1 << 12; - if (SH_FSI_BRM_INV & flags) - data |= 1 << 8; - if (SH_FSI_LRS_INV & flags) - data |= 1 << 4; - if (SH_FSI_BRS_INV & flags) - data |= 1 << 0; - fsi_reg_write(fsi, CKG2, data); /* spdif ? */ @@ -1698,7 +1583,7 @@ static int fsi_hw_startup(struct fsi_priv *fsi, /* start master clock */ if (fsi_is_clk_master(fsi)) - return fsi_set_master_clk(dev, fsi, fsi->rate, 1); + return fsi_clk_enable(dev, fsi); return 0; } @@ -1708,7 +1593,7 @@ static int fsi_hw_shutdown(struct fsi_priv *fsi, { /* stop master clock */ if (fsi_is_clk_master(fsi)) - return fsi_set_master_clk(dev, fsi, fsi->rate, 0); + return fsi_clk_disable(dev, fsi); return 0; } @@ -1719,7 +1604,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, struct fsi_priv *fsi = fsi_get_priv(substream); fsi_clk_invalid(fsi); - fsi->rate = 0; return 0; } @@ -1730,7 +1614,6 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream, struct fsi_priv *fsi = fsi_get_priv(substream); fsi_clk_invalid(fsi); - fsi->rate = 0; } static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, @@ -1795,7 +1678,6 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi) static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); - set_rate_func set_rate = fsi_get_info_set_rate(fsi); int ret; /* set master/slave audio interface */ @@ -1831,14 +1713,6 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } if (fsi_is_clk_master(fsi)) { - /* - * CAUTION - * - * set_rate will be deleted - */ - if (set_rate) - dev_warn(dai->dev, "set_rate will be removed soon\n"); - if (fsi->clk_cpg) fsi_clk_init(dai->dev, fsi, 0, 1, 1, fsi_clk_set_rate_cpg); @@ -1862,10 +1736,8 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream, { struct fsi_priv *fsi = fsi_get_priv(substream); - if (fsi_is_clk_master(fsi)) { - fsi->rate = params_rate(params); - fsi_clk_valid(fsi, fsi->rate); - } + if (fsi_is_clk_master(fsi)) + fsi_clk_valid(fsi, params_rate(params)); return 0; } @@ -2017,6 +1889,33 @@ static struct snd_soc_platform_driver fsi_soc_platform = { /* * platform function */ +static void fsi_of_parse(char *name, + struct device_node *np, + struct sh_fsi_port_info *info, + struct device *dev) +{ + int i; + char prop[128]; + unsigned long flags = 0; + struct { + char *name; + unsigned int val; + } of_parse_property[] = { + { "spdif-connection", SH_FSI_FMT_SPDIF }, + { "stream-mode-support", SH_FSI_ENABLE_STREAM_MODE }, + { "use-internal-clock", SH_FSI_CLK_CPG }, + }; + + for (i = 0; i < ARRAY_SIZE(of_parse_property); i++) { + sprintf(prop, "%s,%s", name, of_parse_property[i].name); + if (of_get_property(np, prop, NULL)) + flags |= of_parse_property[i].val; + } + info->flags = flags; + + dev_dbg(dev, "%s flags : %lx\n", name, info->flags); +} + static void fsi_port_info_init(struct fsi_priv *fsi, struct sh_fsi_port_info *info) { @@ -2044,23 +1943,40 @@ static void fsi_handler_init(struct fsi_priv *fsi, } } +static struct of_device_id fsi_of_match[]; static int fsi_probe(struct platform_device *pdev) { struct fsi_master *master; - const struct platform_device_id *id_entry; - struct sh_fsi_platform_info *info = pdev->dev.platform_data; - struct sh_fsi_port_info nul_info, *pinfo; + struct device_node *np = pdev->dev.of_node; + struct sh_fsi_platform_info info; + const struct fsi_core *core; struct fsi_priv *fsi; struct resource *res; unsigned int irq; int ret; - nul_info.flags = 0; - nul_info.tx_id = 0; - nul_info.rx_id = 0; + memset(&info, 0, sizeof(info)); + + core = NULL; + if (np) { + const struct of_device_id *of_id; - id_entry = pdev->id_entry; - if (!id_entry) { + of_id = of_match_device(fsi_of_match, &pdev->dev); + if (of_id) { + core = of_id->data; + fsi_of_parse("fsia", np, &info.port_a, &pdev->dev); + fsi_of_parse("fsib", np, &info.port_b, &pdev->dev); + } + } else { + const struct platform_device_id *id_entry = pdev->id_entry; + if (id_entry) + core = (struct fsi_core *)id_entry->driver_data; + + if (pdev->dev.platform_data) + memcpy(&info, pdev->dev.platform_data, sizeof(info)); + } + + if (!core) { dev_err(&pdev->dev, "unknown fsi device\n"); return -ENODEV; } @@ -2087,17 +2003,15 @@ static int fsi_probe(struct platform_device *pdev) /* master setting */ master->irq = irq; - master->core = (struct fsi_core *)id_entry->driver_data; + master->core = core; spin_lock_init(&master->lock); /* FSI A setting */ - pinfo = (info) ? &info->port_a : &nul_info; fsi = &master->fsia; fsi->base = master->base; fsi->master = master; - fsi->info = pinfo; - fsi_port_info_init(fsi, pinfo); - fsi_handler_init(fsi, pinfo); + fsi_port_info_init(fsi, &info.port_a); + fsi_handler_init(fsi, &info.port_a); ret = fsi_stream_probe(fsi, &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "FSIA stream probe failed\n"); @@ -2105,13 +2019,11 @@ static int fsi_probe(struct platform_device *pdev) } /* FSI B setting */ - pinfo = (info) ? &info->port_b : &nul_info; fsi = &master->fsib; fsi->base = master->base + 0x40; fsi->master = master; - fsi->info = pinfo; - fsi_port_info_init(fsi, pinfo); - fsi_handler_init(fsi, pinfo); + fsi_port_info_init(fsi, &info.port_b); + fsi_handler_init(fsi, &info.port_b); ret = fsi_stream_probe(fsi, &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "FSIB stream probe failed\n"); @@ -2122,7 +2034,7 @@ static int fsi_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, master); ret = devm_request_irq(&pdev->dev, irq, &fsi_interrupt, 0, - id_entry->name, master); + dev_name(&pdev->dev), master); if (ret) { dev_err(&pdev->dev, "irq request err\n"); goto exit_fsib; @@ -2248,6 +2160,13 @@ static struct fsi_core fsi2_core = { .b_mclk = B_MST_CTLR, }; +static struct of_device_id fsi_of_match[] = { + { .compatible = "renesas,sh_fsi", .data = &fsi1_core}, + { .compatible = "renesas,sh_fsi2", .data = &fsi2_core}, + {}, +}; +MODULE_DEVICE_TABLE(of, fsi_of_match); + static struct platform_device_id fsi_id_table[] = { { "sh_fsi", (kernel_ulong_t)&fsi1_core }, { "sh_fsi2", (kernel_ulong_t)&fsi2_core }, @@ -2259,6 +2178,7 @@ static struct platform_driver fsi_driver = { .driver = { .name = "fsi-pcm-audio", .pm = &fsi_pm_ops, + .of_match_table = fsi_of_match, }, .probe = fsi_probe, .remove = fsi_remove, diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 5fbfb06..b5b3db7 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -33,6 +33,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { @@ -61,15 +63,46 @@ static int soc_compr_open(struct snd_compr_stream *cstream) codec_dai->active++; rtd->codec->active++; + mutex_unlock(&rtd->pcm_mutex); + return 0; machine_err: if (platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); out: + mutex_unlock(&rtd->pcm_mutex); return ret; } +/* + * Power down the audio subsystem pmdown_time msecs after close is called. + * This is to ensure there are no pops or clicks in between any music tracks + * due to DAPM power cycling. + */ +static void close_delayed_work(struct work_struct *work) +{ + struct snd_soc_pcm_runtime *rtd = + container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", + codec_dai->driver->playback.stream_name, + codec_dai->playback_active ? "active" : "inactive", + rtd->pop_wait ? "yes" : "no"); + + /* are we waiting on this codec DAI stream */ + if (rtd->pop_wait == 1) { + rtd->pop_wait = 0; + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, + SND_SOC_DAPM_STREAM_STOP); + } + + mutex_unlock(&rtd->pcm_mutex); +} + static int soc_compr_free(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -78,6 +111,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_codec *codec = rtd->codec; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (cstream->direction == SND_COMPRESS_PLAYBACK) { cpu_dai->playback_active--; codec_dai->playback_active--; @@ -86,7 +121,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream) codec_dai->capture_active--; } - snd_soc_dai_digital_mute(codec_dai, 1); + snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); cpu_dai->active--; codec_dai->active--; @@ -112,10 +147,11 @@ static int soc_compr_free(struct snd_compr_stream *cstream) snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_STOP); - } else + } else { rtd->pop_wait = 1; schedule_delayed_work(&rtd->delayed_work, msecs_to_jiffies(rtd->pmdown_time)); + } } else { /* capture streams can be powered down now */ snd_soc_dapm_stream_event(rtd, @@ -123,6 +159,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream) SND_SOC_DAPM_STREAM_STOP); } + mutex_unlock(&rtd->pcm_mutex); return 0; } @@ -134,17 +171,25 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { ret = platform->driver->compr_ops->trigger(cstream, cmd); if (ret < 0) - return ret; + goto out; } - if (cmd == SNDRV_PCM_TRIGGER_START) - snd_soc_dai_digital_mute(codec_dai, 0); - else if (cmd == SNDRV_PCM_TRIGGER_STOP) - snd_soc_dai_digital_mute(codec_dai, 1); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); + break; + } +out: + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -155,6 +200,8 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + /* first we call set_params for the platform driver * this should configure the soc side * if the machine has compressed ops then we call that as well @@ -164,18 +211,20 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { ret = platform->driver->compr_ops->set_params(cstream, params); if (ret < 0) - return ret; + goto out; } if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) { ret = rtd->dai_link->compr_ops->set_params(cstream); if (ret < 0) - return ret; + goto out; } snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_START); +out: + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -186,9 +235,12 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->get_params) ret = platform->driver->compr_ops->get_params(cstream, params); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -199,9 +251,12 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps) ret = platform->driver->compr_ops->get_caps(cstream, caps); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -212,9 +267,12 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) ret = platform->driver->compr_ops->get_codec_caps(cstream, codec); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -224,9 +282,12 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->ack) ret = platform->driver->compr_ops->ack(cstream, bytes); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -236,12 +297,31 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->pointer) platform->driver->compr_ops->pointer(cstream, tstamp); + mutex_unlock(&rtd->pcm_mutex); return 0; } +static int soc_compr_copy(struct snd_compr_stream *cstream, + const char __user *buf, size_t count) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_platform *platform = rtd->platform; + int ret = 0; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + if (platform->driver->compr_ops && platform->driver->compr_ops->copy) + ret = platform->driver->compr_ops->copy(cstream, buf, count); + + mutex_unlock(&rtd->pcm_mutex); + return ret; +} + /* ASoC Compress operations */ static struct snd_compr_ops soc_compr_ops = { .open = soc_compr_open, @@ -259,6 +339,7 @@ static struct snd_compr_ops soc_compr_ops = { int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_compr *compr; @@ -275,20 +356,38 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) return -ENOMEM; } - compr->ops = &soc_compr_ops; + compr->ops = devm_kzalloc(rtd->card->dev, sizeof(soc_compr_ops), + GFP_KERNEL); + if (compr->ops == NULL) { + dev_err(rtd->card->dev, "Cannot allocate compressed ops\n"); + ret = -ENOMEM; + goto compr_err; + } + memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); + + /* Add copy callback for not memory mapped DSPs */ + if (platform->driver->compr_ops && platform->driver->compr_ops->copy) + compr->ops->copy = soc_compr_copy; + mutex_init(&compr->lock); ret = snd_compress_new(rtd->card->snd_card, num, direction, compr); if (ret < 0) { pr_err("compress asoc: can't create compress for codec %s\n", codec->name); - kfree(compr); - return ret; + goto compr_err; } + /* DAPM dai link stream work */ + INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + rtd->compr = compr; compr->private_data = rtd; printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name, cpu_dai->name); return ret; + +compr_err: + kfree(compr); + return ret; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2370063..7365afb 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1107,6 +1107,10 @@ static int soc_probe_codec(struct snd_soc_card *card, "ASoC: failed to probe CODEC %d\n", ret); goto err_probe; } + WARN(codec->dapm.idle_bias_off && + codec->dapm.bias_level != SND_SOC_BIAS_OFF, + "codec %s can not start from non-off bias" + " with idle_bias_off==1\n", codec->name); } /* If the driver didn't set I/O up try regmap */ @@ -3122,9 +3126,12 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, if (!codec->using_regmap) return -EINVAL; - data = ucontrol->value.bytes.data; len = params->num_regs * codec->val_bytes; + data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + /* * If we've got a mask then we need to preserve the register * bits. We shouldn't modify the incoming data so take a @@ -3137,10 +3144,6 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, val &= params->mask; - data = kmemdup(data, len, GFP_KERNEL); - if (!data) - return -ENOMEM; - switch (codec->val_bytes) { case 1: ((u8 *)data)[0] &= ~params->mask; @@ -3162,8 +3165,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, ret = regmap_raw_write(codec->control_data, params->base, data, len); - if (params->mask) - kfree(data); + kfree(data); return ret; } @@ -3540,12 +3542,20 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); * snd_soc_dai_digital_mute - configure DAI system or master clock. * @dai: DAI * @mute: mute enable + * @direction: stream to mute * * Mutes the DAI DAC. */ -int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute) +int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, + int direction) { - if (dai->driver && dai->driver->ops->digital_mute) + if (!dai->driver) + return -ENOTSUPP; + + if (dai->driver->ops->mute_stream) + return dai->driver->ops->mute_stream(dai, mute, direction); + else if (direction == SNDRV_PCM_STREAM_PLAYBACK && + dai->driver->ops->digital_mute) return dai->driver->ops->digital_mute(dai, mute); else return -ENOTSUPP; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 258acad..1d6a9b3 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3255,14 +3255,16 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMU: - ret = snd_soc_dai_digital_mute(sink, 0); + ret = snd_soc_dai_digital_mute(sink, 0, + SNDRV_PCM_STREAM_PLAYBACK); if (ret != 0 && ret != -ENOTSUPP) dev_warn(sink->dev, "ASoC: Failed to unmute: %d\n", ret); ret = 0; break; case SND_SOC_DAPM_PRE_PMD: - ret = snd_soc_dai_digital_mute(sink, 1); + ret = snd_soc_dai_digital_mute(sink, 1, + SNDRV_PCM_STREAM_PLAYBACK); if (ret != 0 && ret != -ENOTSUPP) dev_warn(sink->dev, "ASoC: Failed to mute: %d\n", ret); ret = 0; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index cf191e6..73bb8ee 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -383,8 +383,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) /* Muting the DAC suppresses artifacts caused during digital * shutdown, for example from stopping clocks. */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dai_digital_mute(codec_dai, 1); + snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); @@ -488,7 +487,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) snd_soc_dapm_stream_event(rtd, substream->stream, SND_SOC_DAPM_STREAM_START); - snd_soc_dai_digital_mute(codec_dai, 0); + snd_soc_dai_digital_mute(codec_dai, 0, substream->stream); out: mutex_unlock(&rtd->pcm_mutex); @@ -586,7 +585,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) /* apply codec digital mute */ if (!codec->active) - snd_soc_dai_digital_mute(codec_dai, 1); + snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); /* free any machine hw params */ if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) @@ -1729,20 +1728,16 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) /* startup must always be called for new BEs */ ret = dpcm_be_dai_startup(fe, stream); - if (ret < 0) { + if (ret < 0) goto disconnect; - return ret; - } /* keep going if FE state is > open */ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN) return 0; ret = dpcm_be_dai_hw_params(fe, stream); - if (ret < 0) { + if (ret < 0) goto close; - return ret; - } /* keep going if FE state is > hw_params */ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS) @@ -1750,10 +1745,8 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) ret = dpcm_be_dai_prepare(fe, stream); - if (ret < 0) { + if (ret < 0) goto hw_free; - return ret; - } /* run the stream event for each BE */ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index ae69907..204b899 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -24,7 +24,7 @@ #include "ux500_pcm.h" #include "ux500_msp_dai.h" -#include <mop500_ab8500.h> +#include "mop500_ab8500.h" /* Define the whole MOP500 soundcard, linking platform to the codec-drivers */ struct snd_soc_dai_link mop500_dai_links[] = { |