diff options
Diffstat (limited to 'sound')
31 files changed, 1151 insertions, 270 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b8fb0a5adb9b..822df971972c 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3654,6 +3654,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) hda_set_power_state(codec, AC_PWRST_D0); restore_shutup_pins(codec); hda_exec_init_verbs(codec); + snd_hda_jack_set_dirty_all(codec); if (codec->patch_ops.resume) codec->patch_ops.resume(codec); else { @@ -3665,10 +3666,8 @@ static void hda_call_codec_resume(struct hda_codec *codec) if (codec->jackpoll_interval) hda_jackpoll_work(&codec->jackpoll_work.work); - else { - snd_hda_jack_set_dirty_all(codec); + else snd_hda_jack_report_sync(codec); - } codec->in_pm = 0; snd_hda_power_down(codec); /* flag down before returning */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 0b6aebacc56b..c78286f6e5d8 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -656,29 +656,43 @@ static char *driver_short_names[] = { #define get_azx_dev(substream) (substream->runtime->private_data) #ifdef CONFIG_X86 -static void __mark_pages_wc(struct azx *chip, void *addr, size_t size, bool on) +static void __mark_pages_wc(struct azx *chip, struct snd_dma_buffer *dmab, bool on) { + int pages; + if (azx_snoop(chip)) return; - if (addr && size) { - int pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; + if (!dmab || !dmab->area || !dmab->bytes) + return; + +#ifdef CONFIG_SND_DMA_SGBUF + if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG) { + struct snd_sg_buf *sgbuf = dmab->private_data; if (on) - set_memory_wc((unsigned long)addr, pages); + set_pages_array_wc(sgbuf->page_table, sgbuf->pages); else - set_memory_wb((unsigned long)addr, pages); + set_pages_array_wb(sgbuf->page_table, sgbuf->pages); + return; } +#endif + + pages = (dmab->bytes + PAGE_SIZE - 1) >> PAGE_SHIFT; + if (on) + set_memory_wc((unsigned long)dmab->area, pages); + else + set_memory_wb((unsigned long)dmab->area, pages); } static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf, bool on) { - __mark_pages_wc(chip, buf->area, buf->bytes, on); + __mark_pages_wc(chip, buf, on); } static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev, - struct snd_pcm_runtime *runtime, bool on) + struct snd_pcm_substream *substream, bool on) { if (azx_dev->wc_marked != on) { - __mark_pages_wc(chip, runtime->dma_area, runtime->dma_bytes, on); + __mark_pages_wc(chip, snd_pcm_get_dma_buf(substream), on); azx_dev->wc_marked = on; } } @@ -689,7 +703,7 @@ static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf, { } static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev, - struct snd_pcm_runtime *runtime, bool on) + struct snd_pcm_substream *substream, bool on) { } #endif @@ -1968,11 +1982,10 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream, { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; - struct snd_pcm_runtime *runtime = substream->runtime; struct azx_dev *azx_dev = get_azx_dev(substream); int ret; - mark_runtime_wc(chip, azx_dev, runtime, false); + mark_runtime_wc(chip, azx_dev, substream, false); azx_dev->bufsize = 0; azx_dev->period_bytes = 0; azx_dev->format_val = 0; @@ -1980,7 +1993,7 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream, params_buffer_bytes(hw_params)); if (ret < 0) return ret; - mark_runtime_wc(chip, azx_dev, runtime, true); + mark_runtime_wc(chip, azx_dev, substream, true); return ret; } @@ -1989,7 +2002,6 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream) struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx_dev *azx_dev = get_azx_dev(substream); struct azx *chip = apcm->chip; - struct snd_pcm_runtime *runtime = substream->runtime; struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; /* reset BDL address */ @@ -2002,7 +2014,7 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream) snd_hda_codec_cleanup(apcm->codec, hinfo, substream); - mark_runtime_wc(chip, azx_dev, runtime, false); + mark_runtime_wc(chip, azx_dev, substream, false); return snd_pcm_lib_free_pages(substream); } @@ -3613,13 +3625,12 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { /* 5 Series/3400 */ { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, - /* SCH */ + /* Poulsbo */ { PCI_DEVICE(0x8086, 0x811b), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_LPIB }, /* Poulsbo */ + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM }, + /* Oaktrail */ { PCI_DEVICE(0x8086, 0x080a), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP | - AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_LPIB }, /* Oaktrail */ + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM }, /* ICH */ { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index dd798c3196ff..009b77a693cf 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -4636,6 +4636,12 @@ static const struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_conexant_auto }, { .id = 0x14f15111, .name = "CX20753/4", .patch = patch_conexant_auto }, + { .id = 0x14f15113, .name = "CX20755", + .patch = patch_conexant_auto }, + { .id = 0x14f15114, .name = "CX20756", + .patch = patch_conexant_auto }, + { .id = 0x14f15115, .name = "CX20757", + .patch = patch_conexant_auto }, {} /* terminator */ }; @@ -4659,6 +4665,9 @@ MODULE_ALIAS("snd-hda-codec-id:14f150b9"); MODULE_ALIAS("snd-hda-codec-id:14f1510f"); MODULE_ALIAS("snd-hda-codec-id:14f15110"); MODULE_ALIAS("snd-hda-codec-id:14f15111"); +MODULE_ALIAS("snd-hda-codec-id:14f15113"); +MODULE_ALIAS("snd-hda-codec-id:14f15114"); +MODULE_ALIAS("snd-hda-codec-id:14f15115"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec"); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f5196277b6e9..5faaad219a7f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4694,6 +4694,7 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB), SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), + SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST), SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_FIXUP_F1734), SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734), @@ -5708,6 +5709,7 @@ static const struct alc_model_fixup alc268_fixup_models[] = { }; static const struct snd_pci_quirk alc268_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC), /* below is codec SSID since multiple Toshiba laptops have the * same PCI SSID 1179:ff00 */ @@ -6251,6 +6253,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x1972, "HP Pavilion 17", ALC269_FIXUP_MIC1_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x1977, "HP Pavilion 14", ALC269_FIXUP_MIC1_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC), SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_DMIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), @@ -6265,6 +6268,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), + SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK), SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK), SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK), diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index d1b691bf8e2d..3fdd87fa18a9 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 6a293c713a38..054ea4d9326a 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 e99f1811300a..3109db7b9017 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 bb45d20e7250..12ae814eff21 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 1c7663422054..e13580d6c476 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 da976291da9e..2d6fbd0125b9 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 1f0cdab03294..2d0378709702 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 1d8bb5917594..d824c984c8a4 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,17 +795,7 @@ 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 = snd_soc_params_to_bclk(params) / params_rate(params); + lrclk = rates[bclk] / params_rate(params); arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", rates[bclk], rates[bclk] / lrclk); @@ -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) { @@ -1082,10 +1179,47 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, id, ret); } + regmap_update_bits(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, 0); + return 0; } 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 4deebeb07177..116372c91f5d 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/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 5708a973a776..49891432af74 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 12bcae63a7f0..eb96b8768098 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 e6cefe1ac677..d5371e0d8909 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1019,8 +1019,6 @@ static const char *wm2200_mixer_texts[] = { "EQR", "LHPF1", "LHPF2", - "LHPF3", - "LHPF4", "DSP1.1", "DSP1.2", "DSP1.3", @@ -1053,7 +1051,6 @@ static int wm2200_mixer_values[] = { 0x25, 0x50, /* EQ */ 0x51, - 0x52, 0x60, /* LHPF1 */ 0x61, /* LHPF2 */ 0x68, /* DSP1 */ @@ -1129,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 7a9048dad1cd..5e85b645f2eb 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); @@ -896,8 +948,7 @@ static const unsigned int wm5102_aec_loopback_values[] = { static const struct soc_enum wm5102_aec_loopback = SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1, - ARIZONA_AEC_LOOPBACK_SRC_SHIFT, - ARIZONA_AEC_LOOPBACK_SRC_MASK, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, ARRAY_SIZE(wm5102_aec_loopback_texts), wm5102_aec_loopback_texts, wm5102_aec_loopback_values); @@ -1003,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, @@ -1139,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"), @@ -1194,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" }, \ @@ -1290,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"), @@ -1337,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" }, @@ -1464,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 ae80c8c28536..23199372d518 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), @@ -344,8 +393,7 @@ static const unsigned int wm5110_aec_loopback_values[] = { static const struct soc_enum wm5110_aec_loopback = SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1, - ARIZONA_AEC_LOOPBACK_SRC_SHIFT, - ARIZONA_AEC_LOOPBACK_SRC_MASK, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, ARRAY_SIZE(wm5110_aec_loopback_texts), wm5110_aec_loopback_texts, wm5110_aec_loopback_values); diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 7b198c38f3ef..f3f7e75f8628 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); + 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); @@ -396,7 +738,17 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) hdr = (void*)&firmware->data[0]; if (memcmp(hdr->magic, "WMDR", 4) != 0) { adsp_err(dsp, "%s: invalid magic\n", file); - return -EINVAL; + 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, @@ -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); + 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 ffd29a4609e2..cb8871a3ec00 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 5632ded67fdd..ef163360a745 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 55e2bf652bef..9321e5c9d8c1 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 1aa51300c564..deb30d59965e 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/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index bf363d8d044a..500f8ce55d78 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 5ec362ae4d01..920f945cb2f4 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 d5cd9eff3b48..0d0625bfcb65 100644 --- a/sound/soc/fsl/imx-pcm.c +++ b/sound/soc/fsl/imx-pcm.c @@ -104,6 +104,38 @@ void imx_pcm_free(struct snd_pcm *pcm) } 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>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index 83c0ed7d55c9..5ae13a13a353 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 ee10e8704e97..13f6dd1ceb00 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/soc-dapm.c b/sound/soc/soc-dapm.c index 4d664f3df805..1d6a9b3ceb27 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1023,7 +1023,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, if (SND_SOC_DAPM_EVENT_ON(event)) { if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { - ret = regulator_allow_bypass(w->regulator, true); + ret = regulator_allow_bypass(w->regulator, false); if (ret != 0) dev_warn(w->dapm->dev, "ASoC: Failed to bypass %s: %d\n", @@ -1033,7 +1033,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, return regulator_enable(w->regulator); } else { if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { - ret = regulator_allow_bypass(w->regulator, false); + ret = regulator_allow_bypass(w->regulator, true); if (ret != 0) dev_warn(w->dapm->dev, "ASoC: Failed to unbypass %s: %d\n", @@ -3039,6 +3039,14 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, w->name, ret); return NULL; } + + if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { + ret = regulator_allow_bypass(w->regulator, true); + if (ret != 0) + dev_warn(w->dapm->dev, + "ASoC: Failed to unbypass %s: %d\n", + w->name, ret); + } break; case snd_soc_dapm_clock_supply: #ifdef CONFIG_CLKDEV_LOOKUP diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index ae6990738783..204b899c2311 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[] = { diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index ed4d89c8b52a..e90daf8cdaa8 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1331,16 +1331,23 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void } channels = (hdr->bLength - 7) / csize - 1; bmaControls = hdr->bmaControls; + if (hdr->bLength < 7 + csize) { + snd_printk(KERN_ERR "usbaudio: unit %u: " + "invalid UAC_FEATURE_UNIT descriptor\n", + unitid); + return -EINVAL; + } } else { struct uac2_feature_unit_descriptor *ftr = _ftr; csize = 4; channels = (hdr->bLength - 6) / 4 - 1; bmaControls = ftr->bmaControls; - } - - if (hdr->bLength < 7 || !csize || hdr->bLength < 7 + csize) { - snd_printk(KERN_ERR "usbaudio: unit %u: invalid UAC_FEATURE_UNIT descriptor\n", unitid); - return -EINVAL; + if (hdr->bLength < 6 + csize) { + snd_printk(KERN_ERR "usbaudio: unit %u: " + "invalid UAC_FEATURE_UNIT descriptor\n", + unitid); + return -EINVAL; + } } /* parse the source unit */ |