diff options
author | Takashi Iwai <tiwai@suse.de> | 2018-08-13 12:12:31 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2018-08-13 12:12:31 +0200 |
commit | f5b6c1fcb42fe7d6f2f6eb2220512e2a5f875133 (patch) | |
tree | 325f29d9788e80a0dd66d907ce38650834060e4b /sound | |
parent | 73b383141d296c55bfbc0ce336a4a946627e7780 (diff) | |
parent | 4aa5db22d35588e1a5d2ee88472348ea73d9fb23 (diff) |
Merge tag 'asoc-v4.19' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v4.19
A fairly big update, including quite a bit of core activity this time
around (which is good to see) along with a fairly large set of new
drivers.
- A new snd_pcm_stop_xrun() helper which is now used in several
drivers.
- Support for providing name prefixes to generic component nodes.
- Quite a few fixes for DPCM as it gains a bit wider use and more
robust testing.
- Generalization of the DIO2125 support to a simple amplifier driver.
- Accessory detection support for the audio graph card.
- DT support for PXA AC'97 devices.
- Quirks for a number of new x86 systems.
- Support for AM Logic Meson, Everest ES7154, Intel systems with
RT5682, Qualcomm QDSP6 and WCD9335, Realtek RT5682 and TI TAS5707.
Diffstat (limited to 'sound')
229 files changed, 13113 insertions, 2687 deletions
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig index 65171f6657a2..5fbd47a9177e 100644 --- a/sound/arm/Kconfig +++ b/sound/arm/Kconfig @@ -17,14 +17,9 @@ config SND_ARMAACI select SND_PCM select SND_AC97_CODEC -config SND_PXA2XX_PCM - tristate - select SND_PCM - config SND_PXA2XX_AC97 tristate "AC97 driver for the Intel PXA2xx chip" depends on ARCH_PXA - select SND_PXA2XX_PCM select SND_AC97_CODEC select SND_PXA2XX_LIB select SND_PXA2XX_LIB_AC97 diff --git a/sound/arm/Makefile b/sound/arm/Makefile index e10d5b169565..34c769489877 100644 --- a/sound/arm/Makefile +++ b/sound/arm/Makefile @@ -6,9 +6,6 @@ obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o snd-aaci-objs := aaci.o -obj-$(CONFIG_SND_PXA2XX_PCM) += snd-pxa2xx-pcm.o -snd-pxa2xx-pcm-objs := pxa2xx-pcm.o - obj-$(CONFIG_SND_PXA2XX_LIB) += snd-pxa2xx-lib.o snd-pxa2xx-lib-y := pxa2xx-pcm-lib.o snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97) += pxa2xx-ac97-lib.o diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index 5950a9e218d9..8eafd3d3dff6 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/io.h> #include <linux/gpio.h> +#include <linux/of_gpio.h> #include <sound/pxa2xx-lib.h> @@ -337,6 +338,17 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) dev_err(&dev->dev, "Invalid reset GPIO %d\n", pdata->reset_gpio); } + } else if (!pdata && dev->dev.of_node) { + pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + pdata->reset_gpio = of_get_named_gpio(dev->dev.of_node, + "reset-gpios", 0); + if (pdata->reset_gpio == -ENOENT) + pdata->reset_gpio = -1; + else if (pdata->reset_gpio < 0) + return pdata->reset_gpio; + reset_gpio = pdata->reset_gpio; } else { if (cpu_is_pxa27x()) reset_gpio = 113; diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index 4bc244c40f80..1f72672262d0 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -15,7 +15,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/dmaengine.h> -#include <linux/dma/pxa-dma.h> +#include <linux/dma-mapping.h> #include <sound/core.h> #include <sound/pcm.h> @@ -27,8 +27,6 @@ #include <mach/regs-ac97.h> #include <mach/audio.h> -#include "pxa2xx-pcm.h" - static void pxa2xx_ac97_legacy_reset(struct snd_ac97 *ac97) { if (!pxa2xx_ac97_try_cold_reset()) @@ -63,61 +61,46 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_legacy_reset, }; -static struct pxad_param pxa2xx_ac97_pcm_out_req = { - .prio = PXAD_PRIO_LOWEST, - .drcmr = 12, -}; - -static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = { - .addr = __PREG(PCDR), - .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, - .maxburst = 32, - .filter_data = &pxa2xx_ac97_pcm_out_req, -}; - -static struct pxad_param pxa2xx_ac97_pcm_in_req = { - .prio = PXAD_PRIO_LOWEST, - .drcmr = 11, -}; - -static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = { - .addr = __PREG(PCDR), - .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, - .maxburst = 32, - .filter_data = &pxa2xx_ac97_pcm_in_req, -}; - static struct snd_pcm *pxa2xx_ac97_pcm; static struct snd_ac97 *pxa2xx_ac97_ac97; -static int pxa2xx_ac97_pcm_startup(struct snd_pcm_substream *substream) +static int pxa2xx_ac97_pcm_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; pxa2xx_audio_ops_t *platform_ops; - int r; + int ret, i; + + ret = pxa2xx_pcm_open(substream); + if (ret) + return ret; runtime->hw.channels_min = 2; runtime->hw.channels_max = 2; - r = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - AC97_RATES_FRONT_DAC : AC97_RATES_ADC; - runtime->hw.rates = pxa2xx_ac97_ac97->rates[r]; + i = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + AC97_RATES_FRONT_DAC : AC97_RATES_ADC; + runtime->hw.rates = pxa2xx_ac97_ac97->rates[i]; snd_pcm_limit_hw_rates(runtime); - platform_ops = substream->pcm->card->dev->platform_data; - if (platform_ops && platform_ops->startup) - return platform_ops->startup(substream, platform_ops->priv); - else - return 0; + platform_ops = substream->pcm->card->dev->platform_data; + if (platform_ops && platform_ops->startup) { + ret = platform_ops->startup(substream, platform_ops->priv); + if (ret < 0) + pxa2xx_pcm_close(substream); + } + + return ret; } -static void pxa2xx_ac97_pcm_shutdown(struct snd_pcm_substream *substream) +static int pxa2xx_ac97_pcm_close(struct snd_pcm_substream *substream) { pxa2xx_audio_ops_t *platform_ops; - platform_ops = substream->pcm->card->dev->platform_data; + platform_ops = substream->pcm->card->dev->platform_data; if (platform_ops && platform_ops->shutdown) platform_ops->shutdown(substream, platform_ops->priv); + + return 0; } static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream) @@ -125,17 +108,15 @@ static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE; + int ret; + + ret = pxa2xx_pcm_prepare(substream); + if (ret < 0) + return ret; + return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate); } -static struct pxa2xx_pcm_client pxa2xx_ac97_pcm_client = { - .playback_params = &pxa2xx_ac97_pcm_out, - .capture_params = &pxa2xx_ac97_pcm_in, - .startup = pxa2xx_ac97_pcm_startup, - .shutdown = pxa2xx_ac97_pcm_shutdown, - .prepare = pxa2xx_ac97_pcm_prepare, -}; - #ifdef CONFIG_PM_SLEEP static int pxa2xx_ac97_do_suspend(struct snd_card *card) @@ -193,6 +174,53 @@ static int pxa2xx_ac97_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume); #endif +static const struct snd_pcm_ops pxa2xx_ac97_pcm_ops = { + .open = pxa2xx_ac97_pcm_open, + .close = pxa2xx_ac97_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pxa2xx_pcm_hw_params, + .hw_free = pxa2xx_pcm_hw_free, + .prepare = pxa2xx_ac97_pcm_prepare, + .trigger = pxa2xx_pcm_trigger, + .pointer = pxa2xx_pcm_pointer, + .mmap = pxa2xx_pcm_mmap, +}; + + +static int pxa2xx_ac97_pcm_new(struct snd_card *card) +{ + struct snd_pcm *pcm; + int stream, ret; + + ret = snd_pcm_new(card, "PXA2xx-PCM", 0, 1, 1, &pcm); + if (ret) + goto out; + + pcm->private_free = pxa2xx_pcm_free_dma_buffers; + + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + goto out; + + stream = SNDRV_PCM_STREAM_PLAYBACK; + snd_pcm_set_ops(pcm, stream, &pxa2xx_ac97_pcm_ops); + ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream); + if (ret) + goto out; + + stream = SNDRV_PCM_STREAM_CAPTURE; + snd_pcm_set_ops(pcm, stream, &pxa2xx_ac97_pcm_ops); + ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream); + if (ret) + goto out; + + pxa2xx_ac97_pcm = pcm; + ret = 0; + + out: + return ret; +} + static int pxa2xx_ac97_probe(struct platform_device *dev) { struct snd_card *card; @@ -214,7 +242,7 @@ static int pxa2xx_ac97_probe(struct platform_device *dev) strlcpy(card->driver, dev->dev.driver->name, sizeof(card->driver)); - ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm); + ret = pxa2xx_ac97_pcm_new(card); if (ret) goto err; diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index e8da3b8ee721..7931789d4a9f 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -16,8 +16,6 @@ #include <sound/pxa2xx-lib.h> #include <sound/dmaengine_pcm.h> -#include "pxa2xx-pcm.h" - static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | @@ -25,8 +23,8 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, .period_bytes_min = 32, .period_bytes_max = 8192 - 32, .periods_min = 1, @@ -35,8 +33,8 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { .fifo_size = 32, }; -int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -64,14 +62,14 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } -EXPORT_SYMBOL(__pxa2xx_pcm_hw_params); +EXPORT_SYMBOL(pxa2xx_pcm_hw_params); -int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) +int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) { snd_pcm_set_runtime_buffer(substream, NULL); return 0; } -EXPORT_SYMBOL(__pxa2xx_pcm_hw_free); +EXPORT_SYMBOL(pxa2xx_pcm_hw_free); int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { @@ -86,13 +84,13 @@ pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(pxa2xx_pcm_pointer); -int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) +int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) { return 0; } -EXPORT_SYMBOL(__pxa2xx_pcm_prepare); +EXPORT_SYMBOL(pxa2xx_pcm_prepare); -int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) +int pxa2xx_pcm_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; @@ -125,17 +123,17 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) if (ret < 0) return ret; - return snd_dmaengine_pcm_open_request_chan(substream, - pxad_filter_fn, - dma_params->filter_data); + return snd_dmaengine_pcm_open( + substream, dma_request_slave_channel(rtd->cpu_dai->dev, + dma_params->chan_name)); } -EXPORT_SYMBOL(__pxa2xx_pcm_open); +EXPORT_SYMBOL(pxa2xx_pcm_open); -int __pxa2xx_pcm_close(struct snd_pcm_substream *substream) +int pxa2xx_pcm_close(struct snd_pcm_substream *substream) { return snd_dmaengine_pcm_close_release_chan(substream); } -EXPORT_SYMBOL(__pxa2xx_pcm_close); +EXPORT_SYMBOL(pxa2xx_pcm_close); int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) @@ -181,6 +179,47 @@ void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) } EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers); +int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + int ret; + + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } + out: + return ret; +} +EXPORT_SYMBOL(pxa2xx_soc_pcm_new); + +const struct snd_pcm_ops pxa2xx_pcm_ops = { + .open = pxa2xx_pcm_open, + .close = pxa2xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pxa2xx_pcm_hw_params, + .hw_free = pxa2xx_pcm_hw_free, + .prepare = pxa2xx_pcm_prepare, + .trigger = pxa2xx_pcm_trigger, + .pointer = pxa2xx_pcm_pointer, + .mmap = pxa2xx_pcm_mmap, +}; +EXPORT_SYMBOL(pxa2xx_pcm_ops); + MODULE_AUTHOR("Nicolas Pitre"); MODULE_DESCRIPTION("Intel PXA2xx sound library"); MODULE_LICENSE("GPL"); diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c deleted file mode 100644 index 1c6f4b436de3..000000000000 --- a/sound/arm/pxa2xx-pcm.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip - * - * Author: Nicolas Pitre - * Created: Nov 30, 2004 - * Copyright: (C) 2004 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/module.h> -#include <linux/dma-mapping.h> -#include <linux/dmaengine.h> - -#include <mach/dma.h> - -#include <sound/core.h> -#include <sound/pxa2xx-lib.h> -#include <sound/dmaengine_pcm.h> - -#include "pxa2xx-pcm.h" - -static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct pxa2xx_pcm_client *client = substream->private_data; - - __pxa2xx_pcm_prepare(substream); - - return client->prepare(substream); -} - -static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) -{ - struct pxa2xx_pcm_client *client = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *rtd; - int ret; - - ret = __pxa2xx_pcm_open(substream); - if (ret) - goto out; - - rtd = runtime->private_data; - - rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - client->playback_params : client->capture_params; - - ret = client->startup(substream); - if (!ret) - goto err2; - - return 0; - - err2: - __pxa2xx_pcm_close(substream); - out: - return ret; -} - -static int pxa2xx_pcm_close(struct snd_pcm_substream *substream) -{ - struct pxa2xx_pcm_client *client = substream->private_data; - - client->shutdown(substream); - - return __pxa2xx_pcm_close(substream); -} - -static const struct snd_pcm_ops pxa2xx_pcm_ops = { - .open = pxa2xx_pcm_open, - .close = pxa2xx_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = __pxa2xx_pcm_hw_params, - .hw_free = __pxa2xx_pcm_hw_free, - .prepare = pxa2xx_pcm_prepare, - .trigger = pxa2xx_pcm_trigger, - .pointer = pxa2xx_pcm_pointer, - .mmap = pxa2xx_pcm_mmap, -}; - -int pxa2xx_pcm_new(struct snd_card *card, struct pxa2xx_pcm_client *client, - struct snd_pcm **rpcm) -{ - struct snd_pcm *pcm; - int play = client->playback_params ? 1 : 0; - int capt = client->capture_params ? 1 : 0; - int ret; - - ret = snd_pcm_new(card, "PXA2xx-PCM", 0, play, capt, &pcm); - if (ret) - goto out; - - pcm->private_data = client; - pcm->private_free = pxa2xx_pcm_free_dma_buffers; - - ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); - if (ret) - goto out; - - if (play) { - int stream = SNDRV_PCM_STREAM_PLAYBACK; - snd_pcm_set_ops(pcm, stream, &pxa2xx_pcm_ops); - ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream); - if (ret) - goto out; - } - if (capt) { - int stream = SNDRV_PCM_STREAM_CAPTURE; - snd_pcm_set_ops(pcm, stream, &pxa2xx_pcm_ops); - ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream); - if (ret) - goto out; - } - - if (rpcm) - *rpcm = pcm; - ret = 0; - - out: - return ret; -} - -EXPORT_SYMBOL(pxa2xx_pcm_new); - -MODULE_AUTHOR("Nicolas Pitre"); -MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module"); -MODULE_LICENSE("GPL"); diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h deleted file mode 100644 index 8fa2b7c9e6b8..000000000000 --- a/sound/arm/pxa2xx-pcm.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * linux/sound/arm/pxa2xx-pcm.h -- ALSA PCM interface for the Intel PXA2xx chip - * - * Author: Nicolas Pitre - * Created: Nov 30, 2004 - * Copyright: MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -struct pxa2xx_runtime_data { - int dma_ch; - struct snd_dmaengine_dai_dma_data *params; -}; - -struct pxa2xx_pcm_client { - struct snd_dmaengine_dai_dma_data *playback_params; - struct snd_dmaengine_dai_dma_data *capture_params; - int (*startup)(struct snd_pcm_substream *); - void (*shutdown)(struct snd_pcm_substream *); - int (*prepare)(struct snd_pcm_substream *); -}; - -extern int pxa2xx_pcm_new(struct snd_card *, struct pxa2xx_pcm_client *, struct snd_pcm **); - diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 41af6b9cc350..1cf11cf51e1d 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -57,6 +57,7 @@ source "sound/soc/kirkwood/Kconfig" source "sound/soc/img/Kconfig" source "sound/soc/intel/Kconfig" source "sound/soc/mediatek/Kconfig" +source "sound/soc/meson/Kconfig" source "sound/soc/mxs/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/qcom/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 06389a5385d7..62a5f87c3cfc 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_SND_SOC) += jz4740/ obj-$(CONFIG_SND_SOC) += img/ obj-$(CONFIG_SND_SOC) += intel/ obj-$(CONFIG_SND_SOC) += mediatek/ +obj-$(CONFIG_SND_SOC) += meson/ obj-$(CONFIG_SND_SOC) += mxs/ obj-$(CONFIG_SND_SOC) += nuc900/ obj-$(CONFIG_SND_SOC) += omap/ diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 6cbf9cf4d1a4..58c1dcb4d255 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -8,6 +8,7 @@ config SND_SOC_AMD_CZ_DA7219MX98357_MACH select SND_SOC_DA7219 select SND_SOC_MAX98357A select SND_SOC_ADAU7002 + select REGULATOR depends on SND_SOC_AMD_ACP && I2C help This option enables machine driver for DA7219 and MAX9835. diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index ccddc6650b9c..8e3275a96a82 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -32,6 +32,8 @@ #include <linux/clk.h> #include <linux/gpio.h> #include <linux/module.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/driver.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/acpi.h> @@ -148,7 +150,8 @@ static int cz_da7219_startup(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); - machine->i2s_instance = I2S_BT_INSTANCE; + machine->i2s_instance = I2S_SP_INSTANCE; + machine->capture_channel = CAP_CHANNEL1; return da7219_clk_enable(substream); } @@ -163,7 +166,7 @@ static int cz_max_startup(struct snd_pcm_substream *substream) struct snd_soc_card *card = rtd->card; struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); - machine->i2s_instance = I2S_SP_INSTANCE; + machine->i2s_instance = I2S_BT_INSTANCE; return da7219_clk_enable(substream); } @@ -172,13 +175,24 @@ static void cz_max_shutdown(struct snd_pcm_substream *substream) da7219_clk_disable(); } -static int cz_dmic_startup(struct snd_pcm_substream *substream) +static int cz_dmic0_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); + + machine->i2s_instance = I2S_BT_INSTANCE; + return da7219_clk_enable(substream); +} + +static int cz_dmic1_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); machine->i2s_instance = I2S_SP_INSTANCE; + machine->capture_channel = CAP_CHANNEL0; return da7219_clk_enable(substream); } @@ -197,23 +211,39 @@ static const struct snd_soc_ops cz_max_play_ops = { .shutdown = cz_max_shutdown, }; -static const struct snd_soc_ops cz_dmic_cap_ops = { - .startup = cz_dmic_startup, +static const struct snd_soc_ops cz_dmic0_cap_ops = { + .startup = cz_dmic0_startup, + .shutdown = cz_dmic_shutdown, +}; + +static const struct snd_soc_ops cz_dmic1_cap_ops = { + .startup = cz_dmic1_startup, .shutdown = cz_dmic_shutdown, }; static struct snd_soc_dai_link cz_dai_7219_98357[] = { { - .name = "amd-da7219-play-cap", - .stream_name = "Playback and Capture", + .name = "amd-da7219-play", + .stream_name = "Playback", .platform_name = "acp_audio_dma.0.auto", - .cpu_dai_name = "designware-i2s.3.auto", + .cpu_dai_name = "designware-i2s.1.auto", .codec_dai_name = "da7219-hifi", .codec_name = "i2c-DLGS7219:00", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, .init = cz_da7219_init, .dpcm_playback = 1, + .ops = &cz_da7219_cap_ops, + }, + { + .name = "amd-da7219-cap", + .stream_name = "Capture", + .platform_name = "acp_audio_dma.0.auto", + .cpu_dai_name = "designware-i2s.2.auto", + .codec_dai_name = "da7219-hifi", + .codec_name = "i2c-DLGS7219:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, .dpcm_capture = 1, .ops = &cz_da7219_cap_ops, }, @@ -221,7 +251,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "amd-max98357-play", .stream_name = "HiFi Playback", .platform_name = "acp_audio_dma.0.auto", - .cpu_dai_name = "designware-i2s.1.auto", + .cpu_dai_name = "designware-i2s.3.auto", .codec_dai_name = "HiFi", .codec_name = "MX98357A:00", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF @@ -230,8 +260,22 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .ops = &cz_max_play_ops, }, { - .name = "dmic", - .stream_name = "DMIC Capture", + /* C panel DMIC */ + .name = "dmic0", + .stream_name = "DMIC0 Capture", + .platform_name = "acp_audio_dma.0.auto", + .cpu_dai_name = "designware-i2s.3.auto", + .codec_dai_name = "adau7002-hifi", + .codec_name = "ADAU7002:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .dpcm_capture = 1, + .ops = &cz_dmic0_cap_ops, + }, + { + /* A/B panel DMIC */ + .name = "dmic1", + .stream_name = "DMIC1 Capture", .platform_name = "acp_audio_dma.0.auto", .cpu_dai_name = "designware-i2s.2.auto", .codec_dai_name = "adau7002-hifi", @@ -239,7 +283,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, .dpcm_capture = 1, - .ops = &cz_dmic_cap_ops, + .ops = &cz_dmic1_cap_ops, }, }; @@ -278,11 +322,52 @@ static struct snd_soc_card cz_card = { .num_controls = ARRAY_SIZE(cz_mc_controls), }; +static struct regulator_consumer_supply acp_da7219_supplies[] = { + REGULATOR_SUPPLY("VDD", "i2c-DLGS7219:00"), + REGULATOR_SUPPLY("VDDMIC", "i2c-DLGS7219:00"), + REGULATOR_SUPPLY("VDDIO", "i2c-DLGS7219:00"), + REGULATOR_SUPPLY("IOVDD", "ADAU7002:00"), +}; + +static struct regulator_init_data acp_da7219_data = { + .constraints = { + .always_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(acp_da7219_supplies), + .consumer_supplies = acp_da7219_supplies, +}; + +static struct regulator_config acp_da7219_cfg = { + .init_data = &acp_da7219_data, +}; + +static struct regulator_ops acp_da7219_ops = { +}; + +static struct regulator_desc acp_da7219_desc = { + .name = "reg-fixed-1.8V", + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &acp_da7219_ops, + .fixed_uV = 1800000, /* 1.8V */ + .n_voltages = 1, +}; + static int cz_probe(struct platform_device *pdev) { int ret; struct snd_soc_card *card; struct acp_platform_info *machine; + struct regulator_dev *rdev; + + acp_da7219_cfg.dev = &pdev->dev; + rdev = devm_regulator_register(&pdev->dev, &acp_da7219_desc, + &acp_da7219_cfg); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to register regulator: %d\n", + (int)PTR_ERR(rdev)); + return -EINVAL; + } machine = devm_kzalloc(&pdev->dev, sizeof(struct acp_platform_info), GFP_KERNEL); diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 77203841c535..e359938e3d7e 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -224,13 +224,11 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio, switch (asic_type) { case CHIP_STONEY: dmadscr[i].xfer_val |= - BIT(22) | (ACP_DMA_ATTR_SHARED_MEM_TO_DAGB_GARLIC << 16) | (size / 2); break; default: dmadscr[i].xfer_val |= - BIT(22) | (ACP_DMA_ATTR_SHAREDMEM_TO_DAGB_ONION << 16) | (size / 2); } @@ -322,22 +320,87 @@ static void config_acp_dma(void __iomem *acp_mmio, struct audio_substream_data *rtd, u32 asic_type) { + u16 ch_acp_sysmem, ch_acp_i2s; + acp_pte_config(acp_mmio, rtd->pg, rtd->num_of_pages, rtd->pte_offset); + + if (rtd->direction == SNDRV_PCM_STREAM_PLAYBACK) { + ch_acp_sysmem = rtd->ch1; + ch_acp_i2s = rtd->ch2; + } else { + ch_acp_i2s = rtd->ch1; + ch_acp_sysmem = rtd->ch2; + } /* Configure System memory <-> ACP SRAM DMA descriptors */ set_acp_sysmem_dma_descriptors(acp_mmio, rtd->size, rtd->direction, rtd->pte_offset, - rtd->ch1, rtd->sram_bank, + ch_acp_sysmem, rtd->sram_bank, rtd->dma_dscr_idx_1, asic_type); /* Configure ACP SRAM <-> I2S DMA descriptors */ set_acp_to_i2s_dma_descriptors(acp_mmio, rtd->size, rtd->direction, rtd->sram_bank, - rtd->destination, rtd->ch2, + rtd->destination, ch_acp_i2s, rtd->dma_dscr_idx_2, asic_type); } +static void acp_dma_cap_channel_enable(void __iomem *acp_mmio, + u16 cap_channel) +{ + u32 val, ch_reg, imr_reg, res_reg; + + switch (cap_channel) { + case CAP_CHANNEL1: + ch_reg = mmACP_I2SMICSP_RER1; + res_reg = mmACP_I2SMICSP_RCR1; + imr_reg = mmACP_I2SMICSP_IMR1; + break; + case CAP_CHANNEL0: + default: + ch_reg = mmACP_I2SMICSP_RER0; + res_reg = mmACP_I2SMICSP_RCR0; + imr_reg = mmACP_I2SMICSP_IMR0; + break; + } + val = acp_reg_read(acp_mmio, + mmACP_I2S_16BIT_RESOLUTION_EN); + if (val & ACP_I2S_MIC_16BIT_RESOLUTION_EN) { + acp_reg_write(0x0, acp_mmio, ch_reg); + /* Set 16bit resolution on capture */ + acp_reg_write(0x2, acp_mmio, res_reg); + } + val = acp_reg_read(acp_mmio, imr_reg); + val &= ~ACP_I2SMICSP_IMR1__I2SMICSP_RXDAM_MASK; + val &= ~ACP_I2SMICSP_IMR1__I2SMICSP_RXFOM_MASK; + acp_reg_write(val, acp_mmio, imr_reg); + acp_reg_write(0x1, acp_mmio, ch_reg); +} + +static void acp_dma_cap_channel_disable(void __iomem *acp_mmio, + u16 cap_channel) +{ + u32 val, ch_reg, imr_reg; + + switch (cap_channel) { + case CAP_CHANNEL1: + imr_reg = mmACP_I2SMICSP_IMR1; + ch_reg = mmACP_I2SMICSP_RER1; + break; + case CAP_CHANNEL0: + default: + imr_reg = mmACP_I2SMICSP_IMR0; + ch_reg = mmACP_I2SMICSP_RER0; + break; + } + val = acp_reg_read(acp_mmio, imr_reg); + val |= ACP_I2SMICSP_IMR1__I2SMICSP_RXDAM_MASK; + val |= ACP_I2SMICSP_IMR1__I2SMICSP_RXFOM_MASK; + acp_reg_write(val, acp_mmio, imr_reg); + acp_reg_write(0x0, acp_mmio, ch_reg); +} + /* Start a given DMA channel transfer */ -static void acp_dma_start(void __iomem *acp_mmio, u16 ch_num) +static void acp_dma_start(void __iomem *acp_mmio, u16 ch_num, bool is_circular) { u32 dma_ctrl; @@ -356,10 +419,8 @@ static void acp_dma_start(void __iomem *acp_mmio, u16 ch_num) switch (ch_num) { case ACP_TO_I2S_DMA_CH_NUM: - case ACP_TO_SYSRAM_CH_NUM: case I2S_TO_ACP_DMA_CH_NUM: case ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM: - case ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM: case I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM: dma_ctrl |= ACP_DMA_CNTL_0__DMAChIOCEn_MASK; break; @@ -368,8 +429,11 @@ static void acp_dma_start(void __iomem *acp_mmio, u16 ch_num) break; } - /* circular for both DMA channel */ - dma_ctrl |= ACP_DMA_CNTL_0__Circular_DMA_En_MASK; + /* enable for ACP to SRAM DMA channel */ + if (is_circular == true) + dma_ctrl |= ACP_DMA_CNTL_0__Circular_DMA_En_MASK; + else + dma_ctrl &= ~ACP_DMA_CNTL_0__Circular_DMA_En_MASK; acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num); } @@ -613,6 +677,7 @@ static int acp_deinit(void __iomem *acp_mmio) /* ACP DMA irq handler routine for playback, capture usecases */ static irqreturn_t dma_irq_handler(int irq, void *arg) { + u16 dscr_idx; u32 intr_flag, ext_intr_status; struct audio_drv_data *irq_data; void __iomem *acp_mmio; @@ -644,32 +709,39 @@ static irqreturn_t dma_irq_handler(int irq, void *arg) if ((intr_flag & BIT(I2S_TO_ACP_DMA_CH_NUM)) != 0) { valid_irq = true; + if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_14) == + CAPTURE_START_DMA_DESCR_CH15) + dscr_idx = CAPTURE_END_DMA_DESCR_CH14; + else + dscr_idx = CAPTURE_START_DMA_DESCR_CH14; + config_acp_dma_channel(acp_mmio, ACP_TO_SYSRAM_CH_NUM, dscr_idx, + 1, 0); + acp_dma_start(acp_mmio, ACP_TO_SYSRAM_CH_NUM, false); + snd_pcm_period_elapsed(irq_data->capture_i2ssp_stream); acp_reg_write((intr_flag & BIT(I2S_TO_ACP_DMA_CH_NUM)) << 16, acp_mmio, mmACP_EXTERNAL_INTR_STAT); } - if ((intr_flag & BIT(ACP_TO_SYSRAM_CH_NUM)) != 0) { - valid_irq = true; - acp_reg_write((intr_flag & BIT(ACP_TO_SYSRAM_CH_NUM)) << 16, - acp_mmio, mmACP_EXTERNAL_INTR_STAT); - } - if ((intr_flag & BIT(I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM)) != 0) { valid_irq = true; + if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_10) == + CAPTURE_START_DMA_DESCR_CH11) + dscr_idx = CAPTURE_END_DMA_DESCR_CH10; + else + dscr_idx = CAPTURE_START_DMA_DESCR_CH10; + config_acp_dma_channel(acp_mmio, + ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM, + dscr_idx, 1, 0); + acp_dma_start(acp_mmio, ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM, + false); + snd_pcm_period_elapsed(irq_data->capture_i2sbt_stream); acp_reg_write((intr_flag & BIT(I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM)) << 16, acp_mmio, mmACP_EXTERNAL_INTR_STAT); } - if ((intr_flag & BIT(ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM)) != 0) { - valid_irq = true; - acp_reg_write((intr_flag & - BIT(ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM)) << 16, - acp_mmio, mmACP_EXTERNAL_INTR_STAT); - } - if (valid_irq) return IRQ_HANDLED; else @@ -773,7 +845,10 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, if (WARN_ON(!rtd)) return -EINVAL; - rtd->i2s_instance = pinfo->i2s_instance; + if (pinfo) { + rtd->i2s_instance = pinfo->i2s_instance; + rtd->capture_channel = pinfo->capture_channel; + } if (adata->asic_type == CHIP_STONEY) { val = acp_reg_read(adata->acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN); @@ -841,8 +916,8 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, switch (rtd->i2s_instance) { case I2S_BT_INSTANCE: rtd->pte_offset = ACP_ST_BT_CAPTURE_PTE_OFFSET; - rtd->ch1 = ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM; - rtd->ch2 = I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM; + rtd->ch1 = I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM; + rtd->ch2 = ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM; rtd->sram_bank = ACP_SRAM_BANK_4_ADDRESS; rtd->destination = FROM_BLUETOOTH; rtd->dma_dscr_idx_1 = CAPTURE_START_DMA_DESCR_CH10; @@ -851,13 +926,14 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, mmACP_I2S_BT_RECEIVE_BYTE_CNT_HIGH; rtd->byte_cnt_low_reg_offset = mmACP_I2S_BT_RECEIVE_BYTE_CNT_LOW; + rtd->dma_curr_dscr = mmACP_DMA_CUR_DSCR_11; adata->capture_i2sbt_stream = substream; break; case I2S_SP_INSTANCE: default: rtd->pte_offset = ACP_CAPTURE_PTE_OFFSET; - rtd->ch1 = ACP_TO_SYSRAM_CH_NUM; - rtd->ch2 = I2S_TO_ACP_DMA_CH_NUM; + rtd->ch1 = I2S_TO_ACP_DMA_CH_NUM; + rtd->ch2 = ACP_TO_SYSRAM_CH_NUM; switch (adata->asic_type) { case CHIP_STONEY: rtd->pte_offset = ACP_ST_CAPTURE_PTE_OFFSET; @@ -874,6 +950,7 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, mmACP_I2S_RECEIVED_BYTE_CNT_HIGH; rtd->byte_cnt_low_reg_offset = mmACP_I2S_RECEIVED_BYTE_CNT_LOW; + rtd->dma_curr_dscr = mmACP_DMA_CUR_DSCR_15; adata->capture_i2ssp_stream = substream; } } @@ -927,6 +1004,8 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream) u32 buffersize; u32 pos = 0; u64 bytescount = 0; + u16 dscr; + u32 period_bytes, delay; struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; @@ -934,12 +1013,25 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream) if (!rtd) return -EINVAL; - buffersize = frames_to_bytes(runtime, runtime->buffer_size); - bytescount = acp_get_byte_count(rtd); - - if (bytescount > rtd->bytescount) - bytescount -= rtd->bytescount; - pos = do_div(bytescount, buffersize); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + period_bytes = frames_to_bytes(runtime, runtime->period_size); + dscr = acp_reg_read(rtd->acp_mmio, rtd->dma_curr_dscr); + if (dscr == rtd->dma_dscr_idx_1) + pos = period_bytes; + else + pos = 0; + bytescount = acp_get_byte_count(rtd); + if (bytescount > rtd->bytescount) + bytescount -= rtd->bytescount; + delay = do_div(bytescount, period_bytes); + runtime->delay = bytes_to_frames(runtime, delay); + } else { + buffersize = frames_to_bytes(runtime, runtime->buffer_size); + bytescount = acp_get_byte_count(rtd); + if (bytescount > rtd->bytescount) + bytescount -= rtd->bytescount; + pos = do_div(bytescount, buffersize); + } return bytes_to_frames(runtime, pos); } @@ -953,16 +1045,24 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; + u16 ch_acp_sysmem, ch_acp_i2s; if (!rtd) return -EINVAL; + if (rtd->direction == SNDRV_PCM_STREAM_PLAYBACK) { + ch_acp_sysmem = rtd->ch1; + ch_acp_i2s = rtd->ch2; + } else { + ch_acp_i2s = rtd->ch1; + ch_acp_sysmem = rtd->ch2; + } config_acp_dma_channel(rtd->acp_mmio, - rtd->ch1, + ch_acp_sysmem, rtd->dma_dscr_idx_1, NUM_DSCRS_PER_CHANNEL, 0); config_acp_dma_channel(rtd->acp_mmio, - rtd->ch2, + ch_acp_i2s, rtd->dma_dscr_idx_2, NUM_DSCRS_PER_CHANNEL, 0); return 0; @@ -971,7 +1071,6 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream) static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) { int ret; - u64 bytescount = 0; struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; @@ -982,37 +1081,32 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: - bytescount = acp_get_byte_count(rtd); - if (rtd->bytescount == 0) - rtd->bytescount = bytescount; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - acp_dma_start(rtd->acp_mmio, rtd->ch1); - acp_dma_start(rtd->acp_mmio, rtd->ch2); + rtd->bytescount = acp_get_byte_count(rtd); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (rtd->capture_channel == CAP_CHANNEL0) { + acp_dma_cap_channel_disable(rtd->acp_mmio, + CAP_CHANNEL1); + acp_dma_cap_channel_enable(rtd->acp_mmio, + CAP_CHANNEL0); + } + if (rtd->capture_channel == CAP_CHANNEL1) { + acp_dma_cap_channel_disable(rtd->acp_mmio, + CAP_CHANNEL0); + acp_dma_cap_channel_enable(rtd->acp_mmio, + CAP_CHANNEL1); + } + acp_dma_start(rtd->acp_mmio, rtd->ch1, true); } else { - acp_dma_start(rtd->acp_mmio, rtd->ch2); - acp_dma_start(rtd->acp_mmio, rtd->ch1); + acp_dma_start(rtd->acp_mmio, rtd->ch1, true); + acp_dma_start(rtd->acp_mmio, rtd->ch2, true); } ret = 0; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: - /* For playback, non circular dma should be stopped first - * i.e Sysram to acp dma transfer channel(rtd->ch1) should be - * stopped before stopping cirular dma which is acp sram to i2s - * fifo dma transfer channel(rtd->ch2). Where as in Capture - * scenario, i2s fifo to acp sram dma channel(rtd->ch2) stopped - * first before stopping acp sram to sysram which is circular - * dma(rtd->ch1). - */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - acp_dma_stop(rtd->acp_mmio, rtd->ch1); - ret = acp_dma_stop(rtd->acp_mmio, rtd->ch2); - } else { - acp_dma_stop(rtd->acp_mmio, rtd->ch2); - ret = acp_dma_stop(rtd->acp_mmio, rtd->ch1); - } - rtd->bytescount = 0; + acp_dma_stop(rtd->acp_mmio, rtd->ch2); + ret = acp_dma_stop(rtd->acp_mmio, rtd->ch1); break; default: ret = -EINVAL; diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h index 9cd3e96c84d4..be3963e8f4fa 100644 --- a/sound/soc/amd/acp.h +++ b/sound/soc/amd/acp.h @@ -55,6 +55,8 @@ #define I2S_SP_INSTANCE 0x01 #define I2S_BT_INSTANCE 0x02 +#define CAP_CHANNEL0 0x00 +#define CAP_CHANNEL1 0x01 #define ACP_TILE_ON_MASK 0x03 #define ACP_TILE_OFF_MASK 0x02 @@ -72,16 +74,16 @@ #define ACP_TO_I2S_DMA_CH_NUM 13 /* Capture DMA channels */ -#define ACP_TO_SYSRAM_CH_NUM 14 -#define I2S_TO_ACP_DMA_CH_NUM 15 +#define I2S_TO_ACP_DMA_CH_NUM 14 +#define ACP_TO_SYSRAM_CH_NUM 15 /* Playback DMA Channels for I2S BT instance */ #define SYSRAM_TO_ACP_BT_INSTANCE_CH_NUM 8 #define ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM 9 /* Capture DMA Channels for I2S BT Instance */ -#define ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM 10 -#define I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM 11 +#define I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM 10 +#define ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM 11 #define NUM_DSCRS_PER_CHANNEL 2 @@ -125,6 +127,7 @@ struct audio_substream_data { unsigned int order; u16 num_of_pages; u16 i2s_instance; + u16 capture_channel; u16 direction; u16 ch1; u16 ch2; @@ -135,6 +138,7 @@ struct audio_substream_data { u32 sram_bank; u32 byte_cnt_high_reg_offset; u32 byte_cnt_low_reg_offset; + u32 dma_curr_dscr; uint64_t size; u64 bytescount; void __iomem *acp_mmio; @@ -155,6 +159,7 @@ struct audio_drv_data { */ struct acp_platform_info { u16 i2s_instance; + u16 capture_channel; }; union acp_dma_count { diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c index 5d3b5af9fd92..d88c1d995036 100644 --- a/sound/soc/atmel/atmel-i2s.c +++ b/sound/soc/atmel/atmel-i2s.c @@ -206,7 +206,6 @@ struct atmel_i2s_dev { struct regmap *regmap; struct clk *pclk; struct clk *gclk; - struct clk *aclk; struct snd_dmaengine_dai_dma_data playback; struct snd_dmaengine_dai_dma_data capture; unsigned int fmt; @@ -303,7 +302,7 @@ static int atmel_i2s_get_gck_param(struct atmel_i2s_dev *dev, int fs) { int i, best; - if (!dev->gclk || !dev->aclk) { + if (!dev->gclk) { dev_err(dev->dev, "cannot generate the I2S Master Clock\n"); return -EINVAL; } @@ -421,7 +420,7 @@ static int atmel_i2s_switch_mck_generator(struct atmel_i2s_dev *dev, bool enabled) { unsigned int mr, mr_mask; - unsigned long aclk_rate; + unsigned long gclk_rate; int ret; mr = 0; @@ -445,35 +444,18 @@ static int atmel_i2s_switch_mck_generator(struct atmel_i2s_dev *dev, /* Disable/unprepare the PMC generated clock. */ clk_disable_unprepare(dev->gclk); - /* Disable/unprepare the PLL audio clock. */ - clk_disable_unprepare(dev->aclk); return 0; } if (!dev->gck_param) return -EINVAL; - aclk_rate = dev->gck_param->mck * (dev->gck_param->imckdiv + 1); + gclk_rate = dev->gck_param->mck * (dev->gck_param->imckdiv + 1); - /* Fist change the PLL audio clock frequency ... */ - ret = clk_set_rate(dev->aclk, aclk_rate); + ret = clk_set_rate(dev->gclk, gclk_rate); if (ret) return ret; - /* - * ... then set the PMC generated clock rate to the very same frequency - * to set the gclk parent to aclk. - */ - ret = clk_set_rate(dev->gclk, aclk_rate); - if (ret) - return ret; - - /* Prepare and enable the PLL audio clock first ... */ - ret = clk_prepare_enable(dev->aclk); - if (ret) - return ret; - - /* ... then prepare and enable the PMC generated clock. */ ret = clk_prepare_enable(dev->gclk); if (ret) return ret; @@ -668,28 +650,14 @@ static int atmel_i2s_probe(struct platform_device *pdev) return err; } - /* Get audio clocks to generate the I2S Master Clock (I2S_MCK) */ - dev->aclk = devm_clk_get(&pdev->dev, "aclk"); + /* Get audio clock to generate the I2S Master Clock (I2S_MCK) */ dev->gclk = devm_clk_get(&pdev->dev, "gclk"); - if (IS_ERR(dev->aclk) && IS_ERR(dev->gclk)) { - if (PTR_ERR(dev->aclk) == -EPROBE_DEFER || - PTR_ERR(dev->gclk) == -EPROBE_DEFER) + if (IS_ERR(dev->gclk)) { + if (PTR_ERR(dev->gclk) == -EPROBE_DEFER) return -EPROBE_DEFER; /* Master Mode not supported */ - dev->aclk = NULL; dev->gclk = NULL; - } else if (IS_ERR(dev->gclk)) { - err = PTR_ERR(dev->gclk); - dev_err(&pdev->dev, - "failed to get the PMC generated clock: %d\n", err); - return err; - } else if (IS_ERR(dev->aclk)) { - err = PTR_ERR(dev->aclk); - dev_err(&pdev->dev, - "failed to get the PLL audio clock: %d\n", err); - return err; } - dev->dev = &pdev->dev; dev->regmap = regmap; platform_set_drvdata(pdev, dev); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 63cf62e9c9aa..efb095dbcd71 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -74,12 +74,12 @@ config SND_SOC_ALL_CODECS select SND_SOC_DA7219 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C - select SND_SOC_DIO2125 select SND_SOC_DMIC if GPIOLIB select SND_SOC_ES8316 if I2C select SND_SOC_ES8328_SPI if SPI_MASTER select SND_SOC_ES8328_I2C if I2C select SND_SOC_ES7134 + select SND_SOC_ES7241 select SND_SOC_GTM601 select SND_SOC_HDAC_HDMI select SND_SOC_ICS43432 @@ -141,8 +141,10 @@ config SND_SOC_ALL_CODECS select SND_SOC_RT5668 if I2C select SND_SOC_RT5670 if I2C select SND_SOC_RT5677 if I2C && SPI_MASTER + select SND_SOC_RT5682 if I2C select SND_SOC_SGTL5000 if I2C select SND_SOC_SI476X if MFD_SI476X_CORE + select SND_SOC_SIMPLE_AMPLIFIER select SND_SOC_SIRF_AUDIO_CODEC select SND_SOC_SPDIF select SND_SOC_SSM2305 @@ -572,10 +574,6 @@ config SND_SOC_DA732X config SND_SOC_DA9055 tristate -config SND_SOC_DIO2125 - tristate "Dioo DIO2125 Amplifier" - select GPIOLIB - config SND_SOC_DMIC tristate @@ -588,6 +586,9 @@ config SND_SOC_HDMI_CODEC config SND_SOC_ES7134 tristate "Everest Semi ES7134 CODEC" +config SND_SOC_ES7241 + tristate "Everest Semi ES7241 CODEC" + config SND_SOC_ES8316 tristate "Everest Semi ES8316 CODEC" depends on I2C @@ -778,6 +779,7 @@ config SND_SOC_RL6231 default y if SND_SOC_RT5668=y default y if SND_SOC_RT5670=y default y if SND_SOC_RT5677=y + default y if SND_SOC_RT5682=y default y if SND_SOC_RT1305=y default m if SND_SOC_RT5514=m default m if SND_SOC_RT5616=m @@ -791,6 +793,7 @@ config SND_SOC_RL6231 default m if SND_SOC_RT5668=m default m if SND_SOC_RT5670=m default m if SND_SOC_RT5677=m + default m if SND_SOC_RT5682=m default m if SND_SOC_RT1305=m config SND_SOC_RL6347A @@ -871,6 +874,9 @@ config SND_SOC_RT5677_SPI tristate default SND_SOC_RT5677 && SPI +config SND_SOC_RT5682 + tristate + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" @@ -891,6 +897,10 @@ config SND_SOC_SIGMADSP_REGMAP tristate select SND_SOC_SIGMADSP +config SND_SOC_SIMPLE_AMPLIFIER + tristate "Simple Audio Amplifier" + select GPIOLIB + config SND_SOC_SIRF_AUDIO_CODEC tristate "SiRF SoC internal audio codec" select REGMAP_MMIO @@ -953,8 +963,11 @@ config SND_SOC_TAS5086 depends on I2C config SND_SOC_TAS571X - tristate "Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 power amplifiers" + tristate "Texas Instruments TAS571x power amplifiers" depends on I2C + help + Enable support for Texas Instruments TAS5707, TAS5711, TAS5717, + TAS5719 and TAS5721 power amplifiers config SND_SOC_TAS5720 tristate "Texas Instruments TAS5720 Mono Audio amplifier" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index e023fdf85221..7ae7c85e8219 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -71,6 +71,7 @@ snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o snd-soc-dmic-objs := dmic.o snd-soc-es7134-objs := es7134.o +snd-soc-es7241-objs := es7241.o snd-soc-es8316-objs := es8316.o snd-soc-es8328-objs := es8328.o snd-soc-es8328-i2c-objs := es8328-i2c.o @@ -146,6 +147,7 @@ snd-soc-rt5668-objs := rt5668.o snd-soc-rt5670-objs := rt5670.o snd-soc-rt5677-objs := rt5677.o snd-soc-rt5677-spi-objs := rt5677-spi.o +snd-soc-rt5682-objs := rt5682.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -249,9 +251,9 @@ snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-zx-aud96p22-objs := zx_aud96p22.o # Amp -snd-soc-dio2125-objs := dio2125.o snd-soc-max9877-objs := max9877.o snd-soc-max98504-objs := max98504.o +snd-soc-simple-amplifier-objs := simple-amplifier.o snd-soc-tpa6130a2-objs := tpa6130a2.o snd-soc-tas2552-objs := tas2552.o @@ -329,6 +331,7 @@ obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o +obj-$(CONFIG_SND_SOC_ES7241) += snd-soc-es7241.o obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o @@ -405,6 +408,7 @@ obj-$(CONFIG_SND_SOC_RT5668) += snd-soc-rt5668.o obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o +obj-$(CONFIG_SND_SOC_RT5682) += snd-soc-rt5682.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o @@ -507,7 +511,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o # Amp -obj-$(CONFIG_SND_SOC_DIO2125) += snd-soc-dio2125.o obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o +obj-$(CONFIG_SND_SOC_SIMPLE_AMPLIFIER) += snd-soc-simple-amplifier.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index ae41edd1c406..57169b8ff14e 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -299,6 +299,7 @@ static const struct snd_soc_dapm_route adau17x1_dsp_dapm_routes[] = { { "DSP", NULL, "Left Decimator" }, { "DSP", NULL, "Right Decimator" }, + { "DSP", NULL, "Playback" }, }; static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = { diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index db21ecbe0762..8b9ca7e7a682 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -648,6 +648,7 @@ static int adav80x_set_pll(struct snd_soc_component *component, int pll_id, pll_ctrl1 |= ADAV80X_PLL_CTRL1_PLLDIV; break; } + /* fall through */ default: return -EINVAL; } diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 31ec0ba2e639..299ada4dfaa0 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -558,7 +558,7 @@ static int __maybe_unused ak4458_runtime_resume(struct device *dev) } #endif /* CONFIG_PM */ -struct snd_soc_component_driver soc_codec_dev_ak4458 = { +static const struct snd_soc_component_driver soc_codec_dev_ak4458 = { .probe = ak4458_probe, .remove = ak4458_remove, .controls = ak4458_snd_controls, diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c index b7ee13406d93..2fa83a1a84cf 100644 --- a/sound/soc/codecs/ak4554.c +++ b/sound/soc/codecs/ak4554.c @@ -1,13 +1,8 @@ -/* - * ak4554.c - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// ak4554.c +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> #include <linux/module.h> #include <sound/soc.h> @@ -97,6 +92,6 @@ static struct platform_driver ak4554_driver = { }; module_platform_driver(ak4554_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SoC AK4554 driver"); MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index 8523ff9351cf..c1181a20714d 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -1,18 +1,14 @@ -/* - * ak4613.c -- Asahi Kasei ALSA Soc Audio driver - * - * Copyright (C) 2015 Renesas Electronics Corporation - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * Based on ak4642.c by Kuninori Morimoto - * Based on wm8731.c by Richard Purdie - * Based on ak4535.c by Richard Purdie - * Based on wm8753.c by Liam Girdwood - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ak4613.c -- Asahi Kasei ALSA Soc Audio driver +// +// Copyright (C) 2015 Renesas Electronics Corporation +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +// +// Based on ak4642.c by Kuninori Morimoto +// Based on wm8731.c by Richard Purdie +// Based on ak4535.c by Richard Purdie +// Based on wm8753.c by Liam Girdwood #include <linux/clk.h> #include <linux/delay.h> diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 605055964529..353237025514 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -1,17 +1,13 @@ -/* - * ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver - * - * Copyright (C) 2009 Renesas Solutions Corp. - * Kuninori Morimoto <morimoto.kuninori@renesas.com> - * - * Based on wm8731.c by Richard Purdie - * Based on ak4535.c by Richard Purdie - * Based on wm8753.c by Liam Girdwood - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver +// +// Copyright (C) 2009 Renesas Solutions Corp. +// Kuninori Morimoto <morimoto.kuninori@renesas.com> +// +// Based on wm8731.c by Richard Purdie +// Based on ak4535.c by Richard Purdie +// Based on wm8753.c by Liam Girdwood /* ** CAUTION ** * @@ -709,4 +705,4 @@ module_i2c_driver(ak4642_i2c_driver); MODULE_DESCRIPTION("Soc AK4642 driver"); MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index f4ed5cc40661..448bb90c9c8e 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -322,13 +322,13 @@ static int __maybe_unused ak5558_runtime_resume(struct device *dev) return regcache_sync(ak5558->regmap); } -const struct dev_pm_ops ak5558_pm = { +static const struct dev_pm_ops ak5558_pm = { SET_RUNTIME_PM_OPS(ak5558_runtime_suspend, ak5558_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; -struct snd_soc_component_driver soc_codec_dev_ak5558 = { +static const struct snd_soc_component_driver soc_codec_dev_ak5558 = { .probe = ak5558_probe, .remove = ak5558_remove, .controls = ak5558_snd_controls, diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 2a7a4168c072..3c266eeb89bf 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -219,7 +219,7 @@ static bool cs4270_reg_is_volatile(struct device *dev, unsigned int reg) { /* Unreadable registers are considered volatile */ if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) - return 1; + return true; return reg == CS4270_CHIPID; } diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 196e9c343aeb..45e50fe3bf25 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -235,6 +235,9 @@ ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP2", 1), +WM_ADSP_FW_CONTROL("DSP3", 2), }; ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); @@ -1283,6 +1286,12 @@ static int cs47l24_probe(struct platform_device *pdev) return ret; } + ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1); + if (ret != 0) + dev_warn(&pdev->dev, + "Failed to set compressed IRQ as a wake source: %d\n", + ret); + arizona_init_common(arizona); ret = arizona_init_vol_limit(arizona); @@ -1306,6 +1315,7 @@ static int cs47l24_probe(struct platform_device *pdev) err_spk_irqs: arizona_free_spk_irqs(arizona); err_dsp_irq: + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24); return ret; @@ -1323,6 +1333,7 @@ static int cs47l24_remove(struct platform_device *pdev) arizona_free_spk_irqs(arizona); + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24); return 0; diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index 07dd33b09596..ab174b5114dc 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -362,8 +362,27 @@ static int cx20442_component_probe(struct snd_soc_component *component) return -ENOMEM; cx20442->por = regulator_get(component->dev, "POR"); - if (IS_ERR(cx20442->por)) - dev_warn(component->dev, "failed to get the regulator"); + if (IS_ERR(cx20442->por)) { + int err = PTR_ERR(cx20442->por); + + dev_warn(component->dev, "failed to get POR supply (%d)", err); + /* + * When running on a non-dt platform and requested regulator + * is not available, regulator_get() never returns + * -EPROBE_DEFER as it is not able to justify if the regulator + * may still appear later. On the other hand, the board can + * still set full constraints flag at late_initcall in order + * to instruct regulator_get() to return a dummy one if + * sufficient. Hence, if we get -ENODEV here, let's convert + * it to -EPROBE_DEFER and wait for the board to decide or + * let Deferred Probe infrastructure handle this error. + */ + if (err == -ENODEV) + err = -EPROBE_DEFER; + kfree(cx20442); + return err; + } + cx20442->tty = NULL; snd_soc_component_set_drvdata(component, cx20442); diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index a664111b7184..e172913d04a4 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -1,19 +1,14 @@ -/* - * DA7210 ALSA Soc codec driver - * - * Copyright (c) 2009 Dialog Semiconductor - * Written by David Chen <Dajun.chen@diasemi.com> - * - * Copyright (C) 2009 Renesas Solutions Corp. - * Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com> - * - * Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// DA7210 ALSA Soc codec driver +// +// Copyright (c) 2009 Dialog Semiconductor +// Written by David Chen <Dajun.chen@diasemi.com> +// +// Copyright (C) 2009 Renesas Solutions Corp. +// Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com> +// +// Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S #include <linux/delay.h> #include <linux/i2c.h> diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 54cb5f24969f..92d006a5283e 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1140,9 +1140,9 @@ static bool da7213_volatile_register(struct device *dev, unsigned int reg) case DA7213_ALC_OFFSET_AUTO_M_R: case DA7213_ALC_OFFSET_AUTO_U_R: case DA7213_ALC_CIC_OP_LVL_DATA: - return 1; + return true; default: - return 0; + return false; } } diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c index a49ab751a036..2c7d5088e6f2 100644 --- a/sound/soc/codecs/da7219-aad.c +++ b/sound/soc/codecs/da7219-aad.c @@ -59,6 +59,7 @@ static void da7219_aad_btn_det_work(struct work_struct *work) container_of(work, struct da7219_aad_priv, btn_det_work); struct snd_soc_component *component = da7219_aad->component; struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); u8 statusa, micbias_ctrl; bool micbias_up = false; int retries = 0; @@ -86,6 +87,8 @@ static void da7219_aad_btn_det_work(struct work_struct *work) if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES) dev_warn(component->dev, "Mic bias status check timed out"); + da7219->micbias_on_event = true; + /* * Mic bias pulse required to enable mic, must be done before enabling * button detection to prevent erroneous button readings. @@ -439,6 +442,8 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data) snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, DA7219_BUTTON_CONFIG_MASK, 0); + da7219->micbias_on_event = false; + /* Disable mic bias */ snd_soc_dapm_disable_pin(dapm, "Mic Bias"); snd_soc_dapm_sync(dapm); diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 980a6a8bf56d..e46e9f4bc994 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -768,6 +768,30 @@ static const struct snd_kcontrol_new da7219_st_out_filtr_mix_controls[] = { * DAPM Events */ +static int da7219_mic_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (da7219->micbias_on_event) { + /* + * Delay only for first capture after bias enabled to + * avoid possible DC offset related noise. + */ + da7219->micbias_on_event = false; + msleep(da7219->mic_pga_delay); + } + break; + default: + break; + } + + return 0; +} + static int da7219_dai_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -937,12 +961,12 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = { SND_SOC_DAPM_INPUT("MIC"), /* Input PGAs */ - SND_SOC_DAPM_PGA("Mic PGA", DA7219_MIC_1_CTRL, - DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT, - NULL, 0), - SND_SOC_DAPM_PGA("Mixin PGA", DA7219_MIXIN_L_CTRL, - DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT, - NULL, 0), + SND_SOC_DAPM_PGA_E("Mic PGA", DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0, da7219_mic_pga_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("Mixin PGA", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0, da7219_settling_event, SND_SOC_DAPM_POST_PMU), /* Input Filters */ SND_SOC_DAPM_ADC("ADC", NULL, DA7219_ADC_L_CTRL, DA7219_ADC_L_EN_SHIFT, @@ -1847,6 +1871,14 @@ static void da7219_handle_pdata(struct snd_soc_component *component) snd_soc_component_write(component, DA7219_MICBIAS_CTRL, micbias_lvl); + /* + * Calculate delay required to compensate for DC offset in + * Mic PGA, based on Mic Bias voltage. + */ + da7219->mic_pga_delay = DA7219_MIC_PGA_BASE_DELAY + + (pdata->micbias_lvl * + DA7219_MIC_PGA_OFFSET_DELAY); + /* Mic */ switch (pdata->mic_amp_in_sel) { case DA7219_MIC_AMP_IN_SEL_DIFF: @@ -2143,9 +2175,9 @@ static bool da7219_volatile_register(struct device *dev, unsigned int reg) case DA7219_ACCDET_IRQ_EVENT_B: case DA7219_ACCDET_CONFIG_8: case DA7219_SYSTEM_STATUS: - return 1; + return true; default: - return 0; + return false; } } diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h index 1b00023e33cd..3a006862f0e7 100644 --- a/sound/soc/codecs/da7219.h +++ b/sound/soc/codecs/da7219.h @@ -781,8 +781,10 @@ #define DA7219_SYS_STAT_CHECK_DELAY 50 /* Power up/down Delays */ -#define DA7219_SETTLING_DELAY 40 -#define DA7219_MIN_GAIN_DELAY 30 +#define DA7219_SETTLING_DELAY 40 +#define DA7219_MIN_GAIN_DELAY 30 +#define DA7219_MIC_PGA_BASE_DELAY 100 +#define DA7219_MIC_PGA_OFFSET_DELAY 40 enum da7219_clk_src { DA7219_CLKSRC_MCLK = 0, @@ -828,6 +830,8 @@ struct da7219_priv { bool master; bool alc_en; + bool micbias_on_event; + unsigned int mic_pga_delay; u8 gain_ramp_ctrl; }; diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index afdf90c78884..f6a7bf9560e7 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -1041,9 +1041,9 @@ static bool da9055_volatile_register(struct device *dev, case DA9055_HP_R_GAIN_STATUS: case DA9055_LINE_GAIN_STATUS: case DA9055_ALC_CIC_OP_LVL_DATA: - return 1; + return true; default: - return 0; + return false; } } diff --git a/sound/soc/codecs/es7134.c b/sound/soc/codecs/es7134.c index 58515bb1a303..6d7bca7b78ca 100644 --- a/sound/soc/codecs/es7134.c +++ b/sound/soc/codecs/es7134.c @@ -17,6 +17,7 @@ * in the file called COPYING. */ +#include <linux/of_platform.h> #include <linux/module.h> #include <sound/soc.h> @@ -24,6 +25,82 @@ * The everest 7134 is a very simple DA converter with no register */ +struct es7134_clock_mode { + unsigned int rate_min; + unsigned int rate_max; + unsigned int *mclk_fs; + unsigned int mclk_fs_num; +}; + +struct es7134_chip { + struct snd_soc_dai_driver *dai_drv; + const struct es7134_clock_mode *modes; + unsigned int mode_num; + const struct snd_soc_dapm_widget *extra_widgets; + unsigned int extra_widget_num; + const struct snd_soc_dapm_route *extra_routes; + unsigned int extra_route_num; +}; + +struct es7134_data { + unsigned int mclk; + const struct es7134_chip *chip; +}; + +static int es7134_check_mclk(struct snd_soc_dai *dai, + struct es7134_data *priv, + unsigned int rate) +{ + unsigned int mfs = priv->mclk / rate; + int i, j; + + for (i = 0; i < priv->chip->mode_num; i++) { + const struct es7134_clock_mode *mode = &priv->chip->modes[i]; + + if (rate < mode->rate_min || rate > mode->rate_max) + continue; + + for (j = 0; j < mode->mclk_fs_num; j++) { + if (mode->mclk_fs[j] == mfs) + return 0; + } + + dev_err(dai->dev, "unsupported mclk_fs %u for rate %u\n", + mfs, rate); + return -EINVAL; + } + + /* should not happen */ + dev_err(dai->dev, "unsupported rate: %u\n", rate); + return -EINVAL; +} + +static int es7134_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct es7134_data *priv = snd_soc_dai_get_drvdata(dai); + + /* mclk has not been provided, assume it is OK */ + if (!priv->mclk) + return 0; + + return es7134_check_mclk(dai, priv, params_rate(params)); +} + +static int es7134_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct es7134_data *priv = snd_soc_dai_get_drvdata(dai); + + if (dir == SND_SOC_CLOCK_IN && clk_id == 0) { + priv->mclk = freq; + return 0; + } + + return -ENOTSUPP; +} + static int es7134_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK | @@ -38,8 +115,38 @@ static int es7134_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return 0; } +static int es7134_component_probe(struct snd_soc_component *c) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(c); + struct es7134_data *priv = snd_soc_component_get_drvdata(c); + const struct es7134_chip *chip = priv->chip; + int ret; + + if (chip->extra_widget_num) { + ret = snd_soc_dapm_new_controls(dapm, chip->extra_widgets, + chip->extra_widget_num); + if (ret) { + dev_err(c->dev, "failed to add extra widgets\n"); + return ret; + } + } + + if (chip->extra_route_num) { + ret = snd_soc_dapm_add_routes(dapm, chip->extra_routes, + chip->extra_route_num); + if (ret) { + dev_err(c->dev, "failed to add extra routes\n"); + return ret; + } + } + + return 0; +} + static const struct snd_soc_dai_ops es7134_dai_ops = { .set_fmt = es7134_set_fmt, + .hw_params = es7134_hw_params, + .set_sysclk = es7134_set_sysclk, }; static struct snd_soc_dai_driver es7134_dai = { @@ -48,7 +155,11 @@ static struct snd_soc_dai_driver es7134_dai = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, + .rates = (SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | @@ -58,18 +169,56 @@ static struct snd_soc_dai_driver es7134_dai = { .ops = &es7134_dai_ops, }; +static const struct es7134_clock_mode es7134_modes[] = { + { + /* Single speed mode */ + .rate_min = 8000, + .rate_max = 50000, + .mclk_fs = (unsigned int[]) { 256, 384, 512, 768, 1024 }, + .mclk_fs_num = 5, + }, { + /* Double speed mode */ + .rate_min = 84000, + .rate_max = 100000, + .mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512 }, + .mclk_fs_num = 5, + }, { + /* Quad speed mode */ + .rate_min = 167000, + .rate_max = 192000, + .mclk_fs = (unsigned int[]) { 128, 192, 256 }, + .mclk_fs_num = 3, + }, +}; + +/* Digital I/O are also supplied by VDD on the es7134 */ +static const struct snd_soc_dapm_route es7134_extra_routes[] = { + { "Playback", NULL, "VDD", } +}; + +static const struct es7134_chip es7134_chip = { + .dai_drv = &es7134_dai, + .modes = es7134_modes, + .mode_num = ARRAY_SIZE(es7134_modes), + .extra_routes = es7134_extra_routes, + .extra_route_num = ARRAY_SIZE(es7134_extra_routes), +}; + static const struct snd_soc_dapm_widget es7134_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("AOUTL"), SND_SOC_DAPM_OUTPUT("AOUTR"), SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDD", 0, 0), }; static const struct snd_soc_dapm_route es7134_dapm_routes[] = { { "AOUTL", NULL, "DAC" }, { "AOUTR", NULL, "DAC" }, + { "DAC", NULL, "VDD" }, }; static const struct snd_soc_component_driver es7134_component_driver = { + .probe = es7134_component_probe, .dapm_widgets = es7134_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(es7134_dapm_widgets), .dapm_routes = es7134_dapm_routes, @@ -80,17 +229,87 @@ static const struct snd_soc_component_driver es7134_component_driver = { .non_legacy_dai_naming = 1, }; +static struct snd_soc_dai_driver es7154_dai = { + .name = "es7154-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S18_3LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + .ops = &es7134_dai_ops, +}; + +static const struct es7134_clock_mode es7154_modes[] = { + { + /* Single speed mode */ + .rate_min = 8000, + .rate_max = 50000, + .mclk_fs = (unsigned int[]) { 32, 64, 128, 192, 256, + 384, 512, 768, 1024 }, + .mclk_fs_num = 9, + }, { + /* Double speed mode */ + .rate_min = 84000, + .rate_max = 100000, + .mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512, + 768, 1024}, + .mclk_fs_num = 7, + } +}; + +/* Es7154 has a separate supply for digital I/O */ +static const struct snd_soc_dapm_widget es7154_extra_widgets[] = { + SND_SOC_DAPM_REGULATOR_SUPPLY("PVDD", 0, 0), +}; + +static const struct snd_soc_dapm_route es7154_extra_routes[] = { + { "Playback", NULL, "PVDD", } +}; + +static const struct es7134_chip es7154_chip = { + .dai_drv = &es7154_dai, + .modes = es7154_modes, + .mode_num = ARRAY_SIZE(es7154_modes), + .extra_routes = es7154_extra_routes, + .extra_route_num = ARRAY_SIZE(es7154_extra_routes), + .extra_widgets = es7154_extra_widgets, + .extra_widget_num = ARRAY_SIZE(es7154_extra_widgets), +}; + static int es7134_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct es7134_data *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + priv->chip = of_device_get_match_data(dev); + if (!priv->chip) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + return devm_snd_soc_register_component(&pdev->dev, &es7134_component_driver, - &es7134_dai, 1); + priv->chip->dai_drv, 1); } #ifdef CONFIG_OF static const struct of_device_id es7134_ids[] = { - { .compatible = "everest,es7134", }, - { .compatible = "everest,es7144", }, + { .compatible = "everest,es7134", .data = &es7134_chip }, + { .compatible = "everest,es7144", .data = &es7134_chip }, + { .compatible = "everest,es7154", .data = &es7154_chip }, { } }; MODULE_DEVICE_TABLE(of, es7134_ids); diff --git a/sound/soc/codecs/es7241.c b/sound/soc/codecs/es7241.c new file mode 100644 index 000000000000..87991bd4acef --- /dev/null +++ b/sound/soc/codecs/es7241.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/gpio/consumer.h> +#include <linux/of_platform.h> +#include <linux/module.h> +#include <sound/soc.h> + +struct es7241_clock_mode { + unsigned int rate_min; + unsigned int rate_max; + unsigned int *slv_mfs; + unsigned int slv_mfs_num; + unsigned int mst_mfs; + unsigned int mst_m0:1; + unsigned int mst_m1:1; +}; + +struct es7241_chip { + const struct es7241_clock_mode *modes; + unsigned int mode_num; +}; + +struct es7241_data { + struct gpio_desc *reset; + struct gpio_desc *m0; + struct gpio_desc *m1; + unsigned int fmt; + unsigned int mclk; + bool is_slave; + const struct es7241_chip *chip; +}; + +static void es7241_set_mode(struct es7241_data *priv, int m0, int m1) +{ + /* put the device in reset */ + gpiod_set_value_cansleep(priv->reset, 0); + + /* set the mode */ + gpiod_set_value_cansleep(priv->m0, m0); + gpiod_set_value_cansleep(priv->m1, m1); + + /* take the device out of reset - datasheet does not specify a delay */ + gpiod_set_value_cansleep(priv->reset, 1); +} + +static int es7241_set_slave_mode(struct es7241_data *priv, + const struct es7241_clock_mode *mode, + unsigned int mfs) +{ + int j; + + if (!mfs) + goto out_ok; + + for (j = 0; j < mode->slv_mfs_num; j++) { + if (mode->slv_mfs[j] == mfs) + goto out_ok; + } + + return -EINVAL; + +out_ok: + es7241_set_mode(priv, 1, 1); + return 0; +} + +static int es7241_set_master_mode(struct es7241_data *priv, + const struct es7241_clock_mode *mode, + unsigned int mfs) +{ + /* + * We can't really set clock ratio, if the mclk/lrclk is different + * from what we provide, then error out + */ + if (mfs && mfs != mode->mst_mfs) + return -EINVAL; + + es7241_set_mode(priv, mode->mst_m0, mode->mst_m1); + + return 0; +} + +static int es7241_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct es7241_data *priv = snd_soc_dai_get_drvdata(dai); + unsigned int rate = params_rate(params); + unsigned int mfs = priv->mclk / rate; + int i; + + for (i = 0; i < priv->chip->mode_num; i++) { + const struct es7241_clock_mode *mode = &priv->chip->modes[i]; + + if (rate < mode->rate_min || rate >= mode->rate_max) + continue; + + if (priv->is_slave) + return es7241_set_slave_mode(priv, mode, mfs); + else + return es7241_set_master_mode(priv, mode, mfs); + } + + /* should not happen */ + dev_err(dai->dev, "unsupported rate: %u\n", rate); + return -EINVAL; +} + +static int es7241_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct es7241_data *priv = snd_soc_dai_get_drvdata(dai); + + if (dir == SND_SOC_CLOCK_IN && clk_id == 0) { + priv->mclk = freq; + return 0; + } + + return -ENOTSUPP; +} + +static int es7241_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct es7241_data *priv = snd_soc_dai_get_drvdata(dai); + + if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) { + dev_err(dai->dev, "Unsupported dai clock inversion\n"); + return -EINVAL; + } + + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != priv->fmt) { + dev_err(dai->dev, "Invalid dai format\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + priv->is_slave = true; + break; + case SND_SOC_DAIFMT_CBM_CFM: + priv->is_slave = false; + break; + + default: + dev_err(dai->dev, "Unsupported clock configuration\n"); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops es7241_dai_ops = { + .set_fmt = es7241_set_fmt, + .hw_params = es7241_hw_params, + .set_sysclk = es7241_set_sysclk, +}; + +static struct snd_soc_dai_driver es7241_dai = { + .name = "es7241-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + .ops = &es7241_dai_ops, +}; + +static const struct es7241_clock_mode es7241_modes[] = { + { + /* Single speed mode */ + .rate_min = 8000, + .rate_max = 50000, + .slv_mfs = (unsigned int[]) { 256, 384, 512, 768, 1024 }, + .slv_mfs_num = 5, + .mst_mfs = 256, + .mst_m0 = 0, + .mst_m1 = 0, + }, { + /* Double speed mode */ + .rate_min = 50000, + .rate_max = 100000, + .slv_mfs = (unsigned int[]) { 128, 192 }, + .slv_mfs_num = 2, + .mst_mfs = 128, + .mst_m0 = 1, + .mst_m1 = 0, + }, { + /* Quad speed mode */ + .rate_min = 100000, + .rate_max = 200000, + .slv_mfs = (unsigned int[]) { 64 }, + .slv_mfs_num = 1, + .mst_mfs = 64, + .mst_m0 = 0, + .mst_m1 = 1, + }, +}; + +static const struct es7241_chip es7241_chip = { + .modes = es7241_modes, + .mode_num = ARRAY_SIZE(es7241_modes), +}; + +static const struct snd_soc_dapm_widget es7241_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("AINL"), + SND_SOC_DAPM_INPUT("AINR"), + SND_SOC_DAPM_DAC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDDP", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDDD", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDDA", 0, 0), +}; + +static const struct snd_soc_dapm_route es7241_dapm_routes[] = { + { "ADC", NULL, "AINL", }, + { "ADC", NULL, "AINR", }, + { "ADC", NULL, "VDDA", }, + { "Capture", NULL, "VDDP", }, + { "Capture", NULL, "VDDD", }, +}; + +static const struct snd_soc_component_driver es7241_component_driver = { + .dapm_widgets = es7241_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es7241_dapm_widgets), + .dapm_routes = es7241_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es7241_dapm_routes), + .idle_bias_on = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static void es7241_parse_fmt(struct device *dev, struct es7241_data *priv) +{ + bool is_leftj; + + /* + * The format is given by a pull resistor on the SDOUT pin: + * pull-up for i2s, pull-down for left justified. + */ + is_leftj = of_property_read_bool(dev->of_node, + "everest,sdout-pull-down"); + if (is_leftj) + priv->fmt = SND_SOC_DAIFMT_LEFT_J; + else + priv->fmt = SND_SOC_DAIFMT_I2S; +} + +static int es7241_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct es7241_data *priv; + int err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + priv->chip = of_device_get_match_data(dev); + if (!priv->chip) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + es7241_parse_fmt(dev, priv); + + priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) { + err = PTR_ERR(priv->reset); + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get 'reset' gpio: %d", err); + return err; + } + + priv->m0 = devm_gpiod_get_optional(dev, "m0", GPIOD_OUT_LOW); + if (IS_ERR(priv->m0)) { + err = PTR_ERR(priv->m0); + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get 'm0' gpio: %d", err); + return err; + } + + priv->m1 = devm_gpiod_get_optional(dev, "m1", GPIOD_OUT_LOW); + if (IS_ERR(priv->m1)) { + err = PTR_ERR(priv->m1); + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get 'm1' gpio: %d", err); + return err; + } + + return devm_snd_soc_register_component(&pdev->dev, + &es7241_component_driver, + &es7241_dai, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id es7241_ids[] = { + { .compatible = "everest,es7241", .data = &es7241_chip }, + { } +}; +MODULE_DEVICE_TABLE(of, es7241_ids); +#endif + +static struct platform_driver es7241_driver = { + .driver = { + .name = "es7241", + .of_match_table = of_match_ptr(es7241_ids), + }, + .probe = es7241_probe, +}; + +module_platform_driver(es7241_driver); + +MODULE_DESCRIPTION("ASoC ES7241 audio codec driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 4748a9d5de3b..7b8533abf637 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -2093,6 +2093,75 @@ static int hdac_hdmi_dev_remove(struct hdac_device *hdev) } #ifdef CONFIG_PM +/* + * Power management sequences + * ========================== + * + * The following explains the PM handling of HDAC HDMI with its parent + * device SKL and display power usage + * + * Probe + * ----- + * In SKL probe, + * 1. skl_probe_work() powers up the display (refcount++ -> 1) + * 2. enumerates the codecs on the link + * 3. powers down the display (refcount-- -> 0) + * + * In HDAC HDMI probe, + * 1. hdac_hdmi_dev_probe() powers up the display (refcount++ -> 1) + * 2. probe the codec + * 3. put the HDAC HDMI device to runtime suspend + * 4. hdac_hdmi_runtime_suspend() powers down the display (refcount-- -> 0) + * + * Once children are runtime suspended, SKL device also goes to runtime + * suspend + * + * HDMI Playback + * ------------- + * Open HDMI device, + * 1. skl_runtime_resume() invoked + * 2. hdac_hdmi_runtime_resume() powers up the display (refcount++ -> 1) + * + * Close HDMI device, + * 1. hdac_hdmi_runtime_suspend() powers down the display (refcount-- -> 0) + * 2. skl_runtime_suspend() invoked + * + * S0/S3 Cycle with playback in progress + * ------------------------------------- + * When the device is opened for playback, the device is runtime active + * already and the display refcount is 1 as explained above. + * + * Entering to S3, + * 1. hdmi_codec_prepare() invoke the runtime resume of codec which just + * increments the PM runtime usage count of the codec since the device + * is in use already + * 2. skl_suspend() powers down the display (refcount-- -> 0) + * + * Wakeup from S3, + * 1. skl_resume() powers up the display (refcount++ -> 1) + * 2. hdmi_codec_complete() invokes the runtime suspend of codec which just + * decrements the PM runtime usage count of the codec since the device + * is in use already + * + * Once playback is stopped, the display refcount is set to 0 as explained + * above in the HDMI playback sequence. The PM handlings are designed in + * such way that to balance the refcount of display power when the codec + * device put to S3 while playback is going on. + * + * S0/S3 Cycle without playback in progress + * ---------------------------------------- + * Entering to S3, + * 1. hdmi_codec_prepare() invoke the runtime resume of codec + * 2. skl_runtime_resume() invoked + * 3. hdac_hdmi_runtime_resume() powers up the display (refcount++ -> 1) + * 4. skl_suspend() powers down the display (refcount-- -> 0) + * + * Wakeup from S3, + * 1. skl_resume() powers up the display (refcount++ -> 1) + * 2. hdmi_codec_complete() invokes the runtime suspend of codec + * 3. hdac_hdmi_runtime_suspend() powers down the display (refcount-- -> 0) + * 4. skl_runtime_suspend() invoked + */ static int hdac_hdmi_runtime_suspend(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 38e4a8515709..d00734d31e04 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -291,10 +291,6 @@ static const struct snd_soc_dapm_widget hdmi_widgets[] = { SND_SOC_DAPM_OUTPUT("TX"), }; -static const struct snd_soc_dapm_route hdmi_routes[] = { - { "TX", NULL, "Playback" }, -}; - enum { DAI_ID_I2S = 0, DAI_ID_SPDIF, @@ -689,9 +685,23 @@ static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd, return snd_ctl_add(rtd->card->snd_card, kctl); } +static int hdmi_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_dapm_route route = { + .sink = "TX", + .source = dai->driver->playback.stream_name, + }; + + dapm = snd_soc_component_get_dapm(dai->component); + + return snd_soc_dapm_add_routes(dapm, &route, 1); +} + static const struct snd_soc_dai_driver hdmi_i2s_dai = { .name = "i2s-hifi", .id = DAI_ID_I2S, + .probe = hdmi_dai_probe, .playback = { .stream_name = "I2S Playback", .channels_min = 2, @@ -707,6 +717,7 @@ static const struct snd_soc_dai_driver hdmi_i2s_dai = { static const struct snd_soc_dai_driver hdmi_spdif_dai = { .name = "spdif-hifi", .id = DAI_ID_SPDIF, + .probe = hdmi_dai_probe, .playback = { .stream_name = "SPDIF Playback", .channels_min = 2, @@ -733,8 +744,6 @@ static int hdmi_of_xlate_dai_id(struct snd_soc_component *component, static const struct snd_soc_component_driver hdmi_driver = { .dapm_widgets = hdmi_widgets, .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), - .dapm_routes = hdmi_routes, - .num_dapm_routes = ARRAY_SIZE(hdmi_routes), .of_xlate_dai_id = hdmi_of_xlate_dai_id, .idle_bias_on = 1, .use_pmdown_time = 1, diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c index a92586106932..92b7125ea169 100644 --- a/sound/soc/codecs/max98373.c +++ b/sound/soc/codecs/max98373.c @@ -488,6 +488,7 @@ static const DECLARE_TLV_DB_RANGE(max98373_bde_gain_tlv, static bool max98373_readable_register(struct device *dev, unsigned int reg) { switch (reg) { + case MAX98373_R2000_SW_RESET: case MAX98373_R2001_INT_RAW1 ... MAX98373_R200C_INT_EN3: case MAX98373_R2010_IRQ_CTRL: case MAX98373_R2014_THERM_WARN_THRESH diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index 74d7f52c7e73..6e6134589588 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -52,9 +52,9 @@ static bool max9850_volatile_register(struct device *dev, unsigned int reg) switch (reg) { case MAX9850_STATUSA: case MAX9850_STATUSB: - return 1; + return true; default: - return 0; + return false; } } diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index 17104f8dc1a9..e3c8cd17daf2 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -362,11 +362,8 @@ static const struct snd_soc_dapm_route nau8540_dapm_routes[] = { static int nau8540_clock_check(struct nau8540 *nau8540, int rate, int osr) { - int osrate; - if (osr >= ARRAY_SIZE(osr_adc_sel)) return -EINVAL; - osrate = osr_adc_sel[osr].osr; if (rate * osr > CLK_ADC_MAX) { dev_err(nau8540->dev, "exceed the maximum frequency of CLK_ADC\n"); diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index 6bd14453f06e..468d5143e2c4 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -1274,7 +1274,7 @@ static int nau8824_calc_fll_param(unsigned int fll_in, fvco_max = 0; fvco_sel = ARRAY_SIZE(mclk_src_scaling); for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { - fvco = 256 * fs * 2 * mclk_src_scaling[i].param; + fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param; if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && fvco_max < fvco) { fvco_max = fvco; diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index dc6ea4987b7d..b9fed99d8b5e 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -2016,7 +2016,7 @@ static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, fvco_max = 0; fvco_sel = ARRAY_SIZE(mclk_src_scaling); for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { - fvco = 256 * fs * 2 * mclk_src_scaling[i].param; + fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param; if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && fvco_max < fvco) { fvco_max = fvco; diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c index 21f15219b3ad..8df6447c76a6 100644 --- a/sound/soc/codecs/pcm1789.c +++ b/sound/soc/codecs/pcm1789.c @@ -262,8 +262,7 @@ int pcm1789_common_exit(struct device *dev) { struct pcm1789_private *priv = dev_get_drvdata(dev); - if (&priv->work) - flush_work(&priv->work); + flush_work(&priv->work); return 0; } diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c index 88fde70b1e9e..690c26e7389e 100644 --- a/sound/soc/codecs/pcm186x.c +++ b/sound/soc/codecs/pcm186x.c @@ -265,7 +265,7 @@ static int pcm186x_hw_params(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component); unsigned int rate = params_rate(params); - unsigned int format = params_format(params); + snd_pcm_format_t format = params_format(params); unsigned int width = params_width(params); unsigned int channels = params_channels(params); unsigned int div_lrck; diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c index f4c8c45f4010..c4452efc7970 100644 --- a/sound/soc/codecs/rt1305.c +++ b/sound/soc/codecs/rt1305.c @@ -1066,7 +1066,7 @@ static void rt1305_calibrate(struct rt1305_priv *rt1305) pr_debug("Left_rhl = 0x%x rh=0x%x rl=0x%x\n", rhl, rh, rl); pr_info("Left channel %d.%dohm\n", (r0ohm/10), (r0ohm%10)); - r0l = 562949953421312; + r0l = 562949953421312ULL; if (rhl != 0) do_div(r0l, rhl); pr_debug("Left_r0 = 0x%llx\n", r0l); @@ -1083,7 +1083,7 @@ static void rt1305_calibrate(struct rt1305_priv *rt1305) pr_debug("Right_rhl = 0x%x rh=0x%x rl=0x%x\n", rhl, rh, rl); pr_info("Right channel %d.%dohm\n", (r0ohm/10), (r0ohm%10)); - r0r = 562949953421312; + r0r = 562949953421312ULL; if (rhl != 0) do_div(r0r, rhl); pr_debug("Right_r0 = 0x%llx\n", r0r); @@ -1150,17 +1150,11 @@ static int rt1305_i2c_probe(struct i2c_client *i2c, rt1305_reset(rt1305->regmap); rt1305_calibrate(rt1305); - return snd_soc_register_component(&i2c->dev, &soc_component_dev_rt1305, + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt1305, rt1305_dai, ARRAY_SIZE(rt1305_dai)); } -static int rt1305_i2c_remove(struct i2c_client *i2c) -{ - snd_soc_unregister_component(&i2c->dev); - - return 0; -} - static void rt1305_i2c_shutdown(struct i2c_client *client) { struct rt1305_priv *rt1305 = i2c_get_clientdata(client); @@ -1180,7 +1174,6 @@ static struct i2c_driver rt1305_i2c_driver = { #endif }, .probe = rt1305_i2c_probe, - .remove = rt1305_i2c_remove, .shutdown = rt1305_i2c_shutdown, .id_table = rt1305_i2c_id, }; diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 1570b91bf018..dca82dd6e3bf 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -64,8 +64,8 @@ static const struct reg_sequence rt5514_patch[] = { {RT5514_ANA_CTRL_LDO10, 0x00028604}, {RT5514_ANA_CTRL_ADCFED, 0x00000800}, {RT5514_ASRC_IN_CTRL1, 0x00000003}, - {RT5514_DOWNFILTER0_CTRL3, 0x10000362}, - {RT5514_DOWNFILTER1_CTRL3, 0x10000362}, + {RT5514_DOWNFILTER0_CTRL3, 0x10000352}, + {RT5514_DOWNFILTER1_CTRL3, 0x10000352}, }; static const struct reg_default rt5514_reg[] = { @@ -92,10 +92,10 @@ static const struct reg_default rt5514_reg[] = { {RT5514_ASRC_IN_CTRL1, 0x00000003}, {RT5514_DOWNFILTER0_CTRL1, 0x00020c2f}, {RT5514_DOWNFILTER0_CTRL2, 0x00020c2f}, - {RT5514_DOWNFILTER0_CTRL3, 0x10000362}, + {RT5514_DOWNFILTER0_CTRL3, 0x10000352}, {RT5514_DOWNFILTER1_CTRL1, 0x00020c2f}, {RT5514_DOWNFILTER1_CTRL2, 0x00020c2f}, - {RT5514_DOWNFILTER1_CTRL3, 0x10000362}, + {RT5514_DOWNFILTER1_CTRL3, 0x10000352}, {RT5514_ANA_CTRL_LDO10, 0x00028604}, {RT5514_ANA_CTRL_LDO18_16, 0x02000345}, {RT5514_ANA_CTRL_ADC12, 0x0000a2a8}, diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index cf6dce69eb2a..865f49ac38dd 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -105,9 +105,9 @@ static bool rt5631_volatile_register(struct device *dev, unsigned int reg) case RT5631_INDEX_ADD: case RT5631_INDEX_DATA: case RT5631_EQ_CTRL: - return 1; + return true; default: - return 0; + return false; } } @@ -164,9 +164,9 @@ static bool rt5631_readable_register(struct device *dev, unsigned int reg) case RT5631_VENDOR_ID: case RT5631_VENDOR_ID1: case RT5631_VENDOR_ID2: - return 1; + return true; default: - return 0; + return false; } } @@ -229,10 +229,10 @@ static SOC_ENUM_SINGLE_DECL(rt5631_spk_ratio_enum, RT5631_GEN_PUR_CTRL_REG, static const struct snd_kcontrol_new rt5631_snd_controls[] = { /* MIC */ SOC_ENUM("MIC1 Mode Control", rt5631_mic1_mode_enum), - SOC_SINGLE_TLV("MIC1 Boost", RT5631_MIC_CTRL_2, + SOC_SINGLE_TLV("MIC1 Boost Volume", RT5631_MIC_CTRL_2, RT5631_MIC1_BOOST_SHIFT, 8, 0, mic_bst_tlv), SOC_ENUM("MIC2 Mode Control", rt5631_mic2_mode_enum), - SOC_SINGLE_TLV("MIC2 Boost", RT5631_MIC_CTRL_2, + SOC_SINGLE_TLV("MIC2 Boost Volume", RT5631_MIC_CTRL_2, RT5631_MIC2_BOOST_SHIFT, 8, 0, mic_bst_tlv), /* MONO IN */ SOC_ENUM("MONOIN Mode Control", rt5631_monoin_mode_enum), diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 8bf8d360c25f..27770143ae8f 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1665,6 +1665,7 @@ static int get_sdp_info(struct snd_soc_component *component, int dai_id) break; case RT5640_IF_113: ret |= RT5640_U_IF1; + /* fall through */ case RT5640_IF_312: case RT5640_IF_213: ret |= RT5640_U_IF2; @@ -1680,6 +1681,7 @@ static int get_sdp_info(struct snd_soc_component *component, int dai_id) break; case RT5640_IF_223: ret |= RT5640_U_IF1; + /* fall through */ case RT5640_IF_123: case RT5640_IF_321: ret |= RT5640_U_IF2; diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index 6b5669f3e85d..985852fd9723 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -331,11 +331,13 @@ static const struct snd_kcontrol_new rt5651_snd_controls[] = { SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5651_DAC2_DIG_VOL, RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 175, 0, dac_vol_tlv), - /* IN1/IN2 Control */ + /* IN1/IN2/IN3 Control */ SOC_SINGLE_TLV("IN1 Boost", RT5651_IN1_IN2, RT5651_BST_SFT1, 8, 0, bst_tlv), SOC_SINGLE_TLV("IN2 Boost", RT5651_IN1_IN2, RT5651_BST_SFT2, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN3 Boost", RT5651_IN3, + RT5651_BST_SFT1, 8, 0, bst_tlv), /* INL/INR Volume Control */ SOC_DOUBLE_TLV("IN Capture Volume", RT5651_INL1_INR1_VOL, RT5651_INL_VOL_SFT, RT5651_INR_VOL_SFT, @@ -1581,6 +1583,24 @@ static void rt5651_disable_micbias1_for_ovcd(struct snd_soc_component *component snd_soc_dapm_mutex_unlock(dapm); } +static void rt5651_enable_micbias1_ovcd_irq(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2, + RT5651_IRQ_MB1_OC_MASK, RT5651_IRQ_MB1_OC_NOR); + rt5651->ovcd_irq_enabled = true; +} + +static void rt5651_disable_micbias1_ovcd_irq(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2, + RT5651_IRQ_MB1_OC_MASK, RT5651_IRQ_MB1_OC_BP); + rt5651->ovcd_irq_enabled = false; +} + static void rt5651_clear_micbias1_ovcd(struct snd_soc_component *component) { snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2, @@ -1622,10 +1642,80 @@ static bool rt5651_jack_inserted(struct snd_soc_component *component) return val == 0; } -/* Jack detect timings */ +/* Jack detect and button-press timings */ #define JACK_SETTLE_TIME 100 /* milli seconds */ #define JACK_DETECT_COUNT 5 #define JACK_DETECT_MAXCOUNT 20 /* Aprox. 2 seconds worth of tries */ +#define JACK_UNPLUG_TIME 80 /* milli seconds */ +#define BP_POLL_TIME 10 /* milli seconds */ +#define BP_POLL_MAXCOUNT 200 /* assume something is wrong after this */ +#define BP_THRESHOLD 3 + +static void rt5651_start_button_press_work(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + rt5651->poll_count = 0; + rt5651->press_count = 0; + rt5651->release_count = 0; + rt5651->pressed = false; + rt5651->press_reported = false; + rt5651_clear_micbias1_ovcd(component); + schedule_delayed_work(&rt5651->bp_work, msecs_to_jiffies(BP_POLL_TIME)); +} + +static void rt5651_button_press_work(struct work_struct *work) +{ + struct rt5651_priv *rt5651 = + container_of(work, struct rt5651_priv, bp_work.work); + struct snd_soc_component *component = rt5651->component; + + /* Check the jack was not removed underneath us */ + if (!rt5651_jack_inserted(component)) + return; + + if (rt5651_micbias1_ovcd(component)) { + rt5651->release_count = 0; + rt5651->press_count++; + /* Remember till after JACK_UNPLUG_TIME wait */ + if (rt5651->press_count >= BP_THRESHOLD) + rt5651->pressed = true; + rt5651_clear_micbias1_ovcd(component); + } else { + rt5651->press_count = 0; + rt5651->release_count++; + } + + /* + * The pins get temporarily shorted on jack unplug, so we poll for + * at least JACK_UNPLUG_TIME milli-seconds before reporting a press. + */ + rt5651->poll_count++; + if (rt5651->poll_count < (JACK_UNPLUG_TIME / BP_POLL_TIME)) { + schedule_delayed_work(&rt5651->bp_work, + msecs_to_jiffies(BP_POLL_TIME)); + return; + } + + if (rt5651->pressed && !rt5651->press_reported) { + dev_dbg(component->dev, "headset button press\n"); + snd_soc_jack_report(rt5651->hp_jack, SND_JACK_BTN_0, + SND_JACK_BTN_0); + rt5651->press_reported = true; + } + + if (rt5651->release_count >= BP_THRESHOLD) { + if (rt5651->press_reported) { + dev_dbg(component->dev, "headset button release\n"); + snd_soc_jack_report(rt5651->hp_jack, 0, SND_JACK_BTN_0); + } + /* Re-enable OVCD IRQ to detect next press */ + rt5651_enable_micbias1_ovcd_irq(component); + return; /* Stop polling */ + } + + schedule_delayed_work(&rt5651->bp_work, msecs_to_jiffies(BP_POLL_TIME)); +} static int rt5651_detect_headset(struct snd_soc_component *component) { @@ -1676,15 +1766,58 @@ static void rt5651_jack_detect_work(struct work_struct *work) { struct rt5651_priv *rt5651 = container_of(work, struct rt5651_priv, jack_detect_work); + struct snd_soc_component *component = rt5651->component; int report = 0; - if (rt5651_jack_inserted(rt5651->component)) { - rt5651_enable_micbias1_for_ovcd(rt5651->component); - report = rt5651_detect_headset(rt5651->component); - rt5651_disable_micbias1_for_ovcd(rt5651->component); + if (!rt5651_jack_inserted(component)) { + /* Jack removed, or spurious IRQ? */ + if (rt5651->hp_jack->status & SND_JACK_HEADPHONE) { + if (rt5651->hp_jack->status & SND_JACK_MICROPHONE) { + cancel_delayed_work_sync(&rt5651->bp_work); + rt5651_disable_micbias1_ovcd_irq(component); + rt5651_disable_micbias1_for_ovcd(component); + } + snd_soc_jack_report(rt5651->hp_jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0); + dev_dbg(component->dev, "jack unplugged\n"); + } + } else if (!(rt5651->hp_jack->status & SND_JACK_HEADPHONE)) { + /* Jack inserted */ + WARN_ON(rt5651->ovcd_irq_enabled); + rt5651_enable_micbias1_for_ovcd(component); + report = rt5651_detect_headset(component); + if (report == SND_JACK_HEADSET) { + /* Enable ovcd IRQ for button press detect. */ + rt5651_enable_micbias1_ovcd_irq(component); + } else { + /* No more need for overcurrent detect. */ + rt5651_disable_micbias1_for_ovcd(component); + } + dev_dbg(component->dev, "detect report %#02x\n", report); + snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET); + } else if (rt5651->ovcd_irq_enabled && rt5651_micbias1_ovcd(component)) { + dev_dbg(component->dev, "OVCD IRQ\n"); + + /* + * The ovcd IRQ keeps firing while the button is pressed, so + * we disable it and start polling the button until released. + * + * The disable will make the IRQ pin 0 again and since we get + * IRQs on both edges (so as to detect both jack plugin and + * unplug) this means we will immediately get another IRQ. + * The ovcd_irq_enabled check above makes the 2ND IRQ a NOP. + */ + rt5651_disable_micbias1_ovcd_irq(component); + rt5651_start_button_press_work(component); + + /* + * If the jack-detect IRQ flag goes high (unplug) after our + * above rt5651_jack_inserted() check and before we have + * disabled the OVCD IRQ, the IRQ pin will stay high and as + * we react to edges, we miss the unplug event -> recheck. + */ + queue_work(system_long_wq, &rt5651->jack_detect_work); } - - snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET); } static irqreturn_t rt5651_irq(int irq, void *data) @@ -1696,14 +1829,18 @@ static irqreturn_t rt5651_irq(int irq, void *data) return IRQ_HANDLED; } -static int rt5651_set_jack(struct snd_soc_component *component, - struct snd_soc_jack *hp_jack, void *data) +static void rt5651_cancel_work(void *data) { - struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); - int ret; + struct rt5651_priv *rt5651 = data; - if (!rt5651->irq) - return -EINVAL; + cancel_work_sync(&rt5651->jack_detect_work); + cancel_delayed_work_sync(&rt5651->bp_work); +} + +static void rt5651_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hp_jack) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); /* IRQ output on GPIO1 */ snd_soc_component_update_bits(component, RT5651_GPIO_CTRL1, @@ -1730,10 +1867,10 @@ static int rt5651_set_jack(struct snd_soc_component *component, RT5651_JD2_IRQ_EN, RT5651_JD2_IRQ_EN); break; case RT5651_JD_NULL: - return 0; + return; default: dev_err(component->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n"); - return -EINVAL; + return; } /* Enable jack detect power */ @@ -1767,19 +1904,39 @@ static int rt5651_set_jack(struct snd_soc_component *component, RT5651_MB1_OC_STKY_MASK, RT5651_MB1_OC_STKY_EN); rt5651->hp_jack = hp_jack; - - ret = devm_request_threaded_irq(component->dev, rt5651->irq, NULL, - rt5651_irq, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, "rt5651", rt5651); - if (ret) { - dev_err(component->dev, "Failed to reguest IRQ: %d\n", ret); - return ret; + if (rt5651->hp_jack->status & SND_JACK_MICROPHONE) { + rt5651_enable_micbias1_for_ovcd(component); + rt5651_enable_micbias1_ovcd_irq(component); } + enable_irq(rt5651->irq); /* sync initial jack state */ queue_work(system_power_efficient_wq, &rt5651->jack_detect_work); +} + +static void rt5651_disable_jack_detect(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + disable_irq(rt5651->irq); + rt5651_cancel_work(rt5651); + + if (rt5651->hp_jack->status & SND_JACK_MICROPHONE) { + rt5651_disable_micbias1_ovcd_irq(component); + rt5651_disable_micbias1_for_ovcd(component); + snd_soc_jack_report(rt5651->hp_jack, 0, SND_JACK_BTN_0); + } + + rt5651->hp_jack = NULL; +} + +static int rt5651_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + if (jack) + rt5651_enable_jack_detect(component, jack); + else + rt5651_disable_jack_detect(component); return 0; } @@ -2034,8 +2191,26 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, rt5651->irq = i2c->irq; rt5651->hp_mute = 1; + INIT_DELAYED_WORK(&rt5651->bp_work, rt5651_button_press_work); INIT_WORK(&rt5651->jack_detect_work, rt5651_jack_detect_work); + /* Make sure work is stopped on probe-error / remove */ + ret = devm_add_action_or_reset(&i2c->dev, rt5651_cancel_work, rt5651); + if (ret) + return ret; + + ret = devm_request_irq(&i2c->dev, rt5651->irq, rt5651_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5651", rt5651); + if (ret == 0) { + /* Gets re-enabled by rt5651_set_jack() */ + disable_irq(rt5651->irq); + } else { + dev_warn(&i2c->dev, "Failed to reguest IRQ %d: %d\n", + rt5651->irq, ret); + rt5651->irq = -ENXIO; + } + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5651, rt5651_dai, ARRAY_SIZE(rt5651_dai)); @@ -2043,15 +2218,6 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, return ret; } -static int rt5651_i2c_remove(struct i2c_client *i2c) -{ - struct rt5651_priv *rt5651 = i2c_get_clientdata(i2c); - - cancel_work_sync(&rt5651->jack_detect_work); - - return 0; -} - static struct i2c_driver rt5651_i2c_driver = { .driver = { .name = "rt5651", @@ -2059,7 +2225,6 @@ static struct i2c_driver rt5651_i2c_driver = { .of_match_table = of_match_ptr(rt5651_of_match), }, .probe = rt5651_i2c_probe, - .remove = rt5651_i2c_remove, .id_table = rt5651_i2c_id, }; module_i2c_driver(rt5651_i2c_driver); diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h index 3a0968c53fde..ac6de6fb5414 100644 --- a/sound/soc/codecs/rt5651.h +++ b/sound/soc/codecs/rt5651.h @@ -2071,8 +2071,16 @@ struct rt5651_pll_code { struct rt5651_priv { struct snd_soc_component *component; struct regmap *regmap; + /* Jack and button detect data */ struct snd_soc_jack *hp_jack; struct work_struct jack_detect_work; + struct delayed_work bp_work; + bool ovcd_irq_enabled; + bool pressed; + bool press_reported; + int press_count; + int release_count; + int poll_count; unsigned int jd_src; unsigned int ovcd_th; unsigned int ovcd_sf; diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 8a0181a2db08..9b7a1833d331 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -4417,6 +4417,7 @@ static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, break; case 25: slot_width_25 = 0x8080; + /* fall through */ case 24: val |= (2 << 8); break; @@ -5007,7 +5008,7 @@ static const struct regmap_config rt5677_regmap = { }; static const struct of_device_id rt5677_of_match[] = { - { .compatible = "realtek,rt5677", RT5677 }, + { .compatible = "realtek,rt5677", .data = (const void *)RT5677 }, { } }; MODULE_DEVICE_TABLE(of, rt5677_of_match); diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c new file mode 100644 index 000000000000..640d400ca013 --- /dev/null +++ b/sound/soc/codecs/rt5682.c @@ -0,0 +1,2681 @@ +/* + * rt5682.c -- RT5682 ALSA SoC audio component driver + * + * Copyright 2018 Realtek Semiconductor Corp. + * Author: Bard Liao <bardliao@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/acpi.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/mutex.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/jack.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/rt5682.h> + +#include "rl6231.h" +#include "rt5682.h" + +#define RT5682_NUM_SUPPLIES 3 + +static const char *rt5682_supply_names[RT5682_NUM_SUPPLIES] = { + "AVDD", + "MICVDD", + "VBAT", +}; + +struct rt5682_priv { + struct snd_soc_component *component; + struct rt5682_platform_data pdata; + struct regmap *regmap; + struct snd_soc_jack *hs_jack; + struct regulator_bulk_data supplies[RT5682_NUM_SUPPLIES]; + struct delayed_work jack_detect_work; + struct delayed_work jd_check_work; + struct mutex calibrate_mutex; + + int sysclk; + int sysclk_src; + int lrck[RT5682_AIFS]; + int bclk[RT5682_AIFS]; + int master[RT5682_AIFS]; + + int pll_src; + int pll_in; + int pll_out; + + int jack_type; +}; + +static const struct reg_sequence patch_list[] = { + {0x01c1, 0x1000}, +}; + +static const struct reg_default rt5682_reg[] = { + {0x0002, 0x8080}, + {0x0003, 0x8000}, + {0x0005, 0x0000}, + {0x0006, 0x0000}, + {0x0008, 0x800f}, + {0x000b, 0x0000}, + {0x0010, 0x4040}, + {0x0011, 0x0000}, + {0x0012, 0x1404}, + {0x0013, 0x1000}, + {0x0014, 0xa00a}, + {0x0015, 0x0404}, + {0x0016, 0x0404}, + {0x0019, 0xafaf}, + {0x001c, 0x2f2f}, + {0x001f, 0x0000}, + {0x0022, 0x5757}, + {0x0023, 0x0039}, + {0x0024, 0x000b}, + {0x0026, 0xc0c4}, + {0x0029, 0x8080}, + {0x002a, 0xa0a0}, + {0x002b, 0x0300}, + {0x0030, 0x0000}, + {0x003c, 0x0080}, + {0x0044, 0x0c0c}, + {0x0049, 0x0000}, + {0x0061, 0x0000}, + {0x0062, 0x0000}, + {0x0063, 0x003f}, + {0x0064, 0x0000}, + {0x0065, 0x0000}, + {0x0066, 0x0030}, + {0x0067, 0x0000}, + {0x006b, 0x0000}, + {0x006c, 0x0000}, + {0x006d, 0x2200}, + {0x006e, 0x0a10}, + {0x0070, 0x8000}, + {0x0071, 0x8000}, + {0x0073, 0x0000}, + {0x0074, 0x0000}, + {0x0075, 0x0002}, + {0x0076, 0x0001}, + {0x0079, 0x0000}, + {0x007a, 0x0000}, + {0x007b, 0x0000}, + {0x007c, 0x0100}, + {0x007e, 0x0000}, + {0x0080, 0x0000}, + {0x0081, 0x0000}, + {0x0082, 0x0000}, + {0x0083, 0x0000}, + {0x0084, 0x0000}, + {0x0085, 0x0000}, + {0x0086, 0x0005}, + {0x0087, 0x0000}, + {0x0088, 0x0000}, + {0x008c, 0x0003}, + {0x008d, 0x0000}, + {0x008e, 0x0060}, + {0x008f, 0x1000}, + {0x0091, 0x0c26}, + {0x0092, 0x0073}, + {0x0093, 0x0000}, + {0x0094, 0x0080}, + {0x0098, 0x0000}, + {0x009a, 0x0000}, + {0x009b, 0x0000}, + {0x009c, 0x0000}, + {0x009d, 0x0000}, + {0x009e, 0x100c}, + {0x009f, 0x0000}, + {0x00a0, 0x0000}, + {0x00a3, 0x0002}, + {0x00a4, 0x0001}, + {0x00ae, 0x2040}, + {0x00af, 0x0000}, + {0x00b6, 0x0000}, + {0x00b7, 0x0000}, + {0x00b8, 0x0000}, + {0x00b9, 0x0002}, + {0x00be, 0x0000}, + {0x00c0, 0x0160}, + {0x00c1, 0x82a0}, + {0x00c2, 0x0000}, + {0x00d0, 0x0000}, + {0x00d1, 0x2244}, + {0x00d2, 0x3300}, + {0x00d3, 0x2200}, + {0x00d4, 0x0000}, + {0x00d9, 0x0009}, + {0x00da, 0x0000}, + {0x00db, 0x0000}, + {0x00dc, 0x00c0}, + {0x00dd, 0x2220}, + {0x00de, 0x3131}, + {0x00df, 0x3131}, + {0x00e0, 0x3131}, + {0x00e2, 0x0000}, + {0x00e3, 0x4000}, + {0x00e4, 0x0aa0}, + {0x00e5, 0x3131}, + {0x00e6, 0x3131}, + {0x00e7, 0x3131}, + {0x00e8, 0x3131}, + {0x00ea, 0xb320}, + {0x00eb, 0x0000}, + {0x00f0, 0x0000}, + {0x00f1, 0x00d0}, + {0x00f2, 0x00d0}, + {0x00f6, 0x0000}, + {0x00fa, 0x0000}, + {0x00fb, 0x0000}, + {0x00fc, 0x0000}, + {0x00fd, 0x0000}, + {0x00fe, 0x10ec}, + {0x00ff, 0x6530}, + {0x0100, 0xa0a0}, + {0x010b, 0x0000}, + {0x010c, 0xae00}, + {0x010d, 0xaaa0}, + {0x010e, 0x8aa2}, + {0x010f, 0x02a2}, + {0x0110, 0xc000}, + {0x0111, 0x04a2}, + {0x0112, 0x2800}, + {0x0113, 0x0000}, + {0x0117, 0x0100}, + {0x0125, 0x0410}, + {0x0132, 0x6026}, + {0x0136, 0x5555}, + {0x0138, 0x3700}, + {0x013a, 0x2000}, + {0x013b, 0x2000}, + {0x013c, 0x2005}, + {0x013f, 0x0000}, + {0x0142, 0x0000}, + {0x0145, 0x0002}, + {0x0146, 0x0000}, + {0x0147, 0x0000}, + {0x0148, 0x0000}, + {0x0149, 0x0000}, + {0x0150, 0x79a1}, + {0x0151, 0x0000}, + {0x0160, 0x4ec0}, + {0x0161, 0x0080}, + {0x0162, 0x0200}, + {0x0163, 0x0800}, + {0x0164, 0x0000}, + {0x0165, 0x0000}, + {0x0166, 0x0000}, + {0x0167, 0x000f}, + {0x0168, 0x000f}, + {0x0169, 0x0021}, + {0x0190, 0x413d}, + {0x0194, 0x0000}, + {0x0195, 0x0000}, + {0x0197, 0x0022}, + {0x0198, 0x0000}, + {0x0199, 0x0000}, + {0x01af, 0x0000}, + {0x01b0, 0x0400}, + {0x01b1, 0x0000}, + {0x01b2, 0x0000}, + {0x01b3, 0x0000}, + {0x01b4, 0x0000}, + {0x01b5, 0x0000}, + {0x01b6, 0x01c3}, + {0x01b7, 0x02a0}, + {0x01b8, 0x03e9}, + {0x01b9, 0x1389}, + {0x01ba, 0xc351}, + {0x01bb, 0x0009}, + {0x01bc, 0x0018}, + {0x01bd, 0x002a}, + {0x01be, 0x004c}, + {0x01bf, 0x0097}, + {0x01c0, 0x433d}, + {0x01c2, 0x0000}, + {0x01c3, 0x0000}, + {0x01c4, 0x0000}, + {0x01c5, 0x0000}, + {0x01c6, 0x0000}, + {0x01c7, 0x0000}, + {0x01c8, 0x40af}, + {0x01c9, 0x0702}, + {0x01ca, 0x0000}, + {0x01cb, 0x0000}, + {0x01cc, 0x5757}, + {0x01cd, 0x5757}, + {0x01ce, 0x5757}, + {0x01cf, 0x5757}, + {0x01d0, 0x5757}, + {0x01d1, 0x5757}, + {0x01d2, 0x5757}, + {0x01d3, 0x5757}, + {0x01d4, 0x5757}, + {0x01d5, 0x5757}, + {0x01d6, 0x0000}, + {0x01d7, 0x0008}, + {0x01d8, 0x0029}, + {0x01d9, 0x3333}, + {0x01da, 0x0000}, + {0x01db, 0x0004}, + {0x01dc, 0x0000}, + {0x01de, 0x7c00}, + {0x01df, 0x0320}, + {0x01e0, 0x06a1}, + {0x01e1, 0x0000}, + {0x01e2, 0x0000}, + {0x01e3, 0x0000}, + {0x01e4, 0x0000}, + {0x01e6, 0x0001}, + {0x01e7, 0x0000}, + {0x01e8, 0x0000}, + {0x01ea, 0x0000}, + {0x01eb, 0x0000}, + {0x01ec, 0x0000}, + {0x01ed, 0x0000}, + {0x01ee, 0x0000}, + {0x01ef, 0x0000}, + {0x01f0, 0x0000}, + {0x01f1, 0x0000}, + {0x01f2, 0x0000}, + {0x01f3, 0x0000}, + {0x01f4, 0x0000}, + {0x0210, 0x6297}, + {0x0211, 0xa005}, + {0x0212, 0x824c}, + {0x0213, 0xf7ff}, + {0x0214, 0xf24c}, + {0x0215, 0x0102}, + {0x0216, 0x00a3}, + {0x0217, 0x0048}, + {0x0218, 0xa2c0}, + {0x0219, 0x0400}, + {0x021a, 0x00c8}, + {0x021b, 0x00c0}, + {0x021c, 0x0000}, + {0x0250, 0x4500}, + {0x0251, 0x40b3}, + {0x0252, 0x0000}, + {0x0253, 0x0000}, + {0x0254, 0x0000}, + {0x0255, 0x0000}, + {0x0256, 0x0000}, + {0x0257, 0x0000}, + {0x0258, 0x0000}, + {0x0259, 0x0000}, + {0x025a, 0x0005}, + {0x0270, 0x0000}, + {0x02ff, 0x0110}, + {0x0300, 0x001f}, + {0x0301, 0x032c}, + {0x0302, 0x5f21}, + {0x0303, 0x4000}, + {0x0304, 0x4000}, + {0x0305, 0x06d5}, + {0x0306, 0x8000}, + {0x0307, 0x0700}, + {0x0310, 0x4560}, + {0x0311, 0xa4a8}, + {0x0312, 0x7418}, + {0x0313, 0x0000}, + {0x0314, 0x0006}, + {0x0315, 0xffff}, + {0x0316, 0xc400}, + {0x0317, 0x0000}, + {0x03c0, 0x7e00}, + {0x03c1, 0x8000}, + {0x03c2, 0x8000}, + {0x03c3, 0x8000}, + {0x03c4, 0x8000}, + {0x03c5, 0x8000}, + {0x03c6, 0x8000}, + {0x03c7, 0x8000}, + {0x03c8, 0x8000}, + {0x03c9, 0x8000}, + {0x03ca, 0x8000}, + {0x03cb, 0x8000}, + {0x03cc, 0x8000}, + {0x03d0, 0x0000}, + {0x03d1, 0x0000}, + {0x03d2, 0x0000}, + {0x03d3, 0x0000}, + {0x03d4, 0x2000}, + {0x03d5, 0x2000}, + {0x03d6, 0x0000}, + {0x03d7, 0x0000}, + {0x03d8, 0x2000}, + {0x03d9, 0x2000}, + {0x03da, 0x2000}, + {0x03db, 0x2000}, + {0x03dc, 0x0000}, + {0x03dd, 0x0000}, + {0x03de, 0x0000}, + {0x03df, 0x2000}, + {0x03e0, 0x0000}, + {0x03e1, 0x0000}, + {0x03e2, 0x0000}, + {0x03e3, 0x0000}, + {0x03e4, 0x0000}, + {0x03e5, 0x0000}, + {0x03e6, 0x0000}, + {0x03e7, 0x0000}, + {0x03e8, 0x0000}, + {0x03e9, 0x0000}, + {0x03ea, 0x0000}, + {0x03eb, 0x0000}, + {0x03ec, 0x0000}, + {0x03ed, 0x0000}, + {0x03ee, 0x0000}, + {0x03ef, 0x0000}, + {0x03f0, 0x0800}, + {0x03f1, 0x0800}, + {0x03f2, 0x0800}, + {0x03f3, 0x0800}, +}; + +static bool rt5682_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5682_RESET: + case RT5682_CBJ_CTRL_2: + case RT5682_INT_ST_1: + case RT5682_4BTN_IL_CMD_1: + case RT5682_AJD1_CTRL: + case RT5682_HP_CALIB_CTRL_1: + case RT5682_DEVICE_ID: + case RT5682_I2C_MODE: + case RT5682_HP_CALIB_CTRL_10: + case RT5682_EFUSE_CTRL_2: + case RT5682_JD_TOP_VC_VTRL: + case RT5682_HP_IMP_SENS_CTRL_19: + case RT5682_IL_CMD_1: + case RT5682_SAR_IL_CMD_2: + case RT5682_SAR_IL_CMD_4: + case RT5682_SAR_IL_CMD_10: + case RT5682_SAR_IL_CMD_11: + case RT5682_EFUSE_CTRL_6...RT5682_EFUSE_CTRL_11: + case RT5682_HP_CALIB_STA_1...RT5682_HP_CALIB_STA_11: + return true; + default: + return false; + } +} + +static bool rt5682_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5682_RESET: + case RT5682_VERSION_ID: + case RT5682_VENDOR_ID: + case RT5682_DEVICE_ID: + case RT5682_HP_CTRL_1: + case RT5682_HP_CTRL_2: + case RT5682_HPL_GAIN: + case RT5682_HPR_GAIN: + case RT5682_I2C_CTRL: + case RT5682_CBJ_BST_CTRL: + case RT5682_CBJ_CTRL_1: + case RT5682_CBJ_CTRL_2: + case RT5682_CBJ_CTRL_3: + case RT5682_CBJ_CTRL_4: + case RT5682_CBJ_CTRL_5: + case RT5682_CBJ_CTRL_6: + case RT5682_CBJ_CTRL_7: + case RT5682_DAC1_DIG_VOL: + case RT5682_STO1_ADC_DIG_VOL: + case RT5682_STO1_ADC_BOOST: + case RT5682_HP_IMP_GAIN_1: + case RT5682_HP_IMP_GAIN_2: + case RT5682_SIDETONE_CTRL: + case RT5682_STO1_ADC_MIXER: + case RT5682_AD_DA_MIXER: + case RT5682_STO1_DAC_MIXER: + case RT5682_A_DAC1_MUX: + case RT5682_DIG_INF2_DATA: + case RT5682_REC_MIXER: + case RT5682_CAL_REC: + case RT5682_ALC_BACK_GAIN: + case RT5682_PWR_DIG_1: + case RT5682_PWR_DIG_2: + case RT5682_PWR_ANLG_1: + case RT5682_PWR_ANLG_2: + case RT5682_PWR_ANLG_3: + case RT5682_PWR_MIXER: + case RT5682_PWR_VOL: + case RT5682_CLK_DET: + case RT5682_RESET_LPF_CTRL: + case RT5682_RESET_HPF_CTRL: + case RT5682_DMIC_CTRL_1: + case RT5682_I2S1_SDP: + case RT5682_I2S2_SDP: + case RT5682_ADDA_CLK_1: + case RT5682_ADDA_CLK_2: + case RT5682_I2S1_F_DIV_CTRL_1: + case RT5682_I2S1_F_DIV_CTRL_2: + case RT5682_TDM_CTRL: + case RT5682_TDM_ADDA_CTRL_1: + case RT5682_TDM_ADDA_CTRL_2: + case RT5682_DATA_SEL_CTRL_1: + case RT5682_TDM_TCON_CTRL: + case RT5682_GLB_CLK: + case RT5682_PLL_CTRL_1: + case RT5682_PLL_CTRL_2: + case RT5682_PLL_TRACK_1: + case RT5682_PLL_TRACK_2: + case RT5682_PLL_TRACK_3: + case RT5682_PLL_TRACK_4: + case RT5682_PLL_TRACK_5: + case RT5682_PLL_TRACK_6: + case RT5682_PLL_TRACK_11: + case RT5682_SDW_REF_CLK: + case RT5682_DEPOP_1: + case RT5682_DEPOP_2: + case RT5682_HP_CHARGE_PUMP_1: + case RT5682_HP_CHARGE_PUMP_2: + case RT5682_MICBIAS_1: + case RT5682_MICBIAS_2: + case RT5682_PLL_TRACK_12: + case RT5682_PLL_TRACK_14: + case RT5682_PLL2_CTRL_1: + case RT5682_PLL2_CTRL_2: + case RT5682_PLL2_CTRL_3: + case RT5682_PLL2_CTRL_4: + case RT5682_RC_CLK_CTRL: + case RT5682_I2S_M_CLK_CTRL_1: + case RT5682_I2S2_F_DIV_CTRL_1: + case RT5682_I2S2_F_DIV_CTRL_2: + case RT5682_EQ_CTRL_1: + case RT5682_EQ_CTRL_2: + case RT5682_IRQ_CTRL_1: + case RT5682_IRQ_CTRL_2: + case RT5682_IRQ_CTRL_3: + case RT5682_IRQ_CTRL_4: + case RT5682_INT_ST_1: + case RT5682_GPIO_CTRL_1: + case RT5682_GPIO_CTRL_2: + case RT5682_GPIO_CTRL_3: + case RT5682_HP_AMP_DET_CTRL_1: + case RT5682_HP_AMP_DET_CTRL_2: + case RT5682_MID_HP_AMP_DET: + case RT5682_LOW_HP_AMP_DET: + case RT5682_DELAY_BUF_CTRL: + case RT5682_SV_ZCD_1: + case RT5682_SV_ZCD_2: + case RT5682_IL_CMD_1: + case RT5682_IL_CMD_2: + case RT5682_IL_CMD_3: + case RT5682_IL_CMD_4: + case RT5682_IL_CMD_5: + case RT5682_IL_CMD_6: + case RT5682_4BTN_IL_CMD_1: + case RT5682_4BTN_IL_CMD_2: + case RT5682_4BTN_IL_CMD_3: + case RT5682_4BTN_IL_CMD_4: + case RT5682_4BTN_IL_CMD_5: + case RT5682_4BTN_IL_CMD_6: + case RT5682_4BTN_IL_CMD_7: + case RT5682_ADC_STO1_HP_CTRL_1: + case RT5682_ADC_STO1_HP_CTRL_2: + case RT5682_AJD1_CTRL: + case RT5682_JD1_THD: + case RT5682_JD2_THD: + case RT5682_JD_CTRL_1: + case RT5682_DUMMY_1: + case RT5682_DUMMY_2: + case RT5682_DUMMY_3: + case RT5682_DAC_ADC_DIG_VOL1: + case RT5682_BIAS_CUR_CTRL_2: + case RT5682_BIAS_CUR_CTRL_3: + case RT5682_BIAS_CUR_CTRL_4: + case RT5682_BIAS_CUR_CTRL_5: + case RT5682_BIAS_CUR_CTRL_6: + case RT5682_BIAS_CUR_CTRL_7: + case RT5682_BIAS_CUR_CTRL_8: + case RT5682_BIAS_CUR_CTRL_9: + case RT5682_BIAS_CUR_CTRL_10: + case RT5682_VREF_REC_OP_FB_CAP_CTRL: + case RT5682_CHARGE_PUMP_1: + case RT5682_DIG_IN_CTRL_1: + case RT5682_PAD_DRIVING_CTRL: + case RT5682_SOFT_RAMP_DEPOP: + case RT5682_CHOP_DAC: + case RT5682_CHOP_ADC: + case RT5682_CALIB_ADC_CTRL: + case RT5682_VOL_TEST: + case RT5682_SPKVDD_DET_STA: + case RT5682_TEST_MODE_CTRL_1: + case RT5682_TEST_MODE_CTRL_2: + case RT5682_TEST_MODE_CTRL_3: + case RT5682_TEST_MODE_CTRL_4: + case RT5682_TEST_MODE_CTRL_5: + case RT5682_PLL1_INTERNAL: + case RT5682_PLL2_INTERNAL: + case RT5682_STO_NG2_CTRL_1: + case RT5682_STO_NG2_CTRL_2: + case RT5682_STO_NG2_CTRL_3: + case RT5682_STO_NG2_CTRL_4: + case RT5682_STO_NG2_CTRL_5: + case RT5682_STO_NG2_CTRL_6: + case RT5682_STO_NG2_CTRL_7: + case RT5682_STO_NG2_CTRL_8: + case RT5682_STO_NG2_CTRL_9: + case RT5682_STO_NG2_CTRL_10: + case RT5682_STO1_DAC_SIL_DET: + case RT5682_SIL_PSV_CTRL1: + case RT5682_SIL_PSV_CTRL2: + case RT5682_SIL_PSV_CTRL3: + case RT5682_SIL_PSV_CTRL4: + case RT5682_SIL_PSV_CTRL5: + case RT5682_HP_IMP_SENS_CTRL_01: + case RT5682_HP_IMP_SENS_CTRL_02: + case RT5682_HP_IMP_SENS_CTRL_03: + case RT5682_HP_IMP_SENS_CTRL_04: + case RT5682_HP_IMP_SENS_CTRL_05: + case RT5682_HP_IMP_SENS_CTRL_06: + case RT5682_HP_IMP_SENS_CTRL_07: + case RT5682_HP_IMP_SENS_CTRL_08: + case RT5682_HP_IMP_SENS_CTRL_09: + case RT5682_HP_IMP_SENS_CTRL_10: + case RT5682_HP_IMP_SENS_CTRL_11: + case RT5682_HP_IMP_SENS_CTRL_12: + case RT5682_HP_IMP_SENS_CTRL_13: + case RT5682_HP_IMP_SENS_CTRL_14: + case RT5682_HP_IMP_SENS_CTRL_15: + case RT5682_HP_IMP_SENS_CTRL_16: + case RT5682_HP_IMP_SENS_CTRL_17: + case RT5682_HP_IMP_SENS_CTRL_18: + case RT5682_HP_IMP_SENS_CTRL_19: + case RT5682_HP_IMP_SENS_CTRL_20: + case RT5682_HP_IMP_SENS_CTRL_21: + case RT5682_HP_IMP_SENS_CTRL_22: + case RT5682_HP_IMP_SENS_CTRL_23: + case RT5682_HP_IMP_SENS_CTRL_24: + case RT5682_HP_IMP_SENS_CTRL_25: + case RT5682_HP_IMP_SENS_CTRL_26: + case RT5682_HP_IMP_SENS_CTRL_27: + case RT5682_HP_IMP_SENS_CTRL_28: + case RT5682_HP_IMP_SENS_CTRL_29: + case RT5682_HP_IMP_SENS_CTRL_30: + case RT5682_HP_IMP_SENS_CTRL_31: + case RT5682_HP_IMP_SENS_CTRL_32: + case RT5682_HP_IMP_SENS_CTRL_33: + case RT5682_HP_IMP_SENS_CTRL_34: + case RT5682_HP_IMP_SENS_CTRL_35: + case RT5682_HP_IMP_SENS_CTRL_36: + case RT5682_HP_IMP_SENS_CTRL_37: + case RT5682_HP_IMP_SENS_CTRL_38: + case RT5682_HP_IMP_SENS_CTRL_39: + case RT5682_HP_IMP_SENS_CTRL_40: + case RT5682_HP_IMP_SENS_CTRL_41: + case RT5682_HP_IMP_SENS_CTRL_42: + case RT5682_HP_IMP_SENS_CTRL_43: + case RT5682_HP_LOGIC_CTRL_1: + case RT5682_HP_LOGIC_CTRL_2: + case RT5682_HP_LOGIC_CTRL_3: + case RT5682_HP_CALIB_CTRL_1: + case RT5682_HP_CALIB_CTRL_2: + case RT5682_HP_CALIB_CTRL_3: + case RT5682_HP_CALIB_CTRL_4: + case RT5682_HP_CALIB_CTRL_5: + case RT5682_HP_CALIB_CTRL_6: + case RT5682_HP_CALIB_CTRL_7: + case RT5682_HP_CALIB_CTRL_9: + case RT5682_HP_CALIB_CTRL_10: + case RT5682_HP_CALIB_CTRL_11: + case RT5682_HP_CALIB_STA_1: + case RT5682_HP_CALIB_STA_2: + case RT5682_HP_CALIB_STA_3: + case RT5682_HP_CALIB_STA_4: + case RT5682_HP_CALIB_STA_5: + case RT5682_HP_CALIB_STA_6: + case RT5682_HP_CALIB_STA_7: + case RT5682_HP_CALIB_STA_8: + case RT5682_HP_CALIB_STA_9: + case RT5682_HP_CALIB_STA_10: + case RT5682_HP_CALIB_STA_11: + case RT5682_SAR_IL_CMD_1: + case RT5682_SAR_IL_CMD_2: + case RT5682_SAR_IL_CMD_3: + case RT5682_SAR_IL_CMD_4: + case RT5682_SAR_IL_CMD_5: + case RT5682_SAR_IL_CMD_6: + case RT5682_SAR_IL_CMD_7: + case RT5682_SAR_IL_CMD_8: + case RT5682_SAR_IL_CMD_9: + case RT5682_SAR_IL_CMD_10: + case RT5682_SAR_IL_CMD_11: + case RT5682_SAR_IL_CMD_12: + case RT5682_SAR_IL_CMD_13: + case RT5682_EFUSE_CTRL_1: + case RT5682_EFUSE_CTRL_2: + case RT5682_EFUSE_CTRL_3: + case RT5682_EFUSE_CTRL_4: + case RT5682_EFUSE_CTRL_5: + case RT5682_EFUSE_CTRL_6: + case RT5682_EFUSE_CTRL_7: + case RT5682_EFUSE_CTRL_8: + case RT5682_EFUSE_CTRL_9: + case RT5682_EFUSE_CTRL_10: + case RT5682_EFUSE_CTRL_11: + case RT5682_JD_TOP_VC_VTRL: + case RT5682_DRC1_CTRL_0: + case RT5682_DRC1_CTRL_1: + case RT5682_DRC1_CTRL_2: + case RT5682_DRC1_CTRL_3: + case RT5682_DRC1_CTRL_4: + case RT5682_DRC1_CTRL_5: + case RT5682_DRC1_CTRL_6: + case RT5682_DRC1_HARD_LMT_CTRL_1: + case RT5682_DRC1_HARD_LMT_CTRL_2: + case RT5682_DRC1_PRIV_1: + case RT5682_DRC1_PRIV_2: + case RT5682_DRC1_PRIV_3: + case RT5682_DRC1_PRIV_4: + case RT5682_DRC1_PRIV_5: + case RT5682_DRC1_PRIV_6: + case RT5682_DRC1_PRIV_7: + case RT5682_DRC1_PRIV_8: + case RT5682_EQ_AUTO_RCV_CTRL1: + case RT5682_EQ_AUTO_RCV_CTRL2: + case RT5682_EQ_AUTO_RCV_CTRL3: + case RT5682_EQ_AUTO_RCV_CTRL4: + case RT5682_EQ_AUTO_RCV_CTRL5: + case RT5682_EQ_AUTO_RCV_CTRL6: + case RT5682_EQ_AUTO_RCV_CTRL7: + case RT5682_EQ_AUTO_RCV_CTRL8: + case RT5682_EQ_AUTO_RCV_CTRL9: + case RT5682_EQ_AUTO_RCV_CTRL10: + case RT5682_EQ_AUTO_RCV_CTRL11: + case RT5682_EQ_AUTO_RCV_CTRL12: + case RT5682_EQ_AUTO_RCV_CTRL13: + case RT5682_ADC_L_EQ_LPF1_A1: + case RT5682_R_EQ_LPF1_A1: + case RT5682_L_EQ_LPF1_H0: + case RT5682_R_EQ_LPF1_H0: + case RT5682_L_EQ_BPF1_A1: + case RT5682_R_EQ_BPF1_A1: + case RT5682_L_EQ_BPF1_A2: + case RT5682_R_EQ_BPF1_A2: + case RT5682_L_EQ_BPF1_H0: + case RT5682_R_EQ_BPF1_H0: + case RT5682_L_EQ_BPF2_A1: + case RT5682_R_EQ_BPF2_A1: + case RT5682_L_EQ_BPF2_A2: + case RT5682_R_EQ_BPF2_A2: + case RT5682_L_EQ_BPF2_H0: + case RT5682_R_EQ_BPF2_H0: + case RT5682_L_EQ_BPF3_A1: + case RT5682_R_EQ_BPF3_A1: + case RT5682_L_EQ_BPF3_A2: + case RT5682_R_EQ_BPF3_A2: + case RT5682_L_EQ_BPF3_H0: + case RT5682_R_EQ_BPF3_H0: + case RT5682_L_EQ_BPF4_A1: + case RT5682_R_EQ_BPF4_A1: + case RT5682_L_EQ_BPF4_A2: + case RT5682_R_EQ_BPF4_A2: + case RT5682_L_EQ_BPF4_H0: + case RT5682_R_EQ_BPF4_H0: + case RT5682_L_EQ_HPF1_A1: + case RT5682_R_EQ_HPF1_A1: + case RT5682_L_EQ_HPF1_H0: + case RT5682_R_EQ_HPF1_H0: + case RT5682_L_EQ_PRE_VOL: + case RT5682_R_EQ_PRE_VOL: + case RT5682_L_EQ_POST_VOL: + case RT5682_R_EQ_POST_VOL: + case RT5682_I2C_MODE: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* Interface data select */ +static const char * const rt5682_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5682_if2_adc_enum, + RT5682_DIG_INF2_DATA, RT5682_IF2_ADC_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_01_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC1_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_23_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC2_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_45_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC3_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_67_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC4_SEL_SFT, rt5682_data_select); + +static const struct snd_kcontrol_new rt5682_if2_adc_swap_mux = + SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5682_if2_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_01_adc_swap_mux = + SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5682_if1_01_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_23_adc_swap_mux = + SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5682_if1_23_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_45_adc_swap_mux = + SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5682_if1_45_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_67_adc_swap_mux = + SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5682_if1_67_adc_enum); + +static void rt5682_reset(struct regmap *regmap) +{ + regmap_write(regmap, RT5682_RESET, 0); + regmap_write(regmap, RT5682_I2C_MODE, 1); +} +/** + * rt5682_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @component: SoC audio component device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5682 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the component driver will turn on + * ASRC for these filters if ASRC is selected as their clock source. + */ +int rt5682_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src) +{ + + switch (clk_src) { + case RT5682_CLK_SEL_SYS: + case RT5682_CLK_SEL_I2S1_ASRC: + case RT5682_CLK_SEL_I2S2_ASRC: + break; + + default: + return -EINVAL; + } + + if (filter_mask & RT5682_DA_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5682_PLL_TRACK_2, + RT5682_FILTER_CLK_SEL_MASK, + clk_src << RT5682_FILTER_CLK_SEL_SFT); + } + + if (filter_mask & RT5682_AD_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5682_PLL_TRACK_3, + RT5682_FILTER_CLK_SEL_MASK, + clk_src << RT5682_FILTER_CLK_SEL_SFT); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5682_sel_asrc_clk_src); + +static int rt5682_button_detect(struct snd_soc_component *component) +{ + int btn_type, val; + + val = snd_soc_component_read32(component, RT5682_4BTN_IL_CMD_1); + btn_type = val & 0xfff0; + snd_soc_component_write(component, RT5682_4BTN_IL_CMD_1, val); + pr_debug("%s btn_type=%x\n", __func__, btn_type); + snd_soc_component_update_bits(component, + RT5682_SAR_IL_CMD_2, 0x10, 0x10); + + return btn_type; +} + +static void rt5682_enable_push_button_irq(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_EN); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_13, + RT5682_SAR_SOUR_MASK, RT5682_SAR_SOUR_BTN); + snd_soc_component_write(component, RT5682_IL_CMD_1, 0x0040); + snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, + RT5682_4BTN_IL_MASK | RT5682_4BTN_IL_RST_MASK, + RT5682_4BTN_IL_EN | RT5682_4BTN_IL_NOR); + snd_soc_component_update_bits(component, RT5682_IRQ_CTRL_3, + RT5682_IL_IRQ_MASK, RT5682_IL_IRQ_EN); + } else { + snd_soc_component_update_bits(component, RT5682_IRQ_CTRL_3, + RT5682_IL_IRQ_MASK, RT5682_IL_IRQ_DIS); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_DIS); + snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, + RT5682_4BTN_IL_MASK, RT5682_4BTN_IL_DIS); + snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, + RT5682_4BTN_IL_RST_MASK, RT5682_4BTN_IL_RST); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_13, + RT5682_SAR_SOUR_MASK, RT5682_SAR_SOUR_TYPE); + } +} + +/** + * rt5682_headset_detect - Detect headset. + * @component: SoC audio component device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ +static int rt5682_headset_detect(struct snd_soc_component *component, + int jack_insert) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + unsigned int val, count; + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_HIGH); + + count = 0; + val = snd_soc_component_read32(component, RT5682_CBJ_CTRL_2) + & RT5682_JACK_TYPE_MASK; + while (val == 0 && count < 50) { + usleep_range(10000, 15000); + val = snd_soc_component_read32(component, + RT5682_CBJ_CTRL_2) & RT5682_JACK_TYPE_MASK; + count++; + } + + switch (val) { + case 0x1: + case 0x2: + rt5682->jack_type = SND_JACK_HEADSET; + rt5682_enable_push_button_irq(component, true); + break; + default: + rt5682->jack_type = SND_JACK_HEADPHONE; + } + + } else { + rt5682_enable_push_button_irq(component, false); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW); + snd_soc_dapm_disable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + + rt5682->jack_type = 0; + } + + dev_dbg(component->dev, "jack_type = %d\n", rt5682->jack_type); + return rt5682->jack_type; +} + +static irqreturn_t rt5682_irq(int irq, void *data) +{ + struct rt5682_priv *rt5682 = data; + + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +static void rt5682_jd_check_handler(struct work_struct *work) +{ + struct rt5682_priv *rt5682 = container_of(work, struct rt5682_priv, + jd_check_work.work); + + if (snd_soc_component_read32(rt5682->component, RT5682_AJD1_CTRL) + & RT5682_JDH_RS_MASK) { + /* jack out */ + rt5682->jack_type = rt5682_headset_detect(rt5682->component, 0); + + snd_soc_jack_report(rt5682->hs_jack, rt5682->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + } else { + schedule_delayed_work(&rt5682->jd_check_work, 500); + } +} + +static int rt5682_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + switch (rt5682->pdata.jd_src) { + case RT5682_JD1: + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_2, + RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL); + snd_soc_component_write(component, RT5682_CBJ_CTRL_1, 0xd042); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_3, + RT5682_CBJ_IN_BUF_EN, RT5682_CBJ_IN_BUF_EN); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_POW_MASK, RT5682_SAR_POW_EN); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_IRQ); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_IRQ | RT5682_POW_JDH | + RT5682_POW_ANA, RT5682_POW_IRQ | + RT5682_POW_JDH | RT5682_POW_ANA); + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2, + RT5682_PWR_JDH | RT5682_PWR_JDL, + RT5682_PWR_JDH | RT5682_PWR_JDL); + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK | RT5682_JD1_POL_MASK, + RT5682_JD1_EN | RT5682_JD1_POL_NOR); + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(250)); + break; + + case RT5682_JD_NULL: + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK, RT5682_JD1_DIS); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_JDH | RT5682_POW_JDL, 0); + break; + + default: + dev_warn(component->dev, "Wrong JD source\n"); + break; + } + + rt5682->hs_jack = hs_jack; + + return 0; +} + +static void rt5682_jack_detect_handler(struct work_struct *work) +{ + struct rt5682_priv *rt5682 = + container_of(work, struct rt5682_priv, jack_detect_work.work); + int val, btn_type; + + while (!rt5682->component) + usleep_range(10000, 15000); + + while (!rt5682->component->card->instantiated) + usleep_range(10000, 15000); + + mutex_lock(&rt5682->calibrate_mutex); + + val = snd_soc_component_read32(rt5682->component, RT5682_AJD1_CTRL) + & RT5682_JDH_RS_MASK; + if (!val) { + /* jack in */ + if (rt5682->jack_type == 0) { + /* jack was out, report jack type */ + rt5682->jack_type = + rt5682_headset_detect(rt5682->component, 1); + } else { + /* jack is already in, report button event */ + rt5682->jack_type = SND_JACK_HEADSET; + btn_type = rt5682_button_detect(rt5682->component); + /** + * rt5682 can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + rt5682->jack_type |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + rt5682->jack_type |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + rt5682->jack_type |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + rt5682->jack_type |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + btn_type = 0; + dev_err(rt5682->component->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + } else { + /* jack out */ + rt5682->jack_type = rt5682_headset_detect(rt5682->component, 0); + } + + snd_soc_jack_report(rt5682->hs_jack, rt5682->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (rt5682->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + schedule_delayed_work(&rt5682->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5682->jd_check_work); + + mutex_unlock(&rt5682->calibrate_mutex); +} + +static const struct snd_kcontrol_new rt5682_snd_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5682_HPL_GAIN, + RT5682_HPR_GAIN, RT5682_G_HP_SFT, 15, 1, hp_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682_DAC1_DIG_VOL, + RT5682_L_VOL_SFT, RT5682_R_VOL_SFT, 175, 0, dac_vol_tlv), + + /* IN Boost Volume */ + SOC_SINGLE_TLV("CBJ Boost Volume", RT5682_CBJ_BST_CTRL, + RT5682_BST_CBJ_SFT, 8, 0, bst_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("STO1 ADC Capture Switch", RT5682_STO1_ADC_DIG_VOL, + RT5682_L_MUTE_SFT, RT5682_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5682_STO1_ADC_DIG_VOL, + RT5682_L_VOL_SFT, RT5682_R_VOL_SFT, 127, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5682_STO1_ADC_BOOST, + RT5682_STO1_ADC_L_BST_SFT, RT5682_STO1_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), +}; + + +static int rt5682_div_sel(struct rt5682_priv *rt5682, + int target, const int div[], int size) +{ + int i; + + if (rt5682->sysclk < target) { + pr_err("sysclk rate %d is too low\n", + rt5682->sysclk); + return 0; + } + + for (i = 0; i < size - 1; i++) { + pr_info("div[%d]=%d\n", i, div[i]); + if (target * div[i] == rt5682->sysclk) + return i; + if (target * div[i + 1] > rt5682->sysclk) { + pr_err("can't find div for sysclk %d\n", + rt5682->sysclk); + return i; + } + } + + if (target * div[i] < rt5682->sysclk) + pr_err("sysclk rate %d is too high\n", + rt5682->sysclk); + + return size - 1; + +} + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + int idx = -EINVAL; + static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128}; + + idx = rt5682_div_sel(rt5682, 1500000, div, ARRAY_SIZE(div)); + + snd_soc_component_update_bits(component, RT5682_DMIC_CTRL_1, + RT5682_DMIC_CLK_MASK, idx << RT5682_DMIC_CLK_SFT); + + return 0; +} + +static int set_filter_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + int ref, val, reg, sft, mask, idx = -EINVAL; + static const int div_f[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48}; + static const int div_o[] = {1, 2, 4, 6, 8, 12, 16, 24, 32, 48}; + + val = snd_soc_component_read32(component, RT5682_GPIO_CTRL_1) & + RT5682_GP4_PIN_MASK; + if (w->shift == RT5682_PWR_ADC_S1F_BIT && + val == RT5682_GP4_PIN_ADCDAT2) + ref = 256 * rt5682->lrck[RT5682_AIF2]; + else + ref = 256 * rt5682->lrck[RT5682_AIF1]; + + idx = rt5682_div_sel(rt5682, ref, div_f, ARRAY_SIZE(div_f)); + + if (w->shift == RT5682_PWR_ADC_S1F_BIT) { + reg = RT5682_PLL_TRACK_3; + sft = RT5682_ADC_OSR_SFT; + mask = RT5682_ADC_OSR_MASK; + } else { + reg = RT5682_PLL_TRACK_2; + sft = RT5682_DAC_OSR_SFT; + mask = RT5682_DAC_OSR_MASK; + } + + snd_soc_component_update_bits(component, reg, + RT5682_FILTER_CLK_DIV_MASK, idx << RT5682_FILTER_CLK_DIV_SFT); + + /* select over sample rate */ + for (idx = 0; idx < ARRAY_SIZE(div_o); idx++) { + if (rt5682->sysclk <= 12288000 * div_o[idx]) + break; + } + + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_1, + mask, idx << sft); + + return 0; +} + +static int is_sys_clk_from_pll1(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + val = snd_soc_component_read32(component, RT5682_GLB_CLK); + val &= RT5682_SCLK_SRC_MASK; + if (val == RT5682_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (w->shift) { + case RT5682_ADC_STO1_ASRC_SFT: + reg = RT5682_PLL_TRACK_3; + shift = RT5682_FILTER_CLK_SEL_SFT; + break; + case RT5682_DAC_STO1_ASRC_SFT: + reg = RT5682_PLL_TRACK_2; + shift = RT5682_FILTER_CLK_SEL_SFT; + break; + default: + return 0; + } + + val = (snd_soc_component_read32(component, reg) >> shift) & 0xf; + switch (val) { + case RT5682_CLK_SEL_I2S1_ASRC: + case RT5682_CLK_SEL_I2S2_ASRC: + return 1; + default: + return 0; + } + +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5682_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682_AD_DA_MIXER, + RT5682_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5682_AD_DA_MIXER, + RT5682_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682_AD_DA_MIXER, + RT5682_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5682_AD_DA_MIXER, + RT5682_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_sto1_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_L1_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_R1_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_sto1_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_L1_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_R1_STO_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5682_rec1_l_mix[] = { + SOC_DAPM_SINGLE("CBJ Switch", RT5682_REC_MIXER, + RT5682_M_CBJ_RM1_L_SFT, 1, 1), +}; + +/* STO1 ADC1 Source */ +/* MX-26 [13] [5] */ +static const char * const rt5682_sto1_adc1_src[] = { + "DAC MIX", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc1l_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC1L_SRC_SFT, rt5682_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc1l_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682_sto1_adc1l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc1r_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC1R_SRC_SFT, rt5682_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc1r_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682_sto1_adc1r_enum); + +/* STO1 ADC Source */ +/* MX-26 [11:10] [3:2] */ +static const char * const rt5682_sto1_adc_src[] = { + "ADC1 L", "ADC1 R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adcl_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADCL_SRC_SFT, rt5682_sto1_adc_src); + +static const struct snd_kcontrol_new rt5682_sto1_adcl_mux = + SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5682_sto1_adcl_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adcr_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADCR_SRC_SFT, rt5682_sto1_adc_src); + +static const struct snd_kcontrol_new rt5682_sto1_adcr_mux = + SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5682_sto1_adcr_enum); + +/* STO1 ADC2 Source */ +/* MX-26 [12] [4] */ +static const char * const rt5682_sto1_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc2l_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC2L_SRC_SFT, rt5682_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc2l_mux = + SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5682_sto1_adc2l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc2r_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC2R_SRC_SFT, rt5682_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc2r_mux = + SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5682_sto1_adc2r_enum); + +/* MX-79 [6:4] I2S1 ADC data location */ +static const unsigned int rt5682_if1_adc_slot_values[] = { + 0, + 2, + 4, + 6, +}; + +static const char * const rt5682_if1_adc_slot_src[] = { + "Slot 0", "Slot 2", "Slot 4", "Slot 6" +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_if1_adc_slot_enum, + RT5682_TDM_CTRL, RT5682_TDM_ADC_LCA_SFT, RT5682_TDM_ADC_LCA_MASK, + rt5682_if1_adc_slot_src, rt5682_if1_adc_slot_values); + +static const struct snd_kcontrol_new rt5682_if1_adc_slot_mux = + SOC_DAPM_ENUM("IF1 ADC Slot location", rt5682_if1_adc_slot_enum); + +/* Analog DAC L1 Source, Analog DAC R1 Source*/ +/* MX-2B [4], MX-2B [0]*/ +static const char * const rt5682_alg_dac1_src[] = { + "Stereo1 DAC Mixer", "DAC1" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_alg_dac_l1_enum, RT5682_A_DAC1_MUX, + RT5682_A_DACL1_SFT, rt5682_alg_dac1_src); + +static const struct snd_kcontrol_new rt5682_alg_dac_l1_mux = + SOC_DAPM_ENUM("Analog DAC L1 Source", rt5682_alg_dac_l1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_alg_dac_r1_enum, RT5682_A_DAC1_MUX, + RT5682_A_DACR1_SFT, rt5682_alg_dac1_src); + +static const struct snd_kcontrol_new rt5682_alg_dac_r1_mux = + SOC_DAPM_ENUM("Analog DAC R1 Source", rt5682_alg_dac_r1_enum); + +/* Out Switch */ +static const struct snd_kcontrol_new hpol_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1, + RT5682_L_MUTE_SFT, 1, 1); +static const struct snd_kcontrol_new hpor_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1, + RT5682_R_MUTE_SFT, 1, 1); + +static int rt5682_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write(component, + RT5682_HP_LOGIC_CTRL_2, 0x0012); + snd_soc_component_write(component, + RT5682_HP_CTRL_2, 0x6000); + snd_soc_component_update_bits(component, RT5682_STO_NG2_CTRL_1, + RT5682_NG2_EN_MASK, RT5682_NG2_EN); + snd_soc_component_update_bits(component, + RT5682_DEPOP_1, 0x60, 0x60); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + RT5682_DEPOP_1, 0x60, 0x0); + snd_soc_component_write(component, + RT5682_HP_CTRL_2, 0x0000); + break; + + default: + return 0; + } + + return 0; + +} + +static int set_dmic_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /*Add delay to avoid pop noise*/ + msleep(150); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5655_set_verf(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (w->shift) { + case RT5682_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV1, 0); + break; + + case RT5682_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV2, 0); + break; + + default: + break; + } + break; + + case SND_SOC_DAPM_POST_PMU: + usleep_range(15000, 20000); + switch (w->shift) { + case RT5682_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV1, + RT5682_PWR_FV1); + break; + + case RT5682_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV2, + RT5682_PWR_FV2); + break; + + default: + break; + } + break; + + default: + return 0; + } + + return 0; +} + +static const unsigned int rt5682_adcdat_pin_values[] = { + 1, + 3, +}; + +static const char * const rt5682_adcdat_pin_select[] = { + "ADCDAT1", + "ADCDAT2", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_adcdat_pin_enum, + RT5682_GPIO_CTRL_1, RT5682_GP4_PIN_SFT, RT5682_GP4_PIN_MASK, + rt5682_adcdat_pin_select, rt5682_adcdat_pin_values); + +static const struct snd_kcontrol_new rt5682_adcdat_pin_ctrl = + SOC_DAPM_ENUM("ADCDAT", rt5682_adcdat_pin_enum); + +static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5682_PWR_ANLG_3, RT5682_PWR_LDO2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5682_PWR_ANLG_3, RT5682_PWR_PLL_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2B_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2F_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Vref1", RT5682_PWR_ANLG_1, RT5682_PWR_VREF1_BIT, 0, + rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("Vref2", RT5682_PWR_ANLG_1, RT5682_PWR_VREF2_BIT, 0, + rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_DAC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_ADC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_AD_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_DA_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_DMIC_ASRC_SFT, 0, NULL, 0), + + /* Input Side */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5682_PWR_ANLG_2, RT5682_PWR_MB1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5682_PWR_ANLG_2, RT5682_PWR_MB2_BIT, + 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + + SND_SOC_DAPM_INPUT("IN1P"), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5682_DMIC_CTRL_1, + RT5682_DMIC_1_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, + 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("CBJ Power", RT5682_PWR_ANLG_3, + RT5682_PWR_CBJ_BIT, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5682_rec1_l_mix, + ARRAY_SIZE(rt5682_rec1_l_mix)), + SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5682_PWR_ANLG_2, + RT5682_PWR_RM1_L_BIT, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5682_PWR_DIG_1, + RT5682_PWR_ADC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5682_PWR_DIG_1, + RT5682_PWR_ADC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5682_CHOP_ADC, + RT5682_CKGEN_ADC1_SFT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc1l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc1r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc2l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc2r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adcl_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adcr_mux), + SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_adc_slot_mux), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5682_PWR_DIG_2, + RT5682_PWR_ADC_S1F_BIT, 0, set_filter_clk, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5682_STO1_ADC_DIG_VOL, + RT5682_L_MUTE_SFT, 1, rt5682_sto1_adc_l_mix, + ARRAY_SIZE(rt5682_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682_STO1_ADC_DIG_VOL, + RT5682_R_MUTE_SFT, 1, rt5682_sto1_adc_r_mix, + ARRAY_SIZE(rt5682_sto1_adc_r_mix)), + SND_SOC_DAPM_SUPPLY("BTN Detection Mode", RT5682_SAR_IL_CMD_1, + 14, 1, NULL, 0), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5682_PWR_DIG_1, RT5682_PWR_I2S1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5682_PWR_DIG_1, RT5682_PWR_I2S2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if2_adc_swap_mux), + + SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0, + &rt5682_adcdat_pin_ctrl), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, + RT5682_I2S1_SDP, RT5682_SEL_ADCDAT_SFT, 1), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, + RT5682_I2S2_SDP, RT5682_I2S2_PIN_CFG_SFT, 1), + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5682_dac_l_mix, ARRAY_SIZE(rt5682_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5682_dac_r_mix, ARRAY_SIZE(rt5682_dac_r_mix)), + + /* DAC channel Mux */ + SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0, + &rt5682_alg_dac_l1_mux), + SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0, + &rt5682_alg_dac_r1_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5682_PWR_DIG_2, + RT5682_PWR_DAC_S1F_BIT, 0, set_filter_clk, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5682_sto1_dac_l_mix, ARRAY_SIZE(rt5682_sto1_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5682_sto1_dac_r_mix, ARRAY_SIZE(rt5682_sto1_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, RT5682_PWR_DIG_1, + RT5682_PWR_DAC_L1_BIT, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, RT5682_PWR_DIG_1, + RT5682_PWR_DAC_R1_BIT, 0), + SND_SOC_DAPM_SUPPLY_S("DAC 1 Clock", 3, RT5682_CHOP_DAC, + RT5682_CKGEN_DAC1_SFT, 0, NULL, 0), + + /* HPO */ + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5682_hp_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("HP Amp L", RT5682_PWR_ANLG_1, + RT5682_PWR_HA_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP Amp R", RT5682_PWR_ANLG_1, + RT5682_PWR_HA_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5682_DEPOP_1, + RT5682_PUMP_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5682_DEPOP_1, + RT5682_CAPLESS_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("HPOL Playback", SND_SOC_NOPM, 0, 0, + &hpol_switch), + SND_SOC_DAPM_SWITCH("HPOR Playback", SND_SOC_NOPM, 0, 0, + &hpor_switch), + + /* CLK DET */ + SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5682_CLK_DET, + RT5682_SYS_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5682_CLK_DET, + RT5682_PLL1_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET PLL2", RT5682_CLK_DET, + RT5682_PLL2_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET", RT5682_CLK_DET, + RT5682_POW_CLK_DET_SFT, 0, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + +}; + +static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { + /*PLL*/ + {"ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + {"DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + + /*ASRC*/ + {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc}, + {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc}, + {"ADC STO1 ASRC", NULL, "AD ASRC"}, + {"ADC STO1 ASRC", NULL, "CLKDET"}, + {"DAC STO1 ASRC", NULL, "DA ASRC"}, + {"DAC STO1 ASRC", NULL, "CLKDET"}, + + /*Vref*/ + {"MICBIAS1", NULL, "Vref1"}, + {"MICBIAS1", NULL, "Vref2"}, + {"MICBIAS2", NULL, "Vref1"}, + {"MICBIAS2", NULL, "Vref2"}, + + {"CLKDET SYS", NULL, "CLKDET"}, + + {"IN1P", NULL, "LDO2"}, + + {"BST1 CBJ", NULL, "IN1P"}, + {"BST1 CBJ", NULL, "CBJ Power"}, + {"CBJ Power", NULL, "Vref2"}, + + {"RECMIX1L", "CBJ Switch", "BST1 CBJ"}, + {"RECMIX1L", NULL, "RECMIX1L Power"}, + + {"ADC1 L", NULL, "RECMIX1L"}, + {"ADC1 L", NULL, "ADC1 L Power"}, + {"ADC1 L", NULL, "ADC1 clock"}, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC1 Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC1 Power"}, + {"DMIC CLK", NULL, "DMIC ASRC"}, + + {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"}, + {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"}, + + {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"}, + {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"}, + {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + + {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"}, + {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"}, + {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + + {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"}, + {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"}, + {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"}, + {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"}, + {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"}, + + {"ADC Stereo1 Filter", NULL, "BTN Detection Mode"}, + + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"}, + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"}, + + {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + + {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"}, + {"IF1_ADC Mux", NULL, "I2S1"}, + {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"}, + {"AIF1TX", NULL, "ADCDAT Mux"}, + {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"}, + {"AIF2TX", NULL, "ADCDAT Mux"}, + + {"IF1 DAC1 L", NULL, "AIF1RX"}, + {"IF1 DAC1 L", NULL, "I2S1"}, + {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"}, + {"IF1 DAC1 R", NULL, "AIF1RX"}, + {"IF1 DAC1 R", NULL, "I2S1"}, + {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"}, + + {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, + {"DAC1 MIXL", "DAC1 Switch", "IF1 DAC1 L"}, + {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, + {"DAC1 MIXR", "DAC1 Switch", "IF1 DAC1 R"}, + + {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, + {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"}, + + {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"}, + {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"}, + + {"DAC L1 Source", "DAC1", "DAC1 MIXL"}, + {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"}, + {"DAC R1 Source", "DAC1", "DAC1 MIXR"}, + {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"}, + + {"DAC L1", NULL, "DAC L1 Source"}, + {"DAC R1", NULL, "DAC R1 Source"}, + + {"DAC L1", NULL, "DAC 1 Clock"}, + {"DAC R1", NULL, "DAC 1 Clock"}, + + {"HP Amp", NULL, "DAC L1"}, + {"HP Amp", NULL, "DAC R1"}, + {"HP Amp", NULL, "HP Amp L"}, + {"HP Amp", NULL, "HP Amp R"}, + {"HP Amp", NULL, "Capless"}, + {"HP Amp", NULL, "Charge Pump"}, + {"HP Amp", NULL, "CLKDET SYS"}, + {"HP Amp", NULL, "CBJ Power"}, + {"HP Amp", NULL, "Vref2"}, + {"HPOL Playback", "Switch", "HP Amp"}, + {"HPOR Playback", "Switch", "HP Amp"}, + {"HPOL", NULL, "HPOL Playback"}, + {"HPOR", NULL, "HPOR Playback"}, +}; + +static int rt5682_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int cl, val = 0; + + if (tx_mask || rx_mask) + snd_soc_component_update_bits(component, RT5682_TDM_ADDA_CTRL_2, + RT5682_TDM_EN, RT5682_TDM_EN); + else + snd_soc_component_update_bits(component, RT5682_TDM_ADDA_CTRL_2, + RT5682_TDM_EN, 0); + + switch (slots) { + case 4: + val |= RT5682_TDM_TX_CH_4; + val |= RT5682_TDM_RX_CH_4; + break; + case 6: + val |= RT5682_TDM_TX_CH_6; + val |= RT5682_TDM_RX_CH_6; + break; + case 8: + val |= RT5682_TDM_TX_CH_8; + val |= RT5682_TDM_RX_CH_8; + break; + case 2: + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5682_TDM_CTRL, + RT5682_TDM_TX_CH_MASK | RT5682_TDM_RX_CH_MASK, val); + + switch (slot_width) { + case 8: + if (tx_mask || rx_mask) + return -EINVAL; + cl = RT5682_I2S1_TX_CHL_8 | RT5682_I2S1_RX_CHL_8; + break; + case 16: + val = RT5682_TDM_CL_16; + cl = RT5682_I2S1_TX_CHL_16 | RT5682_I2S1_RX_CHL_16; + break; + case 20: + val = RT5682_TDM_CL_20; + cl = RT5682_I2S1_TX_CHL_20 | RT5682_I2S1_RX_CHL_20; + break; + case 24: + val = RT5682_TDM_CL_24; + cl = RT5682_I2S1_TX_CHL_24 | RT5682_I2S1_RX_CHL_24; + break; + case 32: + val = RT5682_TDM_CL_32; + cl = RT5682_I2S1_TX_CHL_32 | RT5682_I2S1_RX_CHL_32; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_CL_MASK, val); + snd_soc_component_update_bits(component, RT5682_I2S1_SDP, + RT5682_I2S1_TX_CHL_MASK | RT5682_I2S1_RX_CHL_MASK, cl); + + return 0; +} + + +static int rt5682_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int len_1 = 0, len_2 = 0; + int pre_div, frame_size; + + rt5682->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5682->sysclk, rt5682->lrck[dai->id]); + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", + frame_size); + return -EINVAL; + } + + dev_dbg(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt5682->lrck[dai->id], pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + len_1 |= RT5682_I2S1_DL_20; + len_2 |= RT5682_I2S2_DL_20; + break; + case 24: + len_1 |= RT5682_I2S1_DL_24; + len_2 |= RT5682_I2S2_DL_24; + break; + case 32: + len_1 |= RT5682_I2S1_DL_32; + len_2 |= RT5682_I2S2_DL_24; + break; + case 8: + len_1 |= RT5682_I2S2_DL_8; + len_2 |= RT5682_I2S2_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5682_AIF1: + snd_soc_component_update_bits(component, RT5682_I2S1_SDP, + RT5682_I2S1_DL_MASK, len_1); + if (rt5682->master[RT5682_AIF1]) { + snd_soc_component_update_bits(component, + RT5682_ADDA_CLK_1, RT5682_I2S_M_DIV_MASK, + pre_div << RT5682_I2S_M_DIV_SFT); + } + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, + RT5682_I2S1_SDP, RT5682_I2S1_MONO_MASK, + RT5682_I2S1_MONO_EN); + else + snd_soc_component_update_bits(component, + RT5682_I2S1_SDP, RT5682_I2S1_MONO_MASK, + RT5682_I2S1_MONO_DIS); + break; + case RT5682_AIF2: + snd_soc_component_update_bits(component, RT5682_I2S2_SDP, + RT5682_I2S2_DL_MASK, len_2); + if (rt5682->master[RT5682_AIF2]) { + snd_soc_component_update_bits(component, + RT5682_I2S_M_CLK_CTRL_1, RT5682_I2S2_M_PD_MASK, + pre_div << RT5682_I2S2_M_PD_SFT); + } + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, + RT5682_I2S2_SDP, RT5682_I2S2_MONO_MASK, + RT5682_I2S2_MONO_EN); + else + snd_soc_component_update_bits(component, + RT5682_I2S2_SDP, RT5682_I2S2_MONO_MASK, + RT5682_I2S2_MONO_DIS); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5682_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, tdm_ctrl = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5682->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + rt5682->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5682_I2S_BP_INV; + tdm_ctrl |= RT5682_TDM_S_BP_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + if (dai->id == RT5682_AIF1) + tdm_ctrl |= RT5682_TDM_S_LP_INV | RT5682_TDM_M_BP_INV; + else + return -EINVAL; + break; + case SND_SOC_DAIFMT_IB_IF: + if (dai->id == RT5682_AIF1) + tdm_ctrl |= RT5682_TDM_S_BP_INV | RT5682_TDM_S_LP_INV | + RT5682_TDM_M_BP_INV | RT5682_TDM_M_LP_INV; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5682_I2S_DF_LEFT; + tdm_ctrl |= RT5682_TDM_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5682_I2S_DF_PCM_A; + tdm_ctrl |= RT5682_TDM_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5682_I2S_DF_PCM_B; + tdm_ctrl |= RT5682_TDM_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5682_AIF1: + snd_soc_component_update_bits(component, RT5682_I2S1_SDP, + RT5682_I2S_DF_MASK, reg_val); + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_MS_MASK | RT5682_TDM_S_BP_MASK | + RT5682_TDM_DF_MASK | RT5682_TDM_M_BP_MASK | + RT5682_TDM_M_LP_MASK | RT5682_TDM_S_LP_MASK, + tdm_ctrl | rt5682->master[dai->id]); + break; + case RT5682_AIF2: + if (rt5682->master[dai->id] == 0) + reg_val |= RT5682_I2S2_MS_S; + snd_soc_component_update_bits(component, RT5682_I2S2_SDP, + RT5682_I2S2_MS_MASK | RT5682_I2S_BP_MASK | + RT5682_I2S_DF_MASK, reg_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5682_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, src = 0; + + if (freq == rt5682->sysclk && clk_id == rt5682->sysclk_src) + return 0; + + switch (clk_id) { + case RT5682_SCLK_S_MCLK: + reg_val |= RT5682_SCLK_SRC_MCLK; + src = RT5682_CLK_SRC_MCLK; + break; + case RT5682_SCLK_S_PLL1: + reg_val |= RT5682_SCLK_SRC_PLL1; + src = RT5682_CLK_SRC_PLL1; + break; + case RT5682_SCLK_S_PLL2: + reg_val |= RT5682_SCLK_SRC_PLL2; + src = RT5682_CLK_SRC_PLL2; + break; + case RT5682_SCLK_S_RCCLK: + reg_val |= RT5682_SCLK_SRC_RCCLK; + src = RT5682_CLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_SCLK_SRC_MASK, reg_val); + + if (rt5682->master[RT5682_AIF2]) { + snd_soc_component_update_bits(component, + RT5682_I2S_M_CLK_CTRL_1, RT5682_I2S2_SRC_MASK, + src << RT5682_I2S2_SRC_SFT); + } + + rt5682->sysclk = freq; + rt5682->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static int rt5682_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5682->pll_src && freq_in == rt5682->pll_in && + freq_out == rt5682->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5682->pll_in = 0; + rt5682->pll_out = 0; + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_SCLK_SRC_MASK, RT5682_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5682_PLL1_S_MCLK: + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_PLL1_SRC_MASK, RT5682_PLL1_SRC_MCLK); + break; + case RT5682_PLL1_S_BCLK1: + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_PLL1_SRC_MASK, RT5682_PLL1_SRC_BCLK1); + break; + default: + dev_err(component->dev, "Unknown PLL Source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5682_PLL_CTRL_1, + pll_code.n_code << RT5682_PLL_N_SFT | pll_code.k_code); + snd_soc_component_write(component, RT5682_PLL_CTRL_2, + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5682_PLL_M_SFT | + pll_code.m_bp << RT5682_PLL_M_BP_SFT | RT5682_PLL_RST); + + rt5682->pll_in = freq_in; + rt5682->pll_out = freq_out; + rt5682->pll_src = source; + + return 0; +} + +static int rt5682_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682->bclk[dai->id] = ratio; + + switch (ratio) { + case 64: + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_2, + RT5682_I2S2_BCLK_MS2_MASK, + RT5682_I2S2_BCLK_MS2_64); + break; + case 32: + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_2, + RT5682_I2S2_BCLK_MS2_MASK, + RT5682_I2S2_BCLK_MS2_32); + break; + default: + dev_err(dai->dev, "Invalid bclk ratio %d\n", ratio); + return -EINVAL; + } + + return 0; +} + +static int rt5682_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_PWR_MB | RT5682_PWR_BG, + RT5682_PWR_MB | RT5682_PWR_BG); + regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1, + RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO, + RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO); + break; + + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_PWR_MB, RT5682_PWR_MB); + regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1, + RT5682_DIG_GATE_CTRL, RT5682_DIG_GATE_CTRL); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1, + RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO, 0); + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_PWR_MB | RT5682_PWR_BG, 0); + break; + + default: + break; + } + + return 0; +} + +static int rt5682_probe(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682->component = component; + + return 0; +} + +static void rt5682_remove(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682_reset(rt5682->regmap); +} + +#ifdef CONFIG_PM +static int rt5682_suspend(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5682->regmap, true); + regcache_mark_dirty(rt5682->regmap); + return 0; +} + +static int rt5682_resume(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5682->regmap, false); + regcache_sync(rt5682->regmap); + + return 0; +} +#else +#define rt5682_suspend NULL +#define rt5682_resume NULL +#endif + +#define RT5682_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5682_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5682_aif1_dai_ops = { + .hw_params = rt5682_hw_params, + .set_fmt = rt5682_set_dai_fmt, + .set_tdm_slot = rt5682_set_tdm_slot, +}; + +static const struct snd_soc_dai_ops rt5682_aif2_dai_ops = { + .hw_params = rt5682_hw_params, + .set_fmt = rt5682_set_dai_fmt, + .set_bclk_ratio = rt5682_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt5682_dai[] = { + { + .name = "rt5682-aif1", + .id = RT5682_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .ops = &rt5682_aif1_dai_ops, + }, + { + .name = "rt5682-aif2", + .id = RT5682_AIF2, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .ops = &rt5682_aif2_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5682 = { + .probe = rt5682_probe, + .remove = rt5682_remove, + .suspend = rt5682_suspend, + .resume = rt5682_resume, + .set_bias_level = rt5682_set_bias_level, + .controls = rt5682_snd_controls, + .num_controls = ARRAY_SIZE(rt5682_snd_controls), + .dapm_widgets = rt5682_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5682_dapm_widgets), + .dapm_routes = rt5682_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5682_dapm_routes), + .set_sysclk = rt5682_set_component_sysclk, + .set_pll = rt5682_set_component_pll, + .set_jack = rt5682_set_jack_detect, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config rt5682_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = RT5682_I2C_MODE, + .volatile_reg = rt5682_volatile_register, + .readable_reg = rt5682_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5682_reg, + .num_reg_defaults = ARRAY_SIZE(rt5682_reg), + .use_single_rw = true, +}; + +static const struct i2c_device_id rt5682_i2c_id[] = { + {"rt5682", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5682_i2c_id); + +static int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev) +{ + + device_property_read_u32(dev, "realtek,dmic1-data-pin", + &rt5682->pdata.dmic1_data_pin); + device_property_read_u32(dev, "realtek,dmic1-clk-pin", + &rt5682->pdata.dmic1_clk_pin); + device_property_read_u32(dev, "realtek,jd-src", + &rt5682->pdata.jd_src); + + rt5682->pdata.ldo1_en = of_get_named_gpio(dev->of_node, + "realtek,ldo1-en-gpios", 0); + + return 0; +} + +static void rt5682_calibrate(struct rt5682_priv *rt5682) +{ + int value, count; + + mutex_lock(&rt5682->calibrate_mutex); + + rt5682_reset(rt5682->regmap); + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2bf); + usleep_range(15000, 20000); + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2bf); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0380); + regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x8001); + regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); + regmap_write(rt5682->regmap, RT5682_STO1_DAC_MIXER, 0x2080); + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x4040); + regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0069); + regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x3000); + regmap_write(rt5682->regmap, RT5682_HP_CTRL_2, 0x6000); + regmap_write(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1, 0x0f26); + regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7f05); + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x686c); + regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0d0d); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_9, 0x000f); + regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x8d01); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_2, 0x0321); + regmap_write(rt5682->regmap, RT5682_HP_LOGIC_CTRL_2, 0x0004); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_3, 0x06a1); + regmap_write(rt5682->regmap, RT5682_A_DAC1_MUX, 0x0311); + regmap_write(rt5682->regmap, RT5682_RESET_HPF_CTRL, 0x0000); + regmap_write(rt5682->regmap, RT5682_ADC_STO1_HP_CTRL_1, 0x3320); + + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0xfc00); + + for (count = 0; count < 60; count++) { + regmap_read(rt5682->regmap, RT5682_HP_CALIB_STA_1, &value); + if (!(value & 0x8000)) + break; + + usleep_range(10000, 10005); + } + + if (count >= 60) + pr_err("HP Calibration Failure\n"); + + /* restore settings */ + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4); + regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000); + + mutex_unlock(&rt5682->calibrate_mutex); + +} + +static int rt5682_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5682_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5682_priv *rt5682; + int i, ret; + unsigned int val; + + rt5682 = devm_kzalloc(&i2c->dev, sizeof(struct rt5682_priv), + GFP_KERNEL); + + if (rt5682 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5682); + + if (pdata) + rt5682->pdata = *pdata; + else + rt5682_parse_dt(rt5682, &i2c->dev); + + rt5682->regmap = devm_regmap_init_i2c(i2c, &rt5682_regmap); + if (IS_ERR(rt5682->regmap)) { + ret = PTR_ERR(rt5682->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(rt5682->supplies); i++) + rt5682->supplies[i].supply = rt5682_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5682->supplies), + rt5682->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(rt5682->supplies), + rt5682->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + if (gpio_is_valid(rt5682->pdata.ldo1_en)) { + if (devm_gpio_request_one(&i2c->dev, rt5682->pdata.ldo1_en, + GPIOF_OUT_INIT_HIGH, "rt5682")) + dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n"); + } + + /* Sleep for 300 ms miniumum */ + usleep_range(300000, 350000); + + regmap_write(rt5682->regmap, RT5682_I2C_MODE, 0x1); + usleep_range(10000, 15000); + + regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val); + if (val != DEVICE_ID) { + pr_err("Device with ID register %x is not rt5682\n", val); + return -ENODEV; + } + + rt5682_reset(rt5682->regmap); + + rt5682_calibrate(rt5682); + + ret = regmap_register_patch(rt5682->regmap, patch_list, + ARRAY_SIZE(patch_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0000); + + /* DMIC pin*/ + if (rt5682->pdata.dmic1_data_pin != RT5682_DMIC1_NULL) { + switch (rt5682->pdata.dmic1_data_pin) { + case RT5682_DMIC1_DATA_GPIO2: /* share with LRCK2 */ + regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1, + RT5682_DMIC_1_DP_MASK, RT5682_DMIC_1_DP_GPIO2); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP2_PIN_MASK, RT5682_GP2_PIN_DMIC_SDA); + break; + + case RT5682_DMIC1_DATA_GPIO5: /* share with DACDAT1 */ + regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1, + RT5682_DMIC_1_DP_MASK, RT5682_DMIC_1_DP_GPIO5); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP5_PIN_MASK, RT5682_GP5_PIN_DMIC_SDA); + break; + + default: + dev_warn(&i2c->dev, "invalid DMIC_DAT pin\n"); + break; + } + + switch (rt5682->pdata.dmic1_clk_pin) { + case RT5682_DMIC1_CLK_GPIO1: /* share with IRQ */ + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_DMIC_CLK); + break; + + case RT5682_DMIC1_CLK_GPIO3: /* share with BCLK2 */ + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP3_PIN_MASK, RT5682_GP3_PIN_DMIC_CLK); + break; + + default: + dev_warn(&i2c->dev, "invalid DMIC_CLK pin\n"); + break; + } + } + + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_LDO1_DVO_MASK | RT5682_HP_DRIVER_MASK, + RT5682_LDO1_DVO_12 | RT5682_HP_DRIVER_5X); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0380); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP4_PIN_MASK | RT5682_GP5_PIN_MASK, + RT5682_GP4_PIN_ADCDAT1 | RT5682_GP5_PIN_DACDAT1); + regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); + + INIT_DELAYED_WORK(&rt5682->jack_detect_work, + rt5682_jack_detect_handler); + INIT_DELAYED_WORK(&rt5682->jd_check_work, + rt5682_jd_check_handler); + + mutex_init(&rt5682->calibrate_mutex); + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5682_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5682", rt5682); + if (ret) + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + + } + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5682, + rt5682_dai, ARRAY_SIZE(rt5682_dai)); +} + +static void rt5682_i2c_shutdown(struct i2c_client *client) +{ + struct rt5682_priv *rt5682 = i2c_get_clientdata(client); + + rt5682_reset(rt5682->regmap); +} + +#ifdef CONFIG_OF +static const struct of_device_id rt5682_of_match[] = { + {.compatible = "realtek,rt5682i"}, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5682_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5682_acpi_match[] = { + {"10EC5682", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5682_acpi_match); +#endif + +static struct i2c_driver rt5682_i2c_driver = { + .driver = { + .name = "rt5682", + .of_match_table = of_match_ptr(rt5682_of_match), + .acpi_match_table = ACPI_PTR(rt5682_acpi_match), + }, + .probe = rt5682_i2c_probe, + .shutdown = rt5682_i2c_shutdown, + .id_table = rt5682_i2c_id, +}; +module_i2c_driver(rt5682_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5682 driver"); +MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h new file mode 100644 index 000000000000..8068140ebe3f --- /dev/null +++ b/sound/soc/codecs/rt5682.h @@ -0,0 +1,1324 @@ +/* + * rt5682.h -- RT5682/RT5658 ALSA SoC audio driver + * + * Copyright 2018 Realtek Microelectronics + * Author: Bard Liao <bardliao@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __RT5682_H__ +#define __RT5682_H__ + +#include <sound/rt5682.h> + +#define DEVICE_ID 0x6530 + +/* Info */ +#define RT5682_RESET 0x0000 +#define RT5682_VERSION_ID 0x00fd +#define RT5682_VENDOR_ID 0x00fe +#define RT5682_DEVICE_ID 0x00ff +/* I/O - Output */ +#define RT5682_HP_CTRL_1 0x0002 +#define RT5682_HP_CTRL_2 0x0003 +#define RT5682_HPL_GAIN 0x0005 +#define RT5682_HPR_GAIN 0x0006 + +#define RT5682_I2C_CTRL 0x0008 + +/* I/O - Input */ +#define RT5682_CBJ_BST_CTRL 0x000b +#define RT5682_CBJ_CTRL_1 0x0010 +#define RT5682_CBJ_CTRL_2 0x0011 +#define RT5682_CBJ_CTRL_3 0x0012 +#define RT5682_CBJ_CTRL_4 0x0013 +#define RT5682_CBJ_CTRL_5 0x0014 +#define RT5682_CBJ_CTRL_6 0x0015 +#define RT5682_CBJ_CTRL_7 0x0016 +/* I/O - ADC/DAC/DMIC */ +#define RT5682_DAC1_DIG_VOL 0x0019 +#define RT5682_STO1_ADC_DIG_VOL 0x001c +#define RT5682_STO1_ADC_BOOST 0x001f +#define RT5682_HP_IMP_GAIN_1 0x0022 +#define RT5682_HP_IMP_GAIN_2 0x0023 +/* Mixer - D-D */ +#define RT5682_SIDETONE_CTRL 0x0024 +#define RT5682_STO1_ADC_MIXER 0x0026 +#define RT5682_AD_DA_MIXER 0x0029 +#define RT5682_STO1_DAC_MIXER 0x002a +#define RT5682_A_DAC1_MUX 0x002b +#define RT5682_DIG_INF2_DATA 0x0030 +/* Mixer - ADC */ +#define RT5682_REC_MIXER 0x003c +#define RT5682_CAL_REC 0x0044 +#define RT5682_ALC_BACK_GAIN 0x0049 +/* Power */ +#define RT5682_PWR_DIG_1 0x0061 +#define RT5682_PWR_DIG_2 0x0062 +#define RT5682_PWR_ANLG_1 0x0063 +#define RT5682_PWR_ANLG_2 0x0064 +#define RT5682_PWR_ANLG_3 0x0065 +#define RT5682_PWR_MIXER 0x0066 +#define RT5682_PWR_VOL 0x0067 +/* Clock Detect */ +#define RT5682_CLK_DET 0x006b +/* Filter Auto Reset */ +#define RT5682_RESET_LPF_CTRL 0x006c +#define RT5682_RESET_HPF_CTRL 0x006d +/* DMIC */ +#define RT5682_DMIC_CTRL_1 0x006e +/* Format - ADC/DAC */ +#define RT5682_I2S1_SDP 0x0070 +#define RT5682_I2S2_SDP 0x0071 +#define RT5682_ADDA_CLK_1 0x0073 +#define RT5682_ADDA_CLK_2 0x0074 +#define RT5682_I2S1_F_DIV_CTRL_1 0x0075 +#define RT5682_I2S1_F_DIV_CTRL_2 0x0076 +/* Format - TDM Control */ +#define RT5682_TDM_CTRL 0x0079 +#define RT5682_TDM_ADDA_CTRL_1 0x007a +#define RT5682_TDM_ADDA_CTRL_2 0x007b +#define RT5682_DATA_SEL_CTRL_1 0x007c +#define RT5682_TDM_TCON_CTRL 0x007e +/* Function - Analog */ +#define RT5682_GLB_CLK 0x0080 +#define RT5682_PLL_CTRL_1 0x0081 +#define RT5682_PLL_CTRL_2 0x0082 +#define RT5682_PLL_TRACK_1 0x0083 +#define RT5682_PLL_TRACK_2 0x0084 +#define RT5682_PLL_TRACK_3 0x0085 +#define RT5682_PLL_TRACK_4 0x0086 +#define RT5682_PLL_TRACK_5 0x0087 +#define RT5682_PLL_TRACK_6 0x0088 +#define RT5682_PLL_TRACK_11 0x008c +#define RT5682_SDW_REF_CLK 0x008d +#define RT5682_DEPOP_1 0x008e +#define RT5682_DEPOP_2 0x008f +#define RT5682_HP_CHARGE_PUMP_1 0x0091 +#define RT5682_HP_CHARGE_PUMP_2 0x0092 +#define RT5682_MICBIAS_1 0x0093 +#define RT5682_MICBIAS_2 0x0094 +#define RT5682_PLL_TRACK_12 0x0098 +#define RT5682_PLL_TRACK_14 0x009a +#define RT5682_PLL2_CTRL_1 0x009b +#define RT5682_PLL2_CTRL_2 0x009c +#define RT5682_PLL2_CTRL_3 0x009d +#define RT5682_PLL2_CTRL_4 0x009e +#define RT5682_RC_CLK_CTRL 0x009f +#define RT5682_I2S_M_CLK_CTRL_1 0x00a0 +#define RT5682_I2S2_F_DIV_CTRL_1 0x00a3 +#define RT5682_I2S2_F_DIV_CTRL_2 0x00a4 +/* Function - Digital */ +#define RT5682_EQ_CTRL_1 0x00ae +#define RT5682_EQ_CTRL_2 0x00af +#define RT5682_IRQ_CTRL_1 0x00b6 +#define RT5682_IRQ_CTRL_2 0x00b7 +#define RT5682_IRQ_CTRL_3 0x00b8 +#define RT5682_IRQ_CTRL_4 0x00b9 +#define RT5682_INT_ST_1 0x00be +#define RT5682_GPIO_CTRL_1 0x00c0 +#define RT5682_GPIO_CTRL_2 0x00c1 +#define RT5682_GPIO_CTRL_3 0x00c2 +#define RT5682_HP_AMP_DET_CTRL_1 0x00d0 +#define RT5682_HP_AMP_DET_CTRL_2 0x00d1 +#define RT5682_MID_HP_AMP_DET 0x00d2 +#define RT5682_LOW_HP_AMP_DET 0x00d3 +#define RT5682_DELAY_BUF_CTRL 0x00d4 +#define RT5682_SV_ZCD_1 0x00d9 +#define RT5682_SV_ZCD_2 0x00da +#define RT5682_IL_CMD_1 0x00db +#define RT5682_IL_CMD_2 0x00dc +#define RT5682_IL_CMD_3 0x00dd +#define RT5682_IL_CMD_4 0x00de +#define RT5682_IL_CMD_5 0x00df +#define RT5682_IL_CMD_6 0x00e0 +#define RT5682_4BTN_IL_CMD_1 0x00e2 +#define RT5682_4BTN_IL_CMD_2 0x00e3 +#define RT5682_4BTN_IL_CMD_3 0x00e4 +#define RT5682_4BTN_IL_CMD_4 0x00e5 +#define RT5682_4BTN_IL_CMD_5 0x00e6 +#define RT5682_4BTN_IL_CMD_6 0x00e7 +#define RT5682_4BTN_IL_CMD_7 0x00e8 + +#define RT5682_ADC_STO1_HP_CTRL_1 0x00ea +#define RT5682_ADC_STO1_HP_CTRL_2 0x00eb +#define RT5682_AJD1_CTRL 0x00f0 +#define RT5682_JD1_THD 0x00f1 +#define RT5682_JD2_THD 0x00f2 +#define RT5682_JD_CTRL_1 0x00f6 +/* General Control */ +#define RT5682_DUMMY_1 0x00fa +#define RT5682_DUMMY_2 0x00fb +#define RT5682_DUMMY_3 0x00fc + +#define RT5682_DAC_ADC_DIG_VOL1 0x0100 +#define RT5682_BIAS_CUR_CTRL_2 0x010b +#define RT5682_BIAS_CUR_CTRL_3 0x010c +#define RT5682_BIAS_CUR_CTRL_4 0x010d +#define RT5682_BIAS_CUR_CTRL_5 0x010e +#define RT5682_BIAS_CUR_CTRL_6 0x010f +#define RT5682_BIAS_CUR_CTRL_7 0x0110 +#define RT5682_BIAS_CUR_CTRL_8 0x0111 +#define RT5682_BIAS_CUR_CTRL_9 0x0112 +#define RT5682_BIAS_CUR_CTRL_10 0x0113 +#define RT5682_VREF_REC_OP_FB_CAP_CTRL 0x0117 +#define RT5682_CHARGE_PUMP_1 0x0125 +#define RT5682_DIG_IN_CTRL_1 0x0132 +#define RT5682_PAD_DRIVING_CTRL 0x0136 +#define RT5682_SOFT_RAMP_DEPOP 0x0138 +#define RT5682_CHOP_DAC 0x013a +#define RT5682_CHOP_ADC 0x013b +#define RT5682_CALIB_ADC_CTRL 0x013c +#define RT5682_VOL_TEST 0x013f +#define RT5682_SPKVDD_DET_STA 0x0142 +#define RT5682_TEST_MODE_CTRL_1 0x0145 +#define RT5682_TEST_MODE_CTRL_2 0x0146 +#define RT5682_TEST_MODE_CTRL_3 0x0147 +#define RT5682_TEST_MODE_CTRL_4 0x0148 +#define RT5682_TEST_MODE_CTRL_5 0x0149 +#define RT5682_PLL1_INTERNAL 0x0150 +#define RT5682_PLL2_INTERNAL 0x0151 +#define RT5682_STO_NG2_CTRL_1 0x0160 +#define RT5682_STO_NG2_CTRL_2 0x0161 +#define RT5682_STO_NG2_CTRL_3 0x0162 +#define RT5682_STO_NG2_CTRL_4 0x0163 +#define RT5682_STO_NG2_CTRL_5 0x0164 +#define RT5682_STO_NG2_CTRL_6 0x0165 +#define RT5682_STO_NG2_CTRL_7 0x0166 +#define RT5682_STO_NG2_CTRL_8 0x0167 +#define RT5682_STO_NG2_CTRL_9 0x0168 +#define RT5682_STO_NG2_CTRL_10 0x0169 +#define RT5682_STO1_DAC_SIL_DET 0x0190 +#define RT5682_SIL_PSV_CTRL1 0x0194 +#define RT5682_SIL_PSV_CTRL2 0x0195 +#define RT5682_SIL_PSV_CTRL3 0x0197 +#define RT5682_SIL_PSV_CTRL4 0x0198 +#define RT5682_SIL_PSV_CTRL5 0x0199 +#define RT5682_HP_IMP_SENS_CTRL_01 0x01af +#define RT5682_HP_IMP_SENS_CTRL_02 0x01b0 +#define RT5682_HP_IMP_SENS_CTRL_03 0x01b1 +#define RT5682_HP_IMP_SENS_CTRL_04 0x01b2 +#define RT5682_HP_IMP_SENS_CTRL_05 0x01b3 +#define RT5682_HP_IMP_SENS_CTRL_06 0x01b4 +#define RT5682_HP_IMP_SENS_CTRL_07 0x01b5 +#define RT5682_HP_IMP_SENS_CTRL_08 0x01b6 +#define RT5682_HP_IMP_SENS_CTRL_09 0x01b7 +#define RT5682_HP_IMP_SENS_CTRL_10 0x01b8 +#define RT5682_HP_IMP_SENS_CTRL_11 0x01b9 +#define RT5682_HP_IMP_SENS_CTRL_12 0x01ba +#define RT5682_HP_IMP_SENS_CTRL_13 0x01bb +#define RT5682_HP_IMP_SENS_CTRL_14 0x01bc +#define RT5682_HP_IMP_SENS_CTRL_15 0x01bd +#define RT5682_HP_IMP_SENS_CTRL_16 0x01be +#define RT5682_HP_IMP_SENS_CTRL_17 0x01bf +#define RT5682_HP_IMP_SENS_CTRL_18 0x01c0 +#define RT5682_HP_IMP_SENS_CTRL_19 0x01c1 +#define RT5682_HP_IMP_SENS_CTRL_20 0x01c2 +#define RT5682_HP_IMP_SENS_CTRL_21 0x01c3 +#define RT5682_HP_IMP_SENS_CTRL_22 0x01c4 +#define RT5682_HP_IMP_SENS_CTRL_23 0x01c5 +#define RT5682_HP_IMP_SENS_CTRL_24 0x01c6 +#define RT5682_HP_IMP_SENS_CTRL_25 0x01c7 +#define RT5682_HP_IMP_SENS_CTRL_26 0x01c8 +#define RT5682_HP_IMP_SENS_CTRL_27 0x01c9 +#define RT5682_HP_IMP_SENS_CTRL_28 0x01ca +#define RT5682_HP_IMP_SENS_CTRL_29 0x01cb +#define RT5682_HP_IMP_SENS_CTRL_30 0x01cc +#define RT5682_HP_IMP_SENS_CTRL_31 0x01cd +#define RT5682_HP_IMP_SENS_CTRL_32 0x01ce +#define RT5682_HP_IMP_SENS_CTRL_33 0x01cf +#define RT5682_HP_IMP_SENS_CTRL_34 0x01d0 +#define RT5682_HP_IMP_SENS_CTRL_35 0x01d1 +#define RT5682_HP_IMP_SENS_CTRL_36 0x01d2 +#define RT5682_HP_IMP_SENS_CTRL_37 0x01d3 +#define RT5682_HP_IMP_SENS_CTRL_38 0x01d4 +#define RT5682_HP_IMP_SENS_CTRL_39 0x01d5 +#define RT5682_HP_IMP_SENS_CTRL_40 0x01d6 +#define RT5682_HP_IMP_SENS_CTRL_41 0x01d7 +#define RT5682_HP_IMP_SENS_CTRL_42 0x01d8 +#define RT5682_HP_IMP_SENS_CTRL_43 0x01d9 +#define RT5682_HP_LOGIC_CTRL_1 0x01da +#define RT5682_HP_LOGIC_CTRL_2 0x01db +#define RT5682_HP_LOGIC_CTRL_3 0x01dc +#define RT5682_HP_CALIB_CTRL_1 0x01de +#define RT5682_HP_CALIB_CTRL_2 0x01df +#define RT5682_HP_CALIB_CTRL_3 0x01e0 +#define RT5682_HP_CALIB_CTRL_4 0x01e1 +#define RT5682_HP_CALIB_CTRL_5 0x01e2 +#define RT5682_HP_CALIB_CTRL_6 0x01e3 +#define RT5682_HP_CALIB_CTRL_7 0x01e4 +#define RT5682_HP_CALIB_CTRL_9 0x01e6 +#define RT5682_HP_CALIB_CTRL_10 0x01e7 +#define RT5682_HP_CALIB_CTRL_11 0x01e8 +#define RT5682_HP_CALIB_STA_1 0x01ea +#define RT5682_HP_CALIB_STA_2 0x01eb +#define RT5682_HP_CALIB_STA_3 0x01ec +#define RT5682_HP_CALIB_STA_4 0x01ed +#define RT5682_HP_CALIB_STA_5 0x01ee +#define RT5682_HP_CALIB_STA_6 0x01ef +#define RT5682_HP_CALIB_STA_7 0x01f0 +#define RT5682_HP_CALIB_STA_8 0x01f1 +#define RT5682_HP_CALIB_STA_9 0x01f2 +#define RT5682_HP_CALIB_STA_10 0x01f3 +#define RT5682_HP_CALIB_STA_11 0x01f4 +#define RT5682_SAR_IL_CMD_1 0x0210 +#define RT5682_SAR_IL_CMD_2 0x0211 +#define RT5682_SAR_IL_CMD_3 0x0212 +#define RT5682_SAR_IL_CMD_4 0x0213 +#define RT5682_SAR_IL_CMD_5 0x0214 +#define RT5682_SAR_IL_CMD_6 0x0215 +#define RT5682_SAR_IL_CMD_7 0x0216 +#define RT5682_SAR_IL_CMD_8 0x0217 +#define RT5682_SAR_IL_CMD_9 0x0218 +#define RT5682_SAR_IL_CMD_10 0x0219 +#define RT5682_SAR_IL_CMD_11 0x021a +#define RT5682_SAR_IL_CMD_12 0x021b +#define RT5682_SAR_IL_CMD_13 0x021c +#define RT5682_EFUSE_CTRL_1 0x0250 +#define RT5682_EFUSE_CTRL_2 0x0251 +#define RT5682_EFUSE_CTRL_3 0x0252 +#define RT5682_EFUSE_CTRL_4 0x0253 +#define RT5682_EFUSE_CTRL_5 0x0254 +#define RT5682_EFUSE_CTRL_6 0x0255 +#define RT5682_EFUSE_CTRL_7 0x0256 +#define RT5682_EFUSE_CTRL_8 0x0257 +#define RT5682_EFUSE_CTRL_9 0x0258 +#define RT5682_EFUSE_CTRL_10 0x0259 +#define RT5682_EFUSE_CTRL_11 0x025a +#define RT5682_JD_TOP_VC_VTRL 0x0270 +#define RT5682_DRC1_CTRL_0 0x02ff +#define RT5682_DRC1_CTRL_1 0x0300 +#define RT5682_DRC1_CTRL_2 0x0301 +#define RT5682_DRC1_CTRL_3 0x0302 +#define RT5682_DRC1_CTRL_4 0x0303 +#define RT5682_DRC1_CTRL_5 0x0304 +#define RT5682_DRC1_CTRL_6 0x0305 +#define RT5682_DRC1_HARD_LMT_CTRL_1 0x0306 +#define RT5682_DRC1_HARD_LMT_CTRL_2 0x0307 +#define RT5682_DRC1_PRIV_1 0x0310 +#define RT5682_DRC1_PRIV_2 0x0311 +#define RT5682_DRC1_PRIV_3 0x0312 +#define RT5682_DRC1_PRIV_4 0x0313 +#define RT5682_DRC1_PRIV_5 0x0314 +#define RT5682_DRC1_PRIV_6 0x0315 +#define RT5682_DRC1_PRIV_7 0x0316 +#define RT5682_DRC1_PRIV_8 0x0317 +#define RT5682_EQ_AUTO_RCV_CTRL1 0x03c0 +#define RT5682_EQ_AUTO_RCV_CTRL2 0x03c1 +#define RT5682_EQ_AUTO_RCV_CTRL3 0x03c2 +#define RT5682_EQ_AUTO_RCV_CTRL4 0x03c3 +#define RT5682_EQ_AUTO_RCV_CTRL5 0x03c4 +#define RT5682_EQ_AUTO_RCV_CTRL6 0x03c5 +#define RT5682_EQ_AUTO_RCV_CTRL7 0x03c6 +#define RT5682_EQ_AUTO_RCV_CTRL8 0x03c7 +#define RT5682_EQ_AUTO_RCV_CTRL9 0x03c8 +#define RT5682_EQ_AUTO_RCV_CTRL10 0x03c9 +#define RT5682_EQ_AUTO_RCV_CTRL11 0x03ca +#define RT5682_EQ_AUTO_RCV_CTRL12 0x03cb +#define RT5682_EQ_AUTO_RCV_CTRL13 0x03cc +#define RT5682_ADC_L_EQ_LPF1_A1 0x03d0 +#define RT5682_R_EQ_LPF1_A1 0x03d1 +#define RT5682_L_EQ_LPF1_H0 0x03d2 +#define RT5682_R_EQ_LPF1_H0 0x03d3 +#define RT5682_L_EQ_BPF1_A1 0x03d4 +#define RT5682_R_EQ_BPF1_A1 0x03d5 +#define RT5682_L_EQ_BPF1_A2 0x03d6 +#define RT5682_R_EQ_BPF1_A2 0x03d7 +#define RT5682_L_EQ_BPF1_H0 0x03d8 +#define RT5682_R_EQ_BPF1_H0 0x03d9 +#define RT5682_L_EQ_BPF2_A1 0x03da +#define RT5682_R_EQ_BPF2_A1 0x03db +#define RT5682_L_EQ_BPF2_A2 0x03dc +#define RT5682_R_EQ_BPF2_A2 0x03dd +#define RT5682_L_EQ_BPF2_H0 0x03de +#define RT5682_R_EQ_BPF2_H0 0x03df +#define RT5682_L_EQ_BPF3_A1 0x03e0 +#define RT5682_R_EQ_BPF3_A1 0x03e1 +#define RT5682_L_EQ_BPF3_A2 0x03e2 +#define RT5682_R_EQ_BPF3_A2 0x03e3 +#define RT5682_L_EQ_BPF3_H0 0x03e4 +#define RT5682_R_EQ_BPF3_H0 0x03e5 +#define RT5682_L_EQ_BPF4_A1 0x03e6 +#define RT5682_R_EQ_BPF4_A1 0x03e7 +#define RT5682_L_EQ_BPF4_A2 0x03e8 +#define RT5682_R_EQ_BPF4_A2 0x03e9 +#define RT5682_L_EQ_BPF4_H0 0x03ea +#define RT5682_R_EQ_BPF4_H0 0x03eb +#define RT5682_L_EQ_HPF1_A1 0x03ec +#define RT5682_R_EQ_HPF1_A1 0x03ed +#define RT5682_L_EQ_HPF1_H0 0x03ee +#define RT5682_R_EQ_HPF1_H0 0x03ef +#define RT5682_L_EQ_PRE_VOL 0x03f0 +#define RT5682_R_EQ_PRE_VOL 0x03f1 +#define RT5682_L_EQ_POST_VOL 0x03f2 +#define RT5682_R_EQ_POST_VOL 0x03f3 +#define RT5682_I2C_MODE 0xffff + + +/* global definition */ +#define RT5682_L_MUTE (0x1 << 15) +#define RT5682_L_MUTE_SFT 15 +#define RT5682_VOL_L_MUTE (0x1 << 14) +#define RT5682_VOL_L_SFT 14 +#define RT5682_R_MUTE (0x1 << 7) +#define RT5682_R_MUTE_SFT 7 +#define RT5682_VOL_R_MUTE (0x1 << 6) +#define RT5682_VOL_R_SFT 6 +#define RT5682_L_VOL_MASK (0x3f << 8) +#define RT5682_L_VOL_SFT 8 +#define RT5682_R_VOL_MASK (0x3f) +#define RT5682_R_VOL_SFT 0 + +/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/ +#define RT5682_G_HP (0xf << 8) +#define RT5682_G_HP_SFT 8 +#define RT5682_G_STO_DA_DMIX (0xf) +#define RT5682_G_STO_DA_SFT 0 + +/* CBJ Control (0x000b) */ +#define RT5682_BST_CBJ_MASK (0xf << 8) +#define RT5682_BST_CBJ_SFT 8 + +/* Embeeded Jack and Type Detection Control 1 (0x0010) */ +#define RT5682_EMB_JD_EN (0x1 << 15) +#define RT5682_EMB_JD_EN_SFT 15 +#define RT5682_EMB_JD_RST (0x1 << 14) +#define RT5682_JD_MODE (0x1 << 13) +#define RT5682_JD_MODE_SFT 13 +#define RT5682_DET_TYPE (0x1 << 12) +#define RT5682_DET_TYPE_SFT 12 +#define RT5682_POLA_EXT_JD_MASK (0x1 << 11) +#define RT5682_POLA_EXT_JD_LOW (0x1 << 11) +#define RT5682_POLA_EXT_JD_HIGH (0x0 << 11) +#define RT5682_EXT_JD_DIG (0x1 << 9) +#define RT5682_POL_FAST_OFF_MASK (0x1 << 8) +#define RT5682_POL_FAST_OFF_HIGH (0x1 << 8) +#define RT5682_POL_FAST_OFF_LOW (0x0 << 8) +#define RT5682_FAST_OFF_MASK (0x1 << 7) +#define RT5682_FAST_OFF_EN (0x1 << 7) +#define RT5682_FAST_OFF_DIS (0x0 << 7) +#define RT5682_VREF_POW_MASK (0x1 << 6) +#define RT5682_VREF_POW_FSM (0x0 << 6) +#define RT5682_VREF_POW_REG (0x1 << 6) +#define RT5682_MB1_PATH_MASK (0x1 << 5) +#define RT5682_CTRL_MB1_REG (0x1 << 5) +#define RT5682_CTRL_MB1_FSM (0x0 << 5) +#define RT5682_MB2_PATH_MASK (0x1 << 4) +#define RT5682_CTRL_MB2_REG (0x1 << 4) +#define RT5682_CTRL_MB2_FSM (0x0 << 4) +#define RT5682_TRIG_JD_MASK (0x1 << 3) +#define RT5682_TRIG_JD_HIGH (0x1 << 3) +#define RT5682_TRIG_JD_LOW (0x0 << 3) +#define RT5682_MIC_CAP_MASK (0x1 << 1) +#define RT5682_MIC_CAP_HS (0x1 << 1) +#define RT5682_MIC_CAP_HP (0x0 << 1) +#define RT5682_MIC_CAP_SRC_MASK (0x1) +#define RT5682_MIC_CAP_SRC_REG (0x1) +#define RT5682_MIC_CAP_SRC_ANA (0x0) + +/* Embeeded Jack and Type Detection Control 2 (0x0011) */ +#define RT5682_EXT_JD_SRC (0x7 << 4) +#define RT5682_EXT_JD_SRC_SFT 4 +#define RT5682_EXT_JD_SRC_GPIO_JD1 (0x0 << 4) +#define RT5682_EXT_JD_SRC_GPIO_JD2 (0x1 << 4) +#define RT5682_EXT_JD_SRC_JDH (0x2 << 4) +#define RT5682_EXT_JD_SRC_JDL (0x3 << 4) +#define RT5682_EXT_JD_SRC_MANUAL (0x4 << 4) +#define RT5682_JACK_TYPE_MASK (0x3) + +/* Combo Jack and Type Detection Control 3 (0x0012) */ +#define RT5682_CBJ_IN_BUF_EN (0x1 << 7) + +/* Combo Jack and Type Detection Control 4 (0x0013) */ +#define RT5682_SEL_SHT_MID_TON_MASK (0x3 << 12) +#define RT5682_SEL_SHT_MID_TON_2 (0x0 << 12) +#define RT5682_SEL_SHT_MID_TON_3 (0x1 << 12) +#define RT5682_CBJ_JD_TEST_MASK (0x1 << 6) +#define RT5682_CBJ_JD_TEST_NORM (0x0 << 6) +#define RT5682_CBJ_JD_TEST_MODE (0x1 << 6) + +/* DAC1 Digital Volume (0x0019) */ +#define RT5682_DAC_L1_VOL_MASK (0xff << 8) +#define RT5682_DAC_L1_VOL_SFT 8 +#define RT5682_DAC_R1_VOL_MASK (0xff) +#define RT5682_DAC_R1_VOL_SFT 0 + +/* ADC Digital Volume Control (0x001c) */ +#define RT5682_ADC_L_VOL_MASK (0x7f << 8) +#define RT5682_ADC_L_VOL_SFT 8 +#define RT5682_ADC_R_VOL_MASK (0x7f) +#define RT5682_ADC_R_VOL_SFT 0 + +/* Stereo1 ADC Boost Gain Control (0x001f) */ +#define RT5682_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5682_STO1_ADC_L_BST_SFT 14 +#define RT5682_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5682_STO1_ADC_R_BST_SFT 12 + +/* Sidetone Control (0x0024) */ +#define RT5682_ST_SRC_SEL (0x1 << 8) +#define RT5682_ST_SRC_SFT 8 +#define RT5682_ST_EN_MASK (0x1 << 6) +#define RT5682_ST_DIS (0x0 << 6) +#define RT5682_ST_EN (0x1 << 6) +#define RT5682_ST_EN_SFT 6 + +/* Stereo1 ADC Mixer Control (0x0026) */ +#define RT5682_M_STO1_ADC_L1 (0x1 << 15) +#define RT5682_M_STO1_ADC_L1_SFT 15 +#define RT5682_M_STO1_ADC_L2 (0x1 << 14) +#define RT5682_M_STO1_ADC_L2_SFT 14 +#define RT5682_STO1_ADC1L_SRC_MASK (0x1 << 13) +#define RT5682_STO1_ADC1L_SRC_SFT 13 +#define RT5682_STO1_ADC1_SRC_ADC (0x1 << 13) +#define RT5682_STO1_ADC1_SRC_DACMIX (0x0 << 13) +#define RT5682_STO1_ADC2L_SRC_MASK (0x1 << 12) +#define RT5682_STO1_ADC2L_SRC_SFT 12 +#define RT5682_STO1_ADCL_SRC_MASK (0x3 << 10) +#define RT5682_STO1_ADCL_SRC_SFT 10 +#define RT5682_STO1_DD_L_SRC_MASK (0x1 << 9) +#define RT5682_STO1_DD_L_SRC_SFT 9 +#define RT5682_STO1_DMIC_SRC_MASK (0x1 << 8) +#define RT5682_STO1_DMIC_SRC_SFT 8 +#define RT5682_STO1_DMIC_SRC_DMIC2 (0x1 << 8) +#define RT5682_STO1_DMIC_SRC_DMIC1 (0x0 << 8) +#define RT5682_M_STO1_ADC_R1 (0x1 << 7) +#define RT5682_M_STO1_ADC_R1_SFT 7 +#define RT5682_M_STO1_ADC_R2 (0x1 << 6) +#define RT5682_M_STO1_ADC_R2_SFT 6 +#define RT5682_STO1_ADC1R_SRC_MASK (0x1 << 5) +#define RT5682_STO1_ADC1R_SRC_SFT 5 +#define RT5682_STO1_ADC2R_SRC_MASK (0x1 << 4) +#define RT5682_STO1_ADC2R_SRC_SFT 4 +#define RT5682_STO1_ADCR_SRC_MASK (0x3 << 2) +#define RT5682_STO1_ADCR_SRC_SFT 2 + +/* ADC Mixer to DAC Mixer Control (0x0029) */ +#define RT5682_M_ADCMIX_L (0x1 << 15) +#define RT5682_M_ADCMIX_L_SFT 15 +#define RT5682_M_DAC1_L (0x1 << 14) +#define RT5682_M_DAC1_L_SFT 14 +#define RT5682_DAC1_R_SEL_MASK (0x1 << 10) +#define RT5682_DAC1_R_SEL_SFT 10 +#define RT5682_DAC1_L_SEL_MASK (0x1 << 8) +#define RT5682_DAC1_L_SEL_SFT 8 +#define RT5682_M_ADCMIX_R (0x1 << 7) +#define RT5682_M_ADCMIX_R_SFT 7 +#define RT5682_M_DAC1_R (0x1 << 6) +#define RT5682_M_DAC1_R_SFT 6 + +/* Stereo1 DAC Mixer Control (0x002a) */ +#define RT5682_M_DAC_L1_STO_L (0x1 << 15) +#define RT5682_M_DAC_L1_STO_L_SFT 15 +#define RT5682_G_DAC_L1_STO_L_MASK (0x1 << 14) +#define RT5682_G_DAC_L1_STO_L_SFT 14 +#define RT5682_M_DAC_R1_STO_L (0x1 << 13) +#define RT5682_M_DAC_R1_STO_L_SFT 13 +#define RT5682_G_DAC_R1_STO_L_MASK (0x1 << 12) +#define RT5682_G_DAC_R1_STO_L_SFT 12 +#define RT5682_M_DAC_L1_STO_R (0x1 << 7) +#define RT5682_M_DAC_L1_STO_R_SFT 7 +#define RT5682_G_DAC_L1_STO_R_MASK (0x1 << 6) +#define RT5682_G_DAC_L1_STO_R_SFT 6 +#define RT5682_M_DAC_R1_STO_R (0x1 << 5) +#define RT5682_M_DAC_R1_STO_R_SFT 5 +#define RT5682_G_DAC_R1_STO_R_MASK (0x1 << 4) +#define RT5682_G_DAC_R1_STO_R_SFT 4 + +/* Analog DAC1 Input Source Control (0x002b) */ +#define RT5682_M_ST_STO_L (0x1 << 9) +#define RT5682_M_ST_STO_L_SFT 9 +#define RT5682_M_ST_STO_R (0x1 << 8) +#define RT5682_M_ST_STO_R_SFT 8 +#define RT5682_DAC_L1_SRC_MASK (0x3 << 4) +#define RT5682_A_DACL1_SFT 4 +#define RT5682_DAC_R1_SRC_MASK (0x3) +#define RT5682_A_DACR1_SFT 0 + +/* Digital Interface Data Control (0x0030) */ +#define RT5682_IF2_ADC_SEL_MASK (0x3 << 0) +#define RT5682_IF2_ADC_SEL_SFT 0 + +/* REC Left Mixer Control 2 (0x003c) */ +#define RT5682_G_CBJ_RM1_L (0x7 << 10) +#define RT5682_G_CBJ_RM1_L_SFT 10 +#define RT5682_M_CBJ_RM1_L (0x1 << 7) +#define RT5682_M_CBJ_RM1_L_SFT 7 + +/* Power Management for Digital 1 (0x0061) */ +#define RT5682_PWR_I2S1 (0x1 << 15) +#define RT5682_PWR_I2S1_BIT 15 +#define RT5682_PWR_I2S2 (0x1 << 14) +#define RT5682_PWR_I2S2_BIT 14 +#define RT5682_PWR_DAC_L1 (0x1 << 11) +#define RT5682_PWR_DAC_L1_BIT 11 +#define RT5682_PWR_DAC_R1 (0x1 << 10) +#define RT5682_PWR_DAC_R1_BIT 10 +#define RT5682_PWR_LDO (0x1 << 8) +#define RT5682_PWR_LDO_BIT 8 +#define RT5682_PWR_ADC_L1 (0x1 << 4) +#define RT5682_PWR_ADC_L1_BIT 4 +#define RT5682_PWR_ADC_R1 (0x1 << 3) +#define RT5682_PWR_ADC_R1_BIT 3 +#define RT5682_DIG_GATE_CTRL (0x1 << 0) +#define RT5682_DIG_GATE_CTRL_SFT 0 + + +/* Power Management for Digital 2 (0x0062) */ +#define RT5682_PWR_ADC_S1F (0x1 << 15) +#define RT5682_PWR_ADC_S1F_BIT 15 +#define RT5682_PWR_DAC_S1F (0x1 << 10) +#define RT5682_PWR_DAC_S1F_BIT 10 + +/* Power Management for Analog 1 (0x0063) */ +#define RT5682_PWR_VREF1 (0x1 << 15) +#define RT5682_PWR_VREF1_BIT 15 +#define RT5682_PWR_FV1 (0x1 << 14) +#define RT5682_PWR_FV1_BIT 14 +#define RT5682_PWR_VREF2 (0x1 << 13) +#define RT5682_PWR_VREF2_BIT 13 +#define RT5682_PWR_FV2 (0x1 << 12) +#define RT5682_PWR_FV2_BIT 12 +#define RT5682_LDO1_DBG_MASK (0x3 << 10) +#define RT5682_PWR_MB (0x1 << 9) +#define RT5682_PWR_MB_BIT 9 +#define RT5682_PWR_BG (0x1 << 7) +#define RT5682_PWR_BG_BIT 7 +#define RT5682_LDO1_BYPASS_MASK (0x1 << 6) +#define RT5682_LDO1_BYPASS (0x1 << 6) +#define RT5682_LDO1_NOT_BYPASS (0x0 << 6) +#define RT5682_PWR_MA_BIT 6 +#define RT5682_LDO1_DVO_MASK (0x3 << 4) +#define RT5682_LDO1_DVO_09 (0x0 << 4) +#define RT5682_LDO1_DVO_10 (0x1 << 4) +#define RT5682_LDO1_DVO_12 (0x2 << 4) +#define RT5682_LDO1_DVO_14 (0x3 << 4) +#define RT5682_HP_DRIVER_MASK (0x3 << 2) +#define RT5682_HP_DRIVER_1X (0x0 << 2) +#define RT5682_HP_DRIVER_3X (0x1 << 2) +#define RT5682_HP_DRIVER_5X (0x3 << 2) +#define RT5682_PWR_HA_L (0x1 << 1) +#define RT5682_PWR_HA_L_BIT 1 +#define RT5682_PWR_HA_R (0x1 << 0) +#define RT5682_PWR_HA_R_BIT 0 + +/* Power Management for Analog 2 (0x0064) */ +#define RT5682_PWR_MB1 (0x1 << 11) +#define RT5682_PWR_MB1_PWR_DOWN (0x0 << 11) +#define RT5682_PWR_MB1_BIT 11 +#define RT5682_PWR_MB2 (0x1 << 10) +#define RT5682_PWR_MB2_PWR_DOWN (0x0 << 10) +#define RT5682_PWR_MB2_BIT 10 +#define RT5682_PWR_JDH (0x1 << 3) +#define RT5682_PWR_JDH_BIT 3 +#define RT5682_PWR_JDL (0x1 << 2) +#define RT5682_PWR_JDL_BIT 2 +#define RT5682_PWR_RM1_L (0x1 << 1) +#define RT5682_PWR_RM1_L_BIT 1 + +/* Power Management for Analog 3 (0x0065) */ +#define RT5682_PWR_CBJ (0x1 << 9) +#define RT5682_PWR_CBJ_BIT 9 +#define RT5682_PWR_PLL (0x1 << 6) +#define RT5682_PWR_PLL_BIT 6 +#define RT5682_PWR_PLL2B (0x1 << 5) +#define RT5682_PWR_PLL2B_BIT 5 +#define RT5682_PWR_PLL2F (0x1 << 4) +#define RT5682_PWR_PLL2F_BIT 4 +#define RT5682_PWR_LDO2 (0x1 << 2) +#define RT5682_PWR_LDO2_BIT 2 +#define RT5682_PWR_DET_SPKVDD (0x1 << 1) +#define RT5682_PWR_DET_SPKVDD_BIT 1 + +/* Power Management for Mixer (0x0066) */ +#define RT5682_PWR_STO1_DAC_L (0x1 << 5) +#define RT5682_PWR_STO1_DAC_L_BIT 5 +#define RT5682_PWR_STO1_DAC_R (0x1 << 4) +#define RT5682_PWR_STO1_DAC_R_BIT 4 + +/* MCLK and System Clock Detection Control (0x006b) */ +#define RT5682_SYS_CLK_DET (0x1 << 15) +#define RT5682_SYS_CLK_DET_SFT 15 +#define RT5682_PLL1_CLK_DET (0x1 << 14) +#define RT5682_PLL1_CLK_DET_SFT 14 +#define RT5682_PLL2_CLK_DET (0x1 << 13) +#define RT5682_PLL2_CLK_DET_SFT 13 +#define RT5682_POW_CLK_DET2_SFT 8 +#define RT5682_POW_CLK_DET_SFT 0 + +/* Digital Microphone Control 1 (0x006e) */ +#define RT5682_DMIC_1_EN_MASK (0x1 << 15) +#define RT5682_DMIC_1_EN_SFT 15 +#define RT5682_DMIC_1_DIS (0x0 << 15) +#define RT5682_DMIC_1_EN (0x1 << 15) +#define RT5682_DMIC_1_DP_MASK (0x3 << 4) +#define RT5682_DMIC_1_DP_SFT 4 +#define RT5682_DMIC_1_DP_GPIO2 (0x0 << 4) +#define RT5682_DMIC_1_DP_GPIO5 (0x1 << 4) +#define RT5682_DMIC_CLK_MASK (0xf << 0) +#define RT5682_DMIC_CLK_SFT 0 + +/* I2S1 Audio Serial Data Port Control (0x0070) */ +#define RT5682_SEL_ADCDAT_MASK (0x1 << 15) +#define RT5682_SEL_ADCDAT_OUT (0x0 << 15) +#define RT5682_SEL_ADCDAT_IN (0x1 << 15) +#define RT5682_SEL_ADCDAT_SFT 15 +#define RT5682_I2S1_TX_CHL_MASK (0x7 << 12) +#define RT5682_I2S1_TX_CHL_SFT 12 +#define RT5682_I2S1_TX_CHL_16 (0x0 << 12) +#define RT5682_I2S1_TX_CHL_20 (0x1 << 12) +#define RT5682_I2S1_TX_CHL_24 (0x2 << 12) +#define RT5682_I2S1_TX_CHL_32 (0x3 << 12) +#define RT5682_I2S1_TX_CHL_8 (0x4 << 12) +#define RT5682_I2S1_RX_CHL_MASK (0x7 << 8) +#define RT5682_I2S1_RX_CHL_SFT 8 +#define RT5682_I2S1_RX_CHL_16 (0x0 << 8) +#define RT5682_I2S1_RX_CHL_20 (0x1 << 8) +#define RT5682_I2S1_RX_CHL_24 (0x2 << 8) +#define RT5682_I2S1_RX_CHL_32 (0x3 << 8) +#define RT5682_I2S1_RX_CHL_8 (0x4 << 8) +#define RT5682_I2S1_MONO_MASK (0x1 << 7) +#define RT5682_I2S1_MONO_EN (0x1 << 7) +#define RT5682_I2S1_MONO_DIS (0x0 << 7) +#define RT5682_I2S2_MONO_MASK (0x1 << 6) +#define RT5682_I2S2_MONO_EN (0x1 << 6) +#define RT5682_I2S2_MONO_DIS (0x0 << 6) +#define RT5682_I2S1_DL_MASK (0x7 << 4) +#define RT5682_I2S1_DL_SFT 4 +#define RT5682_I2S1_DL_16 (0x0 << 4) +#define RT5682_I2S1_DL_20 (0x1 << 4) +#define RT5682_I2S1_DL_24 (0x2 << 4) +#define RT5682_I2S1_DL_32 (0x3 << 4) +#define RT5682_I2S1_DL_8 (0x4 << 4) + +/* I2S1/2 Audio Serial Data Port Control (0x0070)(0x0071) */ +#define RT5682_I2S2_MS_MASK (0x1 << 15) +#define RT5682_I2S2_MS_SFT 15 +#define RT5682_I2S2_MS_M (0x0 << 15) +#define RT5682_I2S2_MS_S (0x1 << 15) +#define RT5682_I2S2_PIN_CFG_MASK (0x1 << 14) +#define RT5682_I2S2_PIN_CFG_SFT 14 +#define RT5682_I2S2_CLK_SEL_MASK (0x1 << 11) +#define RT5682_I2S2_CLK_SEL_SFT 11 +#define RT5682_I2S2_OUT_MASK (0x1 << 9) +#define RT5682_I2S2_OUT_SFT 9 +#define RT5682_I2S2_OUT_UM (0x0 << 9) +#define RT5682_I2S2_OUT_M (0x1 << 9) +#define RT5682_I2S_BP_MASK (0x1 << 8) +#define RT5682_I2S_BP_SFT 8 +#define RT5682_I2S_BP_NOR (0x0 << 8) +#define RT5682_I2S_BP_INV (0x1 << 8) +#define RT5682_I2S2_MONO_EN (0x1 << 6) +#define RT5682_I2S2_MONO_DIS (0x0 << 6) +#define RT5682_I2S2_DL_MASK (0x3 << 4) +#define RT5682_I2S2_DL_SFT 4 +#define RT5682_I2S2_DL_16 (0x0 << 4) +#define RT5682_I2S2_DL_20 (0x1 << 4) +#define RT5682_I2S2_DL_24 (0x2 << 4) +#define RT5682_I2S2_DL_8 (0x3 << 4) +#define RT5682_I2S_DF_MASK (0x7) +#define RT5682_I2S_DF_SFT 0 +#define RT5682_I2S_DF_I2S (0x0) +#define RT5682_I2S_DF_LEFT (0x1) +#define RT5682_I2S_DF_PCM_A (0x2) +#define RT5682_I2S_DF_PCM_B (0x3) +#define RT5682_I2S_DF_PCM_A_N (0x6) +#define RT5682_I2S_DF_PCM_B_N (0x7) + +/* ADC/DAC Clock Control 1 (0x0073) */ +#define RT5682_ADC_OSR_MASK (0xf << 12) +#define RT5682_ADC_OSR_SFT 12 +#define RT5682_ADC_OSR_D_1 (0x0 << 12) +#define RT5682_ADC_OSR_D_2 (0x1 << 12) +#define RT5682_ADC_OSR_D_4 (0x2 << 12) +#define RT5682_ADC_OSR_D_6 (0x3 << 12) +#define RT5682_ADC_OSR_D_8 (0x4 << 12) +#define RT5682_ADC_OSR_D_12 (0x5 << 12) +#define RT5682_ADC_OSR_D_16 (0x6 << 12) +#define RT5682_ADC_OSR_D_24 (0x7 << 12) +#define RT5682_ADC_OSR_D_32 (0x8 << 12) +#define RT5682_ADC_OSR_D_48 (0x9 << 12) +#define RT5682_I2S_M_DIV_MASK (0xf << 12) +#define RT5682_I2S_M_DIV_SFT 8 +#define RT5682_I2S_M_D_1 (0x0 << 8) +#define RT5682_I2S_M_D_2 (0x1 << 8) +#define RT5682_I2S_M_D_3 (0x2 << 8) +#define RT5682_I2S_M_D_4 (0x3 << 8) +#define RT5682_I2S_M_D_6 (0x4 << 8) +#define RT5682_I2S_M_D_8 (0x5 << 8) +#define RT5682_I2S_M_D_12 (0x6 << 8) +#define RT5682_I2S_M_D_16 (0x7 << 8) +#define RT5682_I2S_M_D_24 (0x8 << 8) +#define RT5682_I2S_M_D_32 (0x9 << 8) +#define RT5682_I2S_M_D_48 (0x10 << 8) +#define RT5682_I2S_CLK_SRC_MASK (0x7 << 4) +#define RT5682_I2S_CLK_SRC_SFT 4 +#define RT5682_I2S_CLK_SRC_MCLK (0x0 << 4) +#define RT5682_I2S_CLK_SRC_PLL1 (0x1 << 4) +#define RT5682_I2S_CLK_SRC_PLL2 (0x2 << 4) +#define RT5682_I2S_CLK_SRC_SDW (0x3 << 4) +#define RT5682_I2S_CLK_SRC_RCCLK (0x4 << 4) /* 25M */ +#define RT5682_DAC_OSR_MASK (0xf << 0) +#define RT5682_DAC_OSR_SFT 0 +#define RT5682_DAC_OSR_D_1 (0x0 << 0) +#define RT5682_DAC_OSR_D_2 (0x1 << 0) +#define RT5682_DAC_OSR_D_4 (0x2 << 0) +#define RT5682_DAC_OSR_D_6 (0x3 << 0) +#define RT5682_DAC_OSR_D_8 (0x4 << 0) +#define RT5682_DAC_OSR_D_12 (0x5 << 0) +#define RT5682_DAC_OSR_D_16 (0x6 << 0) +#define RT5682_DAC_OSR_D_24 (0x7 << 0) +#define RT5682_DAC_OSR_D_32 (0x8 << 0) +#define RT5682_DAC_OSR_D_48 (0x9 << 0) + +/* ADC/DAC Clock Control 2 (0x0074) */ +#define RT5682_I2S2_BCLK_MS2_MASK (0x1 << 11) +#define RT5682_I2S2_BCLK_MS2_SFT 11 +#define RT5682_I2S2_BCLK_MS2_32 (0x0 << 11) +#define RT5682_I2S2_BCLK_MS2_64 (0x1 << 11) + + +/* TDM control 1 (0x0079) */ +#define RT5682_TDM_TX_CH_MASK (0x3 << 12) +#define RT5682_TDM_TX_CH_2 (0x0 << 12) +#define RT5682_TDM_TX_CH_4 (0x1 << 12) +#define RT5682_TDM_TX_CH_6 (0x2 << 12) +#define RT5682_TDM_TX_CH_8 (0x3 << 12) +#define RT5682_TDM_RX_CH_MASK (0x3 << 8) +#define RT5682_TDM_RX_CH_2 (0x0 << 8) +#define RT5682_TDM_RX_CH_4 (0x1 << 8) +#define RT5682_TDM_RX_CH_6 (0x2 << 8) +#define RT5682_TDM_RX_CH_8 (0x3 << 8) +#define RT5682_TDM_ADC_LCA_MASK (0xf << 4) +#define RT5682_TDM_ADC_LCA_SFT 4 +#define RT5682_TDM_ADC_DL_SFT 0 + +/* TDM control 2 (0x007a) */ +#define RT5682_IF1_ADC1_SEL_SFT 14 +#define RT5682_IF1_ADC2_SEL_SFT 12 +#define RT5682_IF1_ADC3_SEL_SFT 10 +#define RT5682_IF1_ADC4_SEL_SFT 8 +#define RT5682_TDM_ADC_SEL_SFT 4 + +/* TDM control 3 (0x007b) */ +#define RT5682_TDM_EN (0x1 << 7) + +/* TDM/I2S control (0x007e) */ +#define RT5682_TDM_S_BP_MASK (0x1 << 15) +#define RT5682_TDM_S_BP_SFT 15 +#define RT5682_TDM_S_BP_NOR (0x0 << 15) +#define RT5682_TDM_S_BP_INV (0x1 << 15) +#define RT5682_TDM_S_LP_MASK (0x1 << 14) +#define RT5682_TDM_S_LP_SFT 14 +#define RT5682_TDM_S_LP_NOR (0x0 << 14) +#define RT5682_TDM_S_LP_INV (0x1 << 14) +#define RT5682_TDM_DF_MASK (0x7 << 11) +#define RT5682_TDM_DF_SFT 11 +#define RT5682_TDM_DF_I2S (0x0 << 11) +#define RT5682_TDM_DF_LEFT (0x1 << 11) +#define RT5682_TDM_DF_PCM_A (0x2 << 11) +#define RT5682_TDM_DF_PCM_B (0x3 << 11) +#define RT5682_TDM_DF_PCM_A_N (0x6 << 11) +#define RT5682_TDM_DF_PCM_B_N (0x7 << 11) +#define RT5682_TDM_CL_MASK (0x3 << 4) +#define RT5682_TDM_CL_16 (0x0 << 4) +#define RT5682_TDM_CL_20 (0x1 << 4) +#define RT5682_TDM_CL_24 (0x2 << 4) +#define RT5682_TDM_CL_32 (0x3 << 4) +#define RT5682_TDM_M_BP_MASK (0x1 << 2) +#define RT5682_TDM_M_BP_SFT 2 +#define RT5682_TDM_M_BP_NOR (0x0 << 2) +#define RT5682_TDM_M_BP_INV (0x1 << 2) +#define RT5682_TDM_M_LP_MASK (0x1 << 1) +#define RT5682_TDM_M_LP_SFT 1 +#define RT5682_TDM_M_LP_NOR (0x0 << 1) +#define RT5682_TDM_M_LP_INV (0x1 << 1) +#define RT5682_TDM_MS_MASK (0x1 << 0) +#define RT5682_TDM_MS_SFT 0 +#define RT5682_TDM_MS_M (0x0 << 0) +#define RT5682_TDM_MS_S (0x1 << 0) + +/* Global Clock Control (0x0080) */ +#define RT5682_SCLK_SRC_MASK (0x7 << 13) +#define RT5682_SCLK_SRC_SFT 13 +#define RT5682_SCLK_SRC_MCLK (0x0 << 13) +#define RT5682_SCLK_SRC_PLL1 (0x1 << 13) +#define RT5682_SCLK_SRC_PLL2 (0x2 << 13) +#define RT5682_SCLK_SRC_SDW (0x3 << 13) +#define RT5682_SCLK_SRC_RCCLK (0x4 << 13) +#define RT5682_PLL1_SRC_MASK (0x3 << 10) +#define RT5682_PLL1_SRC_SFT 10 +#define RT5682_PLL1_SRC_MCLK (0x0 << 10) +#define RT5682_PLL1_SRC_BCLK1 (0x1 << 10) +#define RT5682_PLL1_SRC_SDW (0x2 << 10) +#define RT5682_PLL1_SRC_RC (0x3 << 10) +#define RT5682_PLL2_SRC_MASK (0x3 << 8) +#define RT5682_PLL2_SRC_SFT 8 +#define RT5682_PLL2_SRC_MCLK (0x0 << 8) +#define RT5682_PLL2_SRC_BCLK1 (0x1 << 8) +#define RT5682_PLL2_SRC_SDW (0x2 << 8) +#define RT5682_PLL2_SRC_RC (0x3 << 8) + + + +#define RT5682_PLL_INP_MAX 40000000 +#define RT5682_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x0081) */ +#define RT5682_PLL_N_MAX 0x001ff +#define RT5682_PLL_N_MASK (RT5682_PLL_N_MAX << 7) +#define RT5682_PLL_N_SFT 7 +#define RT5682_PLL_K_MAX 0x001f +#define RT5682_PLL_K_MASK (RT5682_PLL_K_MAX) +#define RT5682_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x0082) */ +#define RT5682_PLL_M_MAX 0x00f +#define RT5682_PLL_M_MASK (RT5682_PLL_M_MAX << 12) +#define RT5682_PLL_M_SFT 12 +#define RT5682_PLL_M_BP (0x1 << 11) +#define RT5682_PLL_M_BP_SFT 11 +#define RT5682_PLL_K_BP (0x1 << 10) +#define RT5682_PLL_K_BP_SFT 10 +#define RT5682_PLL_RST (0x1 << 1) + +/* PLL tracking mode 1 (0x0083) */ +#define RT5682_DA_ASRC_MASK (0x1 << 13) +#define RT5682_DA_ASRC_SFT 13 +#define RT5682_DAC_STO1_ASRC_MASK (0x1 << 12) +#define RT5682_DAC_STO1_ASRC_SFT 12 +#define RT5682_AD_ASRC_MASK (0x1 << 8) +#define RT5682_AD_ASRC_SFT 8 +#define RT5682_AD_ASRC_SEL_MASK (0x1 << 4) +#define RT5682_AD_ASRC_SEL_SFT 4 +#define RT5682_DMIC_ASRC_MASK (0x1 << 3) +#define RT5682_DMIC_ASRC_SFT 3 +#define RT5682_ADC_STO1_ASRC_MASK (0x1 << 2) +#define RT5682_ADC_STO1_ASRC_SFT 2 +#define RT5682_DA_ASRC_SEL_MASK (0x1 << 0) +#define RT5682_DA_ASRC_SEL_SFT 0 + +/* PLL tracking mode 2 3 (0x0084)(0x0085)*/ +#define RT5682_FILTER_CLK_SEL_MASK (0x7 << 12) +#define RT5682_FILTER_CLK_SEL_SFT 12 +#define RT5682_FILTER_CLK_DIV_MASK (0xf << 8) +#define RT5682_FILTER_CLK_DIV_SFT 8 + +/* ASRC Control 4 (0x0086) */ +#define RT5682_ASRCIN_FTK_N1_MASK (0x3 << 14) +#define RT5682_ASRCIN_FTK_N1_SFT 14 +#define RT5682_ASRCIN_FTK_N2_MASK (0x3 << 12) +#define RT5682_ASRCIN_FTK_N2_SFT 12 +#define RT5682_ASRCIN_FTK_M1_MASK (0x7 << 8) +#define RT5682_ASRCIN_FTK_M1_SFT 8 +#define RT5682_ASRCIN_FTK_M2_MASK (0x7 << 4) +#define RT5682_ASRCIN_FTK_M2_SFT 4 + +/* SoundWire reference clk (0x008d) */ +#define RT5682_PLL2_OUT_MASK (0x1 << 8) +#define RT5682_PLL2_OUT_98M (0x0 << 8) +#define RT5682_PLL2_OUT_49M (0x1 << 8) +#define RT5682_SDW_REF_2_MASK (0xf << 4) +#define RT5682_SDW_REF_2_SFT 4 +#define RT5682_SDW_REF_2_48K (0x0 << 4) +#define RT5682_SDW_REF_2_96K (0x1 << 4) +#define RT5682_SDW_REF_2_192K (0x2 << 4) +#define RT5682_SDW_REF_2_32K (0x3 << 4) +#define RT5682_SDW_REF_2_24K (0x4 << 4) +#define RT5682_SDW_REF_2_16K (0x5 << 4) +#define RT5682_SDW_REF_2_12K (0x6 << 4) +#define RT5682_SDW_REF_2_8K (0x7 << 4) +#define RT5682_SDW_REF_2_44K (0x8 << 4) +#define RT5682_SDW_REF_2_88K (0x9 << 4) +#define RT5682_SDW_REF_2_176K (0xa << 4) +#define RT5682_SDW_REF_2_353K (0xb << 4) +#define RT5682_SDW_REF_2_22K (0xc << 4) +#define RT5682_SDW_REF_2_384K (0xd << 4) +#define RT5682_SDW_REF_2_11K (0xe << 4) +#define RT5682_SDW_REF_1_MASK (0xf << 0) +#define RT5682_SDW_REF_1_SFT 0 +#define RT5682_SDW_REF_1_48K (0x0 << 0) +#define RT5682_SDW_REF_1_96K (0x1 << 0) +#define RT5682_SDW_REF_1_192K (0x2 << 0) +#define RT5682_SDW_REF_1_32K (0x3 << 0) +#define RT5682_SDW_REF_1_24K (0x4 << 0) +#define RT5682_SDW_REF_1_16K (0x5 << 0) +#define RT5682_SDW_REF_1_12K (0x6 << 0) +#define RT5682_SDW_REF_1_8K (0x7 << 0) +#define RT5682_SDW_REF_1_44K (0x8 << 0) +#define RT5682_SDW_REF_1_88K (0x9 << 0) +#define RT5682_SDW_REF_1_176K (0xa << 0) +#define RT5682_SDW_REF_1_353K (0xb << 0) +#define RT5682_SDW_REF_1_22K (0xc << 0) +#define RT5682_SDW_REF_1_384K (0xd << 0) +#define RT5682_SDW_REF_1_11K (0xe << 0) + +/* Depop Mode Control 1 (0x008e) */ +#define RT5682_PUMP_EN (0x1 << 3) +#define RT5682_PUMP_EN_SFT 3 +#define RT5682_CAPLESS_EN (0x1 << 0) +#define RT5682_CAPLESS_EN_SFT 0 + +/* Depop Mode Control 2 (0x8f) */ +#define RT5682_RAMP_MASK (0x1 << 12) +#define RT5682_RAMP_SFT 12 +#define RT5682_RAMP_DIS (0x0 << 12) +#define RT5682_RAMP_EN (0x1 << 12) +#define RT5682_BPS_MASK (0x1 << 11) +#define RT5682_BPS_SFT 11 +#define RT5682_BPS_DIS (0x0 << 11) +#define RT5682_BPS_EN (0x1 << 11) +#define RT5682_FAST_UPDN_MASK (0x1 << 10) +#define RT5682_FAST_UPDN_SFT 10 +#define RT5682_FAST_UPDN_DIS (0x0 << 10) +#define RT5682_FAST_UPDN_EN (0x1 << 10) +#define RT5682_VLO_MASK (0x1 << 7) +#define RT5682_VLO_SFT 7 +#define RT5682_VLO_3V (0x0 << 7) +#define RT5682_VLO_33V (0x1 << 7) + +/* HPOUT charge pump 1 (0x0091) */ +#define RT5682_OSW_L_MASK (0x1 << 11) +#define RT5682_OSW_L_SFT 11 +#define RT5682_OSW_L_DIS (0x0 << 11) +#define RT5682_OSW_L_EN (0x1 << 11) +#define RT5682_OSW_R_MASK (0x1 << 10) +#define RT5682_OSW_R_SFT 10 +#define RT5682_OSW_R_DIS (0x0 << 10) +#define RT5682_OSW_R_EN (0x1 << 10) +#define RT5682_PM_HP_MASK (0x3 << 8) +#define RT5682_PM_HP_SFT 8 +#define RT5682_PM_HP_LV (0x0 << 8) +#define RT5682_PM_HP_MV (0x1 << 8) +#define RT5682_PM_HP_HV (0x2 << 8) +#define RT5682_IB_HP_MASK (0x3 << 6) +#define RT5682_IB_HP_SFT 6 +#define RT5682_IB_HP_125IL (0x0 << 6) +#define RT5682_IB_HP_25IL (0x1 << 6) +#define RT5682_IB_HP_5IL (0x2 << 6) +#define RT5682_IB_HP_1IL (0x3 << 6) + +/* Micbias Control1 (0x93) */ +#define RT5682_MIC1_OV_MASK (0x3 << 14) +#define RT5682_MIC1_OV_SFT 14 +#define RT5682_MIC1_OV_2V7 (0x0 << 14) +#define RT5682_MIC1_OV_2V4 (0x1 << 14) +#define RT5682_MIC1_OV_2V25 (0x3 << 14) +#define RT5682_MIC1_OV_1V8 (0x4 << 14) +#define RT5682_MIC1_CLK_MASK (0x1 << 13) +#define RT5682_MIC1_CLK_SFT 13 +#define RT5682_MIC1_CLK_DIS (0x0 << 13) +#define RT5682_MIC1_CLK_EN (0x1 << 13) +#define RT5682_MIC1_OVCD_MASK (0x1 << 12) +#define RT5682_MIC1_OVCD_SFT 12 +#define RT5682_MIC1_OVCD_DIS (0x0 << 12) +#define RT5682_MIC1_OVCD_EN (0x1 << 12) +#define RT5682_MIC1_OVTH_MASK (0x3 << 10) +#define RT5682_MIC1_OVTH_SFT 10 +#define RT5682_MIC1_OVTH_768UA (0x0 << 10) +#define RT5682_MIC1_OVTH_960UA (0x1 << 10) +#define RT5682_MIC1_OVTH_1152UA (0x2 << 10) +#define RT5682_MIC1_OVTH_1960UA (0x3 << 10) +#define RT5682_MIC2_OV_MASK (0x3 << 8) +#define RT5682_MIC2_OV_SFT 8 +#define RT5682_MIC2_OV_2V7 (0x0 << 8) +#define RT5682_MIC2_OV_2V4 (0x1 << 8) +#define RT5682_MIC2_OV_2V25 (0x3 << 8) +#define RT5682_MIC2_OV_1V8 (0x4 << 8) +#define RT5682_MIC2_CLK_MASK (0x1 << 7) +#define RT5682_MIC2_CLK_SFT 7 +#define RT5682_MIC2_CLK_DIS (0x0 << 7) +#define RT5682_MIC2_CLK_EN (0x1 << 7) +#define RT5682_MIC2_OVTH_MASK (0x3 << 4) +#define RT5682_MIC2_OVTH_SFT 4 +#define RT5682_MIC2_OVTH_768UA (0x0 << 4) +#define RT5682_MIC2_OVTH_960UA (0x1 << 4) +#define RT5682_MIC2_OVTH_1152UA (0x2 << 4) +#define RT5682_MIC2_OVTH_1960UA (0x3 << 4) +#define RT5682_PWR_MB_MASK (0x1 << 3) +#define RT5682_PWR_MB_SFT 3 +#define RT5682_PWR_MB_PD (0x0 << 3) +#define RT5682_PWR_MB_PU (0x1 << 3) + +/* Micbias Control2 (0x0094) */ +#define RT5682_PWR_CLK25M_MASK (0x1 << 9) +#define RT5682_PWR_CLK25M_SFT 9 +#define RT5682_PWR_CLK25M_PD (0x0 << 9) +#define RT5682_PWR_CLK25M_PU (0x1 << 9) +#define RT5682_PWR_CLK1M_MASK (0x1 << 8) +#define RT5682_PWR_CLK1M_SFT 8 +#define RT5682_PWR_CLK1M_PD (0x0 << 8) +#define RT5682_PWR_CLK1M_PU (0x1 << 8) + +/* RC Clock Control (0x009f) */ +#define RT5682_POW_IRQ (0x1 << 15) +#define RT5682_POW_JDH (0x1 << 14) +#define RT5682_POW_JDL (0x1 << 13) +#define RT5682_POW_ANA (0x1 << 12) + +/* I2S Master Mode Clock Control 1 (0x00a0) */ +#define RT5682_CLK_SRC_MCLK (0x0) +#define RT5682_CLK_SRC_PLL1 (0x1) +#define RT5682_CLK_SRC_PLL2 (0x2) +#define RT5682_CLK_SRC_SDW (0x3) +#define RT5682_CLK_SRC_RCCLK (0x4) +#define RT5682_I2S_PD_1 (0x0) +#define RT5682_I2S_PD_2 (0x1) +#define RT5682_I2S_PD_3 (0x2) +#define RT5682_I2S_PD_4 (0x3) +#define RT5682_I2S_PD_6 (0x4) +#define RT5682_I2S_PD_8 (0x5) +#define RT5682_I2S_PD_12 (0x6) +#define RT5682_I2S_PD_16 (0x7) +#define RT5682_I2S_PD_24 (0x8) +#define RT5682_I2S_PD_32 (0x9) +#define RT5682_I2S_PD_48 (0xa) +#define RT5682_I2S2_SRC_MASK (0x3 << 4) +#define RT5682_I2S2_SRC_SFT 4 +#define RT5682_I2S2_M_PD_MASK (0xf << 0) +#define RT5682_I2S2_M_PD_SFT 0 + +/* IRQ Control 1 (0x00b6) */ +#define RT5682_JD1_PULSE_EN_MASK (0x1 << 10) +#define RT5682_JD1_PULSE_EN_SFT 10 +#define RT5682_JD1_PULSE_DIS (0x0 << 10) +#define RT5682_JD1_PULSE_EN (0x1 << 10) + +/* IRQ Control 2 (0x00b7) */ +#define RT5682_JD1_EN_MASK (0x1 << 15) +#define RT5682_JD1_EN_SFT 15 +#define RT5682_JD1_DIS (0x0 << 15) +#define RT5682_JD1_EN (0x1 << 15) +#define RT5682_JD1_POL_MASK (0x1 << 13) +#define RT5682_JD1_POL_NOR (0x0 << 13) +#define RT5682_JD1_POL_INV (0x1 << 13) + +/* IRQ Control 3 (0x00b8) */ +#define RT5682_IL_IRQ_MASK (0x1 << 7) +#define RT5682_IL_IRQ_DIS (0x0 << 7) +#define RT5682_IL_IRQ_EN (0x1 << 7) + +/* GPIO Control 1 (0x00c0) */ +#define RT5682_GP1_PIN_MASK (0x3 << 14) +#define RT5682_GP1_PIN_SFT 14 +#define RT5682_GP1_PIN_GPIO1 (0x0 << 14) +#define RT5682_GP1_PIN_IRQ (0x1 << 14) +#define RT5682_GP1_PIN_DMIC_CLK (0x2 << 14) +#define RT5682_GP2_PIN_MASK (0x3 << 12) +#define RT5682_GP2_PIN_SFT 12 +#define RT5682_GP2_PIN_GPIO2 (0x0 << 12) +#define RT5682_GP2_PIN_LRCK2 (0x1 << 12) +#define RT5682_GP2_PIN_DMIC_SDA (0x2 << 12) +#define RT5682_GP3_PIN_MASK (0x3 << 10) +#define RT5682_GP3_PIN_SFT 10 +#define RT5682_GP3_PIN_GPIO3 (0x0 << 10) +#define RT5682_GP3_PIN_BCLK2 (0x1 << 10) +#define RT5682_GP3_PIN_DMIC_CLK (0x2 << 10) +#define RT5682_GP4_PIN_MASK (0x3 << 8) +#define RT5682_GP4_PIN_SFT 8 +#define RT5682_GP4_PIN_GPIO4 (0x0 << 8) +#define RT5682_GP4_PIN_ADCDAT1 (0x1 << 8) +#define RT5682_GP4_PIN_DMIC_CLK (0x2 << 8) +#define RT5682_GP4_PIN_ADCDAT2 (0x3 << 8) +#define RT5682_GP5_PIN_MASK (0x3 << 6) +#define RT5682_GP5_PIN_SFT 6 +#define RT5682_GP5_PIN_GPIO5 (0x0 << 6) +#define RT5682_GP5_PIN_DACDAT1 (0x1 << 6) +#define RT5682_GP5_PIN_DMIC_SDA (0x2 << 6) +#define RT5682_GP6_PIN_MASK (0x1 << 5) +#define RT5682_GP6_PIN_SFT 5 +#define RT5682_GP6_PIN_GPIO6 (0x0 << 5) +#define RT5682_GP6_PIN_LRCK1 (0x1 << 5) + +/* GPIO Control 2 (0x00c1)*/ +#define RT5682_GP1_PF_MASK (0x1 << 15) +#define RT5682_GP1_PF_IN (0x0 << 15) +#define RT5682_GP1_PF_OUT (0x1 << 15) +#define RT5682_GP1_OUT_MASK (0x1 << 14) +#define RT5682_GP1_OUT_L (0x0 << 14) +#define RT5682_GP1_OUT_H (0x1 << 14) +#define RT5682_GP2_PF_MASK (0x1 << 13) +#define RT5682_GP2_PF_IN (0x0 << 13) +#define RT5682_GP2_PF_OUT (0x1 << 13) +#define RT5682_GP2_OUT_MASK (0x1 << 12) +#define RT5682_GP2_OUT_L (0x0 << 12) +#define RT5682_GP2_OUT_H (0x1 << 12) +#define RT5682_GP3_PF_MASK (0x1 << 11) +#define RT5682_GP3_PF_IN (0x0 << 11) +#define RT5682_GP3_PF_OUT (0x1 << 11) +#define RT5682_GP3_OUT_MASK (0x1 << 10) +#define RT5682_GP3_OUT_L (0x0 << 10) +#define RT5682_GP3_OUT_H (0x1 << 10) +#define RT5682_GP4_PF_MASK (0x1 << 9) +#define RT5682_GP4_PF_IN (0x0 << 9) +#define RT5682_GP4_PF_OUT (0x1 << 9) +#define RT5682_GP4_OUT_MASK (0x1 << 8) +#define RT5682_GP4_OUT_L (0x0 << 8) +#define RT5682_GP4_OUT_H (0x1 << 8) +#define RT5682_GP5_PF_MASK (0x1 << 7) +#define RT5682_GP5_PF_IN (0x0 << 7) +#define RT5682_GP5_PF_OUT (0x1 << 7) +#define RT5682_GP5_OUT_MASK (0x1 << 6) +#define RT5682_GP5_OUT_L (0x0 << 6) +#define RT5682_GP5_OUT_H (0x1 << 6) +#define RT5682_GP6_PF_MASK (0x1 << 5) +#define RT5682_GP6_PF_IN (0x0 << 5) +#define RT5682_GP6_PF_OUT (0x1 << 5) +#define RT5682_GP6_OUT_MASK (0x1 << 4) +#define RT5682_GP6_OUT_L (0x0 << 4) +#define RT5682_GP6_OUT_H (0x1 << 4) + + +/* GPIO Status (0x00c2) */ +#define RT5682_GP6_STA (0x1 << 6) +#define RT5682_GP5_STA (0x1 << 5) +#define RT5682_GP4_STA (0x1 << 4) +#define RT5682_GP3_STA (0x1 << 3) +#define RT5682_GP2_STA (0x1 << 2) +#define RT5682_GP1_STA (0x1 << 1) + +/* Soft volume and zero cross control 1 (0x00d9) */ +#define RT5682_SV_MASK (0x1 << 15) +#define RT5682_SV_SFT 15 +#define RT5682_SV_DIS (0x0 << 15) +#define RT5682_SV_EN (0x1 << 15) +#define RT5682_ZCD_MASK (0x1 << 10) +#define RT5682_ZCD_SFT 10 +#define RT5682_ZCD_PD (0x0 << 10) +#define RT5682_ZCD_PU (0x1 << 10) +#define RT5682_SV_DLY_MASK (0xf) +#define RT5682_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0x00da) */ +#define RT5682_ZCD_BST1_CBJ_MASK (0x1 << 7) +#define RT5682_ZCD_BST1_CBJ_SFT 7 +#define RT5682_ZCD_BST1_CBJ_DIS (0x0 << 7) +#define RT5682_ZCD_BST1_CBJ_EN (0x1 << 7) +#define RT5682_ZCD_RECMIX_MASK (0x1) +#define RT5682_ZCD_RECMIX_SFT 0 +#define RT5682_ZCD_RECMIX_DIS (0x0) +#define RT5682_ZCD_RECMIX_EN (0x1) + +/* 4 Button Inline Command Control 2 (0x00e3) */ +#define RT5682_4BTN_IL_MASK (0x1 << 15) +#define RT5682_4BTN_IL_EN (0x1 << 15) +#define RT5682_4BTN_IL_DIS (0x0 << 15) +#define RT5682_4BTN_IL_RST_MASK (0x1 << 14) +#define RT5682_4BTN_IL_NOR (0x1 << 14) +#define RT5682_4BTN_IL_RST (0x0 << 14) + +/* Analog JD Control (0x00f0) */ +#define RT5682_JDH_RS_MASK (0x1 << 4) +#define RT5682_JDH_NO_PLUG (0x1 << 4) +#define RT5682_JDH_PLUG (0x0 << 4) + +/* Chopper and Clock control for DAC (0x013a)*/ +#define RT5682_CKXEN_DAC1_MASK (0x1 << 13) +#define RT5682_CKXEN_DAC1_SFT 13 +#define RT5682_CKGEN_DAC1_MASK (0x1 << 12) +#define RT5682_CKGEN_DAC1_SFT 12 + +/* Chopper and Clock control for ADC (0x013b)*/ +#define RT5682_CKXEN_ADC1_MASK (0x1 << 13) +#define RT5682_CKXEN_ADC1_SFT 13 +#define RT5682_CKGEN_ADC1_MASK (0x1 << 12) +#define RT5682_CKGEN_ADC1_SFT 12 + +/* Volume test (0x013f)*/ +#define RT5682_SEL_CLK_VOL_MASK (0x1 << 15) +#define RT5682_SEL_CLK_VOL_EN (0x1 << 15) +#define RT5682_SEL_CLK_VOL_DIS (0x0 << 15) + +/* Test Mode Control 1 (0x0145) */ +#define RT5682_AD2DA_LB_MASK (0x1 << 10) +#define RT5682_AD2DA_LB_SFT 10 + +/* Stereo Noise Gate Control 1 (0x0160) */ +#define RT5682_NG2_EN_MASK (0x1 << 15) +#define RT5682_NG2_EN (0x1 << 15) +#define RT5682_NG2_DIS (0x0 << 15) + +/* Stereo1 DAC Silence Detection Control (0x0190) */ +#define RT5682_DEB_STO_DAC_MASK (0x7 << 4) +#define RT5682_DEB_80_MS (0x0 << 4) + +/* SAR ADC Inline Command Control 1 (0x0210) */ +#define RT5682_SAR_BUTT_DET_MASK (0x1 << 15) +#define RT5682_SAR_BUTT_DET_EN (0x1 << 15) +#define RT5682_SAR_BUTT_DET_DIS (0x0 << 15) +#define RT5682_SAR_BUTDET_MODE_MASK (0x1 << 14) +#define RT5682_SAR_BUTDET_POW_SAV (0x1 << 14) +#define RT5682_SAR_BUTDET_POW_NORM (0x0 << 14) +#define RT5682_SAR_BUTDET_RST_MASK (0x1 << 13) +#define RT5682_SAR_BUTDET_RST_NORMAL (0x1 << 13) +#define RT5682_SAR_BUTDET_RST (0x0 << 13) +#define RT5682_SAR_POW_MASK (0x1 << 12) +#define RT5682_SAR_POW_EN (0x1 << 12) +#define RT5682_SAR_POW_DIS (0x0 << 12) +#define RT5682_SAR_RST_MASK (0x1 << 11) +#define RT5682_SAR_RST_NORMAL (0x1 << 11) +#define RT5682_SAR_RST (0x0 << 11) +#define RT5682_SAR_BYPASS_MASK (0x1 << 10) +#define RT5682_SAR_BYPASS_EN (0x1 << 10) +#define RT5682_SAR_BYPASS_DIS (0x0 << 10) +#define RT5682_SAR_SEL_MB1_MASK (0x1 << 9) +#define RT5682_SAR_SEL_MB1_SEL (0x1 << 9) +#define RT5682_SAR_SEL_MB1_NOSEL (0x0 << 9) +#define RT5682_SAR_SEL_MB2_MASK (0x1 << 8) +#define RT5682_SAR_SEL_MB2_SEL (0x1 << 8) +#define RT5682_SAR_SEL_MB2_NOSEL (0x0 << 8) +#define RT5682_SAR_SEL_MODE_MASK (0x1 << 7) +#define RT5682_SAR_SEL_MODE_CMP (0x1 << 7) +#define RT5682_SAR_SEL_MODE_ADC (0x0 << 7) +#define RT5682_SAR_SEL_MB1_MB2_MASK (0x1 << 5) +#define RT5682_SAR_SEL_MB1_MB2_AUTO (0x1 << 5) +#define RT5682_SAR_SEL_MB1_MB2_MANU (0x0 << 5) +#define RT5682_SAR_SEL_SIGNAL_MASK (0x1 << 4) +#define RT5682_SAR_SEL_SIGNAL_AUTO (0x1 << 4) +#define RT5682_SAR_SEL_SIGNAL_MANU (0x0 << 4) + +/* SAR ADC Inline Command Control 13 (0x021c) */ +#define RT5682_SAR_SOUR_MASK (0x3f) +#define RT5682_SAR_SOUR_BTN (0x3f) +#define RT5682_SAR_SOUR_TYPE (0x0) + + +/* System Clock Source */ +enum { + RT5682_SCLK_S_MCLK, + RT5682_SCLK_S_PLL1, + RT5682_SCLK_S_PLL2, + RT5682_SCLK_S_RCCLK, +}; + +/* PLL Source */ +enum { + RT5682_PLL1_S_MCLK, + RT5682_PLL1_S_BCLK1, + RT5682_PLL1_S_RCCLK, +}; + +enum { + RT5682_AIF1, + RT5682_AIF2, + RT5682_AIFS +}; + +/* filter mask */ +enum { + RT5682_DA_STEREO1_FILTER = 0x1, + RT5682_AD_STEREO1_FILTER = (0x1 << 1), +}; + +enum { + RT5682_CLK_SEL_SYS, + RT5682_CLK_SEL_I2S1_ASRC, + RT5682_CLK_SEL_I2S2_ASRC, +}; + +int rt5682_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src); + +#endif /* __RT5682_H__ */ diff --git a/sound/soc/codecs/dio2125.c b/sound/soc/codecs/simple-amplifier.c index 09451cd44f9b..85524acf3e9c 100644 --- a/sound/soc/codecs/dio2125.c +++ b/sound/soc/codecs/simple-amplifier.c @@ -21,9 +21,9 @@ #include <linux/module.h> #include <sound/soc.h> -#define DRV_NAME "dio2125" +#define DRV_NAME "simple-amplifier" -struct dio2125 { +struct simple_amp { struct gpio_desc *gpiod_enable; }; @@ -31,7 +31,7 @@ static int drv_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *control, int event) { struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); - struct dio2125 *priv = snd_soc_component_get_drvdata(c); + struct simple_amp *priv = snd_soc_component_get_drvdata(c); int val; switch (event) { @@ -51,7 +51,7 @@ static int drv_event(struct snd_soc_dapm_widget *w, return 0; } -static const struct snd_soc_dapm_widget dio2125_dapm_widgets[] = { +static const struct snd_soc_dapm_widget simple_amp_dapm_widgets[] = { SND_SOC_DAPM_INPUT("INL"), SND_SOC_DAPM_INPUT("INR"), SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, drv_event, @@ -60,24 +60,24 @@ static const struct snd_soc_dapm_widget dio2125_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("OUTR"), }; -static const struct snd_soc_dapm_route dio2125_dapm_routes[] = { +static const struct snd_soc_dapm_route simple_amp_dapm_routes[] = { { "DRV", NULL, "INL" }, { "DRV", NULL, "INR" }, { "OUTL", NULL, "DRV" }, { "OUTR", NULL, "DRV" }, }; -static const struct snd_soc_component_driver dio2125_component_driver = { - .dapm_widgets = dio2125_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(dio2125_dapm_widgets), - .dapm_routes = dio2125_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(dio2125_dapm_routes), +static const struct snd_soc_component_driver simple_amp_component_driver = { + .dapm_widgets = simple_amp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(simple_amp_dapm_widgets), + .dapm_routes = simple_amp_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(simple_amp_dapm_routes), }; -static int dio2125_probe(struct platform_device *pdev) +static int simple_amp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct dio2125 *priv; + struct simple_amp *priv; int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -93,28 +93,30 @@ static int dio2125_probe(struct platform_device *pdev) return err; } - return devm_snd_soc_register_component(dev, &dio2125_component_driver, + return devm_snd_soc_register_component(dev, + &simple_amp_component_driver, NULL, 0); } #ifdef CONFIG_OF -static const struct of_device_id dio2125_ids[] = { +static const struct of_device_id simple_amp_ids[] = { { .compatible = "dioo,dio2125", }, + { .compatible = "simple-audio-amplifier", }, { } }; -MODULE_DEVICE_TABLE(of, dio2125_ids); +MODULE_DEVICE_TABLE(of, simple_amp_ids); #endif -static struct platform_driver dio2125_driver = { +static struct platform_driver simple_amp_driver = { .driver = { .name = DRV_NAME, - .of_match_table = of_match_ptr(dio2125_ids), + .of_match_table = of_match_ptr(simple_amp_ids), }, - .probe = dio2125_probe, + .probe = simple_amp_probe, }; -module_platform_driver(dio2125_driver); +module_platform_driver(simple_amp_driver); -MODULE_DESCRIPTION("ASoC DIO2125 output driver"); +MODULE_DESCRIPTION("ASoC Simple Audio Amplifier driver"); MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index 52f34c94ec25..ca2dfe12344e 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -7,6 +7,9 @@ * TAS5721 support: * Copyright (C) 2016 Petr Kulhavy, Barix AG <petr@barix.com> * + * TAS5707 support: + * Copyright (C) 2018 Jerome Brunet, Baylibre SAS <jbrunet@baylibre.com> + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -444,6 +447,111 @@ static const struct tas571x_chip tas5711_chip = { .vol_reg_size = 1, }; +static const struct regmap_range tas5707_volatile_regs_range[] = { + regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG), + regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG), + regmap_reg_range(TAS5707_CH1_BQ0_REG, TAS5707_CH2_BQ6_REG), +}; + +static const struct regmap_access_table tas5707_volatile_regs = { + .yes_ranges = tas5707_volatile_regs_range, + .n_yes_ranges = ARRAY_SIZE(tas5707_volatile_regs_range), + +}; + +static const DECLARE_TLV_DB_SCALE(tas5707_volume_tlv, -7900, 50, 1); + +static const char * const tas5707_volume_slew_step_txt[] = { + "256", "512", "1024", "2048", +}; + +static const unsigned int tas5707_volume_slew_step_values[] = { + 3, 0, 1, 2, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(tas5707_volume_slew_step_enum, + TAS571X_VOL_CFG_REG, 0, 0x3, + tas5707_volume_slew_step_txt, + tas5707_volume_slew_step_values); + +static const struct snd_kcontrol_new tas5707_controls[] = { + SOC_SINGLE_TLV("Master Volume", + TAS571X_MVOL_REG, + 0, 0xff, 1, tas5707_volume_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", + TAS571X_CH1_VOL_REG, + TAS571X_CH2_VOL_REG, + 0, 0xff, 1, tas5707_volume_tlv), + SOC_DOUBLE("Speaker Switch", + TAS571X_SOFT_MUTE_REG, + TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, + 1, 1), + + SOC_ENUM("Slew Rate Steps", tas5707_volume_slew_step_enum), + + BIQUAD_COEFS("CH1 - Biquad 0", TAS5707_CH1_BQ0_REG), + BIQUAD_COEFS("CH1 - Biquad 1", TAS5707_CH1_BQ1_REG), + BIQUAD_COEFS("CH1 - Biquad 2", TAS5707_CH1_BQ2_REG), + BIQUAD_COEFS("CH1 - Biquad 3", TAS5707_CH1_BQ3_REG), + BIQUAD_COEFS("CH1 - Biquad 4", TAS5707_CH1_BQ4_REG), + BIQUAD_COEFS("CH1 - Biquad 5", TAS5707_CH1_BQ5_REG), + BIQUAD_COEFS("CH1 - Biquad 6", TAS5707_CH1_BQ6_REG), + + BIQUAD_COEFS("CH2 - Biquad 0", TAS5707_CH2_BQ0_REG), + BIQUAD_COEFS("CH2 - Biquad 1", TAS5707_CH2_BQ1_REG), + BIQUAD_COEFS("CH2 - Biquad 2", TAS5707_CH2_BQ2_REG), + BIQUAD_COEFS("CH2 - Biquad 3", TAS5707_CH2_BQ3_REG), + BIQUAD_COEFS("CH2 - Biquad 4", TAS5707_CH2_BQ4_REG), + BIQUAD_COEFS("CH2 - Biquad 5", TAS5707_CH2_BQ5_REG), + BIQUAD_COEFS("CH2 - Biquad 6", TAS5707_CH2_BQ6_REG), +}; + +static const struct reg_default tas5707_reg_defaults[] = { + {TAS571X_CLK_CTRL_REG, 0x6c}, + {TAS571X_DEV_ID_REG, 0x70}, + {TAS571X_ERR_STATUS_REG, 0x00}, + {TAS571X_SYS_CTRL_1_REG, 0xa0}, + {TAS571X_SDI_REG, 0x05}, + {TAS571X_SYS_CTRL_2_REG, 0x40}, + {TAS571X_SOFT_MUTE_REG, 0x00}, + {TAS571X_MVOL_REG, 0xff}, + {TAS571X_CH1_VOL_REG, 0x30}, + {TAS571X_CH2_VOL_REG, 0x30}, + {TAS571X_VOL_CFG_REG, 0x91}, + {TAS571X_MODULATION_LIMIT_REG, 0x02}, + {TAS571X_IC_DELAY_CH1_REG, 0xac}, + {TAS571X_IC_DELAY_CH2_REG, 0x54}, + {TAS571X_IC_DELAY_CH3_REG, 0xac}, + {TAS571X_IC_DELAY_CH4_REG, 0x54}, + {TAS571X_START_STOP_PERIOD_REG, 0x0f}, + {TAS571X_OSC_TRIM_REG, 0x82}, + {TAS571X_BKND_ERR_REG, 0x02}, + {TAS571X_INPUT_MUX_REG, 0x17772}, + {TAS571X_PWM_MUX_REG, 0x1021345}, +}; + +static const struct regmap_config tas5707_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5707_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5707_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .wr_table = &tas571x_write_regs, + .volatile_table = &tas5707_volatile_regs, +}; + +static const struct tas571x_chip tas5707_chip = { + .supply_names = tas5711_supply_names, + .num_supply_names = ARRAY_SIZE(tas5711_supply_names), + .controls = tas5707_controls, + .num_controls = ARRAY_SIZE(tas5707_controls), + .regmap_config = &tas5707_regmap_config, + .vol_reg_size = 1, +}; + static const char *const tas5717_supply_names[] = { "AVDD", "DVDD", @@ -775,6 +883,7 @@ static int tas571x_i2c_remove(struct i2c_client *client) } static const struct of_device_id tas571x_of_match[] = { + { .compatible = "ti,tas5707", .data = &tas5707_chip, }, { .compatible = "ti,tas5711", .data = &tas5711_chip, }, { .compatible = "ti,tas5717", .data = &tas5717_chip, }, { .compatible = "ti,tas5719", .data = &tas5717_chip, }, @@ -784,6 +893,7 @@ static const struct of_device_id tas571x_of_match[] = { MODULE_DEVICE_TABLE(of, tas571x_of_match); static const struct i2c_device_id tas571x_i2c_id[] = { + { "tas5707", (kernel_ulong_t) &tas5707_chip }, { "tas5711", (kernel_ulong_t) &tas5711_chip }, { "tas5717", (kernel_ulong_t) &tas5717_chip }, { "tas5719", (kernel_ulong_t) &tas5717_chip }, diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h index c45677bc26ad..bd23e89cfe79 100644 --- a/sound/soc/codecs/tas571x.h +++ b/sound/soc/codecs/tas571x.h @@ -53,6 +53,22 @@ #define TAS571X_PWM_MUX_REG 0x25 /* 20-byte biquad registers */ +#define TAS5707_CH1_BQ0_REG 0x29 +#define TAS5707_CH1_BQ1_REG 0x2a +#define TAS5707_CH1_BQ2_REG 0x2b +#define TAS5707_CH1_BQ3_REG 0x2c +#define TAS5707_CH1_BQ4_REG 0x2d +#define TAS5707_CH1_BQ5_REG 0x2e +#define TAS5707_CH1_BQ6_REG 0x2f + +#define TAS5707_CH2_BQ0_REG 0x30 +#define TAS5707_CH2_BQ1_REG 0x31 +#define TAS5707_CH2_BQ2_REG 0x32 +#define TAS5707_CH2_BQ3_REG 0x33 +#define TAS5707_CH2_BQ4_REG 0x34 +#define TAS5707_CH2_BQ5_REG 0x35 +#define TAS5707_CH2_BQ6_REG 0x36 + #define TAS5717_CH1_BQ0_REG 0x26 #define TAS5717_CH1_BQ1_REG 0x27 #define TAS5717_CH1_BQ2_REG 0x28 diff --git a/sound/soc/codecs/tda7419.c b/sound/soc/codecs/tda7419.c index 225c210ac38f..7f3b79c5a563 100644 --- a/sound/soc/codecs/tda7419.c +++ b/sound/soc/codecs/tda7419.c @@ -142,9 +142,9 @@ struct tda7419_vol_control { static inline bool tda7419_vol_is_stereo(struct tda7419_vol_control *tvc) { if (tvc->reg == tvc->rreg) - return 0; + return false; - return 1; + return true; } static int tda7419_vol_info(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c index d18ff17719cc..7396a6e5277e 100644 --- a/sound/soc/codecs/tscs42xx.c +++ b/sound/soc/codecs/tscs42xx.c @@ -625,25 +625,34 @@ static int bytes_info_ext(struct snd_kcontrol *kcontrol, static const struct snd_kcontrol_new tscs42xx_snd_controls[] = { /* Volumes */ - SOC_DOUBLE_R_TLV("Headphone Playback Volume", R_HPVOLL, R_HPVOLR, + SOC_DOUBLE_R_TLV("Headphone Volume", R_HPVOLL, R_HPVOLR, FB_HPVOLL, 0x7F, 0, hpvol_scale), - SOC_DOUBLE_R_TLV("Speaker Playback Volume", R_SPKVOLL, R_SPKVOLR, + SOC_DOUBLE_R_TLV("Speaker Volume", R_SPKVOLL, R_SPKVOLR, FB_SPKVOLL, 0x7F, 0, spkvol_scale), - SOC_DOUBLE_R_TLV("Master Playback Volume", R_DACVOLL, R_DACVOLR, + SOC_DOUBLE_R_TLV("Master Volume", R_DACVOLL, R_DACVOLR, FB_DACVOLL, 0xFF, 0, dacvol_scale), - SOC_DOUBLE_R_TLV("PCM Capture Volume", R_ADCVOLL, R_ADCVOLR, + SOC_DOUBLE_R_TLV("PCM Volume", R_ADCVOLL, R_ADCVOLR, FB_ADCVOLL, 0xFF, 0, adcvol_scale), - SOC_DOUBLE_R_TLV("Master Capture Volume", R_INVOLL, R_INVOLR, + SOC_DOUBLE_R_TLV("Input Volume", R_INVOLL, R_INVOLR, FB_INVOLL, 0x3F, 0, invol_scale), /* INSEL */ - SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", R_INSELL, R_INSELR, + SOC_DOUBLE_R_TLV("Mic Boost Volume", R_INSELL, R_INSELR, FB_INSELL_MICBSTL, FV_INSELL_MICBSTL_30DB, 0, mic_boost_scale), /* Input Channel Map */ SOC_ENUM("Input Channel Map", ch_map_select_enum), + /* Mic Bias */ + SOC_SINGLE("Mic Bias Boost Switch", 0x71, 0x07, 1, 0), + + /* Headphone Auto Switching */ + SOC_SINGLE("Headphone Auto Switching Switch", + R_CTL, FB_CTL_HPSWEN, 1, 0), + SOC_SINGLE("Headphone Detect Polarity Toggle Switch", + R_CTL, FB_CTL_HPSWPOL, 1, 0), + /* Coefficient Ram */ COEFF_RAM_CTL("Cascade1L BiQuad1", BIQUAD_SIZE, 0x00), COEFF_RAM_CTL("Cascade1L BiQuad2", BIQUAD_SIZE, 0x05), @@ -733,9 +742,9 @@ static const struct snd_kcontrol_new tscs42xx_snd_controls[] = { R_CLECTL, FB_CLECTL_LIMIT_EN, 1, 0), SOC_SINGLE("Comp Switch", R_CLECTL, FB_CLECTL_COMP_EN, 1, 0), - SOC_SINGLE_TLV("CLE Make-Up Gain Playback Volume", + SOC_SINGLE_TLV("CLE Make-Up Gain Volume", R_MUGAIN, FB_MUGAIN_CLEMUG, 0x1f, 0, mugain_scale), - SOC_SINGLE_TLV("Comp Thresh Playback Volume", + SOC_SINGLE_TLV("Comp Thresh Volume", R_COMPTH, FB_COMPTH, 0xff, 0, compth_scale), SOC_ENUM("Comp Ratio", compressor_ratio_enum), SND_SOC_BYTES("Comp Atk Time", R_CATKTCL, 2), @@ -766,9 +775,9 @@ static const struct snd_kcontrol_new tscs42xx_snd_controls[] = { SOC_SINGLE("MBC1 Phase Invert Switch", R_DACMBCMUG1, FB_DACMBCMUG1_PHASE, 1, 0), - SOC_SINGLE_TLV("DAC MBC1 Make-Up Gain Playback Volume", + SOC_SINGLE_TLV("DAC MBC1 Make-Up Gain Volume", R_DACMBCMUG1, FB_DACMBCMUG1_MUGAIN, 0x1f, 0, mugain_scale), - SOC_SINGLE_TLV("DAC MBC1 Comp Thresh Playback Volume", + SOC_SINGLE_TLV("DAC MBC1 Comp Thresh Volume", R_DACMBCTHR1, FB_DACMBCTHR1_THRESH, 0xff, 0, compth_scale), SOC_ENUM("DAC MBC1 Comp Ratio", dac_mbc1_compressor_ratio_enum), @@ -778,9 +787,9 @@ static const struct snd_kcontrol_new tscs42xx_snd_controls[] = { SOC_SINGLE("MBC2 Phase Invert Switch", R_DACMBCMUG2, FB_DACMBCMUG2_PHASE, 1, 0), - SOC_SINGLE_TLV("DAC MBC2 Make-Up Gain Playback Volume", + SOC_SINGLE_TLV("DAC MBC2 Make-Up Gain Volume", R_DACMBCMUG2, FB_DACMBCMUG2_MUGAIN, 0x1f, 0, mugain_scale), - SOC_SINGLE_TLV("DAC MBC2 Comp Thresh Playback Volume", + SOC_SINGLE_TLV("DAC MBC2 Comp Thresh Volume", R_DACMBCTHR2, FB_DACMBCTHR2_THRESH, 0xff, 0, compth_scale), SOC_ENUM("DAC MBC2 Comp Ratio", dac_mbc2_compressor_ratio_enum), @@ -790,9 +799,9 @@ static const struct snd_kcontrol_new tscs42xx_snd_controls[] = { SOC_SINGLE("MBC3 Phase Invert Switch", R_DACMBCMUG3, FB_DACMBCMUG3_PHASE, 1, 0), - SOC_SINGLE_TLV("DAC MBC3 Make-Up Gain Playback Volume", + SOC_SINGLE_TLV("DAC MBC3 Make-Up Gain Volume", R_DACMBCMUG3, FB_DACMBCMUG3_MUGAIN, 0x1f, 0, mugain_scale), - SOC_SINGLE_TLV("DAC MBC3 Comp Thresh Playback Volume", + SOC_SINGLE_TLV("DAC MBC3 Comp Thresh Volume", R_DACMBCTHR3, FB_DACMBCTHR3_THRESH, 0xff, 0, compth_scale), SOC_ENUM("DAC MBC3 Comp Ratio", dac_mbc3_compressor_ratio_enum), diff --git a/sound/soc/codecs/tscs42xx.h b/sound/soc/codecs/tscs42xx.h index 814c8f3c4a68..6b3a21081635 100644 --- a/sound/soc/codecs/tscs42xx.h +++ b/sound/soc/codecs/tscs42xx.h @@ -34,6 +34,7 @@ enum { #define R_DACSR 0x19 #define R_PWRM1 0x1A #define R_PWRM2 0x1B +#define R_CTL 0x1C #define R_CONFIG0 0x1F #define R_CONFIG1 0x20 #define R_DMICCTL 0x24 @@ -1110,6 +1111,13 @@ enum { #define RV_PWRM2_VREF_DISABLE \ RV(FV_PWRM2_VREF_DISABLE, FB_PWRM2_VREF) +/****************************** + * R_CTL (0x1C) * + ******************************/ + +/* Fiel Offsets */ +#define FB_CTL_HPSWEN 7 +#define FB_CTL_HPSWPOL 6 /****************************** * R_CONFIG0 (0x1F) * diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index bfd1abd72253..94675da514c8 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -148,7 +148,7 @@ static bool twl6040_can_write_to_chip(struct snd_soc_component *component, case TWL6040_REG_HFRCTL: return priv->dl2_unmuted; default: - return 1; + return true; } } diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 3663b9fd4d65..deff65161504 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1180,6 +1180,9 @@ SOC_DOUBLE_R_TLV("OUT2 Digital Volume", WM2200_DAC_DIGITAL_VOLUME_2L, SOC_DOUBLE("OUT2 Switch", WM2200_PDM_1, WM2200_SPK1L_MUTE_SHIFT, WM2200_SPK1R_MUTE_SHIFT, 1, 1), SOC_ENUM("RxANC Src", wm2200_rxanc_input_sel), + +WM_ADSP_FW_CONTROL("DSP1", 0), +WM_ADSP_FW_CONTROL("DSP2", 1), }; WM2200_MIXER_ENUMS(OUT1L, WM2200_OUT1LMIX_INPUT_1_SOURCE); @@ -1553,15 +1556,10 @@ static const struct snd_soc_dapm_route wm2200_dapm_routes[] = { static int wm2200_probe(struct snd_soc_component *component) { struct wm2200_priv *wm2200 = snd_soc_component_get_drvdata(component); - int ret; wm2200->component = component; - ret = snd_soc_add_component_controls(component, wm_adsp_fw_controls, 2); - if (ret != 0) - return ret; - - return ret; + return 0; } static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) diff --git a/sound/soc/codecs/wm5100-tables.c b/sound/soc/codecs/wm5100-tables.c index e239f4bf2460..9e987cf07450 100644 --- a/sound/soc/codecs/wm5100-tables.c +++ b/sound/soc/codecs/wm5100-tables.c @@ -30,7 +30,7 @@ bool wm5100_volatile_register(struct device *dev, unsigned int reg) case WM5100_OUTPUT_STATUS_2: case WM5100_INPUT_ENABLES_STATUS: case WM5100_MIC_DETECT_3: - return 1; + return true; default: if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) || (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) || @@ -41,9 +41,9 @@ bool wm5100_volatile_register(struct device *dev, unsigned int reg) (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) || (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) || (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511)) - return 1; + return true; else - return 0; + return false; } } @@ -798,7 +798,7 @@ bool wm5100_readable_register(struct device *dev, unsigned int reg) case WM5100_DSP3_CONTROL_28: case WM5100_DSP3_CONTROL_29: case WM5100_DSP3_CONTROL_30: - return 1; + return true; default: if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) || (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) || @@ -809,9 +809,9 @@ bool wm5100_readable_register(struct device *dev, unsigned int reg) (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) || (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) || (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511)) - return 1; + return true; else - return 0; + return false; } } diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 1ac83388d1b8..7e817e1877c2 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -985,6 +985,8 @@ ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP1", 0), }; ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); @@ -2094,6 +2096,12 @@ static int wm5102_probe(struct platform_device *pdev) return ret; } + ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1); + if (ret != 0) + dev_warn(&pdev->dev, + "Failed to set compressed IRQ as a wake source: %d\n", + ret); + arizona_init_common(arizona); ret = arizona_init_vol_limit(arizona); @@ -2117,6 +2125,7 @@ static int wm5102_probe(struct platform_device *pdev) err_spk_irqs: arizona_free_spk_irqs(arizona); err_dsp_irq: + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102); return ret; @@ -2133,6 +2142,7 @@ static int wm5102_remove(struct platform_device *pdev) arizona_free_spk_irqs(arizona); + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102); return 0; diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index fb9835dcd836..b0789a03d699 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -927,6 +927,11 @@ ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP1", 0), +WM_ADSP_FW_CONTROL("DSP2", 1), +WM_ADSP_FW_CONTROL("DSP3", 2), +WM_ADSP_FW_CONTROL("DSP4", 3), }; ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); @@ -2455,6 +2460,12 @@ static int wm5110_probe(struct platform_device *pdev) return ret; } + ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1); + if (ret != 0) + dev_warn(&pdev->dev, + "Failed to set compressed IRQ as a wake source: %d\n", + ret); + arizona_init_common(arizona); ret = arizona_init_vol_limit(arizona); @@ -2478,6 +2489,7 @@ static int wm5110_probe(struct platform_device *pdev) err_spk_irqs: arizona_free_spk_irqs(arizona); err_dsp_irq: + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110); return ret; @@ -2496,6 +2508,7 @@ static int wm5110_remove(struct platform_device *pdev) arizona_free_spk_irqs(arizona); + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110); return 0; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 7b8b6ef2f632..6cb3c153ba19 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -251,10 +251,10 @@ static bool wm8903_volatile_register(struct device *dev, unsigned int reg) case WM8903_DC_SERVO_READBACK_2: case WM8903_DC_SERVO_READBACK_3: case WM8903_DC_SERVO_READBACK_4: - return 1; + return true; default: - return 0; + return false; } } diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 9037a35b931d..1965635ec07c 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1455,6 +1455,7 @@ static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif1 |= 0x3 | WM8904_AIF_LRCLK_INV; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif1 |= 0x3; break; diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index ba44e3d6c1e0..cd204f79647d 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -686,6 +686,7 @@ static int wm8955_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif |= WM8955_LRP; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif |= 0x3; break; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index c30f5aa392c6..8dc1f3d6a988 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -839,6 +839,7 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, iface |= 0x000c; break; } + /* fall through */ default: dev_err(component->dev, "unsupported width %d\n", params_width(params)); diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index f70f563d59f3..68b4cadc308f 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -653,6 +653,7 @@ static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_DSP_B: aif |= WM8961_LRP; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif |= 3; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index a11e9d6bf950..efd8910b1ff7 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2649,6 +2649,7 @@ static int wm8962_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif0 |= WM8962_LRCLK_INV | 3; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif0 |= 3; diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 62200117444b..6e52c6a8bab3 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -522,7 +522,7 @@ static inline int get_coeff(int mclk, int rate) /* The set of rates we can generate from the above for each SYSCLK */ static const unsigned int rates_12288[] = { - 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000, + 8000, 12000, 16000, 24000, 32000, 48000, 96000, }; static const struct snd_pcm_hw_constraint_list constraints_12288 = { @@ -540,7 +540,7 @@ static const struct snd_pcm_hw_constraint_list constraints_112896 = { }; static const unsigned int rates_12[] = { - 8000, 11025, 12000, 16000, 22050, 2400, 32000, 41100, 48000, + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 41100, 48000, 48000, 88235, 96000, }; diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 411b9eee88c2..457bc437ce54 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -40,9 +40,9 @@ static bool wm8990_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { case WM8990_RESET: - return 1; + return true; default: - return 0; + return false; } } diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 7fdfdf3f6e67..14f1b0c0d286 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2432,6 +2432,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_2, WM8994_OPCLK_ENA, 0); } + break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 60e227832331..68c99fe37097 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -1465,6 +1465,7 @@ static int wm8995_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif |= WM8995_AIF1_LRCLK_INV; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif |= (0x3 << WM8995_AIF1_FMT_SHIFT); break; diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index d9d206046f8c..91711f8958c5 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -1498,9 +1498,9 @@ static bool wm8996_readable_register(struct device *dev, unsigned int reg) case WM8996_RIGHT_PDM_SPEAKER: case WM8996_PDM_SPEAKER_MUTE_SEQUENCE: case WM8996_PDM_SPEAKER_VOLUME: - return 1; + return true; default: - return 0; + return false; } } @@ -1522,9 +1522,9 @@ static bool wm8996_volatile_register(struct device *dev, unsigned int reg) case WM8996_MIC_DETECT_3: case WM8996_HEADPHONE_DETECT_1: case WM8996_HEADPHONE_DETECT_2: - return 1; + return true; default: - return 0; + return false; } } @@ -1858,6 +1858,7 @@ static int wm8996_set_sysclk(struct snd_soc_dai *dai, case 24576000: ratediv = WM8996_SYSCLK_DIV; wm8996->sysclk /= 2; + /* fall through */ case 11289600: case 12288000: snd_soc_component_update_bits(component, WM8996_AIF_RATE, diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 5a0ea7b3c149..399255d1f78a 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -933,6 +933,7 @@ static int wm9081_set_dai_fmt(struct snd_soc_dai *dai, switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif2 |= WM9081_AIF_LRCLK_INV; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif2 |= 0x3; break; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 2fcdd84021a5..f61656070225 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include <linux/ctype.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -35,15 +36,15 @@ #include "wm_adsp.h" #define adsp_crit(_dsp, fmt, ...) \ - dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) + dev_crit(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) #define adsp_err(_dsp, fmt, ...) \ - dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) + dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) #define adsp_warn(_dsp, fmt, ...) \ - dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) + dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) #define adsp_info(_dsp, fmt, ...) \ - dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) + dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) #define adsp_dbg(_dsp, fmt, ...) \ - dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) + dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) #define ADSP1_CONTROL_1 0x00 #define ADSP1_CONTROL_2 0x02 @@ -418,7 +419,7 @@ static const struct wm_adsp_fw_caps ctrl_caps[] = { { .id = SND_AUDIOCODEC_BESPOKE, .desc = { - .max_ch = 1, + .max_ch = 8, .sample_rates = { 16000 }, .num_sample_rates = 1, .formats = SNDRV_PCM_FMTBIT_S16_LE, @@ -608,7 +609,6 @@ static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, struct snd_soc_component *component) { struct dentry *root = NULL; - char *root_name; int i; if (!component->debugfs_root) { @@ -616,13 +616,7 @@ static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, goto err; } - root_name = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!root_name) - goto err; - - snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num); - root = debugfs_create_dir(root_name, component->debugfs_root); - kfree(root_name); + root = debugfs_create_dir(dsp->name, component->debugfs_root); if (!root) goto err; @@ -684,8 +678,8 @@ static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) } #endif -static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; @@ -695,9 +689,10 @@ static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, return 0; } +EXPORT_SYMBOL_GPL(wm_adsp_fw_get); -static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; @@ -721,8 +716,9 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, return ret; } +EXPORT_SYMBOL_GPL(wm_adsp_fw_put); -static const struct soc_enum wm_adsp_fw_enum[] = { +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), @@ -731,24 +727,7 @@ static const struct soc_enum wm_adsp_fw_enum[] = { SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), SOC_ENUM_SINGLE(0, 6, 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), - SOC_ENUM_EXT("DSP5 Firmware", wm_adsp_fw_enum[4], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM_EXT("DSP6 Firmware", wm_adsp_fw_enum[5], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM_EXT("DSP7 Firmware", wm_adsp_fw_enum[6], - wm_adsp_fw_get, wm_adsp_fw_put), -}; -EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); +EXPORT_SYMBOL_GPL(wm_adsp_fw_enum); static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, int type) @@ -1330,12 +1309,12 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, switch (dsp->fw_ver) { case 0: case 1: - snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x", - dsp->num, region_name, alg_region->alg); + snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x", + dsp->name, region_name, alg_region->alg); break; default: ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - "DSP%d%c %.12s %x", dsp->num, *region_name, + "%s%c %.12s %x", dsp->name, *region_name, wm_adsp_fw_text[dsp->fw], alg_region->alg); /* Truncate the subname from the start if it is too long */ @@ -1343,6 +1322,9 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; int skip = 0; + if (dsp->component->name_prefix) + avail -= strlen(dsp->component->name_prefix) + 1; + if (subname_len > avail) skip = subname_len - avail; @@ -1604,6 +1586,15 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp, if (ret) return -EINVAL; break; + case WMFW_CTL_TYPE_HOST_BUFFER: + ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, + WMFW_CTL_FLAG_SYS | + WMFW_CTL_FLAG_VOLATILE | + WMFW_CTL_FLAG_READABLE, + 0); + if (ret) + return -EINVAL; + break; default: adsp_err(dsp, "Unknown control type: %d\n", coeff_blk.ctl_type); @@ -1651,7 +1642,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, + snprintf(file, PAGE_SIZE, "%s-%s-%s.wmfw", dsp->part, dsp->fwf_name, wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; @@ -1871,9 +1862,11 @@ static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, } static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, + const struct wm_adsp_region *mem, unsigned int pos, unsigned int len) { void *alg; + unsigned int reg; int ret; __be32 val; @@ -1888,7 +1881,9 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, } /* Read the terminator first to validate the length */ - ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val)); + reg = wm_adsp_region_to_reg(mem, pos + len); + + ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); if (ret != 0) { adsp_err(dsp, "Failed to read algorithm list end: %d\n", ret); @@ -1897,13 +1892,18 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, if (be32_to_cpu(val) != 0xbedead) adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", - pos + len, be32_to_cpu(val)); + reg, be32_to_cpu(val)); + + /* Convert length from DSP words to bytes */ + len *= sizeof(u32); - alg = kcalloc(len, 2, GFP_KERNEL | GFP_DMA); + alg = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!alg) return ERR_PTR(-ENOMEM); - ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2); + reg = wm_adsp_region_to_reg(mem, pos); + + ret = regmap_raw_read(dsp->regmap, reg, alg, len); if (ret != 0) { adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); kfree(alg); @@ -2002,10 +2002,11 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp) if (IS_ERR(alg_region)) return PTR_ERR(alg_region); - pos = sizeof(adsp1_id) / 2; - len = (sizeof(*adsp1_alg) * n_algs) / 2; + /* Calculate offset and length in DSP words */ + pos = sizeof(adsp1_id) / sizeof(u32); + len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); - adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); + adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); if (IS_ERR(adsp1_alg)) return PTR_ERR(adsp1_alg); @@ -2113,10 +2114,11 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp) if (IS_ERR(alg_region)) return PTR_ERR(alg_region); - pos = sizeof(adsp2_id) / 2; - len = (sizeof(*adsp2_alg) * n_algs) / 2; + /* Calculate offset and length in DSP words */ + pos = sizeof(adsp2_id) / sizeof(u32); + len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); - adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); + adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); if (IS_ERR(adsp2_alg)) return PTR_ERR(adsp2_alg); @@ -2218,7 +2220,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, + snprintf(file, PAGE_SIZE, "%s-%s-%s.bin", dsp->part, dsp->fwf_name, wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; @@ -2390,8 +2392,38 @@ out: return ret; } +static int wm_adsp_create_name(struct wm_adsp *dsp) +{ + char *p; + + if (!dsp->name) { + dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d", + dsp->num); + if (!dsp->name) + return -ENOMEM; + } + + if (!dsp->fwf_name) { + p = devm_kstrdup(dsp->dev, dsp->name, GFP_KERNEL); + if (!p) + return -ENOMEM; + + dsp->fwf_name = p; + for (; *p != 0; ++p) + *p = tolower(*p); + } + + return 0; +} + int wm_adsp1_init(struct wm_adsp *dsp) { + int ret; + + ret = wm_adsp_create_name(dsp); + if (ret) + return ret; + INIT_LIST_HEAD(&dsp->alg_regions); mutex_init(&dsp->pwr_lock); @@ -2642,7 +2674,10 @@ int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); + struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct wm_adsp *dsp = &dsps[mc->shift - 1]; ucontrol->value.integer.value[0] = dsp->preloaded; @@ -2654,13 +2689,14 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); + struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct wm_adsp *dsp = &dsps[mc->shift - 1]; char preload[32]; - snprintf(preload, ARRAY_SIZE(preload), "DSP%u Preload", mc->shift); + snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); dsp->preloaded = ucontrol->value.integer.value[0]; @@ -2671,6 +2707,8 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, snd_soc_dapm_sync(dapm); + flush_work(&dsp->boot_work); + return 0; } EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); @@ -2853,17 +2891,14 @@ int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *comp { char preload[32]; - snprintf(preload, ARRAY_SIZE(preload), "DSP%d Preload", dsp->num); - + snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); snd_soc_component_disable_pin(component, preload); wm_adsp2_init_debugfs(dsp, component); dsp->component = component; - return snd_soc_add_component_controls(component, - &wm_adsp_fw_controls[dsp->num - 1], - 1); + return 0; } EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); @@ -2879,6 +2914,10 @@ int wm_adsp2_init(struct wm_adsp *dsp) { int ret; + ret = wm_adsp_create_name(dsp); + if (ret) + return ret; + switch (dsp->rev) { case 0: /* @@ -3186,7 +3225,7 @@ static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, buf->host_buf_ptr + field_offset, data); } -static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) +static int wm_adsp_legacy_host_buf_addr(struct wm_adsp_compr_buf *buf) { struct wm_adsp_alg_region *alg_region; struct wm_adsp *dsp = buf->dsp; @@ -3225,6 +3264,61 @@ static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) return 0; } +static struct wm_coeff_ctl * +wm_adsp_find_host_buffer_ctrl(struct wm_adsp_compr_buf *buf) +{ + struct wm_adsp *dsp = buf->dsp; + struct wm_coeff_ctl *ctl; + + list_for_each_entry(ctl, &dsp->ctl_list, list) { + if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) + continue; + + if (!ctl->enabled) + continue; + + return ctl; + } + + return NULL; +} + +static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) +{ + struct wm_adsp *dsp = buf->dsp; + struct wm_coeff_ctl *ctl; + unsigned int reg; + u32 val; + int i, ret; + + ctl = wm_adsp_find_host_buffer_ctrl(buf); + if (!ctl) + return wm_adsp_legacy_host_buf_addr(buf); + + ret = wm_coeff_base_reg(ctl, ®); + if (ret) + return ret; + + for (i = 0; i < 5; ++i) { + ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); + if (ret < 0) + return ret; + + if (val) + break; + + usleep_range(1000, 2000); + } + + if (!val) + return -EIO; + + buf->host_buf_ptr = be32_to_cpu(val); + adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); + + return 0; +} + static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) { const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index bc6d359f0533..4b8778b0b06c 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -57,6 +57,8 @@ struct wm_adsp_compr_buf; struct wm_adsp { const char *part; + const char *name; + const char *fwf_name; int rev; int num; int type; @@ -121,7 +123,11 @@ struct wm_adsp { .reg = SND_SOC_NOPM, .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[]; +#define WM_ADSP_FW_CONTROL(dspname, num) \ + SOC_ENUM_EXT(dspname " Firmware", wm_adsp_fw_enum[num], \ + wm_adsp_fw_get, wm_adsp_fw_put) + +extern const struct soc_enum wm_adsp_fw_enum[]; int wm_adsp1_init(struct wm_adsp *dsp); int wm_adsp2_init(struct wm_adsp *dsp); @@ -144,6 +150,10 @@ int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream); int wm_adsp_compr_free(struct snd_compr_stream *stream); diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index ec78b9da020f..0c3f50acb8b1 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -29,6 +29,7 @@ /* Non-ALSA coefficient types start at 0x1000 */ #define WMFW_CTL_TYPE_ACKED 0x1000 /* acked control */ #define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */ +#define WMFW_CTL_TYPE_HOST_BUFFER 0x1002 /* host buffer pointer */ struct wmfw_header { char magic[4]; diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 807040bb3921..a3206e65e5e5 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -340,6 +340,7 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, * rate is lowered. */ inv_fs = true; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: dev->mode = MOD_DSP_A; break; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 47c0c821d325..f70db8412c7c 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -320,12 +320,8 @@ static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data) handled_mask |= XUNDRN; substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK]; - if (substream) { - snd_pcm_stream_lock_irq(substream); - if (snd_pcm_running(substream)) - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irq(substream); - } + if (substream) + snd_pcm_stop_xrun(substream); } if (!handled_mask) @@ -355,12 +351,8 @@ static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data) handled_mask |= ROVRN; substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE]; - if (substream) { - snd_pcm_stream_lock_irq(substream); - if (snd_pcm_running(substream)) - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irq(substream); - } + if (substream) + snd_pcm_stop_xrun(substream); } if (!handled_mask) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 4a6750aa3637..44433b20435c 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -1,14 +1,10 @@ -/* - * Freescale Generic ASoC Sound Card driver with ASRC - * - * Copyright (C) 2014 Freescale Semiconductor, Inc. - * - * Author: Nicolin Chen <nicoleotsuka@gmail.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Freescale Generic ASoC Sound Card driver with ASRC +// +// Copyright (C) 2014 Freescale Semiconductor, Inc. +// +// Author: Nicolin Chen <nicoleotsuka@gmail.com> #include <linux/clk.h> #include <linux/i2c.h> @@ -199,7 +195,7 @@ static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); snd_mask_none(mask); - snd_mask_set(mask, (__force int)priv->asrc_format); + snd_mask_set_format(mask, priv->asrc_format); return 0; } diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index adfb8135d739..528e8b108422 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -1,14 +1,10 @@ -/* - * Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver - * - * Copyright (C) 2014 Freescale Semiconductor, Inc. - * - * Author: Nicolin Chen <nicoleotsuka@gmail.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver +// +// Copyright (C) 2014 Freescale Semiconductor, Inc. +// +// Author: Nicolin Chen <nicoleotsuka@gmail.com> #include <linux/clk.h> #include <linux/delay.h> diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index d558dd5499a5..c60075112570 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * fsl_asrc.h - Freescale ASRC ALSA SoC header file * * Copyright (C) 2014 Freescale Semiconductor, Inc. * * Author: Nicolin Chen <nicoleotsuka@gmail.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #ifndef _FSL_ASRC_H diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index 565e16d8fe85..1033ac6631b0 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -1,14 +1,10 @@ -/* - * Freescale ASRC ALSA SoC Platform (DMA) driver - * - * Copyright (C) 2014 Freescale Semiconductor, Inc. - * - * Author: Nicolin Chen <nicoleotsuka@gmail.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Freescale ASRC ALSA SoC Platform (DMA) driver +// +// Copyright (C) 2014 Freescale Semiconductor, Inc. +// +// Author: Nicolin Chen <nicoleotsuka@gmail.com> #include <linux/dma-mapping.h> #include <linux/module.h> diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 8f43110373b8..c1d1d06783e5 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -249,6 +249,7 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, break; case ESAI_HCKT_EXTAL: ecr |= ESAI_ECR_ETI; + /* fall through */ case ESAI_HCKR_EXTAL: ecr |= ESAI_ECR_ERI; break; diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 9b59d87b61bf..740b90df44bb 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1118,7 +1118,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) { for (txclk_df = 1; txclk_df <= 128; txclk_df++) { - rate_ideal = rate[index] * txclk_df * 64; + rate_ideal = rate[index] * txclk_df * 64ULL; if (round) rate_actual = clk_round_rate(clk, rate_ideal); else diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c index 7592b0406370..7f0fa4b52223 100644 --- a/sound/soc/fsl/fsl_utils.c +++ b/sound/soc/fsl/fsl_utils.c @@ -1,14 +1,10 @@ -/** - * Freescale ALSA SoC Machine driver utility - * - * Author: Timur Tabi <timur@freescale.com> - * - * Copyright 2010 Freescale Semiconductor, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Freescale ALSA SoC Machine driver utility +// +// Author: Timur Tabi <timur@freescale.com> +// +// Copyright 2010 Freescale Semiconductor, Inc. #include <linux/module.h> #include <linux/of_address.h> diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h index 1687b66ef18e..c5dc2a14b492 100644 --- a/sound/soc/fsl/fsl_utils.h +++ b/sound/soc/fsl/fsl_utils.h @@ -1,13 +1,10 @@ -/** +/* SPDX-License-Identifier: GPL-2.0 */ +/* * Freescale ALSA SoC Machine driver utility * * Author: Timur Tabi <timur@freescale.com> * * Copyright 2010 Freescale Semiconductor, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #ifndef _FSL_UTILS_H diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index b99e0b5e00e9..c29200cf755a 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -1,14 +1,7 @@ -/* - * Copyright 2012 Freescale Semiconductor, Inc. - * Copyright 2012 Linaro Ltd. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright 2012 Freescale Semiconductor, Inc. +// Copyright 2012 Linaro Ltd. #include <linux/module.h> #include <linux/of.h> diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index d93bacacbd5b..2094d2c8919f 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -1,15 +1,12 @@ -/* - * ASoC audio graph sound card support - * - * Copyright (C) 2016 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * based on ${LINUX}/sound/soc/generic/simple-card.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ASoC audio graph sound card support +// +// Copyright (C) 2016 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +// +// based on ${LINUX}/sound/soc/generic/simple-card.c + #include <linux/clk.h> #include <linux/device.h> #include <linux/gpio.h> @@ -21,7 +18,6 @@ #include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/string.h> -#include <sound/jack.h> #include <sound/simple_card_utils.h> struct graph_card_data { @@ -32,6 +28,8 @@ struct graph_card_data { unsigned int mclk_fs; } *dai_props; unsigned int mclk_fs; + struct asoc_simple_jack hp_jack; + struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; struct gpio_desc *pa_gpio; }; @@ -278,6 +276,22 @@ static int asoc_graph_get_dais_count(struct device *dev) return count; } +static int asoc_graph_soc_card_probe(struct snd_soc_card *card) +{ + struct graph_card_data *priv = snd_soc_card_get_drvdata(card); + int ret; + + ret = asoc_simple_card_init_hp(card, &priv->hp_jack, NULL); + if (ret < 0) + return ret; + + ret = asoc_simple_card_init_mic(card, &priv->mic_jack, NULL); + if (ret < 0) + return ret; + + return 0; +} + static int asoc_graph_card_probe(struct platform_device *pdev) { struct graph_card_data *priv; @@ -319,6 +333,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) card->num_links = num; card->dapm_widgets = asoc_graph_card_dapm_widgets; card->num_dapm_widgets = ARRAY_SIZE(asoc_graph_card_dapm_widgets); + card->probe = asoc_graph_soc_card_probe; ret = asoc_graph_card_parse_of(priv); if (ret < 0) { diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index 095ef6426d42..92882e392d6c 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -1,17 +1,14 @@ -/* - * ASoC audio graph SCU sound card support - * - * Copyright (C) 2017 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * based on - * ${LINUX}/sound/soc/generic/simple-scu-card.c - * ${LINUX}/sound/soc/generic/audio-graph-card.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ASoC audio graph SCU sound card support +// +// Copyright (C) 2017 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +// +// based on +// ${LINUX}/sound/soc/generic/simple-scu-card.c +// ${LINUX}/sound/soc/generic/audio-graph-card.c + #include <linux/clk.h> #include <linux/device.h> #include <linux/gpio.h> diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 3751a07de6aa..d3f3f0fec74c 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -1,16 +1,17 @@ -/* - * simple-card-utils.c - * - * Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// simple-card-utils.c +// +// Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_gpio.h> #include <linux/of_graph.h> +#include <sound/jack.h> #include <sound/simple_card_utils.h> void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data, @@ -419,6 +420,61 @@ int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_widgets); +int asoc_simple_card_init_jack(struct snd_soc_card *card, + struct asoc_simple_jack *sjack, + int is_hp, char *prefix) +{ + struct device *dev = card->dev; + enum of_gpio_flags flags; + char prop[128]; + char *pin_name; + char *gpio_name; + int mask; + int det; + + if (!prefix) + prefix = ""; + + sjack->gpio.gpio = -ENOENT; + + if (is_hp) { + snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix); + pin_name = "Headphones"; + gpio_name = "Headphone detection"; + mask = SND_JACK_HEADPHONE; + } else { + snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix); + pin_name = "Mic Jack"; + gpio_name = "Mic detection"; + mask = SND_JACK_MICROPHONE; + } + + det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags); + if (det == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (gpio_is_valid(det)) { + sjack->pin.pin = pin_name; + sjack->pin.mask = mask; + + sjack->gpio.name = gpio_name; + sjack->gpio.report = mask; + sjack->gpio.gpio = det; + sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW); + sjack->gpio.debounce_time = 150; + + snd_soc_card_jack_new(card, pin_name, mask, + &sjack->jack, + &sjack->pin, 1); + + snd_soc_jack_add_gpios(&sjack->jack, 1, + &sjack->gpio); + } + + return 0; +} +EXPORT_SYMBOL_GPL(asoc_simple_card_init_jack); + /* Module information */ MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 8b374af86a6e..64bf3560c1d1 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -1,32 +1,20 @@ -/* - * ASoC simple sound card support - * - * Copyright (C) 2012 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ASoC simple sound card support +// +// Copyright (C) 2012 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include <linux/clk.h> #include <linux/device.h> -#include <linux/gpio.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/string.h> -#include <sound/jack.h> #include <sound/simple_card.h> #include <sound/soc-dai.h> #include <sound/soc.h> -struct asoc_simple_jack { - struct snd_soc_jack jack; - struct snd_soc_jack_pin pin; - struct snd_soc_jack_gpio gpio; -}; - struct simple_card_data { struct snd_soc_card snd_card; struct simple_dai_props { @@ -49,61 +37,6 @@ struct simple_card_data { #define CELL "#sound-dai-cells" #define PREFIX "simple-audio-card," -#define asoc_simple_card_init_hp(card, sjack, prefix)\ - asoc_simple_card_init_jack(card, sjack, 1, prefix) -#define asoc_simple_card_init_mic(card, sjack, prefix)\ - asoc_simple_card_init_jack(card, sjack, 0, prefix) -static int asoc_simple_card_init_jack(struct snd_soc_card *card, - struct asoc_simple_jack *sjack, - int is_hp, char *prefix) -{ - struct device *dev = card->dev; - enum of_gpio_flags flags; - char prop[128]; - char *pin_name; - char *gpio_name; - int mask; - int det; - - sjack->gpio.gpio = -ENOENT; - - if (is_hp) { - snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix); - pin_name = "Headphones"; - gpio_name = "Headphone detection"; - mask = SND_JACK_HEADPHONE; - } else { - snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix); - pin_name = "Mic Jack"; - gpio_name = "Mic detection"; - mask = SND_JACK_MICROPHONE; - } - - det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags); - if (det == -EPROBE_DEFER) - return -EPROBE_DEFER; - - if (gpio_is_valid(det)) { - sjack->pin.pin = pin_name; - sjack->pin.mask = mask; - - sjack->gpio.name = gpio_name; - sjack->gpio.report = mask; - sjack->gpio.gpio = det; - sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW); - sjack->gpio.debounce_time = 150; - - snd_soc_card_jack_new(card, pin_name, mask, - &sjack->jack, - &sjack->pin, 1); - - snd_soc_jack_add_gpios(&sjack->jack, 1, - &sjack->gpio); - } - - return 0; -} - static int asoc_simple_card_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -213,14 +146,6 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) if (ret < 0) return ret; - ret = asoc_simple_card_init_hp(rtd->card, &priv->hp_jack, PREFIX); - if (ret < 0) - return ret; - - ret = asoc_simple_card_init_mic(rtd->card, &priv->mic_jack, PREFIX); - if (ret < 0) - return ret; - return 0; } @@ -414,6 +339,22 @@ card_parse_end: return ret; } +static int asoc_simple_soc_card_probe(struct snd_soc_card *card) +{ + struct simple_card_data *priv = snd_soc_card_get_drvdata(card); + int ret; + + ret = asoc_simple_card_init_hp(card, &priv->hp_jack, PREFIX); + if (ret < 0) + return ret; + + ret = asoc_simple_card_init_mic(card, &priv->mic_jack, PREFIX); + if (ret < 0) + return ret; + + return 0; +} + static int asoc_simple_card_probe(struct platform_device *pdev) { struct simple_card_data *priv; @@ -449,6 +390,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) card->dev = dev; card->dai_link = priv->dai_link; card->num_links = num; + card->probe = asoc_simple_soc_card_probe; if (np && of_device_is_available(np)) { diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index 487716559deb..16a83bc51e0e 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -1,15 +1,12 @@ -/* - * ASoC simple SCU sound card support - * - * Copyright (C) 2015 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * based on ${LINUX}/sound/soc/generic/simple-card.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ASoC simple SCU sound card support +// +// Copyright (C) 2015 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +// +// based on ${LINUX}/sound/soc/generic/simple-card.c + #include <linux/clk.h> #include <linux/device.h> #include <linux/module.h> diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c index 6a8b253c58d2..5455d6e0ab53 100644 --- a/sound/soc/intel/atom/sst/sst_drv_interface.c +++ b/sound/soc/intel/atom/sst/sst_drv_interface.c @@ -266,17 +266,15 @@ static int sst_cdev_ack(struct device *dev, unsigned int str_id, stream->cumm_bytes += bytes; dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes); - memcpy_fromio(&fw_tstamp, - ((void *)(ctx->mailbox + ctx->tstamp) - +(str_id * sizeof(fw_tstamp))), - sizeof(fw_tstamp)); + addr = ((void __iomem *)(ctx->mailbox + ctx->tstamp)) + + (str_id * sizeof(fw_tstamp)); + + memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp)); fw_tstamp.bytes_copied = stream->cumm_bytes; dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n", fw_tstamp.bytes_copied, bytes); - addr = ((void *)(ctx->mailbox + ctx->tstamp)) + - (str_id * sizeof(fw_tstamp)); offset = offsetof(struct snd_sst_tstamp, bytes_copied); sst_shim_write(addr, offset, fw_tstamp.bytes_copied); return 0; @@ -360,11 +358,12 @@ static int sst_cdev_tstamp(struct device *dev, unsigned int str_id, struct snd_sst_tstamp fw_tstamp = {0,}; struct stream_info *stream; struct intel_sst_drv *ctx = dev_get_drvdata(dev); + void __iomem *addr; + + addr = (void __iomem *)(ctx->mailbox + ctx->tstamp) + + (str_id * sizeof(fw_tstamp)); - memcpy_fromio(&fw_tstamp, - ((void *)(ctx->mailbox + ctx->tstamp) - +(str_id * sizeof(fw_tstamp))), - sizeof(fw_tstamp)); + memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp)); stream = get_stream_info(ctx, str_id); if (!stream) @@ -530,6 +529,7 @@ static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info) struct snd_sst_tstamp fw_tstamp; unsigned int str_id; struct intel_sst_drv *ctx = dev_get_drvdata(dev); + void __iomem *addr; str_id = info->str_id; stream = get_stream_info(ctx, str_id); @@ -540,10 +540,11 @@ static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info) return -EINVAL; substream = stream->pcm_substream; - memcpy_fromio(&fw_tstamp, - ((void *)(ctx->mailbox + ctx->tstamp) - + (str_id * sizeof(fw_tstamp))), - sizeof(fw_tstamp)); + addr = (void __iomem *)(ctx->mailbox + ctx->tstamp) + + (str_id * sizeof(fw_tstamp)); + + memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp)); + return sst_calc_tstamp(ctx, info, substream, &fw_tstamp); } diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c index a686eef2cf7f..27413ebae956 100644 --- a/sound/soc/intel/atom/sst/sst_loader.c +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -44,15 +44,15 @@ void memcpy32_toio(void __iomem *dst, const void *src, int count) /* __iowrite32_copy uses 32-bit count values so divide by 4 for * right count in words */ - __iowrite32_copy(dst, src, count/4); + __iowrite32_copy(dst, src, count / 4); } void memcpy32_fromio(void *dst, const void __iomem *src, int count) { - /* __iowrite32_copy uses 32-bit count values so divide by 4 for + /* __ioread32_copy uses 32-bit count values so divide by 4 for * right count in words */ - __iowrite32_copy(dst, src, count/4); + __ioread32_copy(dst, src, count / 4); } /** diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 24797482a3d2..cccda87f4b34 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -281,6 +281,20 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH Say Y if you have such a device. If unsure select "N". +config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH + tristate "GLK with RT5682 and MAX98357A in I2S Mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT5682 + select SND_SOC_MAX98357A + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + select SND_HDA_DSP_LOADER + help + This adds support for ASoC machine driver for Geminilake platforms + with RT5682 + MAX98357A I2S audio codec. + Say Y if you have such a device. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 92b5507291af..87ef8b4058e5 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -6,6 +6,7 @@ snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o +snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -27,6 +28,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o +obj-$(CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH) += snd-soc-sst-glk-rt5682_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index 6ea360f33575..efcfd906c856 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -154,9 +154,7 @@ static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = channels->max = 2; /* set SSP0 to 16 bit */ - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - SNDRV_PCM_FORMAT_S16_LE); + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); return 0; } diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 40eb979d5ac1..6f052fc8d1e2 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -160,7 +160,7 @@ static int broxton_ssp_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP to 24 bit */ snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } @@ -324,8 +324,22 @@ static const struct snd_pcm_hw_constraint_list constraints_16000 = { .list = rates_16000, }; +static const unsigned int ch_mono[] = { + 1, +}; + +static const struct snd_pcm_hw_constraint_list constraints_refcap = { + .count = ARRAY_SIZE(ch_mono), + .list = ch_mono, +}; + static int broxton_refcap_startup(struct snd_pcm_substream *substream) { + substream->runtime->hw.channels_max = 1; + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_refcap); + return snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_16000); @@ -586,7 +600,7 @@ static int broxton_audio_probe(struct platform_device *pdev) static struct platform_driver broxton_audio = { .probe = broxton_audio_probe, .driver = { - .name = "bxt_da7219_max98357a_i2s", + .name = "bxt_da7219_max98357a", .pm = &snd_soc_pm_ops, }, }; @@ -599,4 +613,4 @@ MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com>"); MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>"); MODULE_AUTHOR("Conrad Cooke <conrad.cooke@intel.com>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:bxt_da7219_max98357a_i2s"); +MODULE_ALIAS("platform:bxt_da7219_max98357a"); diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index b68c289558a8..27308337ab12 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -221,7 +221,7 @@ static int broxton_ssp5_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP5 to 24 bit */ snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 33065ba294a9..d32844f94d74 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -404,7 +404,7 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { }, .driver_data = (void *)(BYT_RT5640_DMIC1_MAP | BYT_RT5640_JD_SRC_JD1_IN4P | - BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_TH_1500UA | BYT_RT5640_OVCD_SF_0P75 | BYT_RT5640_SSP0_AIF1 | BYT_RT5640_MCLK_EN), @@ -464,12 +464,38 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_MCLK_EN), }, { + /* Chuwi Vi10 (CWI505) */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), + DMI_MATCH(DMI_BOARD_NAME, "BYT-PF02"), + DMI_MATCH(DMI_SYS_VENDOR, "ilife"), + DMI_MATCH(DMI_PRODUCT_NAME, "S165"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, + { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), }, .driver_data = (void *)(BYT_RT5640_DMIC1_MAP), }, + { /* Connect Tablet 9 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Connect"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Tablet 9"), + }, + .driver_data = (void *)(BYTCR_INPUT_DEFAULTS | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), @@ -536,6 +562,19 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF1 | BYT_RT5640_MCLK_EN), }, + { /* Lenovo Miix 2 8 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "20326"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Hiking"), + }, + .driver_data = (void *)(BYT_RT5640_DMIC1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_MCLK_EN), + }, { /* MSI S100 tablet */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), @@ -549,6 +588,20 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_DIFF_MIC | BYT_RT5640_MCLK_EN), }, + { /* Nuvison/TMax TM800W560 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TMAX"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TM800W560L"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_JD_NOT_INV | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, { /* Pipo W4 */ .matches = { DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 987720e203f9..f8a68bdb3885 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -26,8 +26,12 @@ #include <linux/clk.h> #include <linux/device.h> #include <linux/dmi.h> +#include <linux/input.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/machine.h> #include <linux/slab.h> #include <asm/cpu_device_id.h> +#include <asm/intel-family.h> #include <asm/platform_sst_audio.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -42,8 +46,6 @@ enum { BYT_RT5651_IN1_MAP, BYT_RT5651_IN2_MAP, BYT_RT5651_IN1_IN2_MAP, - BYT_RT5651_IN1_HS_IN3_MAP, - BYT_RT5651_IN2_HS_IN3_MAP, }; enum { @@ -76,21 +78,26 @@ enum { #define BYT_RT5651_SSP2_AIF2 BIT(19) /* default is using AIF1 */ #define BYT_RT5651_SSP0_AIF1 BIT(20) #define BYT_RT5651_SSP0_AIF2 BIT(21) +#define BYT_RT5651_HP_LR_SWAPPED BIT(22) +#define BYT_RT5651_MONO_SPEAKER BIT(23) + +#define BYT_RT5651_DEFAULT_QUIRKS (BYT_RT5651_MCLK_EN | \ + BYT_RT5651_JD1_1 | \ + BYT_RT5651_OVCD_TH_2000UA | \ + BYT_RT5651_OVCD_SF_0P75) /* jack-detect-source + dmic-en + ovcd-th + -sf + terminating empty entry */ #define MAX_NO_PROPS 5 struct byt_rt5651_private { struct clk *mclk; + struct gpio_desc *ext_amp_gpio; struct snd_soc_jack jack; }; /* Default: jack-detect on JD1_1, internal mic on in2, headsetmic on in3 */ -static unsigned long byt_rt5651_quirk = BYT_RT5651_MCLK_EN | - BYT_RT5651_JD1_1 | - BYT_RT5651_OVCD_TH_2000UA | - BYT_RT5651_OVCD_SF_0P75 | - BYT_RT5651_IN2_HS_IN3_MAP; +static unsigned long byt_rt5651_quirk = BYT_RT5651_DEFAULT_QUIRKS | + BYT_RT5651_IN2_MAP; static void log_quirks(struct device *dev) { @@ -100,10 +107,8 @@ static void log_quirks(struct device *dev) dev_info(dev, "quirk IN1_MAP enabled"); if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN2_MAP) dev_info(dev, "quirk IN2_MAP enabled"); - if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_HS_IN3_MAP) - dev_info(dev, "quirk IN1_HS_IN3_MAP enabled"); - if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN2_HS_IN3_MAP) - dev_info(dev, "quirk IN2_HS_IN3_MAP enabled"); + if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_IN2_MAP) + dev_info(dev, "quirk IN1_IN2_MAP enabled"); if (BYT_RT5651_JDSRC(byt_rt5651_quirk)) { dev_info(dev, "quirk realtek,jack-detect-source %ld\n", BYT_RT5651_JDSRC(byt_rt5651_quirk)); @@ -124,6 +129,8 @@ static void log_quirks(struct device *dev) dev_info(dev, "quirk SSP0_AIF1 enabled\n"); if (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2) dev_info(dev, "quirk SSP0_AIF2 enabled\n"); + if (byt_rt5651_quirk & BYT_RT5651_MONO_SPEAKER) + dev_info(dev, "quirk MONO_SPEAKER enabled\n"); } #define BYT_CODEC_DAI1 "rt5651-aif1" @@ -211,6 +218,20 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, return 0; } +static int rt5651_ext_amp_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_card *card = w->dapm->card; + struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); + + if (SND_SOC_DAPM_EVENT_ON(event)) + gpiod_set_value_cansleep(priv->ext_amp_gpio, 1); + else + gpiod_set_value_cansleep(priv->ext_amp_gpio, 0); + + return 0; +} + static const struct snd_soc_dapm_widget byt_rt5651_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), @@ -220,7 +241,9 @@ static const struct snd_soc_dapm_widget byt_rt5651_widgets[] = { SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - + SND_SOC_DAPM_SUPPLY("Ext Amp Power", SND_SOC_NOPM, 0, 0, + rt5651_ext_amp_power_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), }; static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { @@ -228,6 +251,7 @@ static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { {"Headset Mic", NULL, "Platform Clock"}, {"Internal Mic", NULL, "Platform Clock"}, {"Speaker", NULL, "Platform Clock"}, + {"Speaker", NULL, "Ext Amp Power"}, {"Line In", NULL, "Platform Clock"}, {"Headset Mic", NULL, "micbias1"}, /* lowercase for rt5651 */ @@ -241,38 +265,26 @@ static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { }; static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic_map[] = { - {"IN2P", NULL, "Headset Mic"}, {"DMIC L1", NULL, "Internal Mic"}, {"DMIC R1", NULL, "Internal Mic"}, + {"IN3P", NULL, "Headset Mic"}, }; static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_map[] = { {"Internal Mic", NULL, "micbias1"}, {"IN1P", NULL, "Internal Mic"}, - {"IN2P", NULL, "Headset Mic"}, + {"IN3P", NULL, "Headset Mic"}, }; static const struct snd_soc_dapm_route byt_rt5651_intmic_in2_map[] = { {"Internal Mic", NULL, "micbias1"}, - {"IN1P", NULL, "Headset Mic"}, - {"IN2P", NULL, "Internal Mic"}, -}; - -static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_in2_map[] = { - {"Internal Mic", NULL, "micbias1"}, - {"IN1P", NULL, "Internal Mic"}, {"IN2P", NULL, "Internal Mic"}, {"IN3P", NULL, "Headset Mic"}, }; -static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_hs_in3_map[] = { +static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_in2_map[] = { {"Internal Mic", NULL, "micbias1"}, {"IN1P", NULL, "Internal Mic"}, - {"IN3P", NULL, "Headset Mic"}, -}; - -static const struct snd_soc_dapm_route byt_rt5651_intmic_in2_hs_in3_map[] = { - {"Internal Mic", NULL, "micbias1"}, {"IN2P", NULL, "Internal Mic"}, {"IN3P", NULL, "Headset Mic"}, }; @@ -357,46 +369,72 @@ static int byt_rt5651_quirk_cb(const struct dmi_system_id *id) static const struct dmi_system_id byt_rt5651_quirk_table[] = { { + /* Chuwi Hi8 Pro (CWI513) */ .callback = byt_rt5651_quirk_cb, .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), - DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), + DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"), + DMI_MATCH(DMI_PRODUCT_NAME, "X1D3_C806N"), }, - .driver_data = (void *)(BYT_RT5651_IN1_HS_IN3_MAP), + .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | + BYT_RT5651_IN2_MAP | + BYT_RT5651_HP_LR_SWAPPED | + BYT_RT5651_MONO_SPEAKER), }, { + /* Chuwi Vi8 Plus (CWI519) */ .callback = byt_rt5651_quirk_cb, .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ADI"), - DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Turbot"), + DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"), + DMI_MATCH(DMI_PRODUCT_NAME, "D2D3_Vi8A1"), }, - .driver_data = (void *)(BYT_RT5651_MCLK_EN | - BYT_RT5651_IN1_HS_IN3_MAP), + .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | + BYT_RT5651_IN2_MAP | + BYT_RT5651_HP_LR_SWAPPED | + BYT_RT5651_MONO_SPEAKER), + }, + { + /* I.T.Works TW701, Ployer Momo7w and Trekstor ST70416-6 + * (these all use the same mainboard) */ + .callback = byt_rt5651_quirk_cb, + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "INSYDE Corp."), + /* Partial match for all of itWORKS.G.WI71C.JGBMRBA, + * TREK.G.WI71C.JGBMRBA0x and MOMO.G.WI71C.MABMRBA02 */ + DMI_MATCH(DMI_BIOS_VERSION, ".G.WI71C."), + }, + .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | + BYT_RT5651_IN2_MAP | + BYT_RT5651_SSP0_AIF1 | + BYT_RT5651_MONO_SPEAKER), }, { + /* KIANO SlimNote 14.2 */ .callback = byt_rt5651_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "KIANO"), DMI_MATCH(DMI_PRODUCT_NAME, "KIANO SlimNote 14.2"), }, - .driver_data = (void *)(BYT_RT5651_MCLK_EN | - BYT_RT5651_JD1_1 | - BYT_RT5651_OVCD_TH_2000UA | - BYT_RT5651_OVCD_SF_0P75 | + .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | BYT_RT5651_IN1_IN2_MAP), }, { - /* Chuwi Vi8 Plus (CWI519) */ + /* Minnowboard Max B3 */ .callback = byt_rt5651_quirk_cb, .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"), - DMI_MATCH(DMI_PRODUCT_NAME, "D2D3_Vi8A1"), + DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), + DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), + }, + .driver_data = (void *)(BYT_RT5651_IN1_MAP), + }, + { + /* Minnowboard Turbot */ + .callback = byt_rt5651_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ADI"), + DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Turbot"), }, .driver_data = (void *)(BYT_RT5651_MCLK_EN | - BYT_RT5651_JD1_1 | - BYT_RT5651_OVCD_TH_2000UA | - BYT_RT5651_OVCD_SF_0P75 | - BYT_RT5651_IN2_HS_IN3_MAP), + BYT_RT5651_IN1_MAP), }, { /* VIOS LTH17 */ @@ -405,11 +443,24 @@ static const struct dmi_system_id byt_rt5651_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "VIOS"), DMI_MATCH(DMI_PRODUCT_NAME, "LTH17"), }, - .driver_data = (void *)(BYT_RT5651_MCLK_EN | + .driver_data = (void *)(BYT_RT5651_IN1_IN2_MAP | BYT_RT5651_JD1_1 | BYT_RT5651_OVCD_TH_2000UA | BYT_RT5651_OVCD_SF_1P0 | - BYT_RT5651_IN1_IN2_MAP), + BYT_RT5651_MCLK_EN), + }, + { + /* Yours Y8W81 (and others using the same mainboard) */ + .callback = byt_rt5651_quirk_cb, + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "INSYDE Corp."), + /* Partial match for all devs with a W86C mainboard */ + DMI_MATCH(DMI_BIOS_VERSION, ".F.W86C."), + }, + .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | + BYT_RT5651_IN2_MAP | + BYT_RT5651_SSP0_AIF1 | + BYT_RT5651_MONO_SPEAKER), }, {} }; @@ -418,15 +469,10 @@ static const struct dmi_system_id byt_rt5651_quirk_table[] = { * Note this MUST be called before snd_soc_register_card(), so that the props * are in place before the codec component driver's probe function parses them. */ -static int byt_rt5651_add_codec_device_props(const char *i2c_dev_name) +static int byt_rt5651_add_codec_device_props(struct device *i2c_dev) { struct property_entry props[MAX_NO_PROPS] = {}; - struct device *i2c_dev; - int ret, cnt = 0; - - i2c_dev = bus_find_device_by_name(&i2c_bus_type, NULL, i2c_dev_name); - if (!i2c_dev) - return -EPROBE_DEFER; + int cnt = 0; props[cnt++] = PROPERTY_ENTRY_U32("realtek,jack-detect-source", BYT_RT5651_JDSRC(byt_rt5651_quirk)); @@ -440,10 +486,7 @@ static int byt_rt5651_add_codec_device_props(const char *i2c_dev_name) if (byt_rt5651_quirk & BYT_RT5651_DMIC_EN) props[cnt++] = PROPERTY_ENTRY_BOOL("realtek,dmic-en"); - ret = device_add_properties(i2c_dev, props); - put_device(i2c_dev); - - return ret; + return device_add_properties(i2c_dev, props); } static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) @@ -475,14 +518,6 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) custom_map = byt_rt5651_intmic_in1_in2_map; num_routes = ARRAY_SIZE(byt_rt5651_intmic_in1_in2_map); break; - case BYT_RT5651_IN1_HS_IN3_MAP: - custom_map = byt_rt5651_intmic_in1_hs_in3_map; - num_routes = ARRAY_SIZE(byt_rt5651_intmic_in1_hs_in3_map); - break; - case BYT_RT5651_IN2_HS_IN3_MAP: - custom_map = byt_rt5651_intmic_in2_hs_in3_map; - num_routes = ARRAY_SIZE(byt_rt5651_intmic_in2_hs_in3_map); - break; default: custom_map = byt_rt5651_intmic_dmic_map; num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic_map); @@ -546,13 +581,17 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) if (BYT_RT5651_JDSRC(byt_rt5651_quirk)) { ret = snd_soc_card_jack_new(runtime->card, "Headset", - SND_JACK_HEADSET, &priv->jack, - bytcr_jack_pins, ARRAY_SIZE(bytcr_jack_pins)); + SND_JACK_HEADSET | SND_JACK_BTN_0, + &priv->jack, bytcr_jack_pins, + ARRAY_SIZE(bytcr_jack_pins)); if (ret) { dev_err(runtime->dev, "jack creation failed %d\n", ret); return ret; } + snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, + KEY_PLAYPAUSE); + ret = snd_soc_component_set_jack(codec, &priv->jack, NULL); if (ret) return ret; @@ -691,6 +730,48 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = { }; /* SoC card */ +static char byt_rt5651_codec_name[SND_ACPI_I2C_ID_LEN]; +static char byt_rt5651_codec_aif_name[12]; /* = "rt5651-aif[1|2]" */ +static char byt_rt5651_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ +static char byt_rt5651_long_name[50]; /* = "bytcr-rt5651-*-spk-*-mic[-swapped-hp]" */ + +static int byt_rt5651_suspend(struct snd_soc_card *card) +{ + struct snd_soc_component *component; + + if (!BYT_RT5651_JDSRC(byt_rt5651_quirk)) + return 0; + + list_for_each_entry(component, &card->component_dev_list, card_list) { + if (!strcmp(component->name, byt_rt5651_codec_name)) { + dev_dbg(component->dev, "disabling jack detect before suspend\n"); + snd_soc_component_set_jack(component, NULL, NULL); + break; + } + } + + return 0; +} + +static int byt_rt5651_resume(struct snd_soc_card *card) +{ + struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component; + + if (!BYT_RT5651_JDSRC(byt_rt5651_quirk)) + return 0; + + list_for_each_entry(component, &card->component_dev_list, card_list) { + if (!strcmp(component->name, byt_rt5651_codec_name)) { + dev_dbg(component->dev, "re-enabling jack detect after resume\n"); + snd_soc_component_set_jack(component, &priv->jack, NULL); + break; + } + } + + return 0; +} + static struct snd_soc_card byt_rt5651_card = { .name = "bytcr-rt5651", .owner = THIS_MODULE, @@ -701,23 +782,86 @@ static struct snd_soc_card byt_rt5651_card = { .dapm_routes = byt_rt5651_audio_map, .num_dapm_routes = ARRAY_SIZE(byt_rt5651_audio_map), .fully_routed = true, + .suspend_pre = byt_rt5651_suspend, + .resume_post = byt_rt5651_resume, }; -static char byt_rt5651_codec_name[SND_ACPI_I2C_ID_LEN]; -static char byt_rt5651_codec_aif_name[12]; /* = "rt5651-aif[1|2]" */ -static char byt_rt5651_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ -static char byt_rt5651_long_name[40]; /* = "bytcr-rt5651-*-spk-*-mic" */ +static const struct x86_cpu_id baytrail_cpu_ids[] = { + { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT1 }, /* Valleyview */ + {} +}; + +static const struct x86_cpu_id cherrytrail_cpu_ids[] = { + { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT }, /* Braswell */ + {} +}; + +static const struct acpi_gpio_params first_gpio = { 0, 0, false }; +static const struct acpi_gpio_params second_gpio = { 1, 0, false }; + +static const struct acpi_gpio_mapping byt_rt5651_amp_en_first[] = { + { "ext-amp-enable-gpios", &first_gpio, 1 }, + { }, +}; -static bool is_valleyview(void) +static const struct acpi_gpio_mapping byt_rt5651_amp_en_second[] = { + { "ext-amp-enable-gpios", &second_gpio, 1 }, + { }, +}; + +/* + * Some boards have I2cSerialBusV2, GpioIo, GpioInt as ACPI resources, other + * boards may have I2cSerialBusV2, GpioInt, GpioIo instead. We want the + * GpioIo one for the ext-amp-enable-gpio and both count for the index in + * acpi_gpio_params index. So we have 2 different mappings and the code + * below figures out which one to use. + */ +struct byt_rt5651_acpi_resource_data { + int gpio_count; + int gpio_int_idx; +}; + +static int snd_byt_rt5651_acpi_resource(struct acpi_resource *ares, void *arg) { - static const struct x86_cpu_id cpu_ids[] = { - { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ - {} - }; - - if (!x86_match_cpu(cpu_ids)) - return false; - return true; + struct byt_rt5651_acpi_resource_data *data = arg; + + if (ares->type != ACPI_RESOURCE_TYPE_GPIO) + return 0; + + if (ares->data.gpio.connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) + data->gpio_int_idx = data->gpio_count; + + data->gpio_count++; + return 0; +} + +static void snd_byt_rt5651_mc_add_amp_en_gpio_mapping(struct device *codec) +{ + struct byt_rt5651_acpi_resource_data data = { 0, -1 }; + LIST_HEAD(resources); + int ret; + + ret = acpi_dev_get_resources(ACPI_COMPANION(codec), &resources, + snd_byt_rt5651_acpi_resource, &data); + if (ret < 0) { + dev_warn(codec, "Failed to get ACPI resources, not adding external amplifier GPIO mapping\n"); + return; + } + + /* All info we need is gathered during the walk */ + acpi_dev_free_resource_list(&resources); + + switch (data.gpio_int_idx) { + case 0: + devm_acpi_dev_add_driver_gpios(codec, byt_rt5651_amp_en_second); + break; + case 1: + devm_acpi_dev_add_driver_gpios(codec, byt_rt5651_amp_en_first); + break; + default: + dev_warn(codec, "Unknown GpioInt index %d, not adding external amplifier GPIO mapping\n", + data.gpio_int_idx); + } } struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ @@ -727,13 +871,12 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) { - const char * const intmic_name[] = - { "dmic", "in1", "in2", "in12", "in1", "in2" }; - const char * const hsmic_name[] = - { "in2", "in2", "in1", "in3", "in3", "in3" }; + const char * const mic_name[] = { "dmic", "in1", "in2", "in12" }; struct byt_rt5651_private *priv; struct snd_soc_acpi_mach *mach; + struct device *codec_dev; const char *i2c_name = NULL; + const char *hp_swapped; bool is_bytcr = false; int ret_val = 0; int dai_index = 0; @@ -767,11 +910,16 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) "%s%s", "i2c-", i2c_name); byt_rt5651_dais[dai_index].codec_name = byt_rt5651_codec_name; + codec_dev = bus_find_device_by_name(&i2c_bus_type, NULL, + byt_rt5651_codec_name); + if (!codec_dev) + return -EPROBE_DEFER; + /* * swap SSP0 if bytcr is detected * (will be overridden if DMI quirk is detected) */ - if (is_valleyview()) { + if (x86_match_cpu(baytrail_cpu_ids)) { struct sst_platform_info *p_info = mach->pdata; const struct sst_res_info *res_info = p_info->res_info; @@ -830,9 +978,37 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) dmi_check_system(byt_rt5651_quirk_table); /* Must be called before register_card, also see declaration comment. */ - ret_val = byt_rt5651_add_codec_device_props(byt_rt5651_codec_name); - if (ret_val) + ret_val = byt_rt5651_add_codec_device_props(codec_dev); + if (ret_val) { + put_device(codec_dev); return ret_val; + } + + /* Cherry Trail devices use an external amplifier enable gpio */ + if (x86_match_cpu(cherrytrail_cpu_ids)) { + snd_byt_rt5651_mc_add_amp_en_gpio_mapping(codec_dev); + priv->ext_amp_gpio = devm_fwnode_get_index_gpiod_from_child( + &pdev->dev, "ext-amp-enable", 0, + codec_dev->fwnode, + GPIOD_OUT_LOW, "speaker-amp"); + if (IS_ERR(priv->ext_amp_gpio)) { + ret_val = PTR_ERR(priv->ext_amp_gpio); + switch (ret_val) { + case -ENOENT: + priv->ext_amp_gpio = NULL; + break; + default: + dev_err(&pdev->dev, "Failed to get ext-amp-enable GPIO: %d\n", + ret_val); + /* fall through */ + case -EPROBE_DEFER: + put_device(codec_dev); + return ret_val; + } + } + } + + put_device(codec_dev); log_quirks(&pdev->dev); @@ -876,10 +1052,16 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) } } + if (byt_rt5651_quirk & BYT_RT5651_HP_LR_SWAPPED) + hp_swapped = "-hp-swapped"; + else + hp_swapped = ""; + snprintf(byt_rt5651_long_name, sizeof(byt_rt5651_long_name), - "bytcr-rt5651-%s-intmic-%s-hsmic", - intmic_name[BYT_RT5651_MAP(byt_rt5651_quirk)], - hsmic_name[BYT_RT5651_MAP(byt_rt5651_quirk)]); + "bytcr-rt5651-%s-spk-%s-mic%s", + (byt_rt5651_quirk & BYT_RT5651_MONO_SPEAKER) ? + "mono" : "stereo", + mic_name[BYT_RT5651_MAP(byt_rt5651_quirk)], hp_swapped); byt_rt5651_card.long_name = byt_rt5651_long_name; ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5651_card); diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c new file mode 100644 index 000000000000..c4b94e2617c5 --- /dev/null +++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Intel Corporation. + +/* + * Intel Geminilake I2S Machine Driver with MAX98357A & RT5682 Codecs + * + * Modified from: + * Intel Apollolake I2S Machine driver + */ + +#include <linux/input.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../skylake/skl.h" +#include "../../codecs/rt5682.h" +#include "../../codecs/hdac_hdmi.h" + +/* The platform clock outputs 19.2Mhz clock to codec as I2S MCLK */ +#define GLK_PLAT_CLK_FREQ 19200000 +#define RT5682_PLL_FREQ (48000 * 512) +#define GLK_REALTEK_CODEC_DAI "rt5682-aif1" +#define GLK_MAXIM_CODEC_DAI "HiFi" +#define MAXIM_DEV0_NAME "MX98357A:00" +#define DUAL_CHANNEL 2 +#define QUAD_CHANNEL 4 +#define NAME_SIZE 32 + +static struct snd_soc_jack geminilake_hdmi[3]; + +struct glk_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct glk_card_private { + struct snd_soc_jack geminilake_headset; + struct list_head hdmi_pcm_list; +}; + +enum { + GLK_DPCM_AUDIO_PB = 0, + GLK_DPCM_AUDIO_CP, + GLK_DPCM_AUDIO_HS_PB, + GLK_DPCM_AUDIO_ECHO_REF_CP, + GLK_DPCM_AUDIO_REF_CP, + GLK_DPCM_AUDIO_DMIC_CP, + GLK_DPCM_AUDIO_HDMI1_PB, + GLK_DPCM_AUDIO_HDMI2_PB, + GLK_DPCM_AUDIO_HDMI3_PB, +}; + +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + int ret = 0; + + codec_dai = snd_soc_card_get_codec_dai(card, GLK_REALTEK_CODEC_DAI); + if (!codec_dai) { + dev_err(card->dev, "Codec dai not found; Unable to set/unset codec pll\n"); + return -EIO; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + ret = snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); + if (ret) + dev_err(card->dev, "failed to stop sysclk: %d\n", ret); + } else if (SND_SOC_DAPM_EVENT_ON(event)) { + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5682_PLL1_S_MCLK, + GLK_PLAT_CLK_FREQ, RT5682_PLL_FREQ); + if (ret < 0) { + dev_err(card->dev, "can't set codec pll: %d\n", ret); + return ret; + } + } + + if (ret) + dev_err(card->dev, "failed to start internal clk: %d\n", ret); + + return ret; +} + +static const struct snd_kcontrol_new geminilake_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Spk"), +}; + +static const struct snd_soc_dapm_widget geminilake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Spk", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SPK("HDMI1", NULL), + SND_SOC_DAPM_SPK("HDMI2", NULL), + SND_SOC_DAPM_SPK("HDMI3", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route geminilake_map[] = { + /* HP jack connectors - unknown if we have jack detection */ + { "Headphone Jack", NULL, "Platform Clock" }, + { "Headphone Jack", NULL, "HPOL" }, + { "Headphone Jack", NULL, "HPOR" }, + + /* speaker */ + { "Spk", NULL, "Speaker" }, + + /* other jacks */ + { "Headset Mic", NULL, "Platform Clock" }, + { "IN1P", NULL, "Headset Mic" }, + + /* digital mics */ + { "DMic", NULL, "SoC DMIC" }, + + /* CODEC BE connections */ + { "HiFi Playback", NULL, "ssp1 Tx" }, + { "ssp1 Tx", NULL, "codec0_out" }, + + { "AIF1 Playback", NULL, "ssp2 Tx" }, + { "ssp2 Tx", NULL, "codec1_out" }, + + { "codec0_in", NULL, "ssp2 Rx" }, + { "ssp2 Rx", NULL, "AIF1 Capture" }, + + { "HDMI1", NULL, "hif5-0 Output" }, + { "HDMI2", NULL, "hif6-0 Output" }, + { "HDMI2", NULL, "hif7-0 Output" }, + + { "hifi3", NULL, "iDisp3 Tx" }, + { "iDisp3 Tx", NULL, "iDisp3_out" }, + { "hifi2", NULL, "iDisp2 Tx" }, + { "iDisp2 Tx", NULL, "iDisp2_out" }, + { "hifi1", NULL, "iDisp1 Tx" }, + { "iDisp1 Tx", NULL, "iDisp1_out" }, + + /* DMIC */ + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "DMIC AIF" }, +}; + +static int geminilake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will convert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = DUAL_CHANNEL; + + /* set SSP to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int geminilake_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct glk_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_jack *jack; + int ret; + + /* Configure sysclk for codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, + RT5682_PLL_FREQ, SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); + + /* + * Headset buttons map to the google Reference headset. + * These can be configured by userspace. + */ + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT, + &ctx->geminilake_headset, NULL, 0); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); + return ret; + } + + jack = &ctx->geminilake_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) { + dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); + return ret; + } + + return ret; +}; + +static int geminilake_rt5682_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + /* Set valid bitmask & configuration for I2S in 24 bit */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0, 0x0, 2, 24); + if (ret < 0) { + dev_err(rtd->dev, "set TDM slot err:%d\n", ret); + return ret; + } + + return ret; +} + +static struct snd_soc_ops geminilake_rt5682_ops = { + .hw_params = geminilake_rt5682_hw_params, +}; + +static int geminilake_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct glk_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct glk_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = GLK_DPCM_AUDIO_HDMI1_PB + dai->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static int geminilake_rt5682_fe_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = rtd->cpu_dai->component; + struct snd_soc_dapm_context *dapm; + int ret; + + dapm = snd_soc_component_get_dapm(component); + ret = snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); + if (ret) { + dev_err(rtd->dev, "Ref Cap ignore suspend failed %d\n", ret); + return ret; + } + + return ret; +} + +static const unsigned int rates[] = { + 48000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static const unsigned int channels[] = { + DUAL_CHANNEL, +}; + +static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static unsigned int channels_quad[] = { + QUAD_CHANNEL, +}; + +static struct snd_pcm_hw_constraint_list constraints_channels_quad = { + .count = ARRAY_SIZE(channels_quad), + .list = channels_quad, + .mask = 0, +}; + +static int geminilake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* + * set BE channel constraint as user FE channels + */ + channels->min = channels->max = 4; + + return 0; +} + +static int geminilake_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_min = runtime->hw.channels_max = QUAD_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels_quad); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static const struct snd_soc_ops geminilake_dmic_ops = { + .startup = geminilake_dmic_startup, +}; + +static const unsigned int rates_16000[] = { + 16000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_16000 = { + .count = ARRAY_SIZE(rates_16000), + .list = rates_16000, +}; + +static int geminilake_refcap_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_16000); +}; + +static const struct snd_soc_ops geminilake_refcap_ops = { + .startup = geminilake_refcap_startup, +}; + +/* geminilake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link geminilake_dais[] = { + /* Front End DAI links */ + [GLK_DPCM_AUDIO_PB] = { + .name = "Glk Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:0e.0", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .init = geminilake_rt5682_fe_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + }, + [GLK_DPCM_AUDIO_CP] = { + .name = "Glk Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:0e.0", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + }, + [GLK_DPCM_AUDIO_HS_PB] = { + .name = "Glk Audio Headset Playback", + .stream_name = "Headset Audio", + .cpu_dai_name = "System Pin2", + .platform_name = "0000:00:0e.0", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .dpcm_playback = 1, + .nonatomic = 1, + .dynamic = 1, + }, + [GLK_DPCM_AUDIO_ECHO_REF_CP] = { + .name = "Glk Audio Echo Reference cap", + .stream_name = "Echoreference Capture", + .cpu_dai_name = "Echoref Pin", + .platform_name = "0000:00:0e.0", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = NULL, + .capture_only = 1, + .nonatomic = 1, + }, + [GLK_DPCM_AUDIO_REF_CP] = { + .name = "Glk Audio Reference cap", + .stream_name = "Refcap", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &geminilake_refcap_ops, + }, + [GLK_DPCM_AUDIO_DMIC_CP] = { + .name = "Glk Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &geminilake_dmic_ops, + }, + [GLK_DPCM_AUDIO_HDMI1_PB] = { + .name = "Glk HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [GLK_DPCM_AUDIO_HDMI2_PB] = { + .name = "Glk HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [GLK_DPCM_AUDIO_HDMI3_PB] = { + .name = "Glk HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + /* Back End DAI links */ + { + /* SSP1 - Codec */ + .name = "SSP1-Codec", + .id = 0, + .cpu_dai_name = "SSP1 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = MAXIM_DEV0_NAME, + .codec_dai_name = GLK_MAXIM_CODEC_DAI, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = geminilake_ssp_fixup, + .dpcm_playback = 1, + }, + { + /* SSP2 - Codec */ + .name = "SSP2-Codec", + .id = 1, + .cpu_dai_name = "SSP2 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "i2c-10EC5682:00", + .codec_dai_name = GLK_REALTEK_CODEC_DAI, + .init = geminilake_rt5682_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = geminilake_ssp_fixup, + .ops = &geminilake_rt5682_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .id = 2, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .be_hw_params_fixup = geminilake_dmic_fixup, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + .name = "iDisp1", + .id = 3, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:0e.0", + .init = geminilake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:0e.0", + .init = geminilake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:0e.0", + .init = geminilake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +static int glk_card_late_probe(struct snd_soc_card *card) +{ + struct glk_card_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = NULL; + char jack_name[NAME_SIZE]; + struct glk_hdmi_pcm *pcm; + int err = 0; + int i = 0; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &geminilake_hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &geminilake_hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} + +/* geminilake audio machine driver for SPT + RT5682 */ +static struct snd_soc_card glk_audio_card_rt5682_m98357a = { + .name = "glkrt5682max", + .owner = THIS_MODULE, + .dai_link = geminilake_dais, + .num_links = ARRAY_SIZE(geminilake_dais), + .controls = geminilake_controls, + .num_controls = ARRAY_SIZE(geminilake_controls), + .dapm_widgets = geminilake_widgets, + .num_dapm_widgets = ARRAY_SIZE(geminilake_widgets), + .dapm_routes = geminilake_map, + .num_dapm_routes = ARRAY_SIZE(geminilake_map), + .fully_routed = true, + .late_probe = glk_card_late_probe, +}; + +static int geminilake_audio_probe(struct platform_device *pdev) +{ + struct glk_card_private *ctx; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + glk_audio_card_rt5682_m98357a.dev = &pdev->dev; + snd_soc_card_set_drvdata(&glk_audio_card_rt5682_m98357a, ctx); + + return devm_snd_soc_register_card(&pdev->dev, + &glk_audio_card_rt5682_m98357a); +} + +static const struct platform_device_id glk_board_ids[] = { + { + .name = "glk_rt5682_max98357a", + .driver_data = + (kernel_ulong_t)&glk_audio_card_rt5682_m98357a, + }, + { } +}; + +static struct platform_driver geminilake_audio = { + .probe = geminilake_audio_probe, + .driver = { + .name = "glk_rt5682_max98357a", + .pm = &snd_soc_pm_ops, + }, + .id_table = glk_board_ids, +}; +module_platform_driver(geminilake_audio) + +/* Module information */ +MODULE_DESCRIPTION("Geminilake Audio Machine driver-RT5682 & MAX98357A in I2S mode"); +MODULE_AUTHOR("Naveen Manohar <naveen.m@intel.com>"); +MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:glk_rt5682_max98357a"); diff --git a/sound/soc/intel/boards/kbl_da7219_max98357a.c b/sound/soc/intel/boards/kbl_da7219_max98357a.c index 94294c27d1db..38f6ab74709d 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98357a.c +++ b/sound/soc/intel/boards/kbl_da7219_max98357a.c @@ -152,7 +152,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP to 24 bit */ snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } @@ -380,6 +380,7 @@ static struct snd_soc_dai_link kabylake_dais[] = { .trigger = { SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_capture = 1, + .ops = &kabylake_da7219_fe_ops, }, [KBL_DPCM_AUDIO_DMIC_CP] = { .name = "Kbl Audio DMIC cap", diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index 3a61252fe450..21a6490746a6 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -434,14 +434,14 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = rate->max = 48000; channels->min = channels->max = 2; snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); } /* * The speaker on the SSP0 supports S16_LE and not S24_LE. * thus changing the mask here */ if (!strcmp(be_dai_link->name, "SSP0-Codec")) - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); return 0; } diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index 92f5fb2ae0a3..a892b37eab7c 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -307,7 +307,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = rate->max = 48000; channels->min = channels->max = 2; snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); } else if (!strcmp(fe_dai_link->name, "Kbl Audio DMIC cap")) { if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2) @@ -320,7 +320,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, * thus changing the mask here */ if (!strcmp(be_dai_link->name, "SSP0-Codec")) - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); return 0; } diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 3ff6646cfa21..d31482b8c9bb 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -157,7 +157,7 @@ static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP0 to 24 bit */ snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index b0610bba3cfa..e877bb60beb1 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -346,7 +346,7 @@ static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP0 to 24 bit */ snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 38a1495c29cf..0e1818dd4cc6 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -229,7 +229,7 @@ static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP0 to 24 bit */ snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 7379d8830c39..915a34cdc8ac 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -3,7 +3,11 @@ snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o snd-soc-sst-firmware-objs := sst-firmware.o -snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o soc-acpi-intel-hsw-bdw-match.o +snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o \ + soc-acpi-intel-hsw-bdw-match.o \ + soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ + soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ + soc-acpi-intel-cnl-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c new file mode 100644 index 000000000000..f39386e540d3 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-bxt-match.c - tables and support for BXT ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> + +static struct snd_soc_acpi_codecs bxt_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { + { + .id = "INT343A", + .drv_name = "bxt_alc298s_i2s", + .fw_filename = "intel/dsp_fw_bxtn.bin", + }, + { + .id = "DLGS7219", + .drv_name = "bxt_da7219_max98357a", + .fw_filename = "intel/dsp_fw_bxtn.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &bxt_codecs, + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-da7219.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "104C5122", + .drv_name = "bxt-pcm512x", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-pcm512x.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "1AEC8804", + .drv_name = "bxt-wm8804", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-wm8804.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "INT34C3", + .drv_name = "bxt_tdf8532", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-tdf8532.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c index bfe1ca68a542..4daa8a4f0c0c 100644 --- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -59,8 +59,8 @@ static struct snd_soc_acpi_mach byt_thinkpad_10 = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5670.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }; @@ -98,8 +98,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", .machine_quirk = byt_quirk, - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -107,8 +107,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -116,8 +116,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -125,8 +125,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5651.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5651.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -134,8 +134,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -143,8 +143,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some Baytrail platforms rely on RT5645, use CHT machine driver */ @@ -153,8 +153,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -162,8 +162,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* use CHT driver to Baytrail Chromebooks */ @@ -172,8 +172,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-max98090.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-max98090.tplg", .asoc_plat_name = "sst-mfld-platform", }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index ad1eb2d644be..91bb99b69601 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -44,8 +44,8 @@ static struct snd_soc_acpi_mach cht_surface_mach = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }; @@ -68,8 +68,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -77,8 +77,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -86,8 +86,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -95,8 +95,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -104,8 +104,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -113,8 +113,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-max98090.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-max98090.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -122,8 +122,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-nau8824", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-nau8824.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-nau8824.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -131,8 +131,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -140,8 +140,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -149,8 +149,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_es8316", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_es8316", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-es8316.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-es8316.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ @@ -160,8 +160,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", .machine_quirk = cht_quirk, - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -169,8 +169,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ @@ -179,8 +179,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5651.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5651.tplg", .asoc_plat_name = "sst-mfld-platform", }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c new file mode 100644 index 000000000000..ec8e28e7b937 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-cnl-match.c - tables and support for CNL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include "../skylake/skl.h" + +static struct skl_machine_pdata cnl_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { + { + .id = "INT34C2", + .drv_name = "cnl_rt274", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + .sof_fw_filename = "intel/sof-cnl.ri", + .sof_tplg_filename = "intel/sof-cnl-rt274.tplg", + .asoc_plat_name = "0000:00:1f.3", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c new file mode 100644 index 000000000000..305875af71ca --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-glk-match.c - tables and support for GLK ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> + +static struct snd_soc_acpi_codecs glk_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { + { + .id = "INT343A", + .drv_name = "glk_alc298s_i2s", + .fw_filename = "intel/dsp_fw_glk.bin", + .sof_fw_filename = "intel/sof-glk.ri", + .sof_tplg_filename = "intel/sof-glk-alc298.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "DLGS7219", + .drv_name = "glk_da7219_max98357a", + .fw_filename = "intel/dsp_fw_glk.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &glk_codecs, + .sof_fw_filename = "intel/sof-glk.ri", + .sof_tplg_filename = "intel/sof-glk-da7219.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_glk_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c index e0e8c8c27528..494a0ea9b029 100644 --- a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c @@ -23,8 +23,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[] = { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST1.bin", - .sof_fw_filename = "intel/reef-hsw.ri", - .sof_tplg_filename = "intel/reef-hsw.tplg", + .sof_fw_filename = "intel/sof-hsw.ri", + .sof_tplg_filename = "intel/sof-hsw.tplg", .asoc_plat_name = "haswell-pcm-audio", }, {} @@ -36,24 +36,24 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { .id = "INT343A", .drv_name = "broadwell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt286.tplg", .asoc_plat_name = "haswell-pcm-audio", }, { .id = "RT5677CE", .drv_name = "bdw-rt5677", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt5677.tplg", .asoc_plat_name = "haswell-pcm-audio", }, { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt5640.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt5640.tplg", .asoc_plat_name = "haswell-pcm-audio", }, {} diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c new file mode 100644 index 000000000000..0ee173ca437d --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-kbl-match.c - tables and support for KBL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include "../skylake/skl.h" + +static struct skl_machine_pdata skl_dmic_data; + +static struct snd_soc_acpi_codecs kbl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +static struct snd_soc_acpi_codecs kbl_poppy_codecs = { + .num_codecs = 1, + .codecs = {"10EC5663"} +}; + +static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { + .num_codecs = 2, + .codecs = {"10EC5663", "10EC5514"} +}; + +static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { + { + .id = "INT343A", + .drv_name = "kbl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "INT343B", + .drv_name = "kbl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98357A", + .drv_name = "kbl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98927", + .drv_name = "kbl_r5514_5663_max", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_5663_5514_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98927", + .drv_name = "kbl_rt5663_m98927", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_poppy_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "10EC5663", + .drv_name = "kbl_rt5663", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "DLGS7219", + .drv_name = "kbl_da7219_max98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_7219_98357_codecs, + .pdata = &skl_dmic_data, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_kbl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-skl-match.c b/sound/soc/intel/common/soc-acpi-intel-skl-match.c new file mode 100644 index 000000000000..0c9c0edd35b3 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-skl-match.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-skl-match.c - tables and support for SKL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include "../skylake/skl.h" + +static struct skl_machine_pdata skl_dmic_data; + +static struct snd_soc_acpi_codecs skl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_skl_machines[] = { + { + .id = "INT343A", + .drv_name = "skl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_release.bin", + }, + { + .id = "INT343B", + .drv_name = "skl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98357A", + .drv_name = "skl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_skl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index 657afc02f1c4..11041aedea31 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -270,7 +270,7 @@ void sst_dsp_dma_put_channel(struct sst_dsp *dsp) } EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel); -int sst_dma_new(struct sst_dsp *sst) +static int sst_dma_new(struct sst_dsp *sst) { struct sst_pdata *sst_pdata = sst->pdata; struct sst_dma *dma; @@ -320,9 +320,8 @@ err_dma_dev: devm_kfree(sst->dev, dma); return ret; } -EXPORT_SYMBOL(sst_dma_new); -void sst_dma_free(struct sst_dma *dma) +static void sst_dma_free(struct sst_dma *dma) { if (dma == NULL) @@ -335,7 +334,6 @@ void sst_dma_free(struct sst_dma *dma) dw_remove(dma->chip); } -EXPORT_SYMBOL(sst_dma_free); /* create new generic firmware object */ struct sst_fw *sst_fw_new(struct sst_dsp *dsp, diff --git a/sound/soc/intel/haswell/sst-haswell-dsp.c b/sound/soc/intel/haswell/sst-haswell-dsp.c index b2bec36d074c..a28220e67cdf 100644 --- a/sound/soc/intel/haswell/sst-haswell-dsp.c +++ b/sound/soc/intel/haswell/sst-haswell-dsp.c @@ -93,29 +93,31 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, struct sst_module_template template; int count, ret; void __iomem *ram; + int type = le16_to_cpu(module->type); + int entry_point = le32_to_cpu(module->entry_point); /* TODO: allowed module types need to be configurable */ - if (module->type != SST_HSW_MODULE_BASE_FW - && module->type != SST_HSW_MODULE_PCM_SYSTEM - && module->type != SST_HSW_MODULE_PCM - && module->type != SST_HSW_MODULE_PCM_REFERENCE - && module->type != SST_HSW_MODULE_PCM_CAPTURE - && module->type != SST_HSW_MODULE_WAVES - && module->type != SST_HSW_MODULE_LPAL) + if (type != SST_HSW_MODULE_BASE_FW && + type != SST_HSW_MODULE_PCM_SYSTEM && + type != SST_HSW_MODULE_PCM && + type != SST_HSW_MODULE_PCM_REFERENCE && + type != SST_HSW_MODULE_PCM_CAPTURE && + type != SST_HSW_MODULE_WAVES && + type != SST_HSW_MODULE_LPAL) return 0; dev_dbg(dsp->dev, "new module sign 0x%s size 0x%x blocks 0x%x type 0x%x\n", module->signature, module->mod_size, - module->blocks, module->type); - dev_dbg(dsp->dev, " entrypoint 0x%x\n", module->entry_point); + module->blocks, type); + dev_dbg(dsp->dev, " entrypoint 0x%x\n", entry_point); dev_dbg(dsp->dev, " persistent 0x%x scratch 0x%x\n", module->info.persistent_size, module->info.scratch_size); memset(&template, 0, sizeof(template)); - template.id = module->type; - template.entry = module->entry_point - 4; - template.persistent_size = module->info.persistent_size; - template.scratch_size = module->info.scratch_size; + template.id = type; + template.entry = entry_point - 4; + template.persistent_size = le32_to_cpu(module->info.persistent_size); + template.scratch_size = le32_to_cpu(module->info.scratch_size); mod = sst_module_new(fw, &template, NULL); if (mod == NULL) @@ -123,26 +125,26 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, block = (void *)module + sizeof(*module); - for (count = 0; count < module->blocks; count++) { + for (count = 0; count < le32_to_cpu(module->blocks); count++) { - if (block->size <= 0) { + if (le32_to_cpu(block->size) <= 0) { dev_err(dsp->dev, "error: block %d size invalid\n", count); sst_module_free(mod); return -EINVAL; } - switch (block->type) { + switch (le32_to_cpu(block->type)) { case SST_HSW_IRAM: ram = dsp->addr.lpe; - mod->offset = - block->ram_offset + dsp->addr.iram_offset; + mod->offset = le32_to_cpu(block->ram_offset) + + dsp->addr.iram_offset; mod->type = SST_MEM_IRAM; break; case SST_HSW_DRAM: case SST_HSW_REGS: ram = dsp->addr.lpe; - mod->offset = block->ram_offset; + mod->offset = le32_to_cpu(block->ram_offset); mod->type = SST_MEM_DRAM; break; default: @@ -152,7 +154,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, return -EINVAL; } - mod->size = block->size; + mod->size = le32_to_cpu(block->size); mod->data = (void *)block + sizeof(*block); mod->data_offset = mod->data - fw->dma_buf; @@ -169,7 +171,8 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, return ret; } - block = (void *)block + sizeof(*block) + block->size; + block = (void *)block + sizeof(*block) + + le32_to_cpu(block->size); } mod->state = SST_MODULE_STATE_LOADED; @@ -188,7 +191,8 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw) /* verify FW */ if ((strncmp(header->signature, SST_HSW_FW_SIGN, 4) != 0) || - (sst_fw->size != header->file_size + sizeof(*header))) { + (sst_fw->size != + le32_to_cpu(header->file_size) + sizeof(*header))) { dev_err(dsp->dev, "error: invalid fw sign/filesize mismatch\n"); return -EINVAL; } @@ -199,7 +203,7 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw) /* parse each module */ module = (void *)sst_fw->dma_buf + sizeof(*header); - for (count = 0; count < header->modules; count++) { + for (count = 0; count < le32_to_cpu(header->modules); count++) { /* module */ ret = hsw_parse_module(dsp, sst_fw, module); @@ -207,7 +211,8 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw) dev_err(dsp->dev, "error: invalid module %d\n", count); return ret; } - module = (void *)module + sizeof(*module) + module->mod_size; + module = (void *)module + sizeof(*module) + + le32_to_cpu(module->mod_size); } return 0; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index d7fc3b2d3e68..823e39103edd 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1014,10 +1014,11 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }; -int skl_dai_load(struct snd_soc_component *cmp, - struct snd_soc_dai_driver *pcm_dai) +int skl_dai_load(struct snd_soc_component *cmp, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { - pcm_dai->ops = &skl_pcm_dai_ops; + dai_drv->ops = &skl_pcm_dai_ops; return 0; } diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c index d2b1d60fec02..5bc0d38da7e3 100644 --- a/sound/soc/intel/skylake/skl-sst-cldma.c +++ b/sound/soc/intel/skylake/skl-sst-cldma.c @@ -83,9 +83,9 @@ static void skl_cldma_stream_clear(struct sst_dsp *ctx) /* Code loader helper APIs */ static void skl_cldma_setup_bdle(struct sst_dsp *ctx, struct snd_dma_buffer *dmab_data, - u32 **bdlp, int size, int with_ioc) + __le32 **bdlp, int size, int with_ioc) { - u32 *bdl = *bdlp; + __le32 *bdl = *bdlp; ctx->cl_dev.frags = 0; while (size > 0) { @@ -330,7 +330,7 @@ void skl_cldma_process_intr(struct sst_dsp *ctx) int skl_cldma_prepare(struct sst_dsp *ctx) { int ret; - u32 *bdl; + __le32 *bdl; ctx->cl_dev.bufsize = SKL_MAX_BUFFER_SIZE; @@ -359,7 +359,7 @@ int skl_cldma_prepare(struct sst_dsp *ctx) ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data); return ret; } - bdl = (u32 *)ctx->cl_dev.dmab_bdl.area; + bdl = (__le32 *)ctx->cl_dev.dmab_bdl.area; /* Allocate BDLs */ ctx->cl_dev.ops.cl_setup_bdle(ctx, &ctx->cl_dev.dmab_data, diff --git a/sound/soc/intel/skylake/skl-sst-cldma.h b/sound/soc/intel/skylake/skl-sst-cldma.h index 5b730a1a0ae4..ec736921a083 100644 --- a/sound/soc/intel/skylake/skl-sst-cldma.h +++ b/sound/soc/intel/skylake/skl-sst-cldma.h @@ -203,7 +203,7 @@ struct sst_dsp; struct skl_cl_dev_ops { void (*cl_setup_bdle)(struct sst_dsp *ctx, struct snd_dma_buffer *dmab_data, - u32 **bdlp, int size, int with_ioc); + __le32 **bdlp, int size, int with_ioc); void (*cl_setup_controller)(struct sst_dsp *ctx, struct snd_dma_buffer *dmab_bdl, unsigned int max_size, u32 page_count); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index abfdb67c05cc..2620d77729c5 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -108,6 +108,9 @@ static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w, case snd_soc_dapm_aif_out: case snd_soc_dapm_dai_out: case snd_soc_dapm_switch: + case snd_soc_dapm_output: + case snd_soc_dapm_mux: + return false; default: return true; @@ -3024,7 +3027,7 @@ void skl_cleanup_resources(struct skl *skl) * information to the driver about module and pipeline parameters which DSP * FW expects like ids, resource values, formats etc */ -static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, +static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { @@ -3130,6 +3133,7 @@ static int skl_init_enum_data(struct device *dev, struct soc_enum *se, } static int skl_tplg_control_load(struct snd_soc_component *cmpnt, + int index, struct snd_kcontrol_new *kctl, struct snd_soc_tplg_ctl_hdr *hdr) { @@ -3617,7 +3621,7 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, return 0; } -static int skl_manifest_load(struct snd_soc_component *cmpnt, +static int skl_manifest_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_tplg_manifest *manifest) { struct hdac_bus *bus = snd_soc_component_get_drvdata(cmpnt); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index daeb6d2bb7fc..82282cac9751 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -512,8 +512,9 @@ int skl_pcm_host_dma_prepare(struct device *dev, int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params); -int skl_dai_load(struct snd_soc_component *cmp, - struct snd_soc_dai_driver *pcm_dai); +int skl_dai_load(struct snd_soc_component *cmp, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai); void skl_tplg_add_moduleid_in_bind_params(struct skl *skl, struct snd_soc_dapm_widget *w); #endif diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 00e051467a40..dce649485649 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -29,6 +29,7 @@ #include <linux/delay.h> #include <sound/pcm.h> #include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> #include <sound/hda_register.h> #include <sound/hdaudio.h> #include <sound/hda_i915.h> @@ -36,8 +37,6 @@ #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" -static struct skl_machine_pdata skl_dmic_data; - /* * initialize the PCI registers */ @@ -487,10 +486,12 @@ static int skl_find_machine(struct skl *skl, void *driver_data) skl->mach = mach; skl->fw_name = mach->fw_filename; - pdata = skl->mach->pdata; + pdata = mach->pdata; - if (mach->pdata) + if (pdata) { skl->use_tplg_pcm = pdata->use_tplg_pcm; + pdata->dmic_num = skl_get_dmic_geo(skl); + } return 0; } @@ -917,8 +918,6 @@ static int skl_probe(struct pci_dev *pci, pci_set_drvdata(skl->pci, bus); - skl_dmic_data.dmic_num = skl_get_dmic_geo(skl); - /* check if dsp is there */ if (bus->ppcap) { /* create device for dsp clk */ @@ -1012,172 +1011,23 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } -static struct snd_soc_acpi_codecs skl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - -static struct snd_soc_acpi_codecs kbl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - -static struct snd_soc_acpi_codecs bxt_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - -static struct snd_soc_acpi_codecs kbl_poppy_codecs = { - .num_codecs = 1, - .codecs = {"10EC5663"} -}; - -static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { - .num_codecs = 2, - .codecs = {"10EC5663", "10EC5514"} -}; - -static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - -static struct skl_machine_pdata cnl_pdata = { - .use_tplg_pcm = true, -}; - -static struct snd_soc_acpi_mach sst_skl_devdata[] = { - { - .id = "INT343A", - .drv_name = "skl_alc286s_i2s", - .fw_filename = "intel/dsp_fw_release.bin", - }, - { - .id = "INT343B", - .drv_name = "skl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "skl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - {} -}; - -static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { - { - .id = "INT343A", - .drv_name = "bxt_alc298s_i2s", - .fw_filename = "intel/dsp_fw_bxtn.bin", - }, - { - .id = "DLGS7219", - .drv_name = "bxt_da7219_max98357a_i2s", - .fw_filename = "intel/dsp_fw_bxtn.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &bxt_codecs, - }, - {} -}; - -static struct snd_soc_acpi_mach sst_kbl_devdata[] = { - { - .id = "INT343A", - .drv_name = "kbl_alc286s_i2s", - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "INT343B", - .drv_name = "kbl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "kbl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_r5514_5663_max", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_5663_5514_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_rt5663_m98927", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_poppy_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "10EC5663", - .drv_name = "kbl_rt5663", - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "DLGS7219", - .drv_name = "kbl_da7219_max98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_7219_98357_codecs, - .pdata = &skl_dmic_data - }, - - {} -}; - -static struct snd_soc_acpi_mach sst_glk_devdata[] = { - { - .id = "INT343A", - .drv_name = "glk_alc298s_i2s", - .fw_filename = "intel/dsp_fw_glk.bin", - }, - {} -}; - -static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { - { - .id = "INT34C2", - .drv_name = "cnl_rt274", - .fw_filename = "intel/dsp_fw_cnl.bin", - .pdata = &cnl_pdata, - }, - {} -}; - /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), - .driver_data = (unsigned long)&sst_skl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_skl_machines}, /* BXT-P */ { PCI_DEVICE(0x8086, 0x5a98), - .driver_data = (unsigned long)&sst_bxtp_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_bxt_machines}, /* KBL */ { PCI_DEVICE(0x8086, 0x9D71), - .driver_data = (unsigned long)&sst_kbl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_kbl_machines}, /* GLK */ { PCI_DEVICE(0x8086, 0x3198), - .driver_data = (unsigned long)&sst_glk_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_glk_machines}, /* CNL */ { PCI_DEVICE(0x8086, 0x9dc8), - .driver_data = (unsigned long)&sst_cnl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c index 51ec4ff6ed95..697aa50aff9a 100644 --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c @@ -15,20 +15,12 @@ int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe) { - struct snd_soc_dai_driver *sub_dai_drivers; + struct mtk_base_afe_dai *dai; size_t num_dai_drivers = 0, dai_idx = 0; - int i; - - if (!afe->sub_dais) { - dev_err(afe->dev, "%s(), sub_dais == NULL\n", __func__); - return -EINVAL; - } /* calcualte total dai driver size */ - for (i = 0; i < afe->num_sub_dais; i++) { - if (afe->sub_dais[i].dai_drivers && - afe->sub_dais[i].num_dai_drivers != 0) - num_dai_drivers += afe->sub_dais[i].num_dai_drivers; + list_for_each_entry(dai, &afe->sub_dais, list) { + num_dai_drivers += dai->num_dai_drivers; } dev_info(afe->dev, "%s(), num of dai %zd\n", __func__, num_dai_drivers); @@ -42,19 +34,14 @@ int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe) if (!afe->dai_drivers) return -ENOMEM; - for (i = 0; i < afe->num_sub_dais; i++) { - if (afe->sub_dais[i].dai_drivers && - afe->sub_dais[i].num_dai_drivers != 0) { - sub_dai_drivers = afe->sub_dais[i].dai_drivers; - /* dai driver */ - memcpy(&afe->dai_drivers[dai_idx], - sub_dai_drivers, - afe->sub_dais[i].num_dai_drivers * - sizeof(struct snd_soc_dai_driver)); - dai_idx += afe->sub_dais[i].num_dai_drivers; - } + list_for_each_entry(dai, &afe->sub_dais, list) { + /* dai driver */ + memcpy(&afe->dai_drivers[dai_idx], + dai->dai_drivers, + dai->num_dai_drivers * + sizeof(struct snd_soc_dai_driver)); + dai_idx += dai->num_dai_drivers; } - return 0; } EXPORT_SYMBOL_GPL(mtk_afe_combine_sub_dai); @@ -62,28 +49,25 @@ EXPORT_SYMBOL_GPL(mtk_afe_combine_sub_dai); int mtk_afe_add_sub_dai_control(struct snd_soc_component *component) { struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); - int i; + struct mtk_base_afe_dai *dai; - if (!afe->sub_dais) { - dev_err(afe->dev, "%s(), sub_dais == NULL\n", __func__); - return -EINVAL; - } - - for (i = 0; i < afe->num_sub_dais; i++) { - if (afe->sub_dais[i].controls) + list_for_each_entry(dai, &afe->sub_dais, list) { + if (dai->controls) snd_soc_add_component_controls(component, - afe->sub_dais[i].controls, - afe->sub_dais[i].num_controls); + dai->controls, + dai->num_controls); - if (afe->sub_dais[i].dapm_widgets) + if (dai->dapm_widgets) snd_soc_dapm_new_controls(&component->dapm, - afe->sub_dais[i].dapm_widgets, - afe->sub_dais[i].num_dapm_widgets); - - if (afe->sub_dais[i].dapm_routes) + dai->dapm_widgets, + dai->num_dapm_widgets); + } + /* add routes after all widgets are added */ + list_for_each_entry(dai, &afe->sub_dais, list) { + if (dai->dapm_routes) snd_soc_dapm_add_routes(&component->dapm, - afe->sub_dais[i].dapm_routes, - afe->sub_dais[i].num_dapm_routes); + dai->dapm_routes, + dai->num_dapm_routes); } snd_soc_dapm_new_widgets(component->dapm.card); diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h index bcf562f029b6..bd8d5e0c6843 100644 --- a/sound/soc/mediatek/common/mtk-base-afe.h +++ b/sound/soc/mediatek/common/mtk-base-afe.h @@ -46,6 +46,7 @@ struct mtk_base_irq_data { }; struct device; +struct list_head; struct mtk_base_afe_memif; struct mtk_base_afe_irq; struct mtk_base_afe_dai; @@ -72,8 +73,7 @@ struct mtk_base_afe { struct mtk_base_afe_irq *irqs; int irqs_size; - struct mtk_base_afe_dai *sub_dais; - int num_sub_dais; + struct list_head sub_dais; struct snd_soc_dai_driver *dai_drivers; unsigned int num_dai_drivers; @@ -110,6 +110,8 @@ struct mtk_base_afe_dai { unsigned int num_dapm_widgets; const struct snd_soc_dapm_route *dapm_routes; unsigned int num_dapm_routes; + + struct list_head list; }; #endif diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-common.h b/sound/soc/mediatek/mt6797/mt6797-afe-common.h index 22eb7b455cf1..4eac9977b2b0 100644 --- a/sound/soc/mediatek/mt6797/mt6797-afe-common.h +++ b/sound/soc/mediatek/mt6797/mt6797-afe-common.h @@ -10,6 +10,7 @@ #define _MT_6797_AFE_COMMON_H_ #include <sound/soc.h> +#include <linux/list.h> #include <linux/regmap.h> #include "../common/mtk-base-afe.h" diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c index 6c5dd9fc9976..192f4d7b37b6 100644 --- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c @@ -733,6 +733,34 @@ static const struct snd_soc_component_driver mt6797_afe_component = { .probe = mt6797_afe_component_probe, }; +static int mt6797_dai_memif_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mt6797_memif_dai_driver; + dai->num_dai_drivers = ARRAY_SIZE(mt6797_memif_dai_driver); + + dai->dapm_widgets = mt6797_memif_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mt6797_memif_widgets); + dai->dapm_routes = mt6797_memif_routes; + dai->num_dapm_routes = ARRAY_SIZE(mt6797_memif_routes); + return 0; +} + +typedef int (*dai_register_cb)(struct mtk_base_afe *); +static const dai_register_cb dai_register_cbs[] = { + mt6797_dai_adda_register, + mt6797_dai_pcm_register, + mt6797_dai_hostless_register, + mt6797_dai_memif_register, +}; + static int mt6797_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; @@ -811,29 +839,24 @@ static int mt6797_afe_pcm_dev_probe(struct platform_device *pdev) } /* init sub_dais */ - afe->num_sub_dais = MT6797_DAI_NUM; - afe->sub_dais = devm_kcalloc(dev, afe->num_sub_dais, - sizeof(*afe->sub_dais), - GFP_KERNEL); - if (!afe->sub_dais) - return -ENOMEM; - - mt6797_dai_adda_register(afe); - mt6797_dai_pcm_register(afe); - mt6797_dai_hostless_register(afe); - - afe->sub_dais[MT6797_MEMIF_DL1].dai_drivers = mt6797_memif_dai_driver; - afe->sub_dais[MT6797_MEMIF_DL1].num_dai_drivers = - ARRAY_SIZE(mt6797_memif_dai_driver); - afe->sub_dais[MT6797_MEMIF_DL1].dapm_widgets = mt6797_memif_widgets; - afe->sub_dais[MT6797_MEMIF_DL1].num_dapm_widgets = - ARRAY_SIZE(mt6797_memif_widgets); - afe->sub_dais[MT6797_MEMIF_DL1].dapm_routes = mt6797_memif_routes; - afe->sub_dais[MT6797_MEMIF_DL1].num_dapm_routes = - ARRAY_SIZE(mt6797_memif_routes); + INIT_LIST_HEAD(&afe->sub_dais); + + for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) { + ret = dai_register_cbs[i](afe); + if (ret) { + dev_warn(afe->dev, "dai register i %d fail, ret %d\n", + i, ret); + return ret; + } + } /* init dai_driver and component_driver */ - mtk_afe_combine_sub_dai(afe); + ret = mtk_afe_combine_sub_dai(afe); + if (ret) { + dev_warn(afe->dev, "mtk_afe_combine_sub_dai fail, ret %d\n", + ret); + return ret; + } afe->mtk_afe_hardware = &mt6797_afe_hardware; afe->memif_fs = mt6797_memif_fs; diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-adda.c b/sound/soc/mediatek/mt6797/mt6797-dai-adda.c index ad083265ce94..0ac6409c6d61 100644 --- a/sound/soc/mediatek/mt6797/mt6797-dai-adda.c +++ b/sound/soc/mediatek/mt6797/mt6797-dai-adda.c @@ -383,14 +383,20 @@ static struct snd_soc_dai_driver mtk_dai_adda_driver[] = { int mt6797_dai_adda_register(struct mtk_base_afe *afe) { - int id = MT6797_DAI_ADDA; + struct mtk_base_afe_dai *dai; - afe->sub_dais[id].dai_drivers = mtk_dai_adda_driver; - afe->sub_dais[id].num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver); + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; - afe->sub_dais[id].dapm_widgets = mtk_dai_adda_widgets; - afe->sub_dais[id].num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets); - afe->sub_dais[id].dapm_routes = mtk_dai_adda_routes; - afe->sub_dais[id].num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes); + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_adda_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver); + + dai->dapm_widgets = mtk_dai_adda_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets); + dai->dapm_routes = mtk_dai_adda_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes); return 0; } diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c b/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c index 4cf985b15a11..ed23e6a53b08 100644 --- a/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c +++ b/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c @@ -100,13 +100,19 @@ static struct snd_soc_dai_driver mtk_dai_hostless_driver[] = { int mt6797_dai_hostless_register(struct mtk_base_afe *afe) { - int id = MT6797_DAI_HOSTLESS_LPBK; + struct mtk_base_afe_dai *dai; - afe->sub_dais[id].dai_drivers = mtk_dai_hostless_driver; - afe->sub_dais[id].num_dai_drivers = ARRAY_SIZE(mtk_dai_hostless_driver); + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; - afe->sub_dais[id].dapm_routes = mtk_dai_hostless_routes; - afe->sub_dais[id].num_dapm_routes = ARRAY_SIZE(mtk_dai_hostless_routes); + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_hostless_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_hostless_driver); + + dai->dapm_routes = mtk_dai_hostless_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_hostless_routes); return 0; } diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c index 16d5b5067204..3136f0bc7827 100644 --- a/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c @@ -298,15 +298,20 @@ static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = { int mt6797_dai_pcm_register(struct mtk_base_afe *afe) { - int id = MT6797_DAI_PCM_1; + struct mtk_base_afe_dai *dai; - afe->sub_dais[id].dai_drivers = mtk_dai_pcm_driver; - afe->sub_dais[id].num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver); + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; - afe->sub_dais[id].dapm_widgets = mtk_dai_pcm_widgets; - afe->sub_dais[id].num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets); - afe->sub_dais[id].dapm_routes = mtk_dai_pcm_routes; - afe->sub_dais[id].num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes); + list_add(&dai->list, &afe->sub_dais); + dai->dai_drivers = mtk_dai_pcm_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver); + + dai->dapm_widgets = mtk_dai_pcm_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets); + dai->dapm_routes = mtk_dai_pcm_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes); return 0; } diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig new file mode 100644 index 000000000000..8af8bc358a90 --- /dev/null +++ b/sound/soc/meson/Kconfig @@ -0,0 +1,65 @@ +menu "ASoC support for Amlogic platforms" + depends on ARCH_MESON || COMPILE_TEST + +config SND_MESON_AXG_FIFO + tristate + select REGMAP_MMIO + +config SND_MESON_AXG_FRDDR + tristate "Amlogic AXG Playback FIFO support" + select SND_MESON_AXG_FIFO + help + Select Y or M to add support for the frontend playback interfaces + embedded in the Amlogic AXG SoC family + +config SND_MESON_AXG_TODDR + tristate "Amlogic AXG Capture FIFO support" + select SND_MESON_AXG_FIFO + help + Select Y or M to add support for the frontend capture interfaces + embedded in the Amlogic AXG SoC family + +config SND_MESON_AXG_TDM_FORMATTER + tristate + select REGMAP_MMIO + +config SND_MESON_AXG_TDM_INTERFACE + tristate + select SND_MESON_AXG_TDM_FORMATTER + +config SND_MESON_AXG_TDMIN + tristate "Amlogic AXG TDM Input Support" + select SND_MESON_AXG_TDM_FORMATTER + select SND_MESON_AXG_TDM_INTERFACE + help + Select Y or M to add support for TDM input formatter embedded + in the Amlogic AXG SoC family + +config SND_MESON_AXG_TDMOUT + tristate "Amlogic AXG TDM Output Support" + select SND_MESON_AXG_TDM_FORMATTER + select SND_MESON_AXG_TDM_INTERFACE + help + Select Y or M to add support for TDM output formatter embedded + in the Amlogic AXG SoC family + +config SND_MESON_AXG_SOUND_CARD + tristate "Amlogic AXG Sound Card Support" + select SND_MESON_AXG_TDM_INTERFACE + imply SND_MESON_AXG_FRDDR + imply SND_MESON_AXG_TODDR + imply SND_MESON_AXG_TDMIN + imply SND_MESON_AXG_TDMOUT + imply SND_MESON_AXG_SPDIFOUT + help + Select Y or M to add support for the AXG SoC sound card + +config SND_MESON_AXG_SPDIFOUT + tristate "Amlogic AXG SPDIF Output Support" + select SND_PCM_IEC958 + imply SND_SOC_SPDIF + help + Select Y or M to add support for SPDIF output serializer embedded + in the Amlogic AXG SoC family + +endmenu diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile new file mode 100644 index 000000000000..c5e003b093db --- /dev/null +++ b/sound/soc/meson/Makefile @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: (GPL-2.0 OR MIT) + +snd-soc-meson-axg-fifo-objs := axg-fifo.o +snd-soc-meson-axg-frddr-objs := axg-frddr.o +snd-soc-meson-axg-toddr-objs := axg-toddr.o +snd-soc-meson-axg-tdm-formatter-objs := axg-tdm-formatter.o +snd-soc-meson-axg-tdm-interface-objs := axg-tdm-interface.o +snd-soc-meson-axg-tdmin-objs := axg-tdmin.o +snd-soc-meson-axg-tdmout-objs := axg-tdmout.o +snd-soc-meson-axg-sound-card-objs := axg-card.o +snd-soc-meson-axg-spdifout-objs := axg-spdifout.o + +obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o +obj-$(CONFIG_SND_MESON_AXG_FRDDR) += snd-soc-meson-axg-frddr.o +obj-$(CONFIG_SND_MESON_AXG_TODDR) += snd-soc-meson-axg-toddr.o +obj-$(CONFIG_SND_MESON_AXG_TDM_FORMATTER) += snd-soc-meson-axg-tdm-formatter.o +obj-$(CONFIG_SND_MESON_AXG_TDM_INTERFACE) += snd-soc-meson-axg-tdm-interface.o +obj-$(CONFIG_SND_MESON_AXG_TDMIN) += snd-soc-meson-axg-tdmin.o +obj-$(CONFIG_SND_MESON_AXG_TDMOUT) += snd-soc-meson-axg-tdmout.o +obj-$(CONFIG_SND_MESON_AXG_SOUND_CARD) += snd-soc-meson-axg-sound-card.o +obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c new file mode 100644 index 000000000000..2914ba0d965b --- /dev/null +++ b/sound/soc/meson/axg-card.c @@ -0,0 +1,671 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/module.h> +#include <linux/of_platform.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-tdm.h" + +struct axg_card { + struct snd_soc_card card; + void **link_data; +}; + +struct axg_dai_link_tdm_mask { + u32 tx; + u32 rx; +}; + +struct axg_dai_link_tdm_data { + unsigned int mclk_fs; + unsigned int slots; + unsigned int slot_width; + u32 *tx_mask; + u32 *rx_mask; + struct axg_dai_link_tdm_mask *codec_masks; +}; + +#define PREFIX "amlogic," + +static int axg_card_reallocate_links(struct axg_card *priv, + unsigned int num_links) +{ + struct snd_soc_dai_link *links; + void **ldata; + + links = krealloc(priv->card.dai_link, + num_links * sizeof(*priv->card.dai_link), + GFP_KERNEL | __GFP_ZERO); + ldata = krealloc(priv->link_data, + num_links * sizeof(*priv->link_data), + GFP_KERNEL | __GFP_ZERO); + + if (!links || !ldata) { + dev_err(priv->card.dev, "failed to allocate links\n"); + return -ENOMEM; + } + + priv->card.dai_link = links; + priv->link_data = ldata; + priv->card.num_links = num_links; + return 0; +} + +static int axg_card_parse_dai(struct snd_soc_card *card, + struct device_node *node, + struct device_node **dai_of_node, + const char **dai_name) +{ + struct of_phandle_args args; + int ret; + + if (!dai_name || !dai_of_node || !node) + return -EINVAL; + + ret = of_parse_phandle_with_args(node, "sound-dai", + "#sound-dai-cells", 0, &args); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(card->dev, "can't parse dai %d\n", ret); + return ret; + } + *dai_of_node = args.np; + + return snd_soc_get_dai_name(&args, dai_name); +} + +static int axg_card_set_link_name(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + const char *prefix) +{ + char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", + prefix, link->cpu_of_node->full_name); + if (!name) + return -ENOMEM; + + link->name = name; + link->stream_name = name; + + return 0; +} + +static void axg_card_clean_references(struct axg_card *priv) +{ + struct snd_soc_card *card = &priv->card; + struct snd_soc_dai_link *link; + int i, j; + + if (card->dai_link) { + for (i = 0; i < card->num_links; i++) { + link = &card->dai_link[i]; + of_node_put(link->cpu_of_node); + for (j = 0; j < link->num_codecs; j++) + of_node_put(link->codecs[j].of_node); + } + } + + if (card->aux_dev) { + for (i = 0; i < card->num_aux_devs; i++) + of_node_put(card->aux_dev[i].codec_of_node); + } + + kfree(card->dai_link); + kfree(priv->link_data); +} + +static int axg_card_add_aux_devices(struct snd_soc_card *card) +{ + struct device_node *node = card->dev->of_node; + struct snd_soc_aux_dev *aux; + int num, i; + + num = of_count_phandle_with_args(node, "audio-aux-devs", NULL); + if (num == -ENOENT) { + /* + * It is ok to have no auxiliary devices but for this card it + * is a strange situtation. Let's warn the about it. + */ + dev_warn(card->dev, "card has no auxiliary devices\n"); + return 0; + } else if (num < 0) { + dev_err(card->dev, "error getting auxiliary devices: %d\n", + num); + return num; + } + + aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); + if (!aux) + return -ENOMEM; + card->aux_dev = aux; + card->num_aux_devs = num; + + for (i = 0; i < card->num_aux_devs; i++, aux++) { + aux->codec_of_node = + of_parse_phandle(node, "audio-aux-devs", i); + if (!aux->codec_of_node) + return -EINVAL; + } + + return 0; +} + +static int axg_card_tdm_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct axg_dai_link_tdm_data *be = + (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; + struct snd_soc_dai *codec_dai; + unsigned int mclk; + int ret, i; + + if (be->mclk_fs) { + mclk = params_rate(params) * be->mclk_fs; + + for (i = 0; i < rtd->num_codecs; i++) { + codec_dai = rtd->codec_dais[i]; + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + return ret; + } + + ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret && ret != -ENOTSUPP) + return ret; + } + + return 0; +} + +static const struct snd_soc_ops axg_card_tdm_be_ops = { + .hw_params = axg_card_tdm_be_hw_params, +}; + +static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct axg_dai_link_tdm_data *be = + (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; + struct snd_soc_dai *codec_dai; + int ret, i; + + for (i = 0; i < rtd->num_codecs; i++) { + codec_dai = rtd->codec_dais[i]; + ret = snd_soc_dai_set_tdm_slot(codec_dai, + be->codec_masks[i].tx, + be->codec_masks[i].rx, + be->slots, be->slot_width); + if (ret && ret != -ENOTSUPP) { + dev_err(codec_dai->dev, + "setting tdm link slots failed\n"); + return ret; + } + } + + ret = axg_tdm_set_tdm_slots(rtd->cpu_dai, be->tx_mask, be->rx_mask, + be->slots, be->slot_width); + if (ret) { + dev_err(rtd->cpu_dai->dev, "setting tdm link slots failed\n"); + return ret; + } + + return 0; +} + +static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd) +{ + struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct axg_dai_link_tdm_data *be = + (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; + int ret; + + /* The loopback rx_mask is the pad tx_mask */ + ret = axg_tdm_set_tdm_slots(rtd->cpu_dai, NULL, be->tx_mask, + be->slots, be->slot_width); + if (ret) { + dev_err(rtd->cpu_dai->dev, "setting tdm link slots failed\n"); + return ret; + } + + return 0; +} + +static int axg_card_add_tdm_loopback(struct snd_soc_card *card, + int *index) +{ + struct axg_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *pad = &card->dai_link[*index]; + struct snd_soc_dai_link *lb; + int ret; + + /* extend links */ + ret = axg_card_reallocate_links(priv, card->num_links + 1); + if (ret) + return ret; + + lb = &card->dai_link[*index + 1]; + + lb->name = kasprintf(GFP_KERNEL, "%s-lb", pad->name); + if (!lb->name) + return -ENOMEM; + + lb->stream_name = lb->name; + lb->cpu_of_node = pad->cpu_of_node; + lb->cpu_dai_name = "TDM Loopback"; + lb->codec_name = "snd-soc-dummy"; + lb->codec_dai_name = "snd-soc-dummy-dai"; + lb->dpcm_capture = 1; + lb->no_pcm = 1; + lb->ops = &axg_card_tdm_be_ops; + lb->init = axg_card_tdm_dai_lb_init; + + /* Provide the same link data to the loopback */ + priv->link_data[*index + 1] = priv->link_data[*index]; + + /* + * axg_card_clean_references() will iterate over this link, + * make sure the node count is balanced + */ + of_node_get(lb->cpu_of_node); + + /* Let add_links continue where it should */ + *index += 1; + + return 0; +} + +static unsigned int axg_card_parse_daifmt(struct device_node *node, + struct device_node *cpu_node) +{ + struct device_node *bitclkmaster = NULL; + struct device_node *framemaster = NULL; + unsigned int daifmt; + + daifmt = snd_soc_of_parse_daifmt(node, PREFIX, + &bitclkmaster, &framemaster); + daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; + + /* If no master is provided, default to cpu master */ + if (!bitclkmaster || bitclkmaster == cpu_node) { + daifmt |= (!framemaster || framemaster == cpu_node) ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM; + } else { + daifmt |= (!framemaster || framemaster == cpu_node) ? + SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM; + } + + of_node_put(bitclkmaster); + of_node_put(framemaster); + + return daifmt; +} + +static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node, + struct axg_dai_link_tdm_data *be) +{ + char propname[32]; + u32 tx, rx; + int i; + + be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, + sizeof(*be->tx_mask), GFP_KERNEL); + be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, + sizeof(*be->rx_mask), GFP_KERNEL); + if (!be->tx_mask || !be->rx_mask) + return -ENOMEM; + + for (i = 0, tx = 0; i < AXG_TDM_NUM_LANES; i++) { + snprintf(propname, 32, "dai-tdm-slot-tx-mask-%d", i); + snd_soc_of_get_slot_mask(node, propname, &be->tx_mask[i]); + tx = max(tx, be->tx_mask[i]); + } + + /* Disable playback is the interface has no tx slots */ + if (!tx) + link->dpcm_playback = 0; + + for (i = 0, rx = 0; i < AXG_TDM_NUM_LANES; i++) { + snprintf(propname, 32, "dai-tdm-slot-rx-mask-%d", i); + snd_soc_of_get_slot_mask(node, propname, &be->rx_mask[i]); + rx = max(rx, be->rx_mask[i]); + } + + /* Disable capture is the interface has no rx slots */ + if (!rx) + link->dpcm_capture = 0; + + /* ... but the interface should at least have one of them */ + if (!tx && !rx) { + dev_err(card->dev, "tdm link has no cpu slots\n"); + return -EINVAL; + } + + of_property_read_u32(node, "dai-tdm-slot-num", &be->slots); + if (!be->slots) { + /* + * If the slot number is not provided, set it such as it + * accommodates the largest mask + */ + be->slots = fls(max(tx, rx)); + } else if (be->slots < fls(max(tx, rx)) || be->slots > 32) { + /* + * Error if the slots can't accommodate the largest mask or + * if it is just too big + */ + dev_err(card->dev, "bad slot number\n"); + return -EINVAL; + } + + of_property_read_u32(node, "dai-tdm-slot-width", &be->slot_width); + + return 0; +} + +static int axg_card_parse_codecs_masks(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node, + struct axg_dai_link_tdm_data *be) +{ + struct axg_dai_link_tdm_mask *codec_mask; + struct device_node *np; + + codec_mask = devm_kcalloc(card->dev, link->num_codecs, + sizeof(*codec_mask), GFP_KERNEL); + if (!codec_mask) + return -ENOMEM; + + be->codec_masks = codec_mask; + + for_each_child_of_node(node, np) { + snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", + &codec_mask->rx); + snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", + &codec_mask->tx); + + codec_mask++; + } + + return 0; +} + +static int axg_card_parse_tdm(struct snd_soc_card *card, + struct device_node *node, + int *index) +{ + struct axg_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *link = &card->dai_link[*index]; + struct axg_dai_link_tdm_data *be; + int ret; + + /* Allocate tdm link parameters */ + be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); + if (!be) + return -ENOMEM; + priv->link_data[*index] = be; + + /* Setup tdm link */ + link->ops = &axg_card_tdm_be_ops; + link->init = axg_card_tdm_dai_init; + link->dai_fmt = axg_card_parse_daifmt(node, link->cpu_of_node); + + of_property_read_u32(node, "mclk-fs", &be->mclk_fs); + + ret = axg_card_parse_cpu_tdm_slots(card, link, node, be); + if (ret) { + dev_err(card->dev, "error parsing tdm link slots\n"); + return ret; + } + + ret = axg_card_parse_codecs_masks(card, link, node, be); + if (ret) + return ret; + + /* Add loopback if the pad dai has playback */ + if (link->dpcm_playback) { + ret = axg_card_add_tdm_loopback(card, index); + if (ret) + return ret; + } + + return 0; +} + +static int axg_card_set_be_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node) +{ + struct snd_soc_dai_link_component *codec; + struct device_node *np; + int ret, num_codecs; + + link->no_pcm = 1; + link->dpcm_playback = 1; + link->dpcm_capture = 1; + + num_codecs = of_get_child_count(node); + if (!num_codecs) { + dev_err(card->dev, "be link %s has no codec\n", + node->full_name); + return -EINVAL; + } + + codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + link->codecs = codec; + link->num_codecs = num_codecs; + + for_each_child_of_node(node, np) { + ret = axg_card_parse_dai(card, np, &codec->of_node, + &codec->dai_name); + if (ret) { + of_node_put(np); + return ret; + } + + codec++; + } + + ret = axg_card_set_link_name(card, link, "be"); + if (ret) + dev_err(card->dev, "error setting %s link name\n", np->name); + + return ret; +} + +static int axg_card_set_fe_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + bool is_playback) +{ + link->dynamic = 1; + link->dpcm_merged_format = 1; + link->dpcm_merged_chan = 1; + link->dpcm_merged_rate = 1; + link->codec_dai_name = "snd-soc-dummy-dai"; + link->codec_name = "snd-soc-dummy"; + + if (is_playback) + link->dpcm_playback = 1; + else + link->dpcm_capture = 1; + + return axg_card_set_link_name(card, link, "fe"); +} + +static int axg_card_cpu_is_capture_fe(struct device_node *np) +{ + return of_device_is_compatible(np, PREFIX "axg-toddr"); +} + +static int axg_card_cpu_is_playback_fe(struct device_node *np) +{ + return of_device_is_compatible(np, PREFIX "axg-frddr"); +} + +static int axg_card_cpu_is_tdm_iface(struct device_node *np) +{ + return of_device_is_compatible(np, PREFIX "axg-tdm-iface"); +} + +static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, + int *index) +{ + struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; + int ret; + + ret = axg_card_parse_dai(card, np, &dai_link->cpu_of_node, + &dai_link->cpu_dai_name); + if (ret) + return ret; + + if (axg_card_cpu_is_playback_fe(dai_link->cpu_of_node)) + ret = axg_card_set_fe_link(card, dai_link, true); + else if (axg_card_cpu_is_capture_fe(dai_link->cpu_of_node)) + ret = axg_card_set_fe_link(card, dai_link, false); + else + ret = axg_card_set_be_link(card, dai_link, np); + + if (ret) + return ret; + + if (axg_card_cpu_is_tdm_iface(dai_link->cpu_of_node)) + ret = axg_card_parse_tdm(card, np, index); + + return ret; +} + +static int axg_card_add_links(struct snd_soc_card *card) +{ + struct axg_card *priv = snd_soc_card_get_drvdata(card); + struct device_node *node = card->dev->of_node; + struct device_node *np; + int num, i, ret; + + num = of_get_child_count(node); + if (!num) { + dev_err(card->dev, "card has no links\n"); + return -EINVAL; + } + + ret = axg_card_reallocate_links(priv, num); + if (ret) + return ret; + + i = 0; + for_each_child_of_node(node, np) { + ret = axg_card_add_link(card, np, &i); + if (ret) { + of_node_put(np); + return ret; + } + + i++; + } + + return 0; +} + +static int axg_card_parse_of_optional(struct snd_soc_card *card, + const char *propname, + int (*func)(struct snd_soc_card *c, + const char *p)) +{ + /* If property is not provided, don't fail ... */ + if (!of_property_read_bool(card->dev->of_node, propname)) + return 0; + + /* ... but do fail if it is provided and the parsing fails */ + return func(card, propname); +} + +static const struct of_device_id axg_card_of_match[] = { + { .compatible = "amlogic,axg-sound-card", }, + {} +}; +MODULE_DEVICE_TABLE(of, axg_card_of_match); + +static int axg_card_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct axg_card *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + snd_soc_card_set_drvdata(&priv->card, priv); + + priv->card.owner = THIS_MODULE; + priv->card.dev = dev; + + ret = snd_soc_of_parse_card_name(&priv->card, "model"); + if (ret < 0) + return ret; + + ret = axg_card_parse_of_optional(&priv->card, "audio-routing", + snd_soc_of_parse_audio_routing); + if (ret) { + dev_err(dev, "error while parsing routing\n"); + return ret; + } + + ret = axg_card_parse_of_optional(&priv->card, "audio-widgets", + snd_soc_of_parse_audio_simple_widgets); + if (ret) { + dev_err(dev, "error while parsing widgets\n"); + return ret; + } + + ret = axg_card_add_links(&priv->card); + if (ret) + goto out_err; + + ret = axg_card_add_aux_devices(&priv->card); + if (ret) + goto out_err; + + ret = devm_snd_soc_register_card(dev, &priv->card); + if (ret) + goto out_err; + + return 0; + +out_err: + axg_card_clean_references(priv); + return ret; +} + +static int axg_card_remove(struct platform_device *pdev) +{ + struct axg_card *priv = platform_get_drvdata(pdev); + + axg_card_clean_references(priv); + + return 0; +} + +static struct platform_driver axg_card_pdrv = { + .probe = axg_card_probe, + .remove = axg_card_remove, + .driver = { + .name = "axg-sound-card", + .of_match_table = axg_card_of_match, + }, +}; +module_platform_driver(axg_card_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG ALSA machine driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c new file mode 100644 index 000000000000..30262550e37b --- /dev/null +++ b/sound/soc/meson/axg-fifo.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/clk.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-fifo.h" + +/* + * This file implements the platform operations common to the playback and + * capture frontend DAI. The logic behind this two types of fifo is very + * similar but some difference exist. + * These differences the respective DAI drivers + */ + +static struct snd_pcm_hardware axg_fifo_hw = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE), + + .formats = AXG_FIFO_FORMATS, + .rate_min = 5512, + .rate_max = 192000, + .channels_min = 1, + .channels_max = AXG_FIFO_CH_MAX, + .period_bytes_min = AXG_FIFO_MIN_DEPTH, + .period_bytes_max = UINT_MAX, + .periods_min = 2, + .periods_max = UINT_MAX, + + /* No real justification for this */ + .buffer_bytes_max = 1 * 1024 * 1024, +}; + +static struct snd_soc_dai *axg_fifo_dai(struct snd_pcm_substream *ss) +{ + struct snd_soc_pcm_runtime *rtd = ss->private_data; + + return rtd->cpu_dai; +} + +static struct axg_fifo *axg_fifo_data(struct snd_pcm_substream *ss) +{ + struct snd_soc_dai *dai = axg_fifo_dai(ss); + + return snd_soc_dai_get_drvdata(dai); +} + +static struct device *axg_fifo_dev(struct snd_pcm_substream *ss) +{ + struct snd_soc_dai *dai = axg_fifo_dai(ss); + + return dai->dev; +} + +static void __dma_enable(struct axg_fifo *fifo, bool enable) +{ + regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_DMA_EN, + enable ? CTRL0_DMA_EN : 0); +} + +static int axg_fifo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) +{ + struct axg_fifo *fifo = axg_fifo_data(ss); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + __dma_enable(fifo, true); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + __dma_enable(fifo, false); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t axg_fifo_pcm_pointer(struct snd_pcm_substream *ss) +{ + struct axg_fifo *fifo = axg_fifo_data(ss); + struct snd_pcm_runtime *runtime = ss->runtime; + unsigned int addr; + + regmap_read(fifo->map, FIFO_STATUS2, &addr); + + return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr); +} + +static int axg_fifo_pcm_hw_params(struct snd_pcm_substream *ss, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = ss->runtime; + struct axg_fifo *fifo = axg_fifo_data(ss); + dma_addr_t end_ptr; + unsigned int burst_num; + int ret; + + ret = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(params)); + if (ret < 0) + return ret; + + /* Setup dma memory pointers */ + end_ptr = runtime->dma_addr + runtime->dma_bytes - AXG_FIFO_BURST; + regmap_write(fifo->map, FIFO_START_ADDR, runtime->dma_addr); + regmap_write(fifo->map, FIFO_FINISH_ADDR, end_ptr); + + /* Setup interrupt periodicity */ + burst_num = params_period_bytes(params) / AXG_FIFO_BURST; + regmap_write(fifo->map, FIFO_INT_ADDR, burst_num); + + /* Enable block count irq */ + regmap_update_bits(fifo->map, FIFO_CTRL0, + CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT), + CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT)); + + return 0; +} + +static int axg_fifo_pcm_hw_free(struct snd_pcm_substream *ss) +{ + struct axg_fifo *fifo = axg_fifo_data(ss); + + /* Disable the block count irq */ + regmap_update_bits(fifo->map, FIFO_CTRL0, + CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT), 0); + + return snd_pcm_lib_free_pages(ss); +} + +static void axg_fifo_ack_irq(struct axg_fifo *fifo, u8 mask) +{ + regmap_update_bits(fifo->map, FIFO_CTRL1, + CTRL1_INT_CLR(FIFO_INT_MASK), + CTRL1_INT_CLR(mask)); + + /* Clear must also be cleared */ + regmap_update_bits(fifo->map, FIFO_CTRL1, + CTRL1_INT_CLR(FIFO_INT_MASK), + 0); +} + +static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id) +{ + struct snd_pcm_substream *ss = dev_id; + struct axg_fifo *fifo = axg_fifo_data(ss); + unsigned int status; + + regmap_read(fifo->map, FIFO_STATUS1, &status); + + status = STATUS1_INT_STS(status) & FIFO_INT_MASK; + if (status & FIFO_INT_COUNT_REPEAT) + snd_pcm_period_elapsed(ss); + else + dev_dbg(axg_fifo_dev(ss), "unexpected irq - STS 0x%02x\n", + status); + + /* Ack irqs */ + axg_fifo_ack_irq(fifo, status); + + return IRQ_RETVAL(status); +} + +static int axg_fifo_pcm_open(struct snd_pcm_substream *ss) +{ + struct axg_fifo *fifo = axg_fifo_data(ss); + struct device *dev = axg_fifo_dev(ss); + int ret; + + snd_soc_set_runtime_hwparams(ss, &axg_fifo_hw); + + /* + * Make sure the buffer and period size are multiple of the FIFO + * minimum depth size + */ + ret = snd_pcm_hw_constraint_step(ss->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + AXG_FIFO_MIN_DEPTH); + if (ret) + return ret; + + ret = snd_pcm_hw_constraint_step(ss->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + AXG_FIFO_MIN_DEPTH); + if (ret) + return ret; + + ret = request_irq(fifo->irq, axg_fifo_pcm_irq_block, 0, + dev_name(dev), ss); + + /* Enable pclk to access registers and clock the fifo ip */ + ret = clk_prepare_enable(fifo->pclk); + if (ret) + return ret; + + /* Setup status2 so it reports the memory pointer */ + regmap_update_bits(fifo->map, FIFO_CTRL1, + CTRL1_STATUS2_SEL_MASK, + CTRL1_STATUS2_SEL(STATUS2_SEL_DDR_READ)); + + /* Make sure the dma is initially disabled */ + __dma_enable(fifo, false); + + /* Disable irqs until params are ready */ + regmap_update_bits(fifo->map, FIFO_CTRL0, + CTRL0_INT_EN(FIFO_INT_MASK), 0); + + /* Clear any pending interrupt */ + axg_fifo_ack_irq(fifo, FIFO_INT_MASK); + + /* Take memory arbitror out of reset */ + ret = reset_control_deassert(fifo->arb); + if (ret) + clk_disable_unprepare(fifo->pclk); + + return ret; +} + +static int axg_fifo_pcm_close(struct snd_pcm_substream *ss) +{ + struct axg_fifo *fifo = axg_fifo_data(ss); + int ret; + + /* Put the memory arbitror back in reset */ + ret = reset_control_assert(fifo->arb); + + /* Disable fifo ip and register access */ + clk_disable_unprepare(fifo->pclk); + + /* remove IRQ */ + free_irq(fifo->irq, ss); + + return ret; +} + +const struct snd_pcm_ops axg_fifo_pcm_ops = { + .open = axg_fifo_pcm_open, + .close = axg_fifo_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = axg_fifo_pcm_hw_params, + .hw_free = axg_fifo_pcm_hw_free, + .pointer = axg_fifo_pcm_pointer, + .trigger = axg_fifo_pcm_trigger, +}; +EXPORT_SYMBOL_GPL(axg_fifo_pcm_ops); + +int axg_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, unsigned int type) +{ + struct snd_card *card = rtd->card->snd_card; + size_t size = axg_fifo_hw.buffer_bytes_max; + + return snd_pcm_lib_preallocate_pages(rtd->pcm->streams[type].substream, + SNDRV_DMA_TYPE_DEV, card->dev, + size, size); +} +EXPORT_SYMBOL_GPL(axg_fifo_pcm_new); + +static const struct regmap_config axg_fifo_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = FIFO_STATUS2, +}; + +int axg_fifo_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct axg_fifo_match_data *data; + struct axg_fifo *fifo; + struct resource *res; + void __iomem *regs; + + data = of_device_get_match_data(dev); + if (!data) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + fifo = devm_kzalloc(dev, sizeof(*fifo), GFP_KERNEL); + if (!fifo) + return -ENOMEM; + platform_set_drvdata(pdev, fifo); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + fifo->map = devm_regmap_init_mmio(dev, regs, &axg_fifo_regmap_cfg); + if (IS_ERR(fifo->map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(fifo->map)); + return PTR_ERR(fifo->map); + } + + fifo->pclk = devm_clk_get(dev, NULL); + if (IS_ERR(fifo->pclk)) { + if (PTR_ERR(fifo->pclk) != -EPROBE_DEFER) + dev_err(dev, "failed to get pclk: %ld\n", + PTR_ERR(fifo->pclk)); + return PTR_ERR(fifo->pclk); + } + + fifo->arb = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(fifo->arb)) { + if (PTR_ERR(fifo->arb) != -EPROBE_DEFER) + dev_err(dev, "failed to get arb reset: %ld\n", + PTR_ERR(fifo->arb)); + return PTR_ERR(fifo->arb); + } + + fifo->irq = of_irq_get(dev->of_node, 0); + if (fifo->irq <= 0) { + dev_err(dev, "failed to get irq: %d\n", fifo->irq); + return fifo->irq; + } + + return devm_snd_soc_register_component(dev, data->component_drv, + data->dai_drv, 1); +} +EXPORT_SYMBOL_GPL(axg_fifo_probe); + +MODULE_DESCRIPTION("Amlogic AXG fifo driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-fifo.h b/sound/soc/meson/axg-fifo.h new file mode 100644 index 000000000000..cb6c4013ca33 --- /dev/null +++ b/sound/soc/meson/axg-fifo.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2018 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef _MESON_AXG_FIFO_H +#define _MESON_AXG_FIFO_H + +struct clk; +struct platform_device; +struct regmap; +struct reset_control; + +struct snd_soc_component_driver; +struct snd_soc_dai; +struct snd_soc_dai_driver; +struct snd_pcm_ops; +struct snd_soc_pcm_runtime; + +#define AXG_FIFO_CH_MAX 128 +#define AXG_FIFO_RATES (SNDRV_PCM_RATE_5512 | \ + SNDRV_PCM_RATE_8000_192000) +#define AXG_FIFO_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define AXG_FIFO_BURST 8 +#define AXG_FIFO_MIN_CNT 64 +#define AXG_FIFO_MIN_DEPTH (AXG_FIFO_BURST * AXG_FIFO_MIN_CNT) + +#define FIFO_INT_ADDR_FINISH BIT(0) +#define FIFO_INT_ADDR_INT BIT(1) +#define FIFO_INT_COUNT_REPEAT BIT(2) +#define FIFO_INT_COUNT_ONCE BIT(3) +#define FIFO_INT_FIFO_ZERO BIT(4) +#define FIFO_INT_FIFO_DEPTH BIT(5) +#define FIFO_INT_MASK GENMASK(7, 0) + +#define FIFO_CTRL0 0x00 +#define CTRL0_DMA_EN BIT(31) +#define CTRL0_INT_EN(x) ((x) << 16) +#define CTRL0_SEL_MASK GENMASK(2, 0) +#define CTRL0_SEL_SHIFT 0 +#define FIFO_CTRL1 0x04 +#define CTRL1_INT_CLR(x) ((x) << 0) +#define CTRL1_STATUS2_SEL_MASK GENMASK(11, 8) +#define CTRL1_STATUS2_SEL(x) ((x) << 8) +#define STATUS2_SEL_DDR_READ 0 +#define CTRL1_THRESHOLD_MASK GENMASK(23, 16) +#define CTRL1_THRESHOLD(x) ((x) << 16) +#define CTRL1_FRDDR_DEPTH_MASK GENMASK(31, 24) +#define CTRL1_FRDDR_DEPTH(x) ((x) << 24) +#define FIFO_START_ADDR 0x08 +#define FIFO_FINISH_ADDR 0x0c +#define FIFO_INT_ADDR 0x10 +#define FIFO_STATUS1 0x14 +#define STATUS1_INT_STS(x) ((x) << 0) +#define FIFO_STATUS2 0x18 + +struct axg_fifo { + struct regmap *map; + struct clk *pclk; + struct reset_control *arb; + int irq; +}; + +struct axg_fifo_match_data { + const struct snd_soc_component_driver *component_drv; + struct snd_soc_dai_driver *dai_drv; +}; + +extern const struct snd_pcm_ops axg_fifo_pcm_ops; + +int axg_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, unsigned int type); +int axg_fifo_probe(struct platform_device *pdev); + +#endif /* _MESON_AXG_FIFO_H */ diff --git a/sound/soc/meson/axg-frddr.c b/sound/soc/meson/axg-frddr.c new file mode 100644 index 000000000000..a6f6f6a2eca8 --- /dev/null +++ b/sound/soc/meson/axg-frddr.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +/* This driver implements the frontend playback DAI of AXG based SoCs */ + +#include <linux/clk.h> +#include <linux/regmap.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-fifo.h" + +#define CTRL0_FRDDR_PP_MODE BIT(30) + +static int axg_frddr_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); + unsigned int fifo_depth, fifo_threshold; + int ret; + + /* Enable pclk to access registers and clock the fifo ip */ + ret = clk_prepare_enable(fifo->pclk); + if (ret) + return ret; + + /* Apply single buffer mode to the interface */ + regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_FRDDR_PP_MODE, 0); + + /* + * TODO: We could adapt the fifo depth and the fifo threshold + * depending on the expected memory throughput and lantencies + * For now, we'll just use the same values as the vendor kernel + * Depth and threshold are zero based. + */ + fifo_depth = AXG_FIFO_MIN_CNT - 1; + fifo_threshold = (AXG_FIFO_MIN_CNT / 2) - 1; + regmap_update_bits(fifo->map, FIFO_CTRL1, + CTRL1_FRDDR_DEPTH_MASK | CTRL1_THRESHOLD_MASK, + CTRL1_FRDDR_DEPTH(fifo_depth) | + CTRL1_THRESHOLD(fifo_threshold)); + + return 0; +} + +static void axg_frddr_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); + + clk_disable_unprepare(fifo->pclk); +} + +static int axg_frddr_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *dai) +{ + return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_PLAYBACK); +} + +static const struct snd_soc_dai_ops axg_frddr_ops = { + .startup = axg_frddr_dai_startup, + .shutdown = axg_frddr_dai_shutdown, +}; + +static struct snd_soc_dai_driver axg_frddr_dai_drv = { + .name = "FRDDR", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = AXG_FIFO_CH_MAX, + .rates = AXG_FIFO_RATES, + .formats = AXG_FIFO_FORMATS, + }, + .ops = &axg_frddr_ops, + .pcm_new = axg_frddr_pcm_new, +}; + +static const char * const axg_frddr_sel_texts[] = { + "OUT 0", "OUT 1", "OUT 2", "OUT 3" +}; + +static SOC_ENUM_SINGLE_DECL(axg_frddr_sel_enum, FIFO_CTRL0, CTRL0_SEL_SHIFT, + axg_frddr_sel_texts); + +static const struct snd_kcontrol_new axg_frddr_out_demux = + SOC_DAPM_ENUM("Output Sink", axg_frddr_sel_enum); + +static const struct snd_soc_dapm_widget axg_frddr_dapm_widgets[] = { + SND_SOC_DAPM_DEMUX("SINK SEL", SND_SOC_NOPM, 0, 0, + &axg_frddr_out_demux), + SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route axg_frddr_dapm_routes[] = { + { "SINK SEL", NULL, "Playback" }, + { "OUT 0", "OUT 0", "SINK SEL" }, + { "OUT 1", "OUT 1", "SINK SEL" }, + { "OUT 2", "OUT 2", "SINK SEL" }, + { "OUT 3", "OUT 3", "SINK SEL" }, +}; + +static const struct snd_soc_component_driver axg_frddr_component_drv = { + .dapm_widgets = axg_frddr_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(axg_frddr_dapm_widgets), + .dapm_routes = axg_frddr_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(axg_frddr_dapm_routes), + .ops = &axg_fifo_pcm_ops +}; + +static const struct axg_fifo_match_data axg_frddr_match_data = { + .component_drv = &axg_frddr_component_drv, + .dai_drv = &axg_frddr_dai_drv +}; + +static const struct of_device_id axg_frddr_of_match[] = { + { + .compatible = "amlogic,axg-frddr", + .data = &axg_frddr_match_data, + }, {} +}; +MODULE_DEVICE_TABLE(of, axg_frddr_of_match); + +static struct platform_driver axg_frddr_pdrv = { + .probe = axg_fifo_probe, + .driver = { + .name = "axg-frddr", + .of_match_table = axg_frddr_of_match, + }, +}; +module_platform_driver(axg_frddr_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG playback fifo driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-spdifout.c b/sound/soc/meson/axg-spdifout.c new file mode 100644 index 000000000000..9dea528053ad --- /dev/null +++ b/sound/soc/meson/axg-spdifout.c @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/pcm_params.h> +#include <sound/pcm_iec958.h> + +/* + * NOTE: + * The meaning of bits SPDIFOUT_CTRL0_XXX_SEL is actually the opposite + * of what the documentation says. Manual control on V, U and C bits is + * applied when the related sel bits are cleared + */ + +#define SPDIFOUT_STAT 0x00 +#define SPDIFOUT_GAIN0 0x04 +#define SPDIFOUT_GAIN1 0x08 +#define SPDIFOUT_CTRL0 0x0c +#define SPDIFOUT_CTRL0_EN BIT(31) +#define SPDIFOUT_CTRL0_RST_OUT BIT(29) +#define SPDIFOUT_CTRL0_RST_IN BIT(28) +#define SPDIFOUT_CTRL0_USEL BIT(26) +#define SPDIFOUT_CTRL0_USET BIT(25) +#define SPDIFOUT_CTRL0_CHSTS_SEL BIT(24) +#define SPDIFOUT_CTRL0_DATA_SEL BIT(20) +#define SPDIFOUT_CTRL0_MSB_FIRST BIT(19) +#define SPDIFOUT_CTRL0_VSEL BIT(18) +#define SPDIFOUT_CTRL0_VSET BIT(17) +#define SPDIFOUT_CTRL0_MASK_MASK GENMASK(11, 4) +#define SPDIFOUT_CTRL0_MASK(x) ((x) << 4) +#define SPDIFOUT_CTRL1 0x10 +#define SPDIFOUT_CTRL1_MSB_POS_MASK GENMASK(12, 8) +#define SPDIFOUT_CTRL1_MSB_POS(x) ((x) << 8) +#define SPDIFOUT_CTRL1_TYPE_MASK GENMASK(6, 4) +#define SPDIFOUT_CTRL1_TYPE(x) ((x) << 4) +#define SPDIFOUT_PREAMB 0x14 +#define SPDIFOUT_SWAP 0x18 +#define SPDIFOUT_CHSTS0 0x1c +#define SPDIFOUT_CHSTS1 0x20 +#define SPDIFOUT_CHSTS2 0x24 +#define SPDIFOUT_CHSTS3 0x28 +#define SPDIFOUT_CHSTS4 0x2c +#define SPDIFOUT_CHSTS5 0x30 +#define SPDIFOUT_CHSTS6 0x34 +#define SPDIFOUT_CHSTS7 0x38 +#define SPDIFOUT_CHSTS8 0x3c +#define SPDIFOUT_CHSTS9 0x40 +#define SPDIFOUT_CHSTSA 0x44 +#define SPDIFOUT_CHSTSB 0x48 +#define SPDIFOUT_MUTE_VAL 0x4c + +struct axg_spdifout { + struct regmap *map; + struct clk *mclk; + struct clk *pclk; +}; + +static void axg_spdifout_enable(struct regmap *map) +{ + /* Apply both reset */ + regmap_update_bits(map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_RST_OUT | SPDIFOUT_CTRL0_RST_IN, + 0); + + /* Clear out reset before in reset */ + regmap_update_bits(map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_RST_OUT, SPDIFOUT_CTRL0_RST_OUT); + regmap_update_bits(map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_RST_IN, SPDIFOUT_CTRL0_RST_IN); + + /* Enable spdifout */ + regmap_update_bits(map, SPDIFOUT_CTRL0, SPDIFOUT_CTRL0_EN, + SPDIFOUT_CTRL0_EN); +} + +static void axg_spdifout_disable(struct regmap *map) +{ + regmap_update_bits(map, SPDIFOUT_CTRL0, SPDIFOUT_CTRL0_EN, 0); +} + +static int axg_spdifout_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + axg_spdifout_enable(priv->map); + return 0; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + axg_spdifout_disable(priv->map); + return 0; + + default: + return -EINVAL; + } +} + +static int axg_spdifout_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + + /* Use spdif valid bit to perform digital mute */ + regmap_update_bits(priv->map, SPDIFOUT_CTRL0, SPDIFOUT_CTRL0_VSET, + mute ? SPDIFOUT_CTRL0_VSET : 0); + + return 0; +} + +static int axg_spdifout_sample_fmt(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + unsigned int val; + + /* Set the samples spdifout will pull from the FIFO */ + switch (params_channels(params)) { + case 1: + val = SPDIFOUT_CTRL0_MASK(0x1); + break; + case 2: + val = SPDIFOUT_CTRL0_MASK(0x3); + break; + default: + dev_err(dai->dev, "too many channels for spdif dai: %u\n", + params_channels(params)); + return -EINVAL; + } + + regmap_update_bits(priv->map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_MASK_MASK, val); + + /* FIFO data are arranged in chunks of 64bits */ + switch (params_physical_width(params)) { + case 8: + /* 8 samples of 8 bits */ + val = SPDIFOUT_CTRL1_TYPE(0); + break; + case 16: + /* 4 samples of 16 bits - right justified */ + val = SPDIFOUT_CTRL1_TYPE(2); + break; + case 32: + /* 2 samples of 32 bits - right justified */ + val = SPDIFOUT_CTRL1_TYPE(4); + break; + default: + dev_err(dai->dev, "Unsupported physical width: %u\n", + params_physical_width(params)); + return -EINVAL; + } + + /* Position of the MSB in FIFO samples */ + val |= SPDIFOUT_CTRL1_MSB_POS(params_width(params) - 1); + + regmap_update_bits(priv->map, SPDIFOUT_CTRL1, + SPDIFOUT_CTRL1_MSB_POS_MASK | + SPDIFOUT_CTRL1_TYPE_MASK, val); + + regmap_update_bits(priv->map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_MSB_FIRST | SPDIFOUT_CTRL0_DATA_SEL, + 0); + + return 0; +} + +static int axg_spdifout_set_chsts(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + unsigned int offset; + int ret; + u8 cs[4]; + u32 val; + + ret = snd_pcm_create_iec958_consumer_hw_params(params, cs, 4); + if (ret < 0) { + dev_err(dai->dev, "Creating IEC958 channel status failed %d\n", + ret); + return ret; + } + val = cs[0] | cs[1] << 8 | cs[2] << 16 | cs[3] << 24; + + /* Setup channel status A bits [31 - 0]*/ + regmap_write(priv->map, SPDIFOUT_CHSTS0, val); + + /* Clear channel status A bits [191 - 32] */ + for (offset = SPDIFOUT_CHSTS1; offset <= SPDIFOUT_CHSTS5; + offset += regmap_get_reg_stride(priv->map)) + regmap_write(priv->map, offset, 0); + + /* Setup channel status B bits [31 - 0]*/ + regmap_write(priv->map, SPDIFOUT_CHSTS6, val); + + /* Clear channel status B bits [191 - 32] */ + for (offset = SPDIFOUT_CHSTS7; offset <= SPDIFOUT_CHSTSB; + offset += regmap_get_reg_stride(priv->map)) + regmap_write(priv->map, offset, 0); + + return 0; +} + +static int axg_spdifout_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + unsigned int rate = params_rate(params); + int ret; + + /* 2 * 32bits per subframe * 2 channels = 128 */ + ret = clk_set_rate(priv->mclk, rate * 128); + if (ret) { + dev_err(dai->dev, "failed to set spdif clock\n"); + return ret; + } + + ret = axg_spdifout_sample_fmt(params, dai); + if (ret) { + dev_err(dai->dev, "failed to setup sample format\n"); + return ret; + } + + ret = axg_spdifout_set_chsts(params, dai); + if (ret) { + dev_err(dai->dev, "failed to setup channel status words\n"); + return ret; + } + + return 0; +} + +static int axg_spdifout_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + int ret; + + /* Clock the spdif output block */ + ret = clk_prepare_enable(priv->pclk); + if (ret) { + dev_err(dai->dev, "failed to enable pclk\n"); + return ret; + } + + /* Make sure the block is initially stopped */ + axg_spdifout_disable(priv->map); + + /* Insert data from bit 27 lsb first */ + regmap_update_bits(priv->map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_MSB_FIRST | SPDIFOUT_CTRL0_DATA_SEL, + 0); + + /* Manual control of V, C and U, U = 0 */ + regmap_update_bits(priv->map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_CHSTS_SEL | SPDIFOUT_CTRL0_VSEL | + SPDIFOUT_CTRL0_USEL | SPDIFOUT_CTRL0_USET, + 0); + + /* Static SWAP configuration ATM */ + regmap_write(priv->map, SPDIFOUT_SWAP, 0x10); + + return 0; +} + +static void axg_spdifout_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + + clk_disable_unprepare(priv->pclk); +} + +static const struct snd_soc_dai_ops axg_spdifout_ops = { + .trigger = axg_spdifout_trigger, + .digital_mute = axg_spdifout_digital_mute, + .hw_params = axg_spdifout_hw_params, + .startup = axg_spdifout_startup, + .shutdown = axg_spdifout_shutdown, +}; + +static struct snd_soc_dai_driver axg_spdifout_dai_drv[] = { + { + .name = "SPDIF Output", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000), + .formats = (SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + .ops = &axg_spdifout_ops, + }, +}; + +static const char * const spdifout_sel_texts[] = { + "IN 0", "IN 1", "IN 2", +}; + +static SOC_ENUM_SINGLE_DECL(axg_spdifout_sel_enum, SPDIFOUT_CTRL1, 24, + spdifout_sel_texts); + +static const struct snd_kcontrol_new axg_spdifout_in_mux = + SOC_DAPM_ENUM("Input Source", axg_spdifout_sel_enum); + +static const struct snd_soc_dapm_widget axg_spdifout_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_spdifout_in_mux), +}; + +static const struct snd_soc_dapm_route axg_spdifout_dapm_routes[] = { + { "SRC SEL", "IN 0", "IN 0" }, + { "SRC SEL", "IN 1", "IN 1" }, + { "SRC SEL", "IN 2", "IN 2" }, + { "Playback", NULL, "SRC SEL" }, +}; + +static const struct snd_kcontrol_new axg_spdifout_controls[] = { + SOC_DOUBLE("Playback Volume", SPDIFOUT_GAIN0, 0, 8, 255, 0), + SOC_DOUBLE("Playback Switch", SPDIFOUT_CTRL0, 22, 21, 1, 1), + SOC_SINGLE("Playback Gain Enable Switch", + SPDIFOUT_CTRL1, 26, 1, 0), + SOC_SINGLE("Playback Channels Mix Switch", + SPDIFOUT_CTRL0, 23, 1, 0), +}; + +static int axg_spdifout_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct axg_spdifout *priv = snd_soc_component_get_drvdata(component); + enum snd_soc_bias_level now = + snd_soc_component_get_bias_level(component); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (now == SND_SOC_BIAS_STANDBY) + ret = clk_prepare_enable(priv->mclk); + break; + + case SND_SOC_BIAS_STANDBY: + if (now == SND_SOC_BIAS_PREPARE) + clk_disable_unprepare(priv->mclk); + break; + + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_ON: + break; + } + + return ret; +} + +static const struct snd_soc_component_driver axg_spdifout_component_drv = { + .controls = axg_spdifout_controls, + .num_controls = ARRAY_SIZE(axg_spdifout_controls), + .dapm_widgets = axg_spdifout_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(axg_spdifout_dapm_widgets), + .dapm_routes = axg_spdifout_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(axg_spdifout_dapm_routes), + .set_bias_level = axg_spdifout_set_bias_level, +}; + +static const struct regmap_config axg_spdifout_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPDIFOUT_MUTE_VAL, +}; + +static const struct of_device_id axg_spdifout_of_match[] = { + { .compatible = "amlogic,axg-spdifout", }, + {} +}; +MODULE_DEVICE_TABLE(of, axg_spdifout_of_match); + +static int axg_spdifout_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct axg_spdifout *priv; + struct resource *res; + void __iomem *regs; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + priv->map = devm_regmap_init_mmio(dev, regs, &axg_spdifout_regmap_cfg); + if (IS_ERR(priv->map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(priv->map)); + return PTR_ERR(priv->map); + } + + priv->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(priv->pclk)) { + ret = PTR_ERR(priv->pclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get pclk: %d\n", ret); + return ret; + } + + priv->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(priv->mclk)) { + ret = PTR_ERR(priv->mclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get mclk: %d\n", ret); + return ret; + } + + return devm_snd_soc_register_component(dev, &axg_spdifout_component_drv, + axg_spdifout_dai_drv, ARRAY_SIZE(axg_spdifout_dai_drv)); +} + +static struct platform_driver axg_spdifout_pdrv = { + .probe = axg_spdifout_probe, + .driver = { + .name = "axg-spdifout", + .of_match_table = axg_spdifout_of_match, + }, +}; +module_platform_driver(axg_spdifout_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG SPDIF Output driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c new file mode 100644 index 000000000000..43e390f9358a --- /dev/null +++ b/sound/soc/meson/axg-tdm-formatter.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <sound/soc.h> + +#include "axg-tdm-formatter.h" + +struct axg_tdm_formatter { + struct list_head list; + struct axg_tdm_stream *stream; + const struct axg_tdm_formatter_driver *drv; + struct clk *pclk; + struct clk *sclk; + struct clk *lrclk; + struct clk *sclk_sel; + struct clk *lrclk_sel; + bool enabled; + struct regmap *map; +}; + +int axg_tdm_formatter_set_channel_masks(struct regmap *map, + struct axg_tdm_stream *ts, + unsigned int offset) +{ + unsigned int val, ch = ts->channels; + unsigned long mask; + int i, j; + + /* + * Distribute the channels of the stream over the available slots + * of each TDM lane + */ + for (i = 0; i < AXG_TDM_NUM_LANES; i++) { + val = 0; + mask = ts->mask[i]; + + for (j = find_first_bit(&mask, 32); + (j < 32) && ch; + j = find_next_bit(&mask, 32, j + 1)) { + val |= 1 << j; + ch -= 1; + } + + regmap_write(map, offset, val); + offset += regmap_get_reg_stride(map); + } + + /* + * If we still have channel left at the end of the process, it means + * the stream has more channels than we can accommodate and we should + * have caught this earlier. + */ + if (WARN_ON(ch != 0)) { + pr_err("channel mask error\n"); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks); + +static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter) +{ + struct axg_tdm_stream *ts = formatter->stream; + bool invert = formatter->drv->invert_sclk; + int ret; + + /* Do nothing if the formatter is already enabled */ + if (formatter->enabled) + return 0; + + /* + * If sclk is inverted, invert it back and provide the inversion + * required by the formatter + */ + invert ^= axg_tdm_sclk_invert(ts->iface->fmt); + ret = clk_set_phase(formatter->sclk, invert ? 180 : 0); + if (ret) + return ret; + + /* Setup the stream parameter in the formatter */ + ret = formatter->drv->ops->prepare(formatter->map, formatter->stream); + if (ret) + return ret; + + /* Enable the signal clocks feeding the formatter */ + ret = clk_prepare_enable(formatter->sclk); + if (ret) + return ret; + + ret = clk_prepare_enable(formatter->lrclk); + if (ret) { + clk_disable_unprepare(formatter->sclk); + return ret; + } + + /* Finally, actually enable the formatter */ + formatter->drv->ops->enable(formatter->map); + formatter->enabled = true; + + return 0; +} + +static void axg_tdm_formatter_disable(struct axg_tdm_formatter *formatter) +{ + /* Do nothing if the formatter is already disabled */ + if (!formatter->enabled) + return; + + formatter->drv->ops->disable(formatter->map); + clk_disable_unprepare(formatter->lrclk); + clk_disable_unprepare(formatter->sclk); + formatter->enabled = false; +} + +static int axg_tdm_formatter_attach(struct axg_tdm_formatter *formatter) +{ + struct axg_tdm_stream *ts = formatter->stream; + int ret = 0; + + mutex_lock(&ts->lock); + + /* Catch up if the stream is already running when we attach */ + if (ts->ready) { + ret = axg_tdm_formatter_enable(formatter); + if (ret) { + pr_err("failed to enable formatter\n"); + goto out; + } + } + + list_add_tail(&formatter->list, &ts->formatter_list); +out: + mutex_unlock(&ts->lock); + return ret; +} + +static void axg_tdm_formatter_dettach(struct axg_tdm_formatter *formatter) +{ + struct axg_tdm_stream *ts = formatter->stream; + + mutex_lock(&ts->lock); + list_del(&formatter->list); + mutex_unlock(&ts->lock); + + axg_tdm_formatter_disable(formatter); +} + +static int axg_tdm_formatter_power_up(struct axg_tdm_formatter *formatter, + struct snd_soc_dapm_widget *w) +{ + struct axg_tdm_stream *ts = formatter->drv->ops->get_stream(w); + int ret; + + /* + * If we don't get a stream at this stage, it would mean that the + * widget is powering up but is not attached to any backend DAI. + * It should not happen, ever ! + */ + if (WARN_ON(!ts)) + return -ENODEV; + + /* Clock our device */ + ret = clk_prepare_enable(formatter->pclk); + if (ret) + return ret; + + /* Reparent the bit clock to the TDM interface */ + ret = clk_set_parent(formatter->sclk_sel, ts->iface->sclk); + if (ret) + goto disable_pclk; + + /* Reparent the sample clock to the TDM interface */ + ret = clk_set_parent(formatter->lrclk_sel, ts->iface->lrclk); + if (ret) + goto disable_pclk; + + formatter->stream = ts; + ret = axg_tdm_formatter_attach(formatter); + if (ret) + goto disable_pclk; + + return 0; + +disable_pclk: + clk_disable_unprepare(formatter->pclk); + return ret; +} + +static void axg_tdm_formatter_power_down(struct axg_tdm_formatter *formatter) +{ + axg_tdm_formatter_dettach(formatter); + clk_disable_unprepare(formatter->pclk); + formatter->stream = NULL; +} + +int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, + int event) +{ + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct axg_tdm_formatter *formatter = snd_soc_component_get_drvdata(c); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = axg_tdm_formatter_power_up(formatter, w); + break; + + case SND_SOC_DAPM_PRE_PMD: + axg_tdm_formatter_power_down(formatter); + break; + + default: + dev_err(c->dev, "Unexpected event %d\n", event); + return -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(axg_tdm_formatter_event); + +int axg_tdm_formatter_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct axg_tdm_formatter_driver *drv; + struct axg_tdm_formatter *formatter; + struct resource *res; + void __iomem *regs; + int ret; + + drv = of_device_get_match_data(dev); + if (!drv) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); + if (!formatter) + return -ENOMEM; + platform_set_drvdata(pdev, formatter); + formatter->drv = drv; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); + if (IS_ERR(formatter->map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(formatter->map)); + return PTR_ERR(formatter->map); + } + + /* Peripharal clock */ + formatter->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(formatter->pclk)) { + ret = PTR_ERR(formatter->pclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get pclk: %d\n", ret); + return ret; + } + + /* Formatter bit clock */ + formatter->sclk = devm_clk_get(dev, "sclk"); + if (IS_ERR(formatter->sclk)) { + ret = PTR_ERR(formatter->sclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get sclk: %d\n", ret); + return ret; + } + + /* Formatter sample clock */ + formatter->lrclk = devm_clk_get(dev, "lrclk"); + if (IS_ERR(formatter->lrclk)) { + ret = PTR_ERR(formatter->lrclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get lrclk: %d\n", ret); + return ret; + } + + /* Formatter bit clock input multiplexer */ + formatter->sclk_sel = devm_clk_get(dev, "sclk_sel"); + if (IS_ERR(formatter->sclk_sel)) { + ret = PTR_ERR(formatter->sclk_sel); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get sclk_sel: %d\n", ret); + return ret; + } + + /* Formatter sample clock input multiplexer */ + formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel"); + if (IS_ERR(formatter->lrclk_sel)) { + ret = PTR_ERR(formatter->lrclk_sel); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get lrclk_sel: %d\n", ret); + return ret; + } + + return devm_snd_soc_register_component(dev, drv->component_drv, + NULL, 0); +} +EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe); + +int axg_tdm_stream_start(struct axg_tdm_stream *ts) +{ + struct axg_tdm_formatter *formatter; + int ret = 0; + + mutex_lock(&ts->lock); + ts->ready = true; + + /* Start all the formatters attached to the stream */ + list_for_each_entry(formatter, &ts->formatter_list, list) { + ret = axg_tdm_formatter_enable(formatter); + if (ret) { + pr_err("failed to start tdm stream\n"); + goto out; + } + } + +out: + mutex_unlock(&ts->lock); + return ret; +} +EXPORT_SYMBOL_GPL(axg_tdm_stream_start); + +void axg_tdm_stream_stop(struct axg_tdm_stream *ts) +{ + struct axg_tdm_formatter *formatter; + + mutex_lock(&ts->lock); + ts->ready = false; + + /* Stop all the formatters attached to the stream */ + list_for_each_entry(formatter, &ts->formatter_list, list) { + axg_tdm_formatter_disable(formatter); + } + + mutex_unlock(&ts->lock); +} +EXPORT_SYMBOL_GPL(axg_tdm_stream_stop); + +struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface) +{ + struct axg_tdm_stream *ts; + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts) { + INIT_LIST_HEAD(&ts->formatter_list); + mutex_init(&ts->lock); + ts->iface = iface; + } + + return ts; +} +EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc); + +void axg_tdm_stream_free(struct axg_tdm_stream *ts) +{ + /* + * If the list is not empty, it would mean that one of the formatter + * widget is still powered and attached to the interface while we + * we are removing the TDM DAI. It should not be possible + */ + WARN_ON(!list_empty(&ts->formatter_list)); + mutex_destroy(&ts->lock); + kfree(ts); +} +EXPORT_SYMBOL_GPL(axg_tdm_stream_free); + +MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-tdm-formatter.h b/sound/soc/meson/axg-tdm-formatter.h new file mode 100644 index 000000000000..cf947caf3cb1 --- /dev/null +++ b/sound/soc/meson/axg-tdm-formatter.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) + * + * Copyright (c) 2018 Baylibre SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef _MESON_AXG_TDM_FORMATTER_H +#define _MESON_AXG_TDM_FORMATTER_H + +#include "axg-tdm.h" + +struct platform_device; +struct regmap; +struct snd_soc_dapm_widget; +struct snd_kcontrol; + +struct axg_tdm_formatter_ops { + struct axg_tdm_stream *(*get_stream)(struct snd_soc_dapm_widget *w); + void (*enable)(struct regmap *map); + void (*disable)(struct regmap *map); + int (*prepare)(struct regmap *map, struct axg_tdm_stream *ts); +}; + +struct axg_tdm_formatter_driver { + const struct snd_soc_component_driver *component_drv; + const struct regmap_config *regmap_cfg; + const struct axg_tdm_formatter_ops *ops; + bool invert_sclk; +}; + +int axg_tdm_formatter_set_channel_masks(struct regmap *map, + struct axg_tdm_stream *ts, + unsigned int offset); +int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, + int event); +int axg_tdm_formatter_probe(struct platform_device *pdev); + +#endif /* _MESON_AXG_TDM_FORMATTER_H */ diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c new file mode 100644 index 000000000000..7b8baf46d968 --- /dev/null +++ b/sound/soc/meson/axg-tdm-interface.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-tdm.h" + +enum { + TDM_IFACE_PAD, + TDM_IFACE_LOOPBACK, +}; + +static unsigned int axg_tdm_slots_total(u32 *mask) +{ + unsigned int slots = 0; + int i; + + if (!mask) + return 0; + + /* Count the total number of slots provided by all 4 lanes */ + for (i = 0; i < AXG_TDM_NUM_LANES; i++) + slots += hweight32(mask[i]); + + return slots; +} + +int axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask, + u32 *rx_mask, unsigned int slots, + unsigned int slot_width) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + struct axg_tdm_stream *tx = (struct axg_tdm_stream *) + dai->playback_dma_data; + struct axg_tdm_stream *rx = (struct axg_tdm_stream *) + dai->capture_dma_data; + unsigned int tx_slots, rx_slots; + + tx_slots = axg_tdm_slots_total(tx_mask); + rx_slots = axg_tdm_slots_total(rx_mask); + + /* We should at least have a slot for a valid interface */ + if (!tx_slots && !rx_slots) { + dev_err(dai->dev, "interface has no slot\n"); + return -EINVAL; + } + + /* + * Amend the dai driver channel number and let dpcm channel merge do + * its job + */ + if (tx) { + tx->mask = tx_mask; + dai->driver->playback.channels_max = tx_slots; + } + + if (rx) { + rx->mask = rx_mask; + dai->driver->capture.channels_max = rx_slots; + } + + iface->slots = slots; + + switch (slot_width) { + case 0: + /* defaults width to 32 if not provided */ + iface->slot_width = 32; + break; + case 8: + case 16: + case 24: + case 32: + iface->slot_width = slot_width; + break; + default: + dev_err(dai->dev, "unsupported slot width: %d\n", slot_width); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(axg_tdm_set_tdm_slots); + +static int axg_tdm_iface_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + int ret = -ENOTSUPP; + + if (dir == SND_SOC_CLOCK_OUT && clk_id == 0) { + if (!iface->mclk) { + dev_warn(dai->dev, "master clock not provided\n"); + } else { + ret = clk_set_rate(iface->mclk, freq); + if (!ret) + iface->mclk_rate = freq; + } + } + + return ret; +} + +static int axg_tdm_iface_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + + /* These modes are not supported */ + if (fmt & (SND_SOC_DAIFMT_CBS_CFM | SND_SOC_DAIFMT_CBM_CFS)) { + dev_err(dai->dev, "only CBS_CFS and CBM_CFM are supported\n"); + return -EINVAL; + } + + /* If the TDM interface is the clock master, it requires mclk */ + if (!iface->mclk && (fmt & SND_SOC_DAIFMT_CBS_CFS)) { + dev_err(dai->dev, "cpu clock master: mclk missing\n"); + return -ENODEV; + } + + iface->fmt = fmt; + return 0; +} + +static int axg_tdm_iface_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + struct axg_tdm_stream *ts = + snd_soc_dai_get_dma_data(dai, substream); + int ret; + + if (!axg_tdm_slots_total(ts->mask)) { + dev_err(dai->dev, "interface has not slots\n"); + return -EINVAL; + } + + /* Apply component wide rate symmetry */ + if (dai->component->active) { + ret = snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + iface->rate); + if (ret < 0) { + dev_err(dai->dev, + "can't set iface rate constraint\n"); + return ret; + } + } + + return 0; +} + +static int axg_tdm_iface_set_stream(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + unsigned int channels = params_channels(params); + unsigned int width = params_width(params); + + /* Save rate and sample_bits for component symmetry */ + iface->rate = params_rate(params); + + /* Make sure this interface can cope with the stream */ + if (axg_tdm_slots_total(ts->mask) < channels) { + dev_err(dai->dev, "not enough slots for channels\n"); + return -EINVAL; + } + + if (iface->slot_width < width) { + dev_err(dai->dev, "incompatible slots width for stream\n"); + return -EINVAL; + } + + /* Save the parameter for tdmout/tdmin widgets */ + ts->physical_width = params_physical_width(params); + ts->width = params_width(params); + ts->channels = params_channels(params); + + return 0; +} + +static int axg_tdm_iface_set_lrclk(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + unsigned int ratio_num; + int ret; + + ret = clk_set_rate(iface->lrclk, params_rate(params)); + if (ret) { + dev_err(dai->dev, "setting sample clock failed: %d\n", ret); + return ret; + } + + switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + /* 50% duty cycle ratio */ + ratio_num = 1; + break; + + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* + * A zero duty cycle ratio will result in setting the mininum + * ratio possible which, for this clock, is 1 cycle of the + * parent bclk clock high and the rest low, This is exactly + * what we want here. + */ + ratio_num = 0; + break; + + default: + return -EINVAL; + } + + ret = clk_set_duty_cycle(iface->lrclk, ratio_num, 2); + if (ret) { + dev_err(dai->dev, + "setting sample clock duty cycle failed: %d\n", ret); + return ret; + } + + /* Set sample clock inversion */ + ret = clk_set_phase(iface->lrclk, + axg_tdm_lrclk_invert(iface->fmt) ? 180 : 0); + if (ret) { + dev_err(dai->dev, + "setting sample clock phase failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int axg_tdm_iface_set_sclk(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + unsigned long srate; + int ret; + + srate = iface->slots * iface->slot_width * params_rate(params); + + if (!iface->mclk_rate) { + /* If no specific mclk is requested, default to bit clock * 4 */ + clk_set_rate(iface->mclk, 4 * srate); + } else { + /* Check if we can actually get the bit clock from mclk */ + if (iface->mclk_rate % srate) { + dev_err(dai->dev, + "can't derive sclk %lu from mclk %lu\n", + srate, iface->mclk_rate); + return -EINVAL; + } + } + + ret = clk_set_rate(iface->sclk, srate); + if (ret) { + dev_err(dai->dev, "setting bit clock failed: %d\n", ret); + return ret; + } + + /* Set the bit clock inversion */ + ret = clk_set_phase(iface->sclk, + axg_tdm_sclk_invert(iface->fmt) ? 0 : 180); + if (ret) { + dev_err(dai->dev, "setting bit clock phase failed: %d\n", ret); + return ret; + } + + return ret; +} + +static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + int ret; + + switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + if (iface->slots > 2) { + dev_err(dai->dev, "bad slot number for format: %d\n", + iface->slots); + return -EINVAL; + } + break; + + case SND_SOC_DAI_FORMAT_DSP_A: + case SND_SOC_DAI_FORMAT_DSP_B: + break; + + default: + dev_err(dai->dev, "unsupported dai format\n"); + return -EINVAL; + } + + ret = axg_tdm_iface_set_stream(substream, params, dai); + if (ret) + return ret; + + if (iface->fmt & SND_SOC_DAIFMT_CBS_CFS) { + ret = axg_tdm_iface_set_sclk(dai, params); + if (ret) + return ret; + + ret = axg_tdm_iface_set_lrclk(dai, params); + if (ret) + return ret; + } + + return 0; +} + +static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + + /* Stop all attached formatters */ + axg_tdm_stream_stop(ts); + + return 0; +} + +static int axg_tdm_iface_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + + /* Force all attached formatters to update */ + return axg_tdm_stream_reset(ts); +} + +static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai) +{ + if (dai->capture_dma_data) + axg_tdm_stream_free(dai->capture_dma_data); + + if (dai->playback_dma_data) + axg_tdm_stream_free(dai->playback_dma_data); + + return 0; +} + +static int axg_tdm_iface_probe_dai(struct snd_soc_dai *dai) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + + if (dai->capture_widget) { + dai->capture_dma_data = axg_tdm_stream_alloc(iface); + if (!dai->capture_dma_data) + return -ENOMEM; + } + + if (dai->playback_widget) { + dai->playback_dma_data = axg_tdm_stream_alloc(iface); + if (!dai->playback_dma_data) { + axg_tdm_iface_remove_dai(dai); + return -ENOMEM; + } + } + + return 0; +} + +static const struct snd_soc_dai_ops axg_tdm_iface_ops = { + .set_sysclk = axg_tdm_iface_set_sysclk, + .set_fmt = axg_tdm_iface_set_fmt, + .startup = axg_tdm_iface_startup, + .hw_params = axg_tdm_iface_hw_params, + .prepare = axg_tdm_iface_prepare, + .hw_free = axg_tdm_iface_hw_free, +}; + +/* TDM Backend DAIs */ +static const struct snd_soc_dai_driver axg_tdm_iface_dai_drv[] = { + [TDM_IFACE_PAD] = { + .name = "TDM Pad", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = AXG_TDM_CHANNEL_MAX, + .rates = AXG_TDM_RATES, + .formats = AXG_TDM_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = AXG_TDM_CHANNEL_MAX, + .rates = AXG_TDM_RATES, + .formats = AXG_TDM_FORMATS, + }, + .id = TDM_IFACE_PAD, + .ops = &axg_tdm_iface_ops, + .probe = axg_tdm_iface_probe_dai, + .remove = axg_tdm_iface_remove_dai, + }, + [TDM_IFACE_LOOPBACK] = { + .name = "TDM Loopback", + .capture = { + .stream_name = "Loopback", + .channels_min = 1, + .channels_max = AXG_TDM_CHANNEL_MAX, + .rates = AXG_TDM_RATES, + .formats = AXG_TDM_FORMATS, + }, + .id = TDM_IFACE_LOOPBACK, + .ops = &axg_tdm_iface_ops, + .probe = axg_tdm_iface_probe_dai, + .remove = axg_tdm_iface_remove_dai, + }, +}; + +static int axg_tdm_iface_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct axg_tdm_iface *iface = snd_soc_component_get_drvdata(component); + enum snd_soc_bias_level now = + snd_soc_component_get_bias_level(component); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (now == SND_SOC_BIAS_STANDBY) + ret = clk_prepare_enable(iface->mclk); + break; + + case SND_SOC_BIAS_STANDBY: + if (now == SND_SOC_BIAS_PREPARE) + clk_disable_unprepare(iface->mclk); + break; + + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_ON: + break; + } + + return ret; +} + +static const struct snd_soc_component_driver axg_tdm_iface_component_drv = { + .set_bias_level = axg_tdm_iface_set_bias_level, +}; + +static const struct of_device_id axg_tdm_iface_of_match[] = { + { .compatible = "amlogic,axg-tdm-iface", }, + {} +}; +MODULE_DEVICE_TABLE(of, axg_tdm_iface_of_match); + +static int axg_tdm_iface_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_soc_dai_driver *dai_drv; + struct axg_tdm_iface *iface; + int ret, i; + + iface = devm_kzalloc(dev, sizeof(*iface), GFP_KERNEL); + if (!iface) + return -ENOMEM; + platform_set_drvdata(pdev, iface); + + /* + * Duplicate dai driver: depending on the slot masks configuration + * We'll change the number of channel provided by DAI stream, so dpcm + * channel merge can be done properly + */ + dai_drv = devm_kcalloc(dev, ARRAY_SIZE(axg_tdm_iface_dai_drv), + sizeof(*dai_drv), GFP_KERNEL); + if (!dai_drv) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(axg_tdm_iface_dai_drv); i++) + memcpy(&dai_drv[i], &axg_tdm_iface_dai_drv[i], + sizeof(*dai_drv)); + + /* Bit clock provided on the pad */ + iface->sclk = devm_clk_get(dev, "sclk"); + if (IS_ERR(iface->sclk)) { + ret = PTR_ERR(iface->sclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get sclk: %d\n", ret); + return ret; + } + + /* Sample clock provided on the pad */ + iface->lrclk = devm_clk_get(dev, "lrclk"); + if (IS_ERR(iface->lrclk)) { + ret = PTR_ERR(iface->lrclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get lrclk: %d\n", ret); + return ret; + } + + /* + * mclk maybe be missing when the cpu dai is in slave mode and + * the codec does not require it to provide a master clock. + * At this point, ignore the error if mclk is missing. We'll + * throw an error if the cpu dai is master and mclk is missing + */ + iface->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(iface->mclk)) { + ret = PTR_ERR(iface->mclk); + if (ret == -ENOENT) { + iface->mclk = NULL; + } else { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get mclk: %d\n", ret); + return ret; + } + } + + return devm_snd_soc_register_component(dev, + &axg_tdm_iface_component_drv, dai_drv, + ARRAY_SIZE(axg_tdm_iface_dai_drv)); +} + +static struct platform_driver axg_tdm_iface_pdrv = { + .probe = axg_tdm_iface_probe, + .driver = { + .name = "axg-tdm-iface", + .of_match_table = axg_tdm_iface_of_match, + }, +}; +module_platform_driver(axg_tdm_iface_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG TDM interface driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-tdm.h b/sound/soc/meson/axg-tdm.h new file mode 100644 index 000000000000..e578b6f40a07 --- /dev/null +++ b/sound/soc/meson/axg-tdm.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) + * + * Copyright (c) 2018 Baylibre SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef _MESON_AXG_TDM_H +#define _MESON_AXG_TDM_H + +#include <linux/clk.h> +#include <linux/regmap.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#define AXG_TDM_NUM_LANES 4 +#define AXG_TDM_CHANNEL_MAX 128 +#define AXG_TDM_RATES (SNDRV_PCM_RATE_5512 | \ + SNDRV_PCM_RATE_8000_192000) +#define AXG_TDM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +struct axg_tdm_iface { + struct clk *sclk; + struct clk *lrclk; + struct clk *mclk; + unsigned long mclk_rate; + + /* format is common to all the DAIs of the iface */ + unsigned int fmt; + unsigned int slots; + unsigned int slot_width; + + /* For component wide symmetry */ + int rate; +}; + +static inline bool axg_tdm_lrclk_invert(unsigned int fmt) +{ + return (fmt & SND_SOC_DAIFMT_I2S) ^ + !!(fmt & (SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_NB_IF)); +} + +static inline bool axg_tdm_sclk_invert(unsigned int fmt) +{ + return fmt & (SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_IB_NF); +} + +struct axg_tdm_stream { + struct axg_tdm_iface *iface; + struct list_head formatter_list; + struct mutex lock; + unsigned int channels; + unsigned int width; + unsigned int physical_width; + u32 *mask; + bool ready; +}; + +struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface); +void axg_tdm_stream_free(struct axg_tdm_stream *ts); +int axg_tdm_stream_start(struct axg_tdm_stream *ts); +void axg_tdm_stream_stop(struct axg_tdm_stream *ts); + +static inline int axg_tdm_stream_reset(struct axg_tdm_stream *ts) +{ + axg_tdm_stream_stop(ts); + return axg_tdm_stream_start(ts); +} + +int axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask, + u32 *rx_mask, unsigned int slots, + unsigned int slot_width); + +#endif /* _MESON_AXG_TDM_H */ diff --git a/sound/soc/meson/axg-tdmin.c b/sound/soc/meson/axg-tdmin.c new file mode 100644 index 000000000000..bbac44c81688 --- /dev/null +++ b/sound/soc/meson/axg-tdmin.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-tdm-formatter.h" + +#define TDMIN_CTRL 0x00 +#define TDMIN_CTRL_ENABLE BIT(31) +#define TDMIN_CTRL_I2S_MODE BIT(30) +#define TDMIN_CTRL_RST_OUT BIT(29) +#define TDMIN_CTRL_RST_IN BIT(28) +#define TDMIN_CTRL_WS_INV BIT(25) +#define TDMIN_CTRL_SEL_SHIFT 20 +#define TDMIN_CTRL_IN_BIT_SKEW_MASK GENMASK(18, 16) +#define TDMIN_CTRL_IN_BIT_SKEW(x) ((x) << 16) +#define TDMIN_CTRL_LSB_FIRST BIT(5) +#define TDMIN_CTRL_BITNUM_MASK GENMASK(4, 0) +#define TDMIN_CTRL_BITNUM(x) ((x) << 0) +#define TDMIN_SWAP 0x04 +#define TDMIN_MASK0 0x08 +#define TDMIN_MASK1 0x0c +#define TDMIN_MASK2 0x10 +#define TDMIN_MASK3 0x14 +#define TDMIN_STAT 0x18 +#define TDMIN_MUTE_VAL 0x1c +#define TDMIN_MUTE0 0x20 +#define TDMIN_MUTE1 0x24 +#define TDMIN_MUTE2 0x28 +#define TDMIN_MUTE3 0x2c + +static const struct regmap_config axg_tdmin_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = TDMIN_MUTE3, +}; + +static const char * const axg_tdmin_sel_texts[] = { + "IN 0", "IN 1", "IN 2", "IN 3", "IN 4", "IN 5", +}; + +/* Change to special mux control to reset dapm */ +static SOC_ENUM_SINGLE_DECL(axg_tdmin_sel_enum, TDMIN_CTRL, + TDMIN_CTRL_SEL_SHIFT, axg_tdmin_sel_texts); + +static const struct snd_kcontrol_new axg_tdmin_in_mux = + SOC_DAPM_ENUM("Input Source", axg_tdmin_sel_enum); + +static struct snd_soc_dai * +axg_tdmin_get_be(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p = NULL; + struct snd_soc_dai *be; + + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (!p->connect) + continue; + + if (p->source->id == snd_soc_dapm_dai_out) + return (struct snd_soc_dai *)p->source->priv; + + be = axg_tdmin_get_be(p->source); + if (be) + return be; + } + + return NULL; +} + +static struct axg_tdm_stream * +axg_tdmin_get_tdm_stream(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dai *be = axg_tdmin_get_be(w); + + if (!be) + return NULL; + + return be->capture_dma_data; +} + +static void axg_tdmin_enable(struct regmap *map) +{ + /* Apply both reset */ + regmap_update_bits(map, TDMIN_CTRL, + TDMIN_CTRL_RST_OUT | TDMIN_CTRL_RST_IN, 0); + + /* Clear out reset before in reset */ + regmap_update_bits(map, TDMIN_CTRL, + TDMIN_CTRL_RST_OUT, TDMIN_CTRL_RST_OUT); + regmap_update_bits(map, TDMIN_CTRL, + TDMIN_CTRL_RST_IN, TDMIN_CTRL_RST_IN); + + /* Actually enable tdmin */ + regmap_update_bits(map, TDMIN_CTRL, + TDMIN_CTRL_ENABLE, TDMIN_CTRL_ENABLE); +} + +static void axg_tdmin_disable(struct regmap *map) +{ + regmap_update_bits(map, TDMIN_CTRL, TDMIN_CTRL_ENABLE, 0); +} + +static int axg_tdmin_prepare(struct regmap *map, struct axg_tdm_stream *ts) +{ + unsigned int val = 0; + + /* Set stream skew */ + switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: + val |= TDMIN_CTRL_IN_BIT_SKEW(3); + break; + + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_DSP_B: + val = TDMIN_CTRL_IN_BIT_SKEW(2); + break; + + default: + pr_err("Unsupported format: %u\n", + ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + /* Set stream format mode */ + switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + val |= TDMIN_CTRL_I2S_MODE; + break; + } + + /* If the sample clock is inverted, invert it back for the formatter */ + if (axg_tdm_lrclk_invert(ts->iface->fmt)) + val |= TDMIN_CTRL_WS_INV; + + /* Set the slot width */ + val |= TDMIN_CTRL_BITNUM(ts->iface->slot_width - 1); + + /* + * The following also reset LSB_FIRST which result in the formatter + * placing the first bit received at bit 31 + */ + regmap_update_bits(map, TDMIN_CTRL, + (TDMIN_CTRL_IN_BIT_SKEW_MASK | TDMIN_CTRL_WS_INV | + TDMIN_CTRL_I2S_MODE | TDMIN_CTRL_LSB_FIRST | + TDMIN_CTRL_BITNUM_MASK), val); + + /* Set static swap mask configuration */ + regmap_write(map, TDMIN_SWAP, 0x76543210); + + return axg_tdm_formatter_set_channel_masks(map, ts, TDMIN_MASK0); +} + +static const struct snd_soc_dapm_widget axg_tdmin_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 5", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_tdmin_in_mux), + SND_SOC_DAPM_PGA_E("DEC", SND_SOC_NOPM, 0, 0, NULL, 0, + axg_tdm_formatter_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)), + SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route axg_tdmin_dapm_routes[] = { + { "SRC SEL", "IN 0", "IN 0" }, + { "SRC SEL", "IN 1", "IN 1" }, + { "SRC SEL", "IN 2", "IN 2" }, + { "SRC SEL", "IN 3", "IN 3" }, + { "SRC SEL", "IN 4", "IN 4" }, + { "SRC SEL", "IN 5", "IN 5" }, + { "DEC", NULL, "SRC SEL" }, + { "OUT", NULL, "DEC" }, +}; + +static const struct snd_soc_component_driver axg_tdmin_component_drv = { + .dapm_widgets = axg_tdmin_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(axg_tdmin_dapm_widgets), + .dapm_routes = axg_tdmin_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(axg_tdmin_dapm_routes), +}; + +static const struct axg_tdm_formatter_ops axg_tdmin_ops = { + .get_stream = axg_tdmin_get_tdm_stream, + .prepare = axg_tdmin_prepare, + .enable = axg_tdmin_enable, + .disable = axg_tdmin_disable, +}; + +static const struct axg_tdm_formatter_driver axg_tdmin_drv = { + .component_drv = &axg_tdmin_component_drv, + .regmap_cfg = &axg_tdmin_regmap_cfg, + .ops = &axg_tdmin_ops, + .invert_sclk = false, +}; + +static const struct of_device_id axg_tdmin_of_match[] = { + { + .compatible = "amlogic,axg-tdmin", + .data = &axg_tdmin_drv, + }, {} +}; +MODULE_DEVICE_TABLE(of, axg_tdmin_of_match); + +static struct platform_driver axg_tdmin_pdrv = { + .probe = axg_tdm_formatter_probe, + .driver = { + .name = "axg-tdmin", + .of_match_table = axg_tdmin_of_match, + }, +}; +module_platform_driver(axg_tdmin_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG TDM input formatter driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-tdmout.c b/sound/soc/meson/axg-tdmout.c new file mode 100644 index 000000000000..f73368ee1088 --- /dev/null +++ b/sound/soc/meson/axg-tdmout.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-tdm-formatter.h" + +#define TDMOUT_CTRL0 0x00 +#define TDMOUT_CTRL0_BITNUM_MASK GENMASK(4, 0) +#define TDMOUT_CTRL0_BITNUM(x) ((x) << 0) +#define TDMOUT_CTRL0_SLOTNUM_MASK GENMASK(9, 5) +#define TDMOUT_CTRL0_SLOTNUM(x) ((x) << 5) +#define TDMOUT_CTRL0_INIT_BITNUM_MASK GENMASK(19, 15) +#define TDMOUT_CTRL0_INIT_BITNUM(x) ((x) << 15) +#define TDMOUT_CTRL0_ENABLE BIT(31) +#define TDMOUT_CTRL0_RST_OUT BIT(29) +#define TDMOUT_CTRL0_RST_IN BIT(28) +#define TDMOUT_CTRL1 0x04 +#define TDMOUT_CTRL1_TYPE_MASK GENMASK(6, 4) +#define TDMOUT_CTRL1_TYPE(x) ((x) << 4) +#define TDMOUT_CTRL1_MSB_POS_MASK GENMASK(12, 8) +#define TDMOUT_CTRL1_MSB_POS(x) ((x) << 8) +#define TDMOUT_CTRL1_SEL_SHIFT 24 +#define TDMOUT_CTRL1_GAIN_EN 26 +#define TDMOUT_CTRL1_WS_INV BIT(28) +#define TDMOUT_SWAP 0x08 +#define TDMOUT_MASK0 0x0c +#define TDMOUT_MASK1 0x10 +#define TDMOUT_MASK2 0x14 +#define TDMOUT_MASK3 0x18 +#define TDMOUT_STAT 0x1c +#define TDMOUT_GAIN0 0x20 +#define TDMOUT_GAIN1 0x24 +#define TDMOUT_MUTE_VAL 0x28 +#define TDMOUT_MUTE0 0x2c +#define TDMOUT_MUTE1 0x30 +#define TDMOUT_MUTE2 0x34 +#define TDMOUT_MUTE3 0x38 +#define TDMOUT_MASK_VAL 0x3c + +static const struct regmap_config axg_tdmout_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = TDMOUT_MASK_VAL, +}; + +static const struct snd_kcontrol_new axg_tdmout_controls[] = { + SOC_DOUBLE("Lane 0 Volume", TDMOUT_GAIN0, 0, 8, 255, 0), + SOC_DOUBLE("Lane 1 Volume", TDMOUT_GAIN0, 16, 24, 255, 0), + SOC_DOUBLE("Lane 2 Volume", TDMOUT_GAIN1, 0, 8, 255, 0), + SOC_DOUBLE("Lane 3 Volume", TDMOUT_GAIN1, 16, 24, 255, 0), + SOC_SINGLE("Gain Enable Switch", TDMOUT_CTRL1, + TDMOUT_CTRL1_GAIN_EN, 1, 0), +}; + +static const char * const tdmout_sel_texts[] = { + "IN 0", "IN 1", "IN 2", +}; + +static SOC_ENUM_SINGLE_DECL(axg_tdmout_sel_enum, TDMOUT_CTRL1, + TDMOUT_CTRL1_SEL_SHIFT, tdmout_sel_texts); + +static const struct snd_kcontrol_new axg_tdmout_in_mux = + SOC_DAPM_ENUM("Input Source", axg_tdmout_sel_enum); + +static struct snd_soc_dai * +axg_tdmout_get_be(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p = NULL; + struct snd_soc_dai *be; + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->connect) + continue; + + if (p->sink->id == snd_soc_dapm_dai_in) + return (struct snd_soc_dai *)p->sink->priv; + + be = axg_tdmout_get_be(p->sink); + if (be) + return be; + } + + return NULL; +} + +static struct axg_tdm_stream * +axg_tdmout_get_tdm_stream(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dai *be = axg_tdmout_get_be(w); + + if (!be) + return NULL; + + return be->playback_dma_data; +} + +static void axg_tdmout_enable(struct regmap *map) +{ + /* Apply both reset */ + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_RST_OUT | TDMOUT_CTRL0_RST_IN, 0); + + /* Clear out reset before in reset */ + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_RST_OUT, TDMOUT_CTRL0_RST_OUT); + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_RST_IN, TDMOUT_CTRL0_RST_IN); + + /* Actually enable tdmout */ + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_ENABLE, TDMOUT_CTRL0_ENABLE); +} + +static void axg_tdmout_disable(struct regmap *map) +{ + regmap_update_bits(map, TDMOUT_CTRL0, TDMOUT_CTRL0_ENABLE, 0); +} + +static int axg_tdmout_prepare(struct regmap *map, struct axg_tdm_stream *ts) +{ + unsigned int val = 0; + + /* Set the stream skew */ + switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: + val |= TDMOUT_CTRL0_INIT_BITNUM(1); + break; + + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_DSP_B: + val |= TDMOUT_CTRL0_INIT_BITNUM(2); + break; + + default: + pr_err("Unsupported format: %u\n", + ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + /* Set the slot width */ + val |= TDMOUT_CTRL0_BITNUM(ts->iface->slot_width - 1); + + /* Set the slot number */ + val |= TDMOUT_CTRL0_SLOTNUM(ts->iface->slots - 1); + + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_INIT_BITNUM_MASK | + TDMOUT_CTRL0_BITNUM_MASK | + TDMOUT_CTRL0_SLOTNUM_MASK, val); + + /* Set the sample width */ + val = TDMOUT_CTRL1_MSB_POS(ts->width - 1); + + /* FIFO data are arranged in chunks of 64bits */ + switch (ts->physical_width) { + case 8: + /* 8 samples of 8 bits */ + val |= TDMOUT_CTRL1_TYPE(0); + break; + case 16: + /* 4 samples of 16 bits - right justified */ + val |= TDMOUT_CTRL1_TYPE(2); + break; + case 32: + /* 2 samples of 32 bits - right justified */ + val |= TDMOUT_CTRL1_TYPE(4); + break; + default: + pr_err("Unsupported physical width: %u\n", + ts->physical_width); + return -EINVAL; + } + + /* If the sample clock is inverted, invert it back for the formatter */ + if (axg_tdm_lrclk_invert(ts->iface->fmt)) + val |= TDMOUT_CTRL1_WS_INV; + + regmap_update_bits(map, TDMOUT_CTRL1, + (TDMOUT_CTRL1_TYPE_MASK | TDMOUT_CTRL1_MSB_POS_MASK | + TDMOUT_CTRL1_WS_INV), val); + + /* Set static swap mask configuration */ + regmap_write(map, TDMOUT_SWAP, 0x76543210); + + return axg_tdm_formatter_set_channel_masks(map, ts, TDMOUT_MASK0); +} + +static const struct snd_soc_dapm_widget axg_tdmout_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_tdmout_in_mux), + SND_SOC_DAPM_PGA_E("ENC", SND_SOC_NOPM, 0, 0, NULL, 0, + axg_tdm_formatter_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)), + SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route axg_tdmout_dapm_routes[] = { + { "SRC SEL", "IN 0", "IN 0" }, + { "SRC SEL", "IN 1", "IN 1" }, + { "SRC SEL", "IN 2", "IN 2" }, + { "ENC", NULL, "SRC SEL" }, + { "OUT", NULL, "ENC" }, +}; + +static const struct snd_soc_component_driver axg_tdmout_component_drv = { + .controls = axg_tdmout_controls, + .num_controls = ARRAY_SIZE(axg_tdmout_controls), + .dapm_widgets = axg_tdmout_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(axg_tdmout_dapm_widgets), + .dapm_routes = axg_tdmout_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(axg_tdmout_dapm_routes), +}; + +static const struct axg_tdm_formatter_ops axg_tdmout_ops = { + .get_stream = axg_tdmout_get_tdm_stream, + .prepare = axg_tdmout_prepare, + .enable = axg_tdmout_enable, + .disable = axg_tdmout_disable, +}; + +static const struct axg_tdm_formatter_driver axg_tdmout_drv = { + .component_drv = &axg_tdmout_component_drv, + .regmap_cfg = &axg_tdmout_regmap_cfg, + .ops = &axg_tdmout_ops, + .invert_sclk = true, +}; + +static const struct of_device_id axg_tdmout_of_match[] = { + { + .compatible = "amlogic,axg-tdmout", + .data = &axg_tdmout_drv, + }, {} +}; +MODULE_DEVICE_TABLE(of, axg_tdmout_of_match); + +static struct platform_driver axg_tdmout_pdrv = { + .probe = axg_tdm_formatter_probe, + .driver = { + .name = "axg-tdmout", + .of_match_table = axg_tdmout_of_match, + }, +}; +module_platform_driver(axg_tdmout_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG TDM output formatter driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c new file mode 100644 index 000000000000..c2c9bb312586 --- /dev/null +++ b/sound/soc/meson/axg-toddr.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +/* This driver implements the frontend capture DAI of AXG based SoCs */ + +#include <linux/clk.h> +#include <linux/regmap.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-fifo.h" + +#define CTRL0_TODDR_SEL_RESAMPLE BIT(30) +#define CTRL0_TODDR_EXT_SIGNED BIT(29) +#define CTRL0_TODDR_PP_MODE BIT(28) +#define CTRL0_TODDR_TYPE_MASK GENMASK(15, 13) +#define CTRL0_TODDR_TYPE(x) ((x) << 13) +#define CTRL0_TODDR_MSB_POS_MASK GENMASK(12, 8) +#define CTRL0_TODDR_MSB_POS(x) ((x) << 8) +#define CTRL0_TODDR_LSB_POS_MASK GENMASK(7, 3) +#define CTRL0_TODDR_LSB_POS(x) ((x) << 3) + +static int axg_toddr_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *dai) +{ + return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_CAPTURE); +} + +static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); + unsigned int type, width, msb = 31; + + /* + * NOTE: + * Almost all backend will place the MSB at bit 31, except SPDIF Input + * which will put it at index 28. When adding support for the SPDIF + * Input, we'll need to find which type of backend we are connected to. + */ + + switch (params_physical_width(params)) { + case 8: + type = 0; /* 8 samples of 8 bits */ + break; + case 16: + type = 2; /* 4 samples of 16 bits - right justified */ + break; + case 32: + type = 4; /* 2 samples of 32 bits - right justified */ + break; + default: + return -EINVAL; + } + + width = params_width(params); + + regmap_update_bits(fifo->map, FIFO_CTRL0, + CTRL0_TODDR_TYPE_MASK | + CTRL0_TODDR_MSB_POS_MASK | + CTRL0_TODDR_LSB_POS_MASK, + CTRL0_TODDR_TYPE(type) | + CTRL0_TODDR_MSB_POS(msb) | + CTRL0_TODDR_LSB_POS(msb - (width - 1))); + + return 0; +} + +static int axg_toddr_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); + unsigned int fifo_threshold; + int ret; + + /* Enable pclk to access registers and clock the fifo ip */ + ret = clk_prepare_enable(fifo->pclk); + if (ret) + return ret; + + /* Select orginal data - resampling not supported ATM */ + regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_SEL_RESAMPLE, 0); + + /* Only signed format are supported ATM */ + regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_EXT_SIGNED, + CTRL0_TODDR_EXT_SIGNED); + + /* Apply single buffer mode to the interface */ + regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_PP_MODE, 0); + + /* TODDR does not have a configurable fifo depth */ + fifo_threshold = AXG_FIFO_MIN_CNT - 1; + regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_THRESHOLD_MASK, + CTRL1_THRESHOLD(fifo_threshold)); + + return 0; +} + +static void axg_toddr_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); + + clk_disable_unprepare(fifo->pclk); +} + +static const struct snd_soc_dai_ops axg_toddr_ops = { + .hw_params = axg_toddr_dai_hw_params, + .startup = axg_toddr_dai_startup, + .shutdown = axg_toddr_dai_shutdown, +}; + +static struct snd_soc_dai_driver axg_toddr_dai_drv = { + .name = "TODDR", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = AXG_FIFO_CH_MAX, + .rates = AXG_FIFO_RATES, + .formats = AXG_FIFO_FORMATS, + }, + .ops = &axg_toddr_ops, + .pcm_new = axg_toddr_pcm_new, +}; + +static const char * const axg_toddr_sel_texts[] = { + "IN 0", "IN 1", "IN 2", "IN 3", "IN 4", "IN 6" +}; + +static const unsigned int axg_toddr_sel_values[] = { + 0, 1, 2, 3, 4, 6 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(axg_toddr_sel_enum, FIFO_CTRL0, + CTRL0_SEL_SHIFT, CTRL0_SEL_MASK, + axg_toddr_sel_texts, axg_toddr_sel_values); + +static const struct snd_kcontrol_new axg_toddr_in_mux = + SOC_DAPM_ENUM("Input Source", axg_toddr_sel_enum); + +static const struct snd_soc_dapm_widget axg_toddr_dapm_widgets[] = { + SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_toddr_in_mux), + SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 6", NULL, 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route axg_toddr_dapm_routes[] = { + { "Capture", NULL, "SRC SEL" }, + { "SRC SEL", "IN 0", "IN 0" }, + { "SRC SEL", "IN 1", "IN 1" }, + { "SRC SEL", "IN 2", "IN 2" }, + { "SRC SEL", "IN 3", "IN 3" }, + { "SRC SEL", "IN 4", "IN 4" }, + { "SRC SEL", "IN 6", "IN 6" }, +}; + +static const struct snd_soc_component_driver axg_toddr_component_drv = { + .dapm_widgets = axg_toddr_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets), + .dapm_routes = axg_toddr_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes), + .ops = &axg_fifo_pcm_ops +}; + +static const struct axg_fifo_match_data axg_toddr_match_data = { + .component_drv = &axg_toddr_component_drv, + .dai_drv = &axg_toddr_dai_drv +}; + +static const struct of_device_id axg_toddr_of_match[] = { + { + .compatible = "amlogic,axg-toddr", + .data = &axg_toddr_match_data, + }, {} +}; +MODULE_DEVICE_TABLE(of, axg_toddr_of_match); + +static struct platform_driver axg_toddr_pdrv = { + .probe = axg_fifo_probe, + .driver = { + .name = "axg-toddr", + .of_match_table = axg_toddr_of_match, + }, +}; +module_platform_driver(axg_toddr_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG capture fifo driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c index 15ccbf479c96..d5ae9eb8c756 100644 --- a/sound/soc/omap/omap-abe-twl6040.c +++ b/sound/soc/omap/omap-abe-twl6040.c @@ -40,7 +40,7 @@ struct abe_twl6040 { int mclk_freq; /* MCLK frequency speed for twl6040 */ }; -struct platform_device *dmic_codec_dev; +static struct platform_device *dmic_codec_dev; static int omap_abe_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c index 51dd7c65096b..fe966272bd0c 100644 --- a/sound/soc/omap/omap-dmic.c +++ b/sound/soc/omap/omap-dmic.c @@ -213,8 +213,10 @@ static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream, switch (channels) { case 6: dmic->ch_enabled |= OMAP_DMIC_UP3_ENABLE; + /* fall through */ case 4: dmic->ch_enabled |= OMAP_DMIC_UP2_ENABLE; + /* fall through */ case 2: dmic->ch_enabled |= OMAP_DMIC_UP1_ENABLE; break; diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index 0e97360f9890..4c1be36c2207 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -310,15 +310,19 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, /* up to 3 channels for capture */ return -EINVAL; link_mask |= 1 << 4; + /* fall through */ case 4: if (stream == SNDRV_PCM_STREAM_CAPTURE) /* up to 3 channels for capture */ return -EINVAL; link_mask |= 1 << 3; + /* fall through */ case 3: link_mask |= 1 << 2; + /* fall through */ case 2: link_mask |= 1 << 1; + /* fall through */ case 1: link_mask |= 1 << 0; break; diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 960744e46edc..776e148b0aa2 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -24,15 +24,19 @@ config SND_PXA2XX_AC97 config SND_PXA2XX_SOC_AC97 tristate select AC97_BUS + select SND_PXA2XX_LIB select SND_PXA2XX_LIB_AC97 select SND_SOC_AC97_BUS config SND_PXA2XX_SOC_I2S + select SND_PXA2XX_LIB tristate config SND_PXA_SOC_SSP - tristate + tristate "Soc Audio via PXA2xx/PXA3xx SSP ports" + depends on PLAT_PXA select PXA_SSP + select SND_PXA2XX_LIB config SND_MMP_SOC_SSPA tristate diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index 2fc012b06c43..935a248e5bf6 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -90,95 +90,9 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - unsigned int acps, acds, width; - unsigned int div4 = PXA_SSP_CLK_SCDB_4; + unsigned int width; int ret = 0; - width = snd_pcm_format_physical_width(params_format(params)); - - /* - * rate = SSPSCLK / (2 * width(16 or 32)) - * SSPSCLK = (ACPS / ACDS) / SSPSCLKDIV(div4 or div1) - */ - switch (params_rate(params)) { - case 8000: - /* off by a factor of 2: bug in the PXA27x audio clock? */ - acps = 32842000; - switch (width) { - case 16: - /* 513156 Hz ~= _2_ * 8000 Hz * 32 (+0.23%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_16; - break; - default: /* 32 */ - /* 1026312 Hz ~= _2_ * 8000 Hz * 64 (+0.23%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_8; - } - break; - case 11025: - acps = 5622000; - switch (width) { - case 16: - /* 351375 Hz ~= 11025 Hz * 32 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_4; - break; - default: /* 32 */ - /* 702750 Hz ~= 11025 Hz * 64 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; - } - break; - case 22050: - acps = 5622000; - switch (width) { - case 16: - /* 702750 Hz ~= 22050 Hz * 32 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; - break; - default: /* 32 */ - /* 1405500 Hz ~= 22050 Hz * 64 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_1; - } - break; - case 44100: - acps = 5622000; - switch (width) { - case 16: - /* 1405500 Hz ~= 44100 Hz * 32 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; - break; - default: /* 32 */ - /* 2811000 Hz ~= 44100 Hz * 64 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_1; - } - break; - case 48000: - acps = 12235000; - switch (width) { - case 16: - /* 1529375 Hz ~= 48000 Hz * 32 (-0.44%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; - break; - default: /* 32 */ - /* 3058750 Hz ~= 48000 Hz * 64 (-0.44%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_1; - } - break; - case 96000: - default: - acps = 12235000; - switch (width) { - case 16: - /* 3058750 Hz ~= 96000 Hz * 32 (-0.44%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_1; - break; - default: /* 32 */ - /* 6117500 Hz ~= 96000 Hz * 64 (-0.44%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; - div4 = PXA_SSP_CLK_SCDB_1; - break; - } - break; - } - /* set codec DAI configuration */ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); @@ -191,6 +105,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; + width = snd_pcm_format_physical_width(params_format(params)); ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 0, 1, width); if (ret < 0) return ret; @@ -201,23 +116,6 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - /* set the SSP audio system clock ACDS divider */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, - PXA_SSP_AUDIO_DIV_ACDS, acds); - if (ret < 0) - return ret; - - /* set the SSP audio system clock SCDB divider4 */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, - PXA_SSP_AUDIO_DIV_SCDB, div4); - if (ret < 0) - return ret; - - /* set SSP audio pll clock */ - ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, acps); - if (ret < 0) - return ret; - return 0; } diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 6fc986080130..69033e1a84e6 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -34,7 +34,6 @@ #include <sound/pxa2xx-lib.h> #include <sound/dmaengine_pcm.h> -#include "../../arm/pxa2xx-pcm.h" #include "pxa-ssp.h" /* @@ -42,6 +41,8 @@ */ struct ssp_priv { struct ssp_device *ssp; + struct clk *extclk; + unsigned long ssp_clk; unsigned int sysclk; unsigned int dai_fmt; unsigned int configured_dai_fmt; @@ -105,9 +106,8 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL); if (!dma) return -ENOMEM; - - dma->filter_data = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? - &ssp->drcmr_tx : &ssp->drcmr_rx; + dma->chan_name = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + "tx" : "rx"; snd_soc_dai_set_dma_data(cpu_dai, substream, dma); @@ -194,21 +194,6 @@ static void pxa_ssp_set_scr(struct ssp_device *ssp, u32 div) pxa_ssp_write_reg(ssp, SSCR0, sscr0); } -/** - * pxa_ssp_get_clkdiv - get SSP clock divider - */ -static u32 pxa_ssp_get_scr(struct ssp_device *ssp) -{ - u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0); - u32 div; - - if (ssp->type == PXA25x_SSP) - div = ((sscr0 >> 8) & 0xff) * 2 + 2; - else - div = ((sscr0 >> 8) & 0xfff) + 1; - return div; -} - /* * Set the SSP ports SYSCLK. */ @@ -221,6 +206,21 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); + if (priv->extclk) { + int ret; + + /* + * For DT based boards, if an extclk is given, use it + * here and configure PXA_SSP_CLK_EXT. + */ + + ret = clk_set_rate(priv->extclk, freq); + if (ret < 0) + return ret; + + clk_id = PXA_SSP_CLK_EXT; + } + dev_dbg(&ssp->pdev->dev, "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %u\n", cpu_dai->id, clk_id, freq); @@ -265,66 +265,17 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, } /* - * Set the SSP clock dividers. - */ -static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, - int div_id, int div) -{ - struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); - struct ssp_device *ssp = priv->ssp; - int val; - - switch (div_id) { - case PXA_SSP_AUDIO_DIV_ACDS: - val = (pxa_ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div); - pxa_ssp_write_reg(ssp, SSACD, val); - break; - case PXA_SSP_AUDIO_DIV_SCDB: - val = pxa_ssp_read_reg(ssp, SSACD); - val &= ~SSACD_SCDB; - if (ssp->type == PXA3xx_SSP) - val &= ~SSACD_SCDX8; - switch (div) { - case PXA_SSP_CLK_SCDB_1: - val |= SSACD_SCDB; - break; - case PXA_SSP_CLK_SCDB_4: - break; - case PXA_SSP_CLK_SCDB_8: - if (ssp->type == PXA3xx_SSP) - val |= SSACD_SCDX8; - else - return -EINVAL; - break; - default: - return -EINVAL; - } - pxa_ssp_write_reg(ssp, SSACD, val); - break; - case PXA_SSP_DIV_SCR: - pxa_ssp_set_scr(ssp, div); - break; - default: - return -ENODEV; - } - - return 0; -} - -/* * Configure the PLL frequency pxa27x and (afaik - pxa320 only) */ -static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, - int source, unsigned int freq_in, unsigned int freq_out) +static int pxa_ssp_set_pll(struct ssp_priv *priv, unsigned int freq) { - struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; u32 ssacd = pxa_ssp_read_reg(ssp, SSACD) & ~0x70; if (ssp->type == PXA3xx_SSP) pxa_ssp_write_reg(ssp, SSACDD, 0); - switch (freq_out) { + switch (freq) { case 5622000: break; case 11345000: @@ -355,7 +306,7 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, u64 tmp = 19968; tmp *= 1000000; - do_div(tmp, freq_out); + do_div(tmp, freq); val = tmp; val = (val << 16) | 64; @@ -365,7 +316,7 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, dev_dbg(&ssp->pdev->dev, "Using SSACDD %x to supply %uHz\n", - val, freq_out); + val, freq); break; } @@ -535,6 +486,7 @@ static int pxa_ssp_configure_dai_fmt(struct ssp_priv *priv) case SND_SOC_DAIFMT_DSP_A: sspsp |= SSPSP_FSRT; + /* fall through */ case SND_SOC_DAIFMT_DSP_B: sscr0 |= SSCR0_MOD | SSCR0_PSP; sscr1 |= SSCR1_TRAIL | SSCR1_RWOT; @@ -570,6 +522,24 @@ static int pxa_ssp_configure_dai_fmt(struct ssp_priv *priv) return 0; } +struct pxa_ssp_clock_mode { + int rate; + int pll; + u8 acds; + u8 scdb; +}; + +static const struct pxa_ssp_clock_mode pxa_ssp_clock_modes[] = { + { .rate = 8000, .pll = 32842000, .acds = SSACD_ACDS_32, .scdb = SSACD_SCDB_4X }, + { .rate = 11025, .pll = 5622000, .acds = SSACD_ACDS_4, .scdb = SSACD_SCDB_4X }, + { .rate = 16000, .pll = 32842000, .acds = SSACD_ACDS_16, .scdb = SSACD_SCDB_4X }, + { .rate = 22050, .pll = 5622000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, + { .rate = 44100, .pll = 11345000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, + { .rate = 48000, .pll = 12235000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, + { .rate = 96000, .pll = 12235000, .acds = SSACD_ACDS_4, .scdb = SSACD_SCDB_1X }, + {} +}; + /* * Set the SSP audio DMA parameters and sample size. * Can be called multiple times by oss emulation. @@ -581,11 +551,12 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; int chn = params_channels(params); - u32 sscr0; - u32 sspsp; + u32 sscr0, sspsp; int width = snd_pcm_format_physical_width(params_format(params)); int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; struct snd_dmaengine_dai_dma_data *dma_data; + int rate = params_rate(params); + int bclk = rate * chn * (width / 8); int ret; dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); @@ -625,11 +596,57 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, } pxa_ssp_write_reg(ssp, SSCR0, sscr0); + if (sscr0 & SSCR0_ACS) { + ret = pxa_ssp_set_pll(priv, bclk); + + /* + * If we were able to generate the bclk directly, + * all is fine. Otherwise, look up the closest rate + * from the table and also set the dividers. + */ + + if (ret < 0) { + const struct pxa_ssp_clock_mode *m; + int ssacd, acds; + + for (m = pxa_ssp_clock_modes; m->rate; m++) { + if (m->rate == rate) + break; + } + + if (!m->rate) + return -EINVAL; + + acds = m->acds; + + /* The values in the table are for 16 bits */ + if (width == 32) + acds--; + + ret = pxa_ssp_set_pll(priv, bclk); + if (ret < 0) + return ret; + + ssacd = pxa_ssp_read_reg(ssp, SSACD); + ssacd &= ~(SSACD_ACDS(7) | SSACD_SCDB_1X); + ssacd |= SSACD_ACDS(m->acds); + ssacd |= m->scdb; + pxa_ssp_write_reg(ssp, SSACD, ssacd); + } + } else if (sscr0 & SSCR0_ECS) { + /* + * For setups with external clocking, the PLL and its diviers + * are not active. Instead, the SCR bits in SSCR0 can be used + * to divide the clock. + */ + pxa_ssp_set_scr(ssp, bclk / rate); + } + switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: sspsp = pxa_ssp_read_reg(ssp, SSPSP); - if ((pxa_ssp_get_scr(ssp) == 4) && (width == 16)) { + if (((priv->sysclk / bclk) == 64) && (width == 16)) { /* This is a special case where the bitclk is 64fs * and we're not dealing with 2*32 bits of audio * samples. @@ -773,6 +790,15 @@ static int pxa_ssp_probe(struct snd_soc_dai *dai) ret = -ENODEV; goto err_priv; } + + priv->extclk = devm_clk_get(dev, "extclk"); + if (IS_ERR(priv->extclk)) { + ret = PTR_ERR(priv->extclk); + if (ret == -EPROBE_DEFER) + return ret; + + priv->extclk = NULL; + } } else { priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); if (priv->ssp == NULL) { @@ -814,8 +840,6 @@ static const struct snd_soc_dai_ops pxa_ssp_dai_ops = { .trigger = pxa_ssp_trigger, .hw_params = pxa_ssp_hw_params, .set_sysclk = pxa_ssp_set_dai_sysclk, - .set_clkdiv = pxa_ssp_set_dai_clkdiv, - .set_pll = pxa_ssp_set_dai_pll, .set_fmt = pxa_ssp_set_dai_fmt, .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, .set_tristate = pxa_ssp_set_dai_tristate, @@ -843,6 +867,9 @@ static struct snd_soc_dai_driver pxa_ssp_dai = { static const struct snd_soc_component_driver pxa_ssp_component = { .name = "pxa-ssp", + .ops = &pxa2xx_pcm_ops, + .pcm_new = pxa2xx_soc_pcm_new, + .pcm_free = pxa2xx_pcm_free_dma_buffers, }; #ifdef CONFIG_OF diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 803818aabee9..9f779657bc86 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -68,61 +68,39 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_cold_reset, }; -static struct pxad_param pxa2xx_ac97_pcm_stereo_in_req = { - .prio = PXAD_PRIO_LOWEST, - .drcmr = 11, -}; - static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .chan_name = "pcm_pcm_stereo_in", .maxburst = 32, - .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, -}; - -static struct pxad_param pxa2xx_ac97_pcm_stereo_out_req = { - .prio = PXAD_PRIO_LOWEST, - .drcmr = 12, }; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .chan_name = "pcm_pcm_stereo_out", .maxburst = 32, - .filter_data = &pxa2xx_ac97_pcm_stereo_out_req, }; -static struct pxad_param pxa2xx_ac97_pcm_aux_mono_out_req = { - .prio = PXAD_PRIO_LOWEST, - .drcmr = 10, -}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { .addr = __PREG(MODR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .chan_name = "pcm_aux_mono_out", .maxburst = 16, - .filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req, }; -static struct pxad_param pxa2xx_ac97_pcm_aux_mono_in_req = { - .prio = PXAD_PRIO_LOWEST, - .drcmr = 9, -}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { .addr = __PREG(MODR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .chan_name = "pcm_aux_mono_in", .maxburst = 16, - .filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req, }; -static struct pxad_param pxa2xx_ac97_pcm_aux_mic_mono_req = { - .prio = PXAD_PRIO_LOWEST, - .drcmr = 8, -}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { .addr = __PREG(MCDR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .chan_name = "pcm_aux_mic_mono", .maxburst = 16, - .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req, }; static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream, @@ -236,7 +214,21 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { static const struct snd_soc_component_driver pxa_ac97_component = { .name = "pxa-ac97", + .ops = &pxa2xx_pcm_ops, + .pcm_new = pxa2xx_soc_pcm_new, + .pcm_free = pxa2xx_pcm_free_dma_buffers, +}; + +#ifdef CONFIG_OF +static const struct of_device_id pxa2xx_ac97_dt_ids[] = { + { .compatible = "marvell,pxa250-ac97", }, + { .compatible = "marvell,pxa270-ac97", }, + { .compatible = "marvell,pxa300-ac97", }, + { } }; +MODULE_DEVICE_TABLE(of, pxa2xx_ac97_dt_ids); + +#endif static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) { @@ -296,6 +288,7 @@ static struct platform_driver pxa2xx_ac97_driver = { #ifdef CONFIG_PM_SLEEP .pm = &pxa2xx_ac97_pm_ops, #endif + .of_match_table = of_match_ptr(pxa2xx_ac97_dt_ids), }, }; diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 3fb60baf6eab..42820121e5b9 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -82,20 +82,18 @@ static struct pxa_i2s_port pxa_i2s; static struct clk *clk_i2s; static int clk_ena = 0; -static unsigned long pxa2xx_i2s_pcm_stereo_out_req = 3; static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = { .addr = __PREG(SADR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .chan_name = "tx", .maxburst = 32, - .filter_data = &pxa2xx_i2s_pcm_stereo_out_req, }; -static unsigned long pxa2xx_i2s_pcm_stereo_in_req = 2; static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = { .addr = __PREG(SADR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .chan_name = "rx", .maxburst = 32, - .filter_data = &pxa2xx_i2s_pcm_stereo_in_req, }; static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream, @@ -366,6 +364,9 @@ static struct snd_soc_dai_driver pxa_i2s_dai = { static const struct snd_soc_component_driver pxa_i2s_component = { .name = "pxa-i2s", + .ops = &pxa2xx_pcm_ops, + .pcm_new = pxa2xx_soc_pcm_new, + .pcm_free = pxa2xx_pcm_free_dma_buffers, }; static int pxa2xx_i2s_drv_probe(struct platform_device *pdev) diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 8b6a70e94c01..72eaaef1b426 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -20,70 +20,6 @@ #include <sound/pxa2xx-lib.h> #include <sound/dmaengine_pcm.h> -#include "../../arm/pxa2xx-pcm.h" - -static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_dmaengine_dai_dma_data *dma; - - dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - /* return if this is a bufferless transfer e.g. - * codec <--> BT codec or GSM modem -- lg FIXME */ - if (!dma) - return 0; - - return __pxa2xx_pcm_hw_params(substream, params); -} - -static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) -{ - __pxa2xx_pcm_hw_free(substream); - - return 0; -} - -static const struct snd_pcm_ops pxa2xx_pcm_ops = { - .open = __pxa2xx_pcm_open, - .close = __pxa2xx_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pxa2xx_pcm_hw_params, - .hw_free = pxa2xx_pcm_hw_free, - .prepare = __pxa2xx_pcm_prepare, - .trigger = pxa2xx_pcm_trigger, - .pointer = pxa2xx_pcm_pointer, - .mmap = pxa2xx_pcm_mmap, -}; - -static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret; - - ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - out: - return ret; -} - static const struct snd_soc_component_driver pxa2xx_soc_platform = { .ops = &pxa2xx_pcm_ops, .pcm_new = pxa2xx_soc_pcm_new, @@ -96,18 +32,9 @@ static int pxa2xx_soc_platform_probe(struct platform_device *pdev) NULL, 0); } -#ifdef CONFIG_OF -static const struct of_device_id snd_soc_pxa_audio_match[] = { - { .compatible = "mrvl,pxa-pcm-audio" }, - { } -}; -MODULE_DEVICE_TABLE(of, snd_soc_pxa_audio_match); -#endif - static struct platform_driver pxa_pcm_driver = { .driver = { .name = "pxa-pcm-audio", - .of_match_table = of_match_ptr(snd_soc_pxa_audio_match), }, .probe = pxa2xx_soc_platform_probe, diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index ba468e560dd2..230eee450f45 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -83,11 +83,9 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - unsigned int pll_out = 0; unsigned int wm9713_div = 0; int ret = 0; int rate = params_rate(params); - int width = snd_pcm_format_physical_width(params_format(params)); /* Only support ratios that we can generate neatly from the AC97 * based master clock - in particular, this excludes 44.1kHz. @@ -109,17 +107,10 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - /* Add 1 to the width for the leading clock cycle */ - pll_out = rate * (width + 1) * 8; - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1); if (ret < 0) return ret; - ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, pll_out); - if (ret < 0) - return ret; - if (clk_pout) ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_PLL_DIV, WM9713_PCMDIV(wm9713_div)); diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 87838fa27997..2a4c912d1e48 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -41,6 +41,9 @@ config SND_SOC_APQ8016_SBC APQ8016 SOC-based systems. Say Y if you want to use audio devices on MI2S. +config SND_SOC_QCOM_COMMON + tristate + config SND_SOC_QDSP6_COMMON tristate @@ -86,7 +89,18 @@ config SND_SOC_MSM8996 tristate "SoC Machine driver for MSM8996 and APQ8096 boards" depends on QCOM_APR select SND_SOC_QDSP6 + select SND_SOC_QCOM_COMMON help Support for Qualcomm Technologies LPASS audio block in APQ8096 SoC-based systems. Say Y if you want to use audio device on this SoCs + +config SND_SOC_SDM845 + tristate "SoC Machine driver for SDM845 boards" + depends on QCOM_APR + select SND_SOC_QDSP6 + select SND_SOC_QCOM_COMMON + help + To add support for audio on Qualcomm Technologies Inc. + SDM845 SoC-based systems. + Say Y if you want to use audio device on this SoCs. diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 206945bb9ba1..41b2c7a23a4d 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -14,10 +14,14 @@ obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o snd-soc-storm-objs := storm.o snd-soc-apq8016-sbc-objs := apq8016_sbc.o snd-soc-apq8096-objs := apq8096.o +snd-soc-sdm845-objs := sdm845.o +snd-soc-qcom-common-objs := common.o obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-apq8096.o +obj-$(CONFIG_SND_SOC_SDM845) += snd-soc-sdm845.o +obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o #DSP lib obj-$(CONFIG_SND_SOC_QDSP6) += qdsp6/ diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c index 561cd429e6f2..1543e85629f8 100644 --- a/sound/soc/qcom/apq8096.c +++ b/sound/soc/qcom/apq8096.c @@ -1,14 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2018, Linaro Limited -#include <linux/soc/qcom/apr.h> #include <linux/module.h> -#include <linux/component.h> #include <linux/platform_device.h> #include <linux/of_device.h> #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/pcm.h> +#include "common.h" static int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) @@ -24,211 +23,57 @@ static int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int apq8096_sbc_parse_of(struct snd_soc_card *card) +static void apq8096_add_be_ops(struct snd_soc_card *card) { - struct device_node *np; - struct device_node *codec = NULL; - struct device_node *platform = NULL; - struct device_node *cpu = NULL; - struct device *dev = card->dev; - struct snd_soc_dai_link *link; - int ret, num_links; - - ret = snd_soc_of_parse_card_name(card, "qcom,model"); - if (ret) { - dev_err(dev, "Error parsing card name: %d\n", ret); - return ret; - } - - /* DAPM routes */ - if (of_property_read_bool(dev->of_node, "qcom,audio-routing")) { - ret = snd_soc_of_parse_audio_routing(card, - "qcom,audio-routing"); - if (ret) - return ret; - } - - /* Populate links */ - num_links = of_get_child_count(dev->of_node); + struct snd_soc_dai_link *link = card->dai_link; + int i, num_links = card->num_links; - /* Allocate the DAI link array */ - card->dai_link = kcalloc(num_links, sizeof(*link), GFP_KERNEL); - if (!card->dai_link) - return -ENOMEM; - - card->num_links = num_links; - link = card->dai_link; - - for_each_child_of_node(dev->of_node, np) { - cpu = of_get_child_by_name(np, "cpu"); - if (!cpu) { - dev_err(dev, "Can't find cpu DT node\n"); - ret = -EINVAL; - goto err; - } - - link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0); - if (!link->cpu_of_node) { - dev_err(card->dev, "error getting cpu phandle\n"); - ret = -EINVAL; - goto err; - } - - ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name); - if (ret) { - dev_err(card->dev, "error getting cpu dai name\n"); - goto err; - } - - platform = of_get_child_by_name(np, "platform"); - codec = of_get_child_by_name(np, "codec"); - if (codec && platform) { - link->platform_of_node = of_parse_phandle(platform, - "sound-dai", - 0); - if (!link->platform_of_node) { - dev_err(card->dev, "platform dai not found\n"); - ret = -EINVAL; - goto err; - } - - ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); - if (ret < 0) { - dev_err(card->dev, "codec dai not found\n"); - goto err; - } - link->no_pcm = 1; - link->ignore_pmdown_time = 1; + for (i = 0; i < num_links; i++) { + if (link->no_pcm == 1) link->be_hw_params_fixup = apq8096_be_hw_params_fixup; - } else { - link->platform_of_node = link->cpu_of_node; - link->codec_dai_name = "snd-soc-dummy-dai"; - link->codec_name = "snd-soc-dummy"; - link->dynamic = 1; - } - - link->ignore_suspend = 1; - ret = of_property_read_string(np, "link-name", &link->name); - if (ret) { - dev_err(card->dev, "error getting codec dai_link name\n"); - goto err; - } - - link->dpcm_playback = 1; - link->dpcm_capture = 1; - link->stream_name = link->name; link++; } - - return 0; -err: - of_node_put(cpu); - of_node_put(codec); - of_node_put(platform); - kfree(card->dai_link); - return ret; } -static int apq8096_bind(struct device *dev) +static int apq8096_platform_probe(struct platform_device *pdev) { struct snd_soc_card *card; + struct device *dev = &pdev->dev; int ret; card = kzalloc(sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; - component_bind_all(dev, card); card->dev = dev; - ret = apq8096_sbc_parse_of(card); + dev_set_drvdata(dev, card); + ret = qcom_snd_parse_of(card); if (ret) { dev_err(dev, "Error parsing OF data\n"); goto err; } + apq8096_add_be_ops(card); ret = snd_soc_register_card(card); if (ret) - goto err; + goto err_card_register; return 0; +err_card_register: + kfree(card->dai_link); err: - component_unbind_all(dev, card); kfree(card); return ret; } -static void apq8096_unbind(struct device *dev) +static int apq8096_platform_remove(struct platform_device *pdev) { - struct snd_soc_card *card = dev_get_drvdata(dev); + struct snd_soc_card *card = dev_get_drvdata(&pdev->dev); snd_soc_unregister_card(card); - component_unbind_all(dev, card); kfree(card->dai_link); kfree(card); -} - -static const struct component_master_ops apq8096_ops = { - .bind = apq8096_bind, - .unbind = apq8096_unbind, -}; - -static int apq8016_compare_of(struct device *dev, void *data) -{ - return dev->of_node == data; -} - -static void apq8016_release_of(struct device *dev, void *data) -{ - of_node_put(data); -} - -static int add_audio_components(struct device *dev, - struct component_match **matchptr) -{ - struct device_node *np, *platform, *cpu, *node, *dai_node; - - node = dev->of_node; - - for_each_child_of_node(node, np) { - cpu = of_get_child_by_name(np, "cpu"); - if (cpu) { - dai_node = of_parse_phandle(cpu, "sound-dai", 0); - of_node_get(dai_node); - component_match_add_release(dev, matchptr, - apq8016_release_of, - apq8016_compare_of, - dai_node); - } - - platform = of_get_child_by_name(np, "platform"); - if (platform) { - dai_node = of_parse_phandle(platform, "sound-dai", 0); - component_match_add_release(dev, matchptr, - apq8016_release_of, - apq8016_compare_of, - dai_node); - } - } - - return 0; -} - -static int apq8096_platform_probe(struct platform_device *pdev) -{ - struct component_match *match = NULL; - int ret; - - ret = add_audio_components(&pdev->dev, &match); - if (ret) - return ret; - - return component_master_add_with_match(&pdev->dev, &apq8096_ops, match); -} - -static int apq8096_platform_remove(struct platform_device *pdev) -{ - component_master_del(&pdev->dev, &apq8096_ops); return 0; } @@ -245,7 +90,6 @@ static struct platform_driver msm_snd_apq8096_driver = { .remove = apq8096_platform_remove, .driver = { .name = "msm-snd-apq8096", - .owner = THIS_MODULE, .of_match_table = msm_snd_apq8096_dt_match, }, }; diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c new file mode 100644 index 000000000000..eb1b9da05dd4 --- /dev/null +++ b/sound/soc/qcom/common.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, Linaro Limited. +// Copyright (c) 2018, The Linux Foundation. All rights reserved. + +#include <linux/module.h> +#include "common.h" + +int qcom_snd_parse_of(struct snd_soc_card *card) +{ + struct device_node *np; + struct device_node *codec = NULL; + struct device_node *platform = NULL; + struct device_node *cpu = NULL; + struct device *dev = card->dev; + struct snd_soc_dai_link *link; + int ret, num_links; + + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret) { + dev_err(dev, "Error parsing card name: %d\n", ret); + return ret; + } + + /* DAPM routes */ + if (of_property_read_bool(dev->of_node, "audio-routing")) { + ret = snd_soc_of_parse_audio_routing(card, + "audio-routing"); + if (ret) + return ret; + } + + /* Populate links */ + num_links = of_get_child_count(dev->of_node); + + /* Allocate the DAI link array */ + card->dai_link = kcalloc(num_links, sizeof(*link), GFP_KERNEL); + if (!card->dai_link) + return -ENOMEM; + + card->num_links = num_links; + link = card->dai_link; + for_each_child_of_node(dev->of_node, np) { + cpu = of_get_child_by_name(np, "cpu"); + if (!cpu) { + dev_err(dev, "Can't find cpu DT node\n"); + ret = -EINVAL; + goto err; + } + + link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0); + if (!link->cpu_of_node) { + dev_err(card->dev, "error getting cpu phandle\n"); + ret = -EINVAL; + goto err; + } + + ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name); + if (ret) { + dev_err(card->dev, "error getting cpu dai name\n"); + goto err; + } + + platform = of_get_child_by_name(np, "platform"); + codec = of_get_child_by_name(np, "codec"); + if (codec && platform) { + link->platform_of_node = of_parse_phandle(platform, + "sound-dai", + 0); + if (!link->platform_of_node) { + dev_err(card->dev, "platform dai not found\n"); + ret = -EINVAL; + goto err; + } + + ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); + if (ret < 0) { + dev_err(card->dev, "codec dai not found\n"); + goto err; + } + link->no_pcm = 1; + link->ignore_pmdown_time = 1; + } else { + link->platform_of_node = link->cpu_of_node; + link->codec_dai_name = "snd-soc-dummy-dai"; + link->codec_name = "snd-soc-dummy"; + link->dynamic = 1; + } + + link->ignore_suspend = 1; + ret = of_property_read_string(np, "link-name", &link->name); + if (ret) { + dev_err(card->dev, "error getting codec dai_link name\n"); + goto err; + } + + link->dpcm_playback = 1; + link->dpcm_capture = 1; + link->stream_name = link->name; + link++; + } + + return 0; +err: + of_node_put(cpu); + of_node_put(codec); + of_node_put(platform); + kfree(card->dai_link); + return ret; +} +EXPORT_SYMBOL(qcom_snd_parse_of); + +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h new file mode 100644 index 000000000000..f05c05b12bd7 --- /dev/null +++ b/sound/soc/qcom/common.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// Copyright (c) 2018, The Linux Foundation. All rights reserved. + +#ifndef __QCOM_SND_COMMON_H__ +#define __QCOM_SND_COMMON_H__ + +#include <sound/soc.h> + +int qcom_snd_parse_of(struct snd_soc_card *card); + +#endif diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 31fe78aa207f..d07271ea4c45 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -458,7 +458,7 @@ static irqreturn_t lpass_dma_interrupt_handler( return IRQ_NONE; } dev_warn(soc_runtime->dev, "xrun warning\n"); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stop_xrun(substream); ret = IRQ_HANDLED; } diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c index 9983c665a941..932c3ebfd252 100644 --- a/sound/soc/qcom/qdsp6/q6adm.c +++ b/sound/soc/qcom/qdsp6/q6adm.c @@ -64,7 +64,6 @@ struct q6adm { struct aprv2_ibasic_rsp_result_t result; struct mutex lock; wait_queue_head_t matrix_map_wait; - struct platform_device *pdev_routing; }; struct q6adm_cmd_device_open_v5 { @@ -588,7 +587,6 @@ EXPORT_SYMBOL_GPL(q6adm_close); static int q6adm_probe(struct apr_device *adev) { struct device *dev = &adev->dev; - struct device_node *dais_np; struct q6adm *adm; adm = devm_kzalloc(&adev->dev, sizeof(*adm), GFP_KERNEL); @@ -605,22 +603,12 @@ static int q6adm_probe(struct apr_device *adev) INIT_LIST_HEAD(&adm->copps_list); spin_lock_init(&adm->copps_list_lock); - dais_np = of_get_child_by_name(dev->of_node, "routing"); - if (dais_np) { - adm->pdev_routing = of_platform_device_create(dais_np, - "q6routing", dev); - of_node_put(dais_np); - } - - return 0; + return of_platform_populate(dev->of_node, NULL, NULL, dev); } static int q6adm_remove(struct apr_device *adev) { - struct q6adm *adm = dev_get_drvdata(&adev->dev); - - if (adm->pdev_routing) - of_platform_device_destroy(&adm->pdev_routing->dev, NULL); + of_platform_depopulate(&adev->dev); return 0; } diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index 5002dd05bf27..60ff4a2d3577 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -4,7 +4,6 @@ #include <linux/err.h> #include <linux/init.h> -#include <linux/component.h> #include <linux/module.h> #include <linux/device.h> #include <linux/platform_device.h> @@ -81,7 +80,6 @@ static int q6slim_hw_params(struct snd_pcm_substream *substream, struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); struct q6afe_slim_cfg *slim = &dai_data->port_config[dai->id].slim; - slim->num_channels = params_channels(params); slim->sample_rate = params_rate(params); switch (params_format(params)) { @@ -315,6 +313,9 @@ static void q6afe_dai_shutdown(struct snd_pcm_substream *substream, struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); int rc; + if (!dai_data->is_port_started[dai->id]) + return; + rc = q6afe_port_stop(dai_data->port[dai->id]); if (rc < 0) dev_err(dai->dev, "fail to close AFE port (%d)\n", rc); @@ -382,23 +383,31 @@ static int q6slim_set_channel_map(struct snd_soc_dai *dai, struct q6afe_port_config *pcfg = &dai_data->port_config[dai->id]; int i; - if (!rx_slot) { - pr_err("%s: rx slot not found\n", __func__); - return -EINVAL; - } + if (dai->id & 0x1) { + /* TX */ + if (!tx_slot) { + pr_err("%s: tx slot not found\n", __func__); + return -EINVAL; + } - for (i = 0; i < rx_num; i++) { - pcfg->slim.ch_mapping[i] = rx_slot[i]; - pr_debug("%s: find number of channels[%d] ch[%d]\n", - __func__, i, rx_slot[i]); - } + for (i = 0; i < tx_num; i++) + pcfg->slim.ch_mapping[i] = tx_slot[i]; + + pcfg->slim.num_channels = tx_num; + + + } else { + if (!rx_slot) { + pr_err("%s: rx slot not found\n", __func__); + return -EINVAL; + } - pcfg->slim.num_channels = rx_num; + for (i = 0; i < rx_num; i++) + pcfg->slim.ch_mapping[i] = rx_slot[i]; - pr_debug("%s: SLIMBUS_%d_RX cnt[%d] ch[%d %d]\n", __func__, - (dai->id - SLIMBUS_0_RX) / 2, rx_num, - pcfg->slim.ch_mapping[0], - pcfg->slim.ch_mapping[1]); + pcfg->slim.num_channels = rx_num; + + } return 0; } @@ -443,6 +452,14 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"Slimbus5 Playback", NULL, "SLIMBUS_5_RX"}, {"Slimbus6 Playback", NULL, "SLIMBUS_6_RX"}, + {"SLIMBUS_0_TX", NULL, "Slimbus Capture"}, + {"SLIMBUS_1_TX", NULL, "Slimbus1 Capture"}, + {"SLIMBUS_2_TX", NULL, "Slimbus2 Capture"}, + {"SLIMBUS_3_TX", NULL, "Slimbus3 Capture"}, + {"SLIMBUS_4_TX", NULL, "Slimbus4 Capture"}, + {"SLIMBUS_5_TX", NULL, "Slimbus5 Capture"}, + {"SLIMBUS_6_TX", NULL, "Slimbus6 Capture"}, + {"Primary MI2S Playback", NULL, "PRI_MI2S_RX"}, {"Secondary MI2S Playback", NULL, "SEC_MI2S_RX"}, {"Tertiary MI2S Playback", NULL, "TERT_MI2S_RX"}, @@ -637,6 +654,24 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .rate_max = 192000, }, }, { + .name = "SLIMBUS_0_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_0_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + }, { .playback = { .stream_name = "Slimbus1 Playback", .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | @@ -655,6 +690,24 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, }, { + .name = "SLIMBUS_1_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_1_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus1 Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + }, { .playback = { .stream_name = "Slimbus2 Playback", .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | @@ -672,6 +725,25 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .id = SLIMBUS_2_RX, .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, + + }, { + .name = "SLIMBUS_2_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_2_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus2 Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, }, { .playback = { .stream_name = "Slimbus3 Playback", @@ -690,6 +762,25 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .id = SLIMBUS_3_RX, .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, + + }, { + .name = "SLIMBUS_3_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_3_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus3 Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, }, { .playback = { .stream_name = "Slimbus4 Playback", @@ -708,6 +799,25 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .id = SLIMBUS_4_RX, .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, + + }, { + .name = "SLIMBUS_4_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_4_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus4 Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, }, { .playback = { .stream_name = "Slimbus5 Playback", @@ -726,6 +836,25 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .id = SLIMBUS_5_RX, .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, + + }, { + .name = "SLIMBUS_5_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_5_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus5 Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, }, { .playback = { .stream_name = "Slimbus6 Playback", @@ -744,6 +873,25 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .id = SLIMBUS_6_RX, .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, + + }, { + .name = "SLIMBUS_6_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_6_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus6 Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, }, { .playback = { .stream_name = "Primary MI2S Playback", @@ -972,6 +1120,13 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_RX", "Slimbus4 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_RX", "Slimbus5 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_RX", "Slimbus6 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_1_TX", "Slimbus1 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_2_TX", "Slimbus2 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_3_TX", "Slimbus3 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_4_TX", "Slimbus4 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_5_TX", "Slimbus5 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_6_TX", "Slimbus6 Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary MI2S Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX", "Quaternary MI2S Capture", @@ -1180,7 +1335,7 @@ static void of_q6afe_parse_dai_data(struct device *dev, int id, i, num_lines; ret = of_property_read_u32(node, "reg", &id); - if (ret || id > AFE_PORT_MAX) { + if (ret || id < 0 || id >= AFE_PORT_MAX) { dev_err(dev, "valid dai id not found:%d\n", ret); continue; } @@ -1249,11 +1404,12 @@ static void of_q6afe_parse_dai_data(struct device *dev, } } -static int q6afe_dai_bind(struct device *dev, struct device *master, void *data) +static int q6afe_dai_dev_probe(struct platform_device *pdev) { struct q6afe_dai_data *dai_data; + struct device *dev = &pdev->dev; - dai_data = kzalloc(sizeof(*dai_data), GFP_KERNEL); + dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); if (!dai_data) return -ENOMEM; @@ -1261,41 +1417,22 @@ static int q6afe_dai_bind(struct device *dev, struct device *master, void *data) of_q6afe_parse_dai_data(dev, dai_data); - return snd_soc_register_component(dev, &q6afe_dai_component, + return devm_snd_soc_register_component(dev, &q6afe_dai_component, q6afe_dais, ARRAY_SIZE(q6afe_dais)); } -static void q6afe_dai_unbind(struct device *dev, struct device *master, - void *data) -{ - struct q6afe_dai_data *dai_data = dev_get_drvdata(dev); - - snd_soc_unregister_component(dev); - kfree(dai_data); -} - -static const struct component_ops q6afe_dai_comp_ops = { - .bind = q6afe_dai_bind, - .unbind = q6afe_dai_unbind, +static const struct of_device_id q6afe_dai_device_id[] = { + { .compatible = "qcom,q6afe-dais" }, + {}, }; - -static int q6afe_dai_dev_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &q6afe_dai_comp_ops); -} - -static int q6afe_dai_dev_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &q6afe_dai_comp_ops); - return 0; -} +MODULE_DEVICE_TABLE(of, q6afe_dai_device_id); static struct platform_driver q6afe_dai_platform_driver = { .driver = { .name = "q6afe-dai", + .of_match_table = of_match_ptr(q6afe_dai_device_id), }, .probe = q6afe_dai_dev_probe, - .remove = q6afe_dai_dev_remove, }; module_platform_driver(q6afe_dai_platform_driver); diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 01f43218984b..000775b4bba8 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -316,7 +316,6 @@ struct q6afe { struct mutex lock; struct list_head port_list; spinlock_t port_list_lock; - struct platform_device *pdev_dais; }; struct afe_port_cmd_device_start { @@ -515,6 +514,20 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { SLIMBUS_5_RX, 1, 1}, [SLIMBUS_6_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX, SLIMBUS_6_RX, 1, 1}, + [SLIMBUS_0_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX, + SLIMBUS_0_TX, 0, 1}, + [SLIMBUS_1_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX, + SLIMBUS_1_TX, 0, 1}, + [SLIMBUS_2_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX, + SLIMBUS_2_TX, 0, 1}, + [SLIMBUS_3_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX, + SLIMBUS_3_TX, 0, 1}, + [SLIMBUS_4_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX, + SLIMBUS_4_TX, 0, 1}, + [SLIMBUS_5_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX, + SLIMBUS_5_TX, 0, 1}, + [SLIMBUS_6_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX, + SLIMBUS_6_TX, 0, 1}, [PRIMARY_MI2S_RX] = { AFE_PORT_ID_PRIMARY_MI2S_RX, PRIMARY_MI2S_RX, 1, 1}, [PRIMARY_MI2S_TX] = { AFE_PORT_ID_PRIMARY_MI2S_TX, @@ -777,7 +790,7 @@ static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data) */ int q6afe_get_port_id(int index) { - if (index < 0 || index > AFE_PORT_MAX) + if (index < 0 || index >= AFE_PORT_MAX) return -EINVAL; return port_maps[index].port_id; @@ -1014,7 +1027,7 @@ int q6afe_port_stop(struct q6afe_port *port) port_id = port->id; index = port->token; - if (index < 0 || index > AFE_PORT_MAX) { + if (index < 0 || index >= AFE_PORT_MAX) { dev_err(afe->dev, "AFE port index[%d] invalid!\n", index); return -EINVAL; } @@ -1355,7 +1368,7 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) unsigned long flags; int cfg_type; - if (id < 0 || id > AFE_PORT_MAX) { + if (id < 0 || id >= AFE_PORT_MAX) { dev_err(dev, "AFE port token[%d] invalid!\n", id); return ERR_PTR(-EINVAL); } @@ -1373,6 +1386,13 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) case AFE_PORT_ID_MULTICHAN_HDMI_RX: cfg_type = AFE_PARAM_ID_HDMI_CONFIG; break; + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX: case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX: case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX: case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX: @@ -1438,7 +1458,6 @@ static int q6afe_probe(struct apr_device *adev) { struct q6afe *afe; struct device *dev = &adev->dev; - struct device_node *dais_np; afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL); if (!afe) @@ -1453,22 +1472,12 @@ static int q6afe_probe(struct apr_device *adev) dev_set_drvdata(dev, afe); - dais_np = of_get_child_by_name(dev->of_node, "dais"); - if (dais_np) { - afe->pdev_dais = of_platform_device_create(dais_np, - "q6afe-dai", dev); - of_node_put(dais_np); - } - - return 0; + return of_platform_populate(dev->of_node, NULL, NULL, dev); } static int q6afe_remove(struct apr_device *adev) { - struct q6afe *afe = dev_get_drvdata(&adev->dev); - - if (afe->pdev_dais) - of_platform_device_destroy(&afe->pdev_dais->dev, NULL); + of_platform_depopulate(&adev->dev); return 0; } diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 349c6a883c63..9db9a2944ef2 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -7,7 +7,6 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> -#include <linux/component.h> #include <sound/soc.h> #include <sound/soc.h> #include <sound/soc-dapm.h> @@ -390,7 +389,9 @@ static int q6asm_dai_close(struct snd_pcm_substream *substream) struct q6asm_dai_rtd *prtd = runtime->private_data; if (prtd->audio_client) { - q6asm_cmd(prtd->audio_client, CMD_CLOSE); + if (prtd->state) + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); q6asm_audio_client_free(prtd->audio_client); @@ -561,14 +562,15 @@ static struct snd_soc_dai_driver q6asm_fe_dais[] = { Q6ASM_FEDAI_DRIVER(8), }; -static int q6asm_dai_bind(struct device *dev, struct device *master, void *data) +static int q6asm_dai_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct of_phandle_args args; struct q6asm_dai_data *pdata; int rc; - pdata = kzalloc(sizeof(struct q6asm_dai_data), GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; @@ -580,43 +582,23 @@ static int q6asm_dai_bind(struct device *dev, struct device *master, void *data) dev_set_drvdata(dev, pdata); - return snd_soc_register_component(dev, &q6asm_fe_dai_component, + return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component, q6asm_fe_dais, ARRAY_SIZE(q6asm_fe_dais)); } -static void q6asm_dai_unbind(struct device *dev, struct device *master, - void *data) -{ - struct q6asm_dai_data *pdata = dev_get_drvdata(dev); - - snd_soc_unregister_component(dev); - - kfree(pdata); - -} -static const struct component_ops q6asm_dai_comp_ops = { - .bind = q6asm_dai_bind, - .unbind = q6asm_dai_unbind, +static const struct of_device_id q6asm_dai_device_id[] = { + { .compatible = "qcom,q6asm-dais" }, + {}, }; - -static int q6asm_dai_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &q6asm_dai_comp_ops); -} - -static int q6asm_dai_dev_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &q6asm_dai_comp_ops); - return 0; -} +MODULE_DEVICE_TABLE(of, q6asm_dai_device_id); static struct platform_driver q6asm_dai_platform_driver = { .driver = { .name = "q6asm-dai", + .of_match_table = of_match_ptr(q6asm_dai_device_id), }, .probe = q6asm_dai_probe, - .remove = q6asm_dai_dev_remove, }; module_platform_driver(q6asm_dai_platform_driver); diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index 530852385cad..2b2c7233bb5f 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -174,10 +174,8 @@ struct q6asm { struct device *dev; struct q6core_svc_api_info ainfo; wait_queue_head_t mem_wait; - struct platform_device *pcmdev; spinlock_t slock; struct audio_client *session[MAX_SESSIONS + 1]; - struct platform_device *pdev_dais; }; struct audio_client { @@ -1344,7 +1342,6 @@ EXPORT_SYMBOL_GPL(q6asm_cmd_nowait); static int q6asm_probe(struct apr_device *adev) { struct device *dev = &adev->dev; - struct device_node *dais_np; struct q6asm *q6asm; q6asm = devm_kzalloc(dev, sizeof(*q6asm), GFP_KERNEL); @@ -1359,22 +1356,12 @@ static int q6asm_probe(struct apr_device *adev) spin_lock_init(&q6asm->slock); dev_set_drvdata(dev, q6asm); - dais_np = of_get_child_by_name(dev->of_node, "dais"); - if (dais_np) { - q6asm->pdev_dais = of_platform_device_create(dais_np, - "q6asm-dai", dev); - of_node_put(dais_np); - } - - return 0; + return of_platform_populate(dev->of_node, NULL, NULL, dev); } static int q6asm_remove(struct apr_device *adev) { - struct q6asm *q6asm = dev_get_drvdata(&adev->dev); - - if (q6asm->pdev_dais) - of_platform_device_destroy(&q6asm->pdev_dais->dev, NULL); + of_platform_depopulate(&adev->dev); return 0; } diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c index 593f66b8622f..dc94c5c53788 100644 --- a/sound/soc/qcom/qdsp6/q6routing.c +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -8,7 +8,6 @@ #include <linux/platform_device.h> #include <linux/of_platform.h> #include <linux/bitops.h> -#include <linux/component.h> #include <linux/mutex.h> #include <linux/of_device.h> #include <linux/slab.h> @@ -68,6 +67,13 @@ { mix_name, "SEC_MI2S_TX", "SEC_MI2S_TX" }, \ { mix_name, "QUAT_MI2S_TX", "QUAT_MI2S_TX" }, \ { mix_name, "TERT_MI2S_TX", "TERT_MI2S_TX" }, \ + { mix_name, "SLIMBUS_0_TX", "SLIMBUS_0_TX" }, \ + { mix_name, "SLIMBUS_1_TX", "SLIMBUS_1_TX" }, \ + { mix_name, "SLIMBUS_2_TX", "SLIMBUS_2_TX" }, \ + { mix_name, "SLIMBUS_3_TX", "SLIMBUS_3_TX" }, \ + { mix_name, "SLIMBUS_4_TX", "SLIMBUS_4_TX" }, \ + { mix_name, "SLIMBUS_5_TX", "SLIMBUS_5_TX" }, \ + { mix_name, "SLIMBUS_6_TX", "SLIMBUS_6_TX" }, \ { mix_name, "PRIMARY_TDM_TX_0", "PRIMARY_TDM_TX_0"}, \ { mix_name, "PRIMARY_TDM_TX_1", "PRIMARY_TDM_TX_1"}, \ { mix_name, "PRIMARY_TDM_TX_2", "PRIMARY_TDM_TX_2"}, \ @@ -122,6 +128,27 @@ SOC_SINGLE_EXT("QUAT_MI2S_TX", QUATERNARY_MI2S_TX, \ id, 1, 0, msm_routing_get_audio_mixer, \ msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_0_TX", SLIMBUS_0_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_1_TX", SLIMBUS_1_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_2_TX", SLIMBUS_2_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_3_TX", SLIMBUS_3_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_4_TX", SLIMBUS_4_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_5_TX", SLIMBUS_5_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_6_TX", SLIMBUS_6_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ SOC_SINGLE_EXT("PRIMARY_TDM_TX_0", PRIMARY_TDM_TX_0, \ id, 1, 0, msm_routing_get_audio_mixer, \ msm_routing_put_audio_mixer), \ @@ -310,7 +337,7 @@ int q6routing_stream_open(int fedai_id, int perf_mode, session->channels, topology, perf_mode, session->bits_per_sample, 0, 0); - if (!copp) { + if (IS_ERR_OR_NULL(copp)) { mutex_unlock(&routing_data->lock); return -EINVAL; } @@ -899,7 +926,7 @@ static int routing_hw_params(struct snd_pcm_substream *substream, else path_type = ADM_PATH_LIVE_REC; - if (be_id > AFE_MAX_PORTS) + if (be_id >= AFE_MAX_PORTS) return -EINVAL; session = &data->port_data[be_id]; @@ -949,9 +976,10 @@ static const struct snd_soc_component_driver msm_soc_routing_component = { .num_dapm_routes = ARRAY_SIZE(intercon), }; -static int q6routing_dai_bind(struct device *dev, struct device *master, - void *data) +static int q6pcm_routing_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + routing_data = kzalloc(sizeof(*routing_data), GFP_KERNEL); if (!routing_data) return -ENOMEM; @@ -961,41 +989,28 @@ static int q6routing_dai_bind(struct device *dev, struct device *master, mutex_init(&routing_data->lock); dev_set_drvdata(dev, routing_data); - return snd_soc_register_component(dev, &msm_soc_routing_component, + return devm_snd_soc_register_component(dev, &msm_soc_routing_component, NULL, 0); } -static void q6routing_dai_unbind(struct device *dev, struct device *master, - void *d) +static int q6pcm_routing_remove(struct platform_device *pdev) { - struct msm_routing_data *data = dev_get_drvdata(dev); - - snd_soc_unregister_component(dev); - - kfree(data); - + kfree(routing_data); routing_data = NULL; -} - -static const struct component_ops q6routing_dai_comp_ops = { - .bind = q6routing_dai_bind, - .unbind = q6routing_dai_unbind, -}; -static int q6pcm_routing_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &q6routing_dai_comp_ops); -} - -static int q6pcm_routing_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &q6routing_dai_comp_ops); return 0; } +static const struct of_device_id q6pcm_routing_device_id[] = { + { .compatible = "qcom,q6adm-routing" }, + {}, +}; +MODULE_DEVICE_TABLE(of, q6pcm_routing_device_id); + static struct platform_driver q6pcm_routing_platform_driver = { .driver = { .name = "q6routing", + .of_match_table = of_match_ptr(q6pcm_routing_device_id), }, .probe = q6pcm_routing_probe, .remove = q6pcm_routing_remove, diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c new file mode 100644 index 000000000000..2a781d87ee65 --- /dev/null +++ b/sound/soc/qcom/sdm845.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include "common.h" +#include "qdsp6/q6afe.h" + +#define DEFAULT_SAMPLE_RATE_48K 48000 +#define DEFAULT_MCLK_RATE 24576000 +#define DEFAULT_BCLK_RATE 12288000 + +struct sdm845_snd_data { + struct snd_soc_card *card; + uint32_t pri_mi2s_clk_count; + uint32_t quat_tdm_clk_count; +}; + +static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; + +static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + slot_width = 32; + break; + default: + dev_err(rtd->dev, "%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + + channels = params_channels(params); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0x3, + 8, slot_width); + if (ret < 0) { + dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + channels, tdm_slot_offset); + if (ret < 0) { + dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xf, 0, + 8, slot_width); + if (ret < 0) { + dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, channels, + tdm_slot_offset, 0, NULL); + if (ret < 0) { + dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} + +static int sdm845_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + + switch (cpu_dai->id) { + case QUATERNARY_TDM_RX_0: + case QUATERNARY_TDM_TX_0: + ret = sdm845_tdm_snd_hw_params(substream, params); + break; + default: + pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); + break; + } + return ret; +} + +static int sdm845_snd_startup(struct snd_pcm_substream *substream) +{ + unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX: + case PRIMARY_MI2S_TX: + if (++(data->pri_mi2s_clk_count) == 1) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_MCLK_1, + DEFAULT_MCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + DEFAULT_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + } + snd_soc_dai_set_fmt(cpu_dai, fmt); + break; + + case QUATERNARY_TDM_RX_0: + case QUATERNARY_TDM_TX_0: + if (++(data->quat_tdm_clk_count) == 1) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT, + DEFAULT_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + } + break; + + default: + pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); + break; + } + return 0; +} + +static void sdm845_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX: + case PRIMARY_MI2S_TX: + if (--(data->pri_mi2s_clk_count) == 0) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_MCLK_1, + 0, SNDRV_PCM_STREAM_PLAYBACK); + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + 0, SNDRV_PCM_STREAM_PLAYBACK); + }; + break; + + case QUATERNARY_TDM_RX_0: + case QUATERNARY_TDM_TX_0: + if (--(data->quat_tdm_clk_count) == 0) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT, + 0, SNDRV_PCM_STREAM_PLAYBACK); + } + break; + + default: + pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); + break; + } +} + +static struct snd_soc_ops sdm845_be_ops = { + .hw_params = sdm845_snd_hw_params, + .startup = sdm845_snd_startup, + .shutdown = sdm845_snd_shutdown, +}; + +static int sdm845_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + rate->min = rate->max = DEFAULT_SAMPLE_RATE_48K; + channels->min = channels->max = 2; + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + +static void sdm845_add_be_ops(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *link = card->dai_link; + int i, num_links = card->num_links; + + for (i = 0; i < num_links; i++) { + if (link->no_pcm == 1) { + link->ops = &sdm845_be_ops; + link->be_hw_params_fixup = sdm845_be_hw_params_fixup; + } + link++; + } +} + +static int sdm845_snd_platform_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct sdm845_snd_data *data; + struct device *dev = &pdev->dev; + int ret; + + card = kzalloc(sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + /* Allocate the private data */ + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto data_alloc_fail; + } + + card->dev = dev; + dev_set_drvdata(dev, card); + ret = qcom_snd_parse_of(card); + if (ret) { + dev_err(dev, "Error parsing OF data\n"); + goto parse_dt_fail; + } + + data->card = card; + snd_soc_card_set_drvdata(card, data); + + sdm845_add_be_ops(card); + ret = snd_soc_register_card(card); + if (ret) { + dev_err(dev, "Sound card registration failed\n"); + goto register_card_fail; + } + return ret; + +register_card_fail: + kfree(card->dai_link); +parse_dt_fail: + kfree(data); +data_alloc_fail: + kfree(card); + return ret; +} + +static int sdm845_snd_platform_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = dev_get_drvdata(&pdev->dev); + struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + kfree(card->dai_link); + kfree(data); + kfree(card); + return 0; +} + +static const struct of_device_id sdm845_snd_device_id[] = { + { .compatible = "qcom,sdm845-sndcard" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sdm845_snd_device_id); + +static struct platform_driver sdm845_snd_driver = { + .probe = sdm845_snd_platform_probe, + .remove = sdm845_snd_platform_remove, + .driver = { + .name = "msm-snd-sdm845", + .of_match_table = sdm845_snd_device_id, + }, +}; +module_platform_driver(sdm845_snd_driver); + +MODULE_DESCRIPTION("sdm845 ASoC Machine Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 05b078e7b87f..65e814d46006 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -1,10 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 # ROCKCHIP Platform Support snd-soc-rockchip-i2s-objs := rockchip_i2s.o +snd-soc-rockchip-pcm-objs := rockchip_pcm.o snd-soc-rockchip-pdm-objs := rockchip_pdm.o snd-soc-rockchip-spdif-objs := rockchip_spdif.o -obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o snd-soc-rockchip-pcm.o obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 950823d69e9c..60d43d53a8f5 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -22,6 +22,7 @@ #include <sound/dmaengine_pcm.h> #include "rockchip_i2s.h" +#include "rockchip_pcm.h" #define DRV_NAME "rockchip-i2s" @@ -674,7 +675,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev) goto err_suspend; } - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + ret = rockchip_pcm_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "Could not register PCM\n"); return ret; diff --git a/sound/soc/rockchip/rockchip_pcm.c b/sound/soc/rockchip/rockchip_pcm.c new file mode 100644 index 000000000000..f77538319221 --- /dev/null +++ b/sound/soc/rockchip/rockchip_pcm.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Rockchip Electronics Co. Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/module.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/dmaengine_pcm.h> + +#include "rockchip_pcm.h" + +static const struct snd_pcm_hardware snd_rockchip_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 52, + .buffer_bytes_max = 64 * 1024, + .fifo_size = 32, +}; + +static const struct snd_dmaengine_pcm_config rk_dmaengine_pcm_config = { + .pcm_hardware = &snd_rockchip_hardware, + .prealloc_buffer_size = 32 * 1024, +}; + +int rockchip_pcm_platform_register(struct device *dev) +{ + return devm_snd_dmaengine_pcm_register(dev, &rk_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_COMPAT); +} +EXPORT_SYMBOL_GPL(rockchip_pcm_platform_register); + +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/rockchip/rockchip_pcm.h b/sound/soc/rockchip/rockchip_pcm.h new file mode 100644 index 000000000000..d6c36115c60a --- /dev/null +++ b/sound/soc/rockchip/rockchip_pcm.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2018 Rockchip Electronics Co. Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _ROCKCHIP_PCM_H +#define _ROCKCHIP_PCM_H + +int rockchip_pcm_platform_register(struct device *dev); + +#endif diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c index 4db4fd56db35..881c32498808 100644 --- a/sound/soc/rockchip/rockchip_rt5645.c +++ b/sound/soc/rockchip/rockchip_rt5645.c @@ -181,7 +181,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) if (!rk_dailink.cpu_of_node) { dev_err(&pdev->dev, "Property 'rockchip,i2s-controller' missing or invalid\n"); - return -EINVAL; + ret = -EINVAL; + goto put_codec_of_node; } rk_dailink.platform_of_node = rk_dailink.cpu_of_node; @@ -190,17 +191,36 @@ static int snd_rk_mc_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Soc parse card name failed %d\n", ret); - return ret; + goto put_cpu_of_node; } ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "Soc register card failed %d\n", ret); - return ret; + goto put_cpu_of_node; } return ret; + +put_cpu_of_node: + of_node_put(rk_dailink.cpu_of_node); + rk_dailink.cpu_of_node = NULL; +put_codec_of_node: + of_node_put(rk_dailink.codec_of_node); + rk_dailink.codec_of_node = NULL; + + return ret; +} + +static int snd_rk_mc_remove(struct platform_device *pdev) +{ + of_node_put(rk_dailink.cpu_of_node); + rk_dailink.cpu_of_node = NULL; + of_node_put(rk_dailink.codec_of_node); + rk_dailink.codec_of_node = NULL; + + return 0; } static const struct of_device_id rockchip_rt5645_of_match[] = { @@ -212,6 +232,7 @@ MODULE_DEVICE_TABLE(of, rockchip_rt5645_of_match); static struct platform_driver snd_rk_mc_driver = { .probe = snd_rk_mc_probe, + .remove = snd_rk_mc_remove, .driver = { .name = DRV_NAME, .pm = &snd_soc_pm_ops, diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index f914ed45db7d..d6c62aa13041 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -710,6 +710,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, switch (params_channels(params)) { case 6: val |= MOD_DC2_EN; + /* fall through */ case 4: val |= MOD_DC1_EN; break; diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 0ae0800bf3a8..dc20f0f7080a 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 menu "SoC Audio support for Renesas SoCs" depends on SUPERH || ARCH_RENESAS || COMPILE_TEST diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index 2dc3b762fdd9..922fb6aa3ed1 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c @@ -1,16 +1,14 @@ -/* - * SH7760 ("camelot") DMABRG audio DMA unit support - * - * Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> - * licensed under the terms outlined in the file COPYING at the root - * of the linux kernel sources. - * - * The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which - * trigger an interrupt when one half of the programmed transfer size - * has been xmitted. - * - * FIXME: little-endian only for now - */ +// SPDX-License-Identifier: GPL-2.0 +// +// SH7760 ("camelot") DMABRG audio DMA unit support +// +// Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> +// +// The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which +// trigger an interrupt when one half of the programmed transfer size +// has been xmitted. +// +// FIXME: little-endian only for now #include <linux/module.h> #include <linux/gfp.h> @@ -341,6 +339,6 @@ static struct platform_driver sh7760_pcm_driver = { module_platform_driver(sh7760_pcm_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver"); MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 3bae06dd121f..aa7e902f0c02 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1,16 +1,12 @@ -/* - * Fifo-attached Serial Interface (FSI) support for SH7724 - * - * Copyright (C) 2009 Renesas Solutions Corp. - * Kuninori Morimoto <morimoto.kuninori@renesas.com> - * - * Based on ssi.c - * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Fifo-attached Serial Interface (FSI) support for SH7724 +// +// Copyright (C) 2009 Renesas Solutions Corp. +// Kuninori Morimoto <morimoto.kuninori@renesas.com> +// +// Based on ssi.c +// Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> #include <linux/delay.h> #include <linux/dma-mapping.h> diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c index 624aaf569fef..c2b496398e6b 100644 --- a/sound/soc/sh/hac.c +++ b/sound/soc/sh/hac.c @@ -1,13 +1,11 @@ -/* - * Hitachi Audio Controller (AC97) support for SH7760/SH7780 - * - * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> - * licensed under the terms outlined in the file COPYING at the root - * of the linux kernel sources. - * - * dont forget to set IPSEL/OMSEL register bits (in your board code) to - * enable HAC output pins! - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Hitachi Audio Controller (AC97) support for SH7760/SH7780 +// +// Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> +// +// dont forget to set IPSEL/OMSEL register bits (in your board code) to +// enable HAC output pins! /* BIG FAT FIXME: although the SH7760 has 2 independent AC97 units, only * the FIRST can be used since ASoC does not pass any information to the @@ -343,6 +341,6 @@ static struct platform_driver hac_pcm_driver = { module_platform_driver(hac_pcm_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver"); MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c index ecb057ff9fbb..8739c9f60672 100644 --- a/sound/soc/sh/migor.c +++ b/sound/soc/sh/migor.c @@ -1,12 +1,8 @@ -/* - * ALSA SoC driver for Migo-R - * - * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC driver for Migo-R +// +// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> #include <linux/clkdev.h> #include <linux/device.h> diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 9c3d5aed99d1..5d1ff8ef26f9 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 4672688cac32..3a3064dda57f 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -1,12 +1,9 @@ -/* - * Helper routines for R-Car sound ADG. - * - * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Helper routines for R-Car sound ADG. +// +// Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include <linux/clk-provider.h> #include "rsnd.h" diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index 5900fb535a2b..cc191cd5fb82 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -1,13 +1,10 @@ -/* - * Renesas R-Car CMD support - * - * Copyright (C) 2015 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car CMD support +// +// Copyright (C) 2015 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include "rsnd.h" struct rsnd_cmd { @@ -89,7 +86,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, cmd_case[rsnd_mod_id(src)] << 16; } - dev_dbg(dev, "ctu/mix path = 0x%08x", data); + dev_dbg(dev, "ctu/mix path = 0x%08x\n", data); rsnd_mod_write(mod, CMD_ROUTE_SLCT, data); rsnd_mod_write(mod, CMD_BUSIF_MODE, rsnd_get_busif_shift(io, mod) | 1); diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index f237002180c0..f8425d8b44d2 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1,16 +1,12 @@ -/* - * Renesas R-Car SRU/SCU/SSIU/SSI support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * Based on fsi.c - * Kuninori Morimoto <morimoto.kuninori@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car SRU/SCU/SSIU/SSI support +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +// +// Based on fsi.c +// Kuninori Morimoto <morimoto.kuninori@renesas.com> /* * Renesas R-Car sound device structure @@ -552,6 +548,15 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id) return priv->rdai + id; } +static struct snd_soc_dai_driver +*rsnd_daidrv_get(struct rsnd_priv *priv, int id) +{ + if ((id < 0) || (id >= rsnd_rdai_nr(priv))) + return NULL; + + return priv->daidrv + id; +} + #define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai) static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) { @@ -1037,7 +1042,7 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, int io_i; rdai = rsnd_rdai_get(priv, dai_i); - drv = priv->daidrv + dai_i; + drv = rsnd_daidrv_get(priv, dai_i); io_playback = &rdai->playback; io_capture = &rdai->capture; @@ -1085,6 +1090,12 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, of_node_put(capture); } + if (rsnd_ssi_is_pin_sharing(io_capture) || + rsnd_ssi_is_pin_sharing(io_playback)) { + /* should have symmetric_rates if pin sharing */ + drv->symmetric_rates = 1; + } + dev_dbg(dev, "%s (%s/%s)\n", rdai->name, rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ", rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- "); @@ -1606,7 +1617,7 @@ static struct platform_driver rsnd_driver = { }; module_platform_driver(rsnd_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Renesas R-Car audio driver"); MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); MODULE_ALIAS("platform:rcar-pcm-audio"); diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 83be7d3ae0a8..6a55aa753003 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -1,12 +1,9 @@ -/* - * ctu.c - * - * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ctu.c +// +// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include "rsnd.h" #define CTU_NAME_SIZE 16 diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index ef82b94d038b..fe63ef8600d0 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -1,13 +1,10 @@ -/* - * Renesas R-Car Audio DMAC support - * - * Copyright (C) 2015 Renesas Electronics Corp. - * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car Audio DMAC support +// +// Copyright (C) 2015 Renesas Electronics Corp. +// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include <linux/delay.h> #include <linux/of_dma.h> #include "rsnd.h" diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index ca1780e0b830..2b16e0ce6bc5 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -1,13 +1,9 @@ -/* - * Renesas R-Car DVC support - * - * Copyright (C) 2014 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car DVC support +// +// Copyright (C) 2014 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> /* * Playback Volume diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 25642e92dae0..0230301fe078 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -1,13 +1,9 @@ -/* - * Renesas R-Car Gen1 SRU/SSI support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car Gen1 SRU/SSI support +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> /* * #define DEBUG diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 1881b2de9126..8e3b57eaa708 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -1,12 +1,8 @@ -/* - * mix.c - * - * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// mix.c +// +// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> /* * CTUn MIXn diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 6d7280d2d9be..96d93330b1e1 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -1,13 +1,10 @@ -/* - * Renesas R-Car - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #ifndef RSND_H #define RSND_H diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 6c72d1a81cf5..beccfbac7581 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -1,13 +1,9 @@ -/* - * Renesas R-Car SRC support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car SRC support +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> /* * you can enable below define if you don't need diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 6e1166ec24a0..8304e4ec9242 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -1,16 +1,12 @@ -/* - * Renesas R-Car SSIU/SSI support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * Based on fsi.c - * Kuninori Morimoto <morimoto.kuninori@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car SSIU/SSI support +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +// +// Based on fsi.c +// Kuninori Morimoto <morimoto.kuninori@renesas.com> /* * you can enable below define if you don't need @@ -37,6 +33,7 @@ #define CHNL_4 (1 << 22) /* Channels */ #define CHNL_6 (2 << 22) /* Channels */ #define CHNL_8 (3 << 22) /* Channels */ +#define DWL_MASK (7 << 19) /* Data Word Length mask */ #define DWL_8 (0 << 19) /* Data Word Length */ #define DWL_16 (1 << 19) /* Data Word Length */ #define DWL_18 (2 << 19) /* Data Word Length */ @@ -353,21 +350,18 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - u32 cr_own; - u32 cr_mode; - u32 wsr; + u32 cr_own = ssi->cr_own; + u32 cr_mode = ssi->cr_mode; + u32 wsr = ssi->wsr; int is_tdm; - if (rsnd_ssi_is_parent(mod, io)) - return; - is_tdm = rsnd_runtime_is_ssi_tdm(io); /* * always use 32bit system word. * see also rsnd_ssi_master_clk_enable() */ - cr_own = FORCE | SWL_32; + cr_own |= FORCE | SWL_32; if (rdai->bit_clk_inv) cr_own |= SCKP; @@ -377,9 +371,18 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, cr_own |= SDTA; if (rdai->sys_delay) cr_own |= DEL; + + /* + * We shouldn't exchange SWSP after running. + * This means, parent needs to care it. + */ + if (rsnd_ssi_is_parent(mod, io)) + goto init_end; + if (rsnd_io_is_play(io)) cr_own |= TRMD; + cr_own &= ~DWL_MASK; switch (snd_pcm_format_width(runtime->format)) { case 16: cr_own |= DWL_16; @@ -406,7 +409,7 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, wsr |= WS_MODE; cr_own |= CHNL_8; } - +init_end: ssi->cr_own = cr_own; ssi->cr_mode = cr_mode; ssi->wsr = wsr; @@ -470,15 +473,18 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, return -EIO; } - if (!rsnd_ssi_is_parent(mod, io)) - ssi->cr_own = 0; - rsnd_ssi_master_clk_stop(mod, io); rsnd_mod_power_off(mod); ssi->usrcnt--; + if (!ssi->usrcnt) { + ssi->cr_own = 0; + ssi->cr_mode = 0; + ssi->wsr = 0; + } + return 0; } @@ -1055,9 +1061,10 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + if (!mod) + return 0; - return !!(rsnd_flags_has(ssi, RSND_SSI_CLK_PIN_SHARE)); + return !!(rsnd_flags_has(rsnd_mod_to_ssi(mod), RSND_SSI_CLK_PIN_SHARE)); } static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io, diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 47bdba9fc582..016fbf5ac242 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -1,12 +1,9 @@ -/* - * Renesas R-Car SSIU support - * - * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car SSIU support +// +// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include "rsnd.h" #define SSIU_NAME "ssiu" diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c index 4a3568a9bf59..4bb4c13cf860 100644 --- a/sound/soc/sh/sh7760-ac97.c +++ b/sound/soc/sh/sh7760-ac97.c @@ -1,10 +1,8 @@ -/* - * Generic AC97 sound support for SH7760 - * - * (c) 2007 Manuel Lauss - * - * Licensed under the GPLv2. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Generic AC97 sound support for SH7760 +// +// (c) 2007 Manuel Lauss #include <linux/module.h> #include <linux/moduleparam.h> @@ -68,6 +66,6 @@ static void __exit sh7760_ac97_exit(void) module_init(sh7760_ac97_init); module_exit(sh7760_ac97_exit); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Generic SH7760 AC97 sound machine"); MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); diff --git a/sound/soc/sh/siu.h b/sound/soc/sh/siu.h index 6088d627c0e4..63a508fdfe78 100644 --- a/sound/soc/sh/siu.h +++ b/sound/soc/sh/siu.h @@ -1,23 +1,9 @@ -/* - * siu.h - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. - * - * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// siu.h - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. +// +// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> +// Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> #ifndef SIU_H #define SIU_H diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c index ee2211635e92..f2a386fcd92e 100644 --- a/sound/soc/sh/siu_dai.c +++ b/sound/soc/sh/siu_dai.c @@ -1,23 +1,9 @@ -/* - * siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. - * - * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. +// +// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> +// Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> #include <linux/delay.h> #include <linux/firmware.h> diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c index 172909570ed5..e263757e4a69 100644 --- a/sound/soc/sh/siu_pcm.c +++ b/sound/soc/sh/siu_pcm.c @@ -1,23 +1,10 @@ -/* - * siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral. - * - * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral. +// +// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> +// Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> + #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c index 89ed1b107ac5..8125fa3840b6 100644 --- a/sound/soc/sh/ssi.c +++ b/sound/soc/sh/ssi.c @@ -1,14 +1,11 @@ -/* - * Serial Sound Interface (I2S) support for SH7760/SH7780 - * - * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> - * - * licensed under the terms outlined in the file COPYING at the root - * of the linux kernel sources. - * - * dont forget to set IPSEL/OMSEL register bits (in your board code) to - * enable SSI output pins! - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Serial Sound Interface (I2S) support for SH7760/SH7780 +// +// Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> +// +// dont forget to set IPSEL/OMSEL register bits (in your board code) to +// enable SSI output pins! /* * LIMITATIONS: @@ -400,6 +397,6 @@ static struct platform_driver sh4_ssi_driver = { module_platform_driver(sh4_ssi_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver"); MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); diff --git a/sound/soc/sirf/sirf-usp.c b/sound/soc/sirf/sirf-usp.c index 77e7dcf969d0..d70fcd4a1adf 100644 --- a/sound/soc/sirf/sirf-usp.c +++ b/sound/soc/sirf/sirf-usp.c @@ -370,10 +370,9 @@ static int sirf_usp_pcm_probe(struct platform_device *pdev) platform_set_drvdata(pdev, usp); mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap(&pdev->dev, mem_res->start, - resource_size(mem_res)); - if (base == NULL) - return -ENOMEM; + base = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR(base)) + return PTR_ERR(base); usp->regmap = devm_regmap_init_mmio(&pdev->dev, base, &sirf_usp_regmap_config); if (IS_ERR(usp->regmap)) diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index 3f424f214bca..c086786e4471 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -1,20 +1,15 @@ -/* - * soc-ac97.c -- ALSA SoC Audio Layer AC97 support - * - * Copyright 2005 Wolfson Microelectronics PLC. - * Copyright 2005 Openedhand Ltd. - * Copyright (C) 2010 Slimlogic Ltd. - * Copyright (C) 2010 Texas Instruments Inc. - * - * Author: Liam Girdwood <lrg@slimlogic.co.uk> - * with code, comments and ideas from :- - * Richard Purdie <richard@openedhand.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-ac97.c -- ALSA SoC Audio Layer AC97 support +// +// Copyright 2005 Wolfson Microelectronics PLC. +// Copyright 2005 Openedhand Ltd. +// Copyright (C) 2010 Slimlogic Ltd. +// Copyright (C) 2010 Texas Instruments Inc. +// +// Author: Liam Girdwood <lrg@slimlogic.co.uk> +// with code, comments and ideas from :- +// Richard Purdie <richard@openedhand.com> #include <linux/ctype.h> #include <linux/delay.h> diff --git a/sound/soc/soc-acpi.c b/sound/soc/soc-acpi.c index 3d7e1ff79139..b8e72b52db30 100644 --- a/sound/soc/soc-acpi.c +++ b/sound/soc/soc-acpi.c @@ -1,18 +1,8 @@ -/* - * soc-apci.c - support for ACPI enumeration. - * - * Copyright (c) 2013-15, Intel Corporation. - * - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// soc-apci.c - support for ACPI enumeration. +// +// Copyright (c) 2013-15, Intel Corporation. #include <sound/soc-acpi.h> diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index e095115fa9f9..409d082e80d1 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -1,18 +1,12 @@ -/* - * soc-compress.c -- ALSA SoC Compress - * - * Copyright (C) 2012 Intel Corp. - * - * Authors: Namarta Kohli <namartax.kohli@intel.com> - * Ramesh Babu K V <ramesh.babu@linux.intel.com> - * Vinod Koul <vinod.koul@linux.intel.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-compress.c -- ALSA SoC Compress +// +// Copyright (C) 2012 Intel Corp. +// +// Authors: Namarta Kohli <namartax.kohli@intel.com> +// Ramesh Babu K V <ramesh.babu@linux.intel.com> +// Vinod Koul <vinod.koul@linux.intel.com> #include <linux/kernel.h> #include <linux/init.h> @@ -146,6 +140,30 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) stream = SNDRV_PCM_STREAM_CAPTURE; mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + fe->dpcm[stream].runtime = fe_substream->runtime; + + ret = dpcm_path_get(fe, stream, &list); + if (ret < 0) + goto be_err; + else if (ret == 0) + dev_dbg(fe->dev, "Compress ASoC: %s no valid %s route\n", + fe->dai_link->name, stream ? "capture" : "playback"); + /* calculate valid and active FE <-> BE dpcms */ + dpcm_process_paths(fe, stream, &list, 1); + fe->dpcm[stream].runtime = fe_substream->runtime; + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_startup(fe, stream); + if (ret < 0) { + /* clean up all links */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) + dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; + + dpcm_be_disconnect(fe, stream); + fe->dpcm[stream].runtime = NULL; + goto out; + } if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) { ret = cpu_dai->driver->cops->startup(cstream, cpu_dai); @@ -159,7 +177,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) ret = soc_compr_components_open(cstream, &component); if (ret < 0) - goto machine_err; + goto open_err; if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) { ret = fe->dai_link->compr_ops->startup(cstream); @@ -170,31 +188,6 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) } } - fe->dpcm[stream].runtime = fe_substream->runtime; - - ret = dpcm_path_get(fe, stream, &list); - if (ret < 0) - goto fe_err; - else if (ret == 0) - dev_dbg(fe->dev, "Compress ASoC: %s no valid %s route\n", - fe->dai_link->name, stream ? "capture" : "playback"); - - /* calculate valid and active FE <-> BE dpcms */ - dpcm_process_paths(fe, stream, &list, 1); - - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; - - ret = dpcm_be_dai_startup(fe, stream); - if (ret < 0) { - /* clean up all links */ - list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) - dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; - - dpcm_be_disconnect(fe, stream); - fe->dpcm[stream].runtime = NULL; - goto path_err; - } - dpcm_clear_pending_state(fe, stream); dpcm_path_put(&list); @@ -207,17 +200,14 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) return 0; -path_err: - dpcm_path_put(&list); -fe_err: - if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) - fe->dai_link->compr_ops->shutdown(cstream); machine_err: soc_compr_components_free(cstream, component); - +open_err: if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) cpu_dai->driver->cops->shutdown(cstream, cpu_dai); out: + dpcm_path_put(&list); +be_err: fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; mutex_unlock(&fe->card->mutex); return ret; @@ -557,6 +547,24 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + /* + * Create an empty hw_params for the BE as the machine driver must + * fix this up to match DSP decoder and ASRC configuration. + * I.e. machine driver fixup for compressed BE is mandatory. + */ + memset(&fe->dpcm[fe_substream->stream].hw_params, 0, + sizeof(struct snd_pcm_hw_params)); + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_hw_params(fe, stream); + if (ret < 0) + goto out; + + ret = dpcm_be_dai_prepare(fe, stream); + if (ret < 0) + goto out; + if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_params) { ret = cpu_dai->driver->cops->set_params(cstream, params, cpu_dai); if (ret < 0) @@ -583,24 +591,6 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, goto out; } - /* - * Create an empty hw_params for the BE as the machine driver must - * fix this up to match DSP decoder and ASRC configuration. - * I.e. machine driver fixup for compressed BE is mandatory. - */ - memset(&fe->dpcm[fe_substream->stream].hw_params, 0, - sizeof(struct snd_pcm_hw_params)); - - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; - - ret = dpcm_be_dai_hw_params(fe, stream); - if (ret < 0) - goto out; - - ret = dpcm_be_dai_prepare(fe, stream); - if (ret < 0) - goto out; - dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4663de3cf495..9cfe10d8040c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1,26 +1,21 @@ -/* - * soc-core.c -- ALSA SoC Audio Layer - * - * Copyright 2005 Wolfson Microelectronics PLC. - * Copyright 2005 Openedhand Ltd. - * Copyright (C) 2010 Slimlogic Ltd. - * Copyright (C) 2010 Texas Instruments Inc. - * - * Author: Liam Girdwood <lrg@slimlogic.co.uk> - * with code, comments and ideas from :- - * Richard Purdie <richard@openedhand.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * TODO: - * o Add hw rules to enforce rates, etc. - * o More testing with other codecs/machines. - * o Add more codecs and platforms to ensure good API coverage. - * o Support TDM on PCM and I2S - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-core.c -- ALSA SoC Audio Layer +// +// Copyright 2005 Wolfson Microelectronics PLC. +// Copyright 2005 Openedhand Ltd. +// Copyright (C) 2010 Slimlogic Ltd. +// Copyright (C) 2010 Texas Instruments Inc. +// +// Author: Liam Girdwood <lrg@slimlogic.co.uk> +// with code, comments and ideas from :- +// Richard Purdie <richard@openedhand.com> +// +// TODO: +// o Add hw rules to enforce rates, etc. +// o More testing with other codecs/machines. +// o Add more codecs and platforms to ensure good API coverage. +// o Support TDM on PCM and I2S #include <linux/module.h> #include <linux/moduleparam.h> @@ -533,6 +528,7 @@ int snd_soc_suspend(struct device *dev) "ASoC: idle_bias_off CODEC on over suspend\n"); break; } + /* fall through */ case SND_SOC_BIAS_OFF: if (component->driver->suspend) @@ -852,6 +848,9 @@ static int soc_bind_dai_link(struct snd_soc_card *card, const char *platform_name; int i; + if (dai_link->ignore) + return 0; + dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name); if (soc_is_dai_link_bound(card, dai_link)) { @@ -1195,15 +1194,27 @@ void snd_soc_remove_dai_link(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link); +static void soc_set_of_name_prefix(struct snd_soc_component *component) +{ + struct device_node *component_of_node = component->dev->of_node; + const char *str; + int ret; + + if (!component_of_node && component->dev->parent) + component_of_node = component->dev->parent->of_node; + + ret = of_property_read_string(component_of_node, "sound-name-prefix", + &str); + if (!ret) + component->name_prefix = str; +} + static void soc_set_name_prefix(struct snd_soc_card *card, struct snd_soc_component *component) { int i; - if (card->codec_conf == NULL) - return; - - for (i = 0; i < card->num_configs; i++) { + for (i = 0; i < card->num_configs && card->codec_conf; i++) { struct snd_soc_codec_conf *map = &card->codec_conf[i]; struct device_node *component_of_node = component->dev->of_node; @@ -1215,8 +1226,14 @@ static void soc_set_name_prefix(struct snd_soc_card *card, if (map->dev_name && strcmp(component->name, map->dev_name)) continue; component->name_prefix = map->name_prefix; - break; + return; } + + /* + * If there is no configuration table or no match in the table, + * check if a prefix is provided in the node + */ + soc_set_of_name_prefix(component); } static int soc_probe_component(struct snd_soc_card *card, @@ -1461,7 +1478,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, { struct snd_soc_dai_link *dai_link = rtd->dai_link; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int i, ret; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + int i, ret, num; dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", card->name, rtd->num, order); @@ -1507,9 +1526,28 @@ static int soc_probe_link_dais(struct snd_soc_card *card, soc_dpcm_debugfs_add(rtd); #endif + num = rtd->num; + + /* + * most drivers will register their PCMs using DAI link ordering but + * topology based drivers can use the DAI link id field to set PCM + * device number and then use rtd + a base offset of the BEs. + */ + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->use_dai_pcm_id) + continue; + + if (rtd->dai_link->no_pcm) + num += component->driver->be_pcm_base; + else + num = rtd->dai_link->id; + } + if (cpu_dai->driver->compress_new) { /*create compress_device"*/ - ret = cpu_dai->driver->compress_new(rtd, rtd->num); + ret = cpu_dai->driver->compress_new(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create compress %s\n", dai_link->stream_name); @@ -1519,7 +1557,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, if (!dai_link->params) { /* create the pcm */ - ret = soc_new_pcm(rtd, rtd->num); + ret = soc_new_pcm(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", dai_link->stream_name, ret); @@ -1846,6 +1884,74 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name); #endif /* CONFIG_DMI */ +static void soc_check_tplg_fes(struct snd_soc_card *card) +{ + struct snd_soc_component *component; + const struct snd_soc_component_driver *comp_drv; + struct snd_soc_dai_link *dai_link; + int i; + + list_for_each_entry(component, &component_list, list) { + + /* does this component override FEs ? */ + if (!component->driver->ignore_machine) + continue; + + /* for this machine ? */ + if (strcmp(component->driver->ignore_machine, + card->dev->driver->name)) + continue; + + /* machine matches, so override the rtd data */ + for (i = 0; i < card->num_links; i++) { + + dai_link = &card->dai_link[i]; + + /* ignore this FE */ + if (dai_link->dynamic) { + dai_link->ignore = true; + continue; + } + + dev_info(card->dev, "info: override FE DAI link %s\n", + card->dai_link[i].name); + + /* override platform component */ + dai_link->platform_name = component->name; + + /* convert non BE into BE */ + dai_link->no_pcm = 1; + + /* override any BE fixups */ + dai_link->be_hw_params_fixup = + component->driver->be_hw_params_fixup; + + /* most BE links don't set stream name, so set it to + * dai link name if it's NULL to help bind widgets. + */ + if (!dai_link->stream_name) + dai_link->stream_name = dai_link->name; + } + + /* Inform userspace we are using alternate topology */ + if (component->driver->topology_name_prefix) { + + /* topology shortname created ? */ + if (!card->topology_shortname_created) { + comp_drv = component->driver; + + snprintf(card->topology_shortname, 32, "%s-%s", + comp_drv->topology_name_prefix, + card->name); + card->topology_shortname_created = true; + } + + /* use topology shortname */ + card->name = card->topology_shortname; + } + } +} + static int snd_soc_instantiate_card(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; @@ -1855,6 +1961,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) mutex_lock(&client_mutex); mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); + /* check whether any platform is ignore machine FE and using topology */ + soc_check_tplg_fes(card); + /* bind DAIs */ for (i = 0; i < card->num_links; i++) { ret = soc_bind_dai_link(card, &card->dai_link[i]); @@ -2523,6 +2632,28 @@ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); /** + * snd_soc_dai_get_channel_map - Get DAI audio channel map + * @dai: DAI + * @tx_num: how many TX channels + * @tx_slot: pointer to an array which imply the TX slot number channel + * 0~num-1 uses + * @rx_num: how many RX channels + * @rx_slot: pointer to an array which imply the RX slot number channel + * 0~num-1 uses + */ +int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + if (dai->driver->ops->get_channel_map) + return dai->driver->ops->get_channel_map(dai, tx_num, tx_slot, + rx_num, rx_slot); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_get_channel_map); + +/** * snd_soc_dai_set_tristate - configure DAI system or master clock. * @dai: DAI * @tristate: tristate enable @@ -3258,9 +3389,9 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); -static int snd_soc_of_get_slot_mask(struct device_node *np, - const char *prop_name, - unsigned int *mask) +int snd_soc_of_get_slot_mask(struct device_node *np, + const char *prop_name, + unsigned int *mask) { u32 val; const __be32 *of_slot_mask = of_get_property(np, prop_name, &val); @@ -3275,6 +3406,7 @@ static int snd_soc_of_get_slot_mask(struct device_node *np, return val; } +EXPORT_SYMBOL_GPL(snd_soc_of_get_slot_mask); int snd_soc_of_parse_tdm_slot(struct device_node *np, unsigned int *tx_mask, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 229c12349803..7e96793050c9 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1,27 +1,21 @@ -/* - * soc-dapm.c -- ALSA SoC Dynamic Audio Power Management - * - * Copyright 2005 Wolfson Microelectronics PLC. - * Author: Liam Girdwood <lrg@slimlogic.co.uk> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * Features: - * o Changes power status of internal codec blocks depending on the - * dynamic configuration of codec internal audio paths and active - * DACs/ADCs. - * o Platform power domain - can support external components i.e. amps and - * mic/headphone insertion events. - * o Automatic Mic Bias support - * o Jack insertion power event initiation - e.g. hp insertion will enable - * sinks, dacs, etc - * o Delayed power down of audio subsystem to reduce pops between a quick - * device reopen. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-dapm.c -- ALSA SoC Dynamic Audio Power Management +// +// Copyright 2005 Wolfson Microelectronics PLC. +// Author: Liam Girdwood <lrg@slimlogic.co.uk> +// +// Features: +// o Changes power status of internal codec blocks depending on the +// dynamic configuration of codec internal audio paths and active +// DACs/ADCs. +// o Platform power domain - can support external components i.e. amps and +// mic/headphone insertion events. +// o Automatic Mic Bias support +// o Jack insertion power event initiation - e.g. hp insertion will enable +// sinks, dacs, etc +// o Delayed power down of audio subsystem to reduce pops between a quick +// device reopen. #include <linux/module.h> #include <linux/moduleparam.h> @@ -3662,7 +3656,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, struct snd_pcm_substream substream; struct snd_pcm_hw_params *params = NULL; struct snd_pcm_runtime *runtime = NULL; - u64 fmt; + unsigned int fmt; int ret; if (WARN_ON(!config) || @@ -4073,6 +4067,13 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) continue; } + /* let users know there is no DAI to link */ + if (!dai_w->priv) { + dev_dbg(card->dev, "dai widget %s has no DAI\n", + dai_w->name); + continue; + } + dai = dai_w->priv; /* ...find all widgets with the same stream and link them */ diff --git a/sound/soc/soc-devres.c b/sound/soc/soc-devres.c index 7ac745df1412..a9ea172a66a7 100644 --- a/sound/soc/soc-devres.c +++ b/sound/soc/soc-devres.c @@ -1,13 +1,8 @@ -/* - * soc-devres.c -- ALSA SoC Audio Layer devres functions - * - * Copyright (C) 2013 Linaro Ltd - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-devres.c -- ALSA SoC Audio Layer devres functions +// +// Copyright (C) 2013 Linaro Ltd #include <linux/module.h> #include <linux/moduleparam.h> diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 56a541b9ff9e..52fd7af952a5 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -1,17 +1,8 @@ -/* - * Copyright (C) 2013, Analog Devices Inc. - * Author: Lars-Peter Clausen <lars@metafoo.de> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2013, Analog Devices Inc. +// Author: Lars-Peter Clausen <lars@metafoo.de> + #include <linux/module.h> #include <linux/init.h> #include <linux/dmaengine.h> @@ -197,7 +188,7 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea case 32: case 64: if (addr_widths & (1 << (bits / 8))) - hw.formats |= (1LL << i); + hw.formats |= pcm_format_to_bits(i); break; default: /* Unsupported types */ @@ -343,7 +334,7 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer( static int dmaengine_copy_user(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - void *buf, unsigned long bytes) + void __user *buf, unsigned long bytes) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component = @@ -359,18 +350,17 @@ static int dmaengine_copy_user(struct snd_pcm_substream *substream, int ret; if (is_playback) - if (copy_from_user(dma_ptr, (void __user *)buf, bytes)) + if (copy_from_user(dma_ptr, buf, bytes)) return -EFAULT; if (process) { - ret = process(substream, channel, hwoff, - (void __user *)buf, bytes); + ret = process(substream, channel, hwoff, (__force void *)buf, bytes); if (ret < 0) return ret; } if (!is_playback) - if (copy_to_user((void __user *)buf, dma_ptr, bytes)) + if (copy_to_user(buf, dma_ptr, bytes)) return -EFAULT; return 0; diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 026cd5347e53..1ff9175e9d5e 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -1,15 +1,10 @@ -/* - * soc-io.c -- ASoC register I/O helpers - * - * Copyright 2009-2011 Wolfson Microelectronics PLC. - * - * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-io.c -- ASoC register I/O helpers +// +// Copyright 2009-2011 Wolfson Microelectronics PLC. +// +// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> #include <linux/i2c.h> #include <linux/spi/spi.h> diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index b2b16044ae80..c7b990abdbaa 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -1,15 +1,10 @@ -/* - * soc-jack.c -- ALSA SoC jack handling - * - * Copyright 2008 Wolfson Microelectronics PLC. - * - * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-jack.c -- ALSA SoC jack handling +// +// Copyright 2008 Wolfson Microelectronics PLC. +// +// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> #include <sound/jack.h> #include <sound/soc.h> diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 7144a51ddfa9..592efb370c44 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -1,20 +1,15 @@ -/* - * soc-ops.c -- Generic ASoC operations - * - * Copyright 2005 Wolfson Microelectronics PLC. - * Copyright 2005 Openedhand Ltd. - * Copyright (C) 2010 Slimlogic Ltd. - * Copyright (C) 2010 Texas Instruments Inc. - * - * Author: Liam Girdwood <lrg@slimlogic.co.uk> - * with code, comments and ideas from :- - * Richard Purdie <richard@openedhand.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-ops.c -- Generic ASoC operations +// +// Copyright 2005 Wolfson Microelectronics PLC. +// Copyright 2005 Openedhand Ltd. +// Copyright (C) 2010 Slimlogic Ltd. +// Copyright (C) 2010 Texas Instruments Inc. +// +// Author: Liam Girdwood <lrg@slimlogic.co.uk> +// with code, comments and ideas from :- +// Richard Purdie <richard@openedhand.com> #include <linux/module.h> #include <linux/moduleparam.h> diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 5e7ae47a9658..e8b98bfd4cf1 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1,20 +1,14 @@ -/* - * soc-pcm.c -- ALSA SoC PCM - * - * Copyright 2005 Wolfson Microelectronics PLC. - * Copyright 2005 Openedhand Ltd. - * Copyright (C) 2010 Slimlogic Ltd. - * Copyright (C) 2010 Texas Instruments Inc. - * - * Authors: Liam Girdwood <lrg@ti.com> - * Mark Brown <broonie@opensource.wolfsonmicro.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-pcm.c -- ALSA SoC PCM +// +// Copyright 2005 Wolfson Microelectronics PLC. +// Copyright 2005 Openedhand Ltd. +// Copyright (C) 2010 Slimlogic Ltd. +// Copyright (C) 2010 Texas Instruments Inc. +// +// Authors: Liam Girdwood <lrg@ti.com> +// Mark Brown <broonie@opensource.wolfsonmicro.com> #include <linux/kernel.h> #include <linux/init.h> @@ -448,6 +442,29 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) hw->rate_max = min_not_zero(hw->rate_max, rate_max); } +static int soc_pcm_components_close(struct snd_pcm_substream *substream, + struct snd_soc_component *last) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (component == last) + break; + + if (!component->driver->ops || + !component->driver->ops->close) + continue; + + component->driver->ops->close(substream); + } + + return 0; +} + /* * Called by ALSA when a PCM substream is opened, the runtime->hw record is * then initialized and any private data can be allocated. This also calls @@ -462,7 +479,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; const char *codec_dai_name = "multicodec"; - int i, ret = 0, __ret; + int i, ret = 0; pinctrl_pm_select_default_state(cpu_dai->dev); for (i = 0; i < rtd->num_codecs; i++) @@ -486,7 +503,6 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - ret = 0; for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; @@ -494,16 +510,15 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) !component->driver->ops->open) continue; - __ret = component->driver->ops->open(substream); - if (__ret < 0) { + ret = component->driver->ops->open(substream); + if (ret < 0) { dev_err(component->dev, "ASoC: can't open component %s: %d\n", - component->name, __ret); - ret = __ret; + component->name, ret); + goto component_err; } } - if (ret < 0) - goto component_err; + component = NULL; for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; @@ -612,15 +627,7 @@ codec_dai_err: } component_err: - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - if (!component->driver->ops || - !component->driver->ops->close) - continue; - - component->driver->ops->close(substream); - } + soc_pcm_components_close(substream, component); if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); @@ -714,15 +721,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) if (rtd->dai_link->ops->shutdown) rtd->dai_link->ops->shutdown(substream); - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - if (!component->driver->ops || - !component->driver->ops->close) - continue; - - component->driver->ops->close(substream); - } + soc_pcm_components_close(substream, NULL); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (snd_soc_runtime_ignore_pmdown_time(rtd)) { @@ -860,8 +859,20 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; int ret; + /* perform any topology hw_params fixups before DAI */ + if (rtd->dai_link->be_hw_params_fixup) { + ret = rtd->dai_link->be_hw_params_fixup(rtd, params); + if (ret < 0) { + dev_err(rtd->dev, + "ASoC: hw_params topology fixup failed %d\n", + ret); + return ret; + } + } + if (dai->driver->ops->hw_params) { ret = dai->driver->ops->hw_params(substream, params, dai); if (ret < 0) { @@ -874,6 +885,29 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, return 0; } +static int soc_pcm_components_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_component *last) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (component == last) + break; + + if (!component->driver->ops || + !component->driver->ops->hw_free) + continue; + + component->driver->ops->hw_free(substream); + } + + return 0; +} + /* * Called by ALSA when the hardware params are set by application. This * function can also be called multiple times and can allocate buffers @@ -886,7 +920,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int i, ret = 0, __ret; + int i, ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); if (rtd->dai_link->ops->hw_params) { @@ -944,7 +978,6 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, if (ret < 0) goto interface_err; - ret = 0; for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; @@ -952,16 +985,15 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, !component->driver->ops->hw_params) continue; - __ret = component->driver->ops->hw_params(substream, params); - if (__ret < 0) { + ret = component->driver->ops->hw_params(substream, params); + if (ret < 0) { dev_err(component->dev, "ASoC: %s hw params failed: %d\n", - component->name, __ret); - ret = __ret; + component->name, ret); + goto component_err; } } - if (ret < 0) - goto component_err; + component = NULL; /* store the parameters for each DAIs */ cpu_dai->rate = params_rate(params); @@ -977,15 +1009,7 @@ out: return ret; component_err: - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - if (!component->driver->ops || - !component->driver->ops->hw_free) - continue; - - component->driver->ops->hw_free(substream); - } + soc_pcm_components_hw_free(substream, component); if (cpu_dai->driver->ops->hw_free) cpu_dai->driver->ops->hw_free(substream, cpu_dai); @@ -1014,8 +1038,6 @@ codec_err: static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; @@ -1052,15 +1074,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) rtd->dai_link->ops->hw_free(substream); /* free any component resources */ - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - if (!component->driver->ops || - !component->driver->ops->hw_free) - continue; - - component->driver->ops->hw_free(substream); - } + soc_pcm_components_hw_free(substream, NULL); /* now free hw params for the DAIs */ for (i = 0; i < rtd->num_codecs; i++) { @@ -1165,6 +1179,9 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) snd_pcm_sframes_t codec_delay = 0; int i; + /* clearing the previous total delay */ + runtime->delay = 0; + for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; @@ -1176,6 +1193,8 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) offset = component->driver->ops->pointer(substream); break; } + /* base delay if assigned in pointer callback */ + delay = runtime->delay; if (cpu_dai->driver->ops->delay) delay += cpu_dai->driver->ops->delay(substream, cpu_dai); @@ -1658,29 +1677,28 @@ unwind: } static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime, - struct snd_soc_pcm_stream *stream, - u64 formats) + struct snd_soc_pcm_stream *stream) { runtime->hw.rate_min = stream->rate_min; runtime->hw.rate_max = stream->rate_max; runtime->hw.channels_min = stream->channels_min; runtime->hw.channels_max = stream->channels_max; if (runtime->hw.formats) - runtime->hw.formats &= formats & stream->formats; + runtime->hw.formats &= stream->formats; else - runtime->hw.formats = formats & stream->formats; + runtime->hw.formats = stream->formats; runtime->hw.rates = stream->rates; } -static u64 dpcm_runtime_base_format(struct snd_pcm_substream *substream) +static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream, + u64 *formats) { struct snd_soc_pcm_runtime *fe = substream->private_data; struct snd_soc_dpcm *dpcm; - u64 formats = ULLONG_MAX; int stream = substream->stream; if (!fe->dai_link->dpcm_merged_format) - return formats; + return; /* * It returns merged BE codec format @@ -1694,17 +1712,132 @@ static u64 dpcm_runtime_base_format(struct snd_pcm_substream *substream) int i; for (i = 0; i < be->num_codecs; i++) { + /* + * Skip CODECs which don't support the current stream + * type. See soc_pcm_init_runtime_hw() for more details + */ + if (!snd_soc_dai_stream_valid(be->codec_dais[i], + stream)) + continue; + codec_dai_drv = be->codec_dais[i]->driver; if (stream == SNDRV_PCM_STREAM_PLAYBACK) codec_stream = &codec_dai_drv->playback; else codec_stream = &codec_dai_drv->capture; - formats &= codec_stream->formats; + *formats &= codec_stream->formats; } } +} + +static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream, + unsigned int *channels_min, + unsigned int *channels_max) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + struct snd_soc_dpcm *dpcm; + int stream = substream->stream; - return formats; + if (!fe->dai_link->dpcm_merged_chan) + return; + + /* + * It returns merged BE codec channel; + * if FE want to use it (= dpcm_merged_chan) + */ + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver; + struct snd_soc_dai_driver *codec_dai_drv; + struct snd_soc_pcm_stream *codec_stream; + struct snd_soc_pcm_stream *cpu_stream; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + cpu_stream = &cpu_dai_drv->playback; + else + cpu_stream = &cpu_dai_drv->capture; + + *channels_min = max(*channels_min, cpu_stream->channels_min); + *channels_max = min(*channels_max, cpu_stream->channels_max); + + /* + * chan min/max cannot be enforced if there are multiple CODEC + * DAIs connected to a single CPU DAI, use CPU DAI's directly + */ + if (be->num_codecs == 1) { + codec_dai_drv = be->codec_dais[0]->driver; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + codec_stream = &codec_dai_drv->playback; + else + codec_stream = &codec_dai_drv->capture; + + *channels_min = max(*channels_min, + codec_stream->channels_min); + *channels_max = min(*channels_max, + codec_stream->channels_max); + } + } +} + +static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream, + unsigned int *rates, + unsigned int *rate_min, + unsigned int *rate_max) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + struct snd_soc_dpcm *dpcm; + int stream = substream->stream; + + if (!fe->dai_link->dpcm_merged_rate) + return; + + /* + * It returns merged BE codec channel; + * if FE want to use it (= dpcm_merged_chan) + */ + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver; + struct snd_soc_dai_driver *codec_dai_drv; + struct snd_soc_pcm_stream *codec_stream; + struct snd_soc_pcm_stream *cpu_stream; + int i; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + cpu_stream = &cpu_dai_drv->playback; + else + cpu_stream = &cpu_dai_drv->capture; + + *rate_min = max(*rate_min, cpu_stream->rate_min); + *rate_max = min_not_zero(*rate_max, cpu_stream->rate_max); + *rates = snd_pcm_rate_mask_intersect(*rates, cpu_stream->rates); + + for (i = 0; i < be->num_codecs; i++) { + /* + * Skip CODECs which don't support the current stream + * type. See soc_pcm_init_runtime_hw() for more details + */ + if (!snd_soc_dai_stream_valid(be->codec_dais[i], + stream)) + continue; + + codec_dai_drv = be->codec_dais[i]->driver; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + codec_stream = &codec_dai_drv->playback; + else + codec_stream = &codec_dai_drv->capture; + + *rate_min = max(*rate_min, codec_stream->rate_min); + *rate_max = min_not_zero(*rate_max, + codec_stream->rate_max); + *rates = snd_pcm_rate_mask_intersect(*rates, + codec_stream->rates); + } + } } static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) @@ -1713,12 +1846,17 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; - u64 format = dpcm_runtime_base_format(substream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback, format); + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback); else - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture, format); + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); + + dpcm_runtime_merge_format(substream, &runtime->hw.formats); + dpcm_runtime_merge_chan(substream, &runtime->hw.channels_min, + &runtime->hw.channels_max); + dpcm_runtime_merge_rate(substream, &runtime->hw.rates, + &runtime->hw.rate_min, &runtime->hw.rate_max); } static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd); @@ -2543,106 +2681,113 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream) return ret; } -/* Called by DAPM mixer/mux changes to update audio routing between PCMs and - * any DAI links. - */ -int soc_dpcm_runtime_update(struct snd_soc_card *card) +static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) { - struct snd_soc_pcm_runtime *fe; - int old, new, paths; + struct snd_soc_dapm_widget_list *list; + int count, paths; - mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); - list_for_each_entry(fe, &card->rtd_list, list) { - struct snd_soc_dapm_widget_list *list; + if (!fe->dai_link->dynamic) + return 0; - /* make sure link is FE */ - if (!fe->dai_link->dynamic) - continue; + /* only check active links */ + if (!fe->cpu_dai->active) + return 0; - /* only check active links */ - if (!fe->cpu_dai->active) - continue; + /* DAPM sync will call this to update DSP paths */ + dev_dbg(fe->dev, "ASoC: DPCM %s runtime update for FE %s\n", + new ? "new" : "old", fe->dai_link->name); - /* DAPM sync will call this to update DSP paths */ - dev_dbg(fe->dev, "ASoC: DPCM runtime update for FE %s\n", - fe->dai_link->name); + /* skip if FE doesn't have playback capability */ + if (!fe->cpu_dai->driver->playback.channels_min || + !fe->codec_dai->driver->playback.channels_min) + goto capture; - /* skip if FE doesn't have playback capability */ - if (!fe->cpu_dai->driver->playback.channels_min - || !fe->codec_dai->driver->playback.channels_min) - goto capture; - - /* skip if FE isn't currently playing */ - if (!fe->cpu_dai->playback_active - || !fe->codec_dai->playback_active) - goto capture; - - paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list); - if (paths < 0) { - dev_warn(fe->dev, "ASoC: %s no valid %s path\n", - fe->dai_link->name, "playback"); - mutex_unlock(&card->mutex); - return paths; - } + /* skip if FE isn't currently playing */ + if (!fe->cpu_dai->playback_active || !fe->codec_dai->playback_active) + goto capture; - /* update any new playback paths */ - new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 1); - if (new) { - dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK); - dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); - dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); - } + paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list); + if (paths < 0) { + dev_warn(fe->dev, "ASoC: %s no valid %s path\n", + fe->dai_link->name, "playback"); + return paths; + } - /* update any old playback paths */ - old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 0); - if (old) { + /* update any playback paths */ + count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, new); + if (count) { + if (new) + dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK); + else dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK); - dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); - dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); - } - dpcm_path_put(&list); + dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); + dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); + } + + dpcm_path_put(&list); + capture: - /* skip if FE doesn't have capture capability */ - if (!fe->cpu_dai->driver->capture.channels_min - || !fe->codec_dai->driver->capture.channels_min) - continue; + /* skip if FE doesn't have capture capability */ + if (!fe->cpu_dai->driver->capture.channels_min || + !fe->codec_dai->driver->capture.channels_min) + return 0; - /* skip if FE isn't currently capturing */ - if (!fe->cpu_dai->capture_active - || !fe->codec_dai->capture_active) - continue; + /* skip if FE isn't currently capturing */ + if (!fe->cpu_dai->capture_active || !fe->codec_dai->capture_active) + return 0; - paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list); - if (paths < 0) { - dev_warn(fe->dev, "ASoC: %s no valid %s path\n", - fe->dai_link->name, "capture"); - mutex_unlock(&card->mutex); - return paths; - } + paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list); + if (paths < 0) { + dev_warn(fe->dev, "ASoC: %s no valid %s path\n", + fe->dai_link->name, "capture"); + return paths; + } - /* update any new capture paths */ - new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 1); - if (new) { + /* update any old capture paths */ + count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, new); + if (count) { + if (new) dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE); - dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); - dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); - } - - /* update any old capture paths */ - old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 0); - if (old) { + else dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE); - dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); - dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); - } - dpcm_path_put(&list); + dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); + dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); } - mutex_unlock(&card->mutex); + dpcm_path_put(&list); + return 0; } + +/* Called by DAPM mixer/mux changes to update audio routing between PCMs and + * any DAI links. + */ +int soc_dpcm_runtime_update(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *fe; + int ret = 0; + + mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + /* shutdown all old paths first */ + list_for_each_entry(fe, &card->rtd_list, list) { + ret = soc_dpcm_fe_runtime_update(fe, 0); + if (ret) + goto out; + } + + /* bring new paths up */ + list_for_each_entry(fe, &card->rtd_list, list) { + ret = soc_dpcm_fe_runtime_update(fe, 1); + if (ret) + goto out; + } + +out: + mutex_unlock(&card->mutex); + return ret; +} int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) { struct snd_soc_dpcm *dpcm; diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 53f121a50c97..66e77e020745 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1,29 +1,24 @@ -/* - * soc-topology.c -- ALSA SoC Topology - * - * Copyright (C) 2012 Texas Instruments Inc. - * Copyright (C) 2015 Intel Corporation. - * - * Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> - * K, Mythri P <mythri.p.k@intel.com> - * Prusty, Subhransu S <subhransu.s.prusty@intel.com> - * B, Jayachandran <jayachandran.b@intel.com> - * Abdullah, Omair M <omair.m.abdullah@intel.com> - * Jin, Yao <yao.jin@intel.com> - * Lin, Mengdong <mengdong.lin@intel.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * Add support to read audio firmware topology alongside firmware text. The - * topology data can contain kcontrols, DAPM graphs, widgets, DAIs, DAI links, - * equalizers, firmware, coefficients etc. - * - * This file only manages the core ALSA and ASoC components, all other bespoke - * firmware topology data is passed to component drivers for bespoke handling. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-topology.c -- ALSA SoC Topology +// +// Copyright (C) 2012 Texas Instruments Inc. +// Copyright (C) 2015 Intel Corporation. +// +// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> +// K, Mythri P <mythri.p.k@intel.com> +// Prusty, Subhransu S <subhransu.s.prusty@intel.com> +// B, Jayachandran <jayachandran.b@intel.com> +// Abdullah, Omair M <omair.m.abdullah@intel.com> +// Jin, Yao <yao.jin@intel.com> +// Lin, Mengdong <mengdong.lin@intel.com> +// +// Add support to read audio firmware topology alongside firmware text. The +// topology data can contain kcontrols, DAPM graphs, widgets, DAIs, DAI links, +// equalizers, firmware, coefficients etc. +// +// This file only manages the core ALSA and ASoC components, all other bespoke +// firmware topology data is passed to component drivers for bespoke handling. #include <linux/kernel.h> #include <linux/export.h> @@ -259,7 +254,7 @@ static int soc_tplg_vendor_load_(struct soc_tplg *tplg, int ret = 0; if (tplg->comp && tplg->ops && tplg->ops->vendor_load) - ret = tplg->ops->vendor_load(tplg->comp, hdr); + ret = tplg->ops->vendor_load(tplg->comp, tplg->index, hdr); else { dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n", hdr->vendor_type); @@ -291,7 +286,8 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { if (tplg->comp && tplg->ops && tplg->ops->widget_load) - return tplg->ops->widget_load(tplg->comp, w, tplg_w); + return tplg->ops->widget_load(tplg->comp, tplg->index, w, + tplg_w); return 0; } @@ -302,27 +298,30 @@ static int soc_tplg_widget_ready(struct soc_tplg *tplg, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { if (tplg->comp && tplg->ops && tplg->ops->widget_ready) - return tplg->ops->widget_ready(tplg->comp, w, tplg_w); + return tplg->ops->widget_ready(tplg->comp, tplg->index, w, + tplg_w); return 0; } /* pass DAI configurations to component driver for extra initialization */ static int soc_tplg_dai_load(struct soc_tplg *tplg, - struct snd_soc_dai_driver *dai_drv) + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { if (tplg->comp && tplg->ops && tplg->ops->dai_load) - return tplg->ops->dai_load(tplg->comp, dai_drv); + return tplg->ops->dai_load(tplg->comp, tplg->index, dai_drv, + pcm, dai); return 0; } /* pass link configurations to component driver for extra initialization */ static int soc_tplg_dai_link_load(struct soc_tplg *tplg, - struct snd_soc_dai_link *link) + struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg) { if (tplg->comp && tplg->ops && tplg->ops->link_load) - return tplg->ops->link_load(tplg->comp, link); + return tplg->ops->link_load(tplg->comp, tplg->index, link, cfg); return 0; } @@ -643,7 +642,8 @@ static int soc_tplg_init_kcontrol(struct soc_tplg *tplg, struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr) { if (tplg->comp && tplg->ops && tplg->ops->control_load) - return tplg->ops->control_load(tplg->comp, k, hdr); + return tplg->ops->control_load(tplg->comp, tplg->index, k, + hdr); return 0; } @@ -1100,6 +1100,17 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, return 0; } +/* optionally pass new dynamic kcontrol to component driver. */ +static int soc_tplg_add_route(struct soc_tplg *tplg, + struct snd_soc_dapm_route *route) +{ + if (tplg->comp && tplg->ops && tplg->ops->dapm_route_load) + return tplg->ops->dapm_route_load(tplg->comp, tplg->index, + route); + + return 0; +} + static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { @@ -1148,6 +1159,8 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, else route.control = elem->control; + soc_tplg_add_route(tplg, &route); + /* add route, but keep going if some fail */ snd_soc_dapm_add_routes(dapm, &route, 1); } @@ -1702,7 +1715,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, dai_drv->compress_new = snd_soc_new_compress; /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_load(tplg, dai_drv); + ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); kfree(dai_drv); @@ -1772,7 +1785,7 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, set_link_flags(link, pcm->flag_mask, pcm->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_link_load(tplg, link); + ret = soc_tplg_dai_link_load(tplg, link, NULL); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n"); kfree(link); @@ -2080,7 +2093,7 @@ static int soc_tplg_link_config(struct soc_tplg *tplg, set_link_flags(link, cfg->flag_mask, cfg->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_link_load(tplg, link); + ret = soc_tplg_dai_link_load(tplg, link, cfg); if (ret < 0) { dev_err(tplg->dev, "ASoC: physical link loading failed\n"); return ret; @@ -2202,7 +2215,7 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg, set_dai_flags(dai_drv, d->flag_mask, d->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_load(tplg, dai_drv); + ret = soc_tplg_dai_load(tplg, dai_drv, NULL, dai); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); return ret; @@ -2311,7 +2324,7 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg, /* pass control to component driver for optional further init */ if (tplg->comp && tplg->ops && tplg->ops->manifest) - return tplg->ops->manifest(tplg->comp, _manifest); + return tplg->ops->manifest(tplg->comp, tplg->index, _manifest); if (!abi_match) /* free the duplicated one */ kfree(_manifest); diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 2d9e98bd1530..e0c93496c0cd 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -1,17 +1,11 @@ -/* - * soc-util.c -- ALSA SoC Audio Layer utility functions - * - * Copyright 2009 Wolfson Microelectronics PLC. - * - * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> - * Liam Girdwood <lrg@slimlogic.co.uk> - * - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-util.c -- ALSA SoC Audio Layer utility functions +// +// Copyright 2009 Wolfson Microelectronics PLC. +// +// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> +// Liam Girdwood <lrg@slimlogic.co.uk> #include <linux/platform_device.h> #include <linux/export.h> @@ -381,6 +375,6 @@ int __init snd_soc_util_init(void) void __exit snd_soc_util_exit(void) { - platform_device_unregister(soc_dummy_dev); platform_driver_unregister(&soc_dummy_driver); + platform_device_unregister(soc_dummy_dev); } diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c index d8b6936e544e..313dab2857ef 100644 --- a/sound/soc/sti/uniperif_player.c +++ b/sound/soc/sti/uniperif_player.c @@ -91,7 +91,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(player); /* Stop the player */ - snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stop_xrun(player->substream); } ret = IRQ_HANDLED; @@ -105,7 +105,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) SET_UNIPERIF_ITM_BCLR_DMA_ERROR(player); /* Stop the player */ - snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stop_xrun(player->substream); ret = IRQ_HANDLED; } @@ -138,7 +138,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) dev_err(player->dev, "Underflow recovery failed\n"); /* Stop the player */ - snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stop_xrun(player->substream); ret = IRQ_HANDLED; } diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c index ee0055e60852..7b63d35ef428 100644 --- a/sound/soc/sti/uniperif_reader.c +++ b/sound/soc/sti/uniperif_reader.c @@ -65,7 +65,7 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id) if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(reader))) { dev_err(reader->dev, "FIFO error detected\n"); - snd_pcm_stop(reader->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stop_xrun(reader->substream); ret = IRQ_HANDLED; } diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig index 48f9ddd94016..9b2681397dba 100644 --- a/sound/soc/stm/Kconfig +++ b/sound/soc/stm/Kconfig @@ -6,6 +6,7 @@ config SND_SOC_STM32_SAI depends on SND_SOC select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO + select SND_PCM_IEC958 help Say Y if you want to enable SAI for STM32 diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index db73fef3e500..706ff005234f 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -149,7 +149,7 @@ static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private) unsigned int old_pos = priv->pos; unsigned int cur_size = size; - dev_dbg(rtd->dev, "%s: buff_add :%p, pos = %d, size = %zu\n", + dev_dbg(rtd->dev, "%s: buff_add :%pK, pos = %d, size = %zu\n", __func__, &pcm_buff[priv->pos], priv->pos, size); if ((priv->pos + size) > buff_size) { @@ -269,16 +269,10 @@ static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd) static void stm32_adfsdm_pcm_free(struct snd_pcm *pcm) { struct snd_pcm_substream *substream; - struct snd_soc_pcm_runtime *rtd; - struct stm32_adfsdm_priv *priv; substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - if (substream) { - rtd = substream->private_data; - priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); - + if (substream) snd_pcm_lib_preallocate_free_for_all(pcm); - } } static struct snd_soc_component_driver stm32_adfsdm_soc_platform = { diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index cfeb219e1d78..06fba9650ac4 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -96,7 +96,8 @@ * @slot_mask: rx or tx active slots mask. set at init or at runtime * @data_size: PCM data width. corresponds to PCM substream width. * @spdif_frm_cnt: S/PDIF playback frame counter - * @spdif_status_bits: S/PDIF status bits + * @snd_aes_iec958: iec958 data + * @ctrl_lock: control lock */ struct stm32_sai_sub_data { struct platform_device *pdev; @@ -125,7 +126,8 @@ struct stm32_sai_sub_data { int slot_mask; int data_size; unsigned int spdif_frm_cnt; - unsigned char spdif_status_bits[SAI_IEC60958_STATUS_BYTES]; + struct snd_aes_iec958 iec958; + struct mutex ctrl_lock; /* protect resources accessed by controls */ }; enum stm32_sai_fifo_th { @@ -184,10 +186,6 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) } } -static const unsigned char default_status_bits[SAI_IEC60958_STATUS_BYTES] = { - 0, 0, 0, IEC958_AES3_CON_FS_48000, -}; - static const struct regmap_config stm32_sai_sub_regmap_config_f4 = { .reg_bits = 32, .reg_stride = 4, @@ -210,6 +208,49 @@ static const struct regmap_config stm32_sai_sub_regmap_config_h7 = { .fast_io = true, }; +static int snd_pcm_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int snd_pcm_iec958_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uctl) +{ + struct stm32_sai_sub_data *sai = snd_kcontrol_chip(kcontrol); + + mutex_lock(&sai->ctrl_lock); + memcpy(uctl->value.iec958.status, sai->iec958.status, 4); + mutex_unlock(&sai->ctrl_lock); + + return 0; +} + +static int snd_pcm_iec958_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uctl) +{ + struct stm32_sai_sub_data *sai = snd_kcontrol_chip(kcontrol); + + mutex_lock(&sai->ctrl_lock); + memcpy(sai->iec958.status, uctl->value.iec958.status, 4); + mutex_unlock(&sai->ctrl_lock); + + return 0; +} + +static const struct snd_kcontrol_new iec958_ctls = { + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = snd_pcm_iec958_info, + .get = snd_pcm_iec958_get, + .put = snd_pcm_iec958_put, +}; + static irqreturn_t stm32_sai_isr(int irq, void *devid) { struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; @@ -259,11 +300,8 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) status = SNDRV_PCM_STATE_XRUN; } - if (status != SNDRV_PCM_STATE_RUNNING) { - snd_pcm_stream_lock(sai->substream); - snd_pcm_stop(sai->substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(sai->substream); - } + if (status != SNDRV_PCM_STATE_RUNNING) + snd_pcm_stop_xrun(sai->substream); return IRQ_HANDLED; } @@ -619,6 +657,59 @@ static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai) } } +static void stm32_sai_init_iec958_status(struct stm32_sai_sub_data *sai) +{ + unsigned char *cs = sai->iec958.status; + + cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE; + cs[1] = IEC958_AES1_CON_GENERAL; + cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC; + cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | IEC958_AES3_CON_FS_NOTID; +} + +static void stm32_sai_set_iec958_status(struct stm32_sai_sub_data *sai, + struct snd_pcm_runtime *runtime) +{ + if (!runtime) + return; + + /* Force the sample rate according to runtime rate */ + mutex_lock(&sai->ctrl_lock); + switch (runtime->rate) { + case 22050: + sai->iec958.status[3] = IEC958_AES3_CON_FS_22050; + break; + case 44100: + sai->iec958.status[3] = IEC958_AES3_CON_FS_44100; + break; + case 88200: + sai->iec958.status[3] = IEC958_AES3_CON_FS_88200; + break; + case 176400: + sai->iec958.status[3] = IEC958_AES3_CON_FS_176400; + break; + case 24000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_24000; + break; + case 48000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_48000; + break; + case 96000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_96000; + break; + case 192000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_192000; + break; + case 32000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_32000; + break; + default: + sai->iec958.status[3] = IEC958_AES3_CON_FS_NOTID; + break; + } + mutex_unlock(&sai->ctrl_lock); +} + static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, struct snd_pcm_hw_params *params) { @@ -709,7 +800,11 @@ static int stm32_sai_hw_params(struct snd_pcm_substream *substream, sai->data_size = params_width(params); - if (!STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + /* Rate not already set in runtime structure */ + substream->runtime->rate = params_rate(params); + stm32_sai_set_iec958_status(sai, substream->runtime); + } else { ret = stm32_sai_set_slots(cpu_dai); if (ret < 0) return ret; @@ -789,6 +884,20 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, sai->substream = NULL; } +static int stm32_sai_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *cpu_dai) +{ + struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); + + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + dev_dbg(&sai->pdev->dev, "%s: register iec controls", __func__); + return snd_ctl_add(rtd->pcm->card, + snd_ctl_new1(&iec958_ctls, sai)); + } + + return 0; +} + static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); @@ -809,6 +918,10 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) else snd_soc_dai_init_dma_data(cpu_dai, NULL, &sai->dma_params); + /* Next settings are not relevant for spdif mode */ + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) + return 0; + cr1_mask = SAI_XCR1_RX_TX; if (STM_SAI_IS_CAPTURE(sai)) cr1 |= SAI_XCR1_RX_TX; @@ -820,10 +933,6 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) sai->synco, sai->synci); } - if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) - memcpy(sai->spdif_status_bits, default_status_bits, - sizeof(default_status_bits)); - cr1_mask |= SAI_XCR1_SYNCEN_MASK; cr1 |= SAI_XCR1_SYNCEN_SET(sai->sync); @@ -861,7 +970,7 @@ static int stm32_sai_pcm_process_spdif(struct snd_pcm_substream *substream, /* Set channel status bit */ byte = frm_cnt >> 3; mask = 1 << (frm_cnt - (byte << 3)); - if (sai->spdif_status_bits[byte] & mask) + if (sai->iec958.status[byte] & mask) *ptr |= 0x04000000; ptr++; @@ -888,6 +997,7 @@ static const struct snd_pcm_hardware stm32_sai_pcm_hw = { static struct snd_soc_dai_driver stm32_sai_playback_dai[] = { { .probe = stm32_sai_dai_probe, + .pcm_new = stm32_sai_pcm_new, .id = 1, /* avoid call to fmt_single_name() */ .playback = { .channels_min = 1, @@ -998,6 +1108,7 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, dev_err(&pdev->dev, "S/PDIF IEC60958 not supported\n"); return -EINVAL; } + stm32_sai_init_iec958_status(sai); sai->spdif = true; sai->master = true; } @@ -1114,6 +1225,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) sai->id = (uintptr_t)of_id->data; sai->pdev = pdev; + mutex_init(&sai->ctrl_lock); platform_set_drvdata(pdev, sai); sai->pdata = dev_get_drvdata(pdev->dev.parent); diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index affad46bf188..682ef33afb5f 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -377,7 +377,7 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) ret = clk_prepare_enable(ac97->clk_ac97); if (ret) { dev_err(&pdev->dev, "clk_enable failed: %d\n", ret); - goto err; + goto err_clk_put; } ret = snd_soc_set_ac97_ops(&tegra20_ac97_ops); diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h index 774fc6ad2026..2e561e946de2 100644 --- a/sound/soc/tegra/tegra30_i2s.h +++ b/sound/soc/tegra/tegra30_i2s.h @@ -173,7 +173,7 @@ /* Number of slots in frame, minus 1 */ #define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT 16 #define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK_US 7 -#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK (TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_MASK_US << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_SHIFT) +#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK (TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK_US << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT) /* TDM mode slot enable bitmask */ #define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT 8 diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index 5197d6b18cb6..98d87801d57a 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c @@ -190,14 +190,14 @@ static int tegra_alc5632_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Property 'nvidia,i2s-controller' missing or invalid\n"); ret = -EINVAL; - goto err; + goto err_put_codec_of_node; } tegra_alc5632_dai.platform_of_node = tegra_alc5632_dai.cpu_of_node; ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev); if (ret) - goto err; + goto err_put_cpu_of_node; ret = snd_soc_register_card(card); if (ret) { @@ -210,6 +210,13 @@ static int tegra_alc5632_probe(struct platform_device *pdev) err_fini_utils: tegra_asoc_utils_fini(&alc5632->util_data); +err_put_cpu_of_node: + of_node_put(tegra_alc5632_dai.cpu_of_node); + tegra_alc5632_dai.cpu_of_node = NULL; + tegra_alc5632_dai.platform_of_node = NULL; +err_put_codec_of_node: + of_node_put(tegra_alc5632_dai.codec_of_node); + tegra_alc5632_dai.codec_of_node = NULL; err: return ret; } @@ -223,6 +230,12 @@ static int tegra_alc5632_remove(struct platform_device *pdev) tegra_asoc_utils_fini(&machine->util_data); + of_node_put(tegra_alc5632_dai.cpu_of_node); + tegra_alc5632_dai.cpu_of_node = NULL; + tegra_alc5632_dai.platform_of_node = NULL; + of_node_put(tegra_alc5632_dai.codec_of_node); + tegra_alc5632_dai.codec_of_node = NULL; + return 0; } diff --git a/sound/soc/tegra/tegra_rt5677.c b/sound/soc/tegra/tegra_rt5677.c index 0e4805c7b4ca..7081f15302cc 100644 --- a/sound/soc/tegra/tegra_rt5677.c +++ b/sound/soc/tegra/tegra_rt5677.c @@ -264,13 +264,13 @@ static int tegra_rt5677_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Property 'nvidia,i2s-controller' missing or invalid\n"); ret = -EINVAL; - goto err; + goto err_put_codec_of_node; } tegra_rt5677_dai.platform_of_node = tegra_rt5677_dai.cpu_of_node; ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); if (ret) - goto err; + goto err_put_cpu_of_node; ret = snd_soc_register_card(card); if (ret) { @@ -283,6 +283,13 @@ static int tegra_rt5677_probe(struct platform_device *pdev) err_fini_utils: tegra_asoc_utils_fini(&machine->util_data); +err_put_cpu_of_node: + of_node_put(tegra_rt5677_dai.cpu_of_node); + tegra_rt5677_dai.cpu_of_node = NULL; + tegra_rt5677_dai.platform_of_node = NULL; +err_put_codec_of_node: + of_node_put(tegra_rt5677_dai.codec_of_node); + tegra_rt5677_dai.codec_of_node = NULL; err: return ret; } @@ -296,6 +303,12 @@ static int tegra_rt5677_remove(struct platform_device *pdev) tegra_asoc_utils_fini(&machine->util_data); + tegra_rt5677_dai.platform_of_node = NULL; + of_node_put(tegra_rt5677_dai.codec_of_node); + tegra_rt5677_dai.codec_of_node = NULL; + of_node_put(tegra_rt5677_dai.cpu_of_node); + tegra_rt5677_dai.cpu_of_node = NULL; + return 0; } diff --git a/sound/soc/uniphier/aio-core.c b/sound/soc/uniphier/aio-core.c index 638cb3fc5f7b..9bcba06ba52e 100644 --- a/sound/soc/uniphier/aio-core.c +++ b/sound/soc/uniphier/aio-core.c @@ -265,6 +265,57 @@ void aio_port_reset(struct uniphier_aio_sub *sub) } /** + * aio_port_set_ch - set channels of LPCM + * @sub: the AIO substream pointer, PCM substream only + * @ch : count of channels + * + * Set suitable slot selecting to input/output port block of AIO. + * + * This function may return error if non-PCM substream. + * + * Return: Zero if successful, otherwise a negative value on error. + */ +static int aio_port_set_ch(struct uniphier_aio_sub *sub) +{ + struct regmap *r = sub->aio->chip->regmap; + u32 slotsel_2ch[] = { + 0, 0, 0, 0, 0, + }; + u32 slotsel_multi[] = { + OPORTMXTYSLOTCTR_SLOTSEL_SLOT0, + OPORTMXTYSLOTCTR_SLOTSEL_SLOT1, + OPORTMXTYSLOTCTR_SLOTSEL_SLOT2, + OPORTMXTYSLOTCTR_SLOTSEL_SLOT3, + OPORTMXTYSLOTCTR_SLOTSEL_SLOT4, + }; + u32 mode, *slotsel; + int i; + + switch (params_channels(&sub->params)) { + case 8: + case 6: + mode = OPORTMXTYSLOTCTR_MODE; + slotsel = slotsel_multi; + break; + case 2: + mode = 0; + slotsel = slotsel_2ch; + break; + default: + return -EINVAL; + } + + for (i = 0; i < AUD_MAX_SLOTSEL; i++) { + regmap_update_bits(r, OPORTMXTYSLOTCTR(sub->swm->oport.map, i), + OPORTMXTYSLOTCTR_MODE, mode); + regmap_update_bits(r, OPORTMXTYSLOTCTR(sub->swm->oport.map, i), + OPORTMXTYSLOTCTR_SLOTSEL_MASK, slotsel[i]); + } + + return 0; +} + +/** * aio_port_set_rate - set sampling rate of LPCM * @sub: the AIO substream pointer, PCM substream only * @rate: Sampling rate in Hz. @@ -276,7 +327,7 @@ void aio_port_reset(struct uniphier_aio_sub *sub) * * Return: Zero if successful, otherwise a negative value on error. */ -int aio_port_set_rate(struct uniphier_aio_sub *sub, int rate) +static int aio_port_set_rate(struct uniphier_aio_sub *sub, int rate) { struct regmap *r = sub->aio->chip->regmap; struct device *dev = &sub->aio->chip->pdev->dev; @@ -395,7 +446,7 @@ int aio_port_set_rate(struct uniphier_aio_sub *sub, int rate) * * Return: Zero if successful, otherwise a negative value on error. */ -int aio_port_set_fmt(struct uniphier_aio_sub *sub) +static int aio_port_set_fmt(struct uniphier_aio_sub *sub) { struct regmap *r = sub->aio->chip->regmap; struct device *dev = &sub->aio->chip->pdev->dev; @@ -460,7 +511,7 @@ int aio_port_set_fmt(struct uniphier_aio_sub *sub) * * Return: Zero if successful, otherwise a negative value on error. */ -int aio_port_set_clk(struct uniphier_aio_sub *sub) +static int aio_port_set_clk(struct uniphier_aio_sub *sub) { struct uniphier_aio_chip *chip = sub->aio->chip; struct device *dev = &sub->aio->chip->pdev->dev; @@ -575,6 +626,10 @@ int aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through, rate = params_rate(params); } + ret = aio_port_set_ch(sub); + if (ret) + return ret; + ret = aio_port_set_rate(sub, rate); if (ret) return ret; @@ -731,15 +786,28 @@ void aio_port_set_volume(struct uniphier_aio_sub *sub, int vol) int aio_if_set_param(struct uniphier_aio_sub *sub, int pass_through) { struct regmap *r = sub->aio->chip->regmap; - u32 v; + u32 memfmt, v; if (sub->swm->dir == PORT_DIR_OUTPUT) { - if (pass_through) + if (pass_through) { v = PBOUTMXCTR0_ENDIAN_0123 | PBOUTMXCTR0_MEMFMT_STREAM; - else - v = PBOUTMXCTR0_ENDIAN_3210 | - PBOUTMXCTR0_MEMFMT_2CH; + } else { + switch (params_channels(&sub->params)) { + case 2: + memfmt = PBOUTMXCTR0_MEMFMT_2CH; + break; + case 6: + memfmt = PBOUTMXCTR0_MEMFMT_6CH; + break; + case 8: + memfmt = PBOUTMXCTR0_MEMFMT_8CH; + break; + default: + return -EINVAL; + } + v = PBOUTMXCTR0_ENDIAN_3210 | memfmt; + } regmap_write(r, PBOUTMXCTR0(sub->swm->oif.map), v); regmap_write(r, PBOUTMXCTR1(sub->swm->oif.map), 0); diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c index 2d9b7dde2ffa..ee90e6c3937c 100644 --- a/sound/soc/uniphier/aio-cpu.c +++ b/sound/soc/uniphier/aio-cpu.c @@ -219,15 +219,10 @@ static int uniphier_aio_set_pll(struct snd_soc_dai *dai, int pll_id, unsigned int freq_out) { struct uniphier_aio *aio = uniphier_priv(dai); - struct device *dev = &aio->chip->pdev->dev; int ret; if (!is_valid_pll(aio->chip, pll_id)) return -EINVAL; - if (!aio->chip->plls[pll_id].enable) { - dev_err(dev, "PLL(%d) is not implemented\n", pll_id); - return -ENOTSUPP; - } ret = aio_chip_set_pll(aio->chip, pll_id, freq_out); if (ret < 0) diff --git a/sound/soc/uniphier/aio-ld11.c b/sound/soc/uniphier/aio-ld11.c index ab04d3331be9..de962df245ba 100644 --- a/sound/soc/uniphier/aio-ld11.c +++ b/sound/soc/uniphier/aio-ld11.c @@ -286,7 +286,7 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .formats = SNDRV_PCM_FMTBIT_S32_LE, .rates = SNDRV_PCM_RATE_48000, .channels_min = 2, - .channels_max = 2, + .channels_max = 8, }, .ops = &uniphier_aio_i2s_ops, }, diff --git a/sound/soc/uniphier/aio-reg.h b/sound/soc/uniphier/aio-reg.h index 45fdc6ae358a..734395dbcffb 100644 --- a/sound/soc/uniphier/aio-reg.h +++ b/sound/soc/uniphier/aio-reg.h @@ -374,6 +374,7 @@ #define OPORTMXTYVOLGAINSTATUS(n, m) (0x42108 + 0x400 * (n) + 0x20 * (m)) #define OPORTMXTYVOLGAINSTATUS_CUR_MASK GENMASK(15, 0) #define OPORTMXTYSLOTCTR(n, m) (0x42114 + 0x400 * (n) + 0x20 * (m)) +#define OPORTMXTYSLOTCTR_MODE BIT(15) #define OPORTMXTYSLOTCTR_SLOTSEL_MASK GENMASK(11, 8) #define OPORTMXTYSLOTCTR_SLOTSEL_SLOT0 (0x8 << 8) #define OPORTMXTYSLOTCTR_SLOTSEL_SLOT1 (0x9 << 8) diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h index aa89c2f6fa24..ca6ccbae0ee8 100644 --- a/sound/soc/uniphier/aio.h +++ b/sound/soc/uniphier/aio.h @@ -141,6 +141,9 @@ enum IEC61937_PC { #define AUD_MIN_FRAGMENT_SIZE (4 * 1024) #define AUD_MAX_FRAGMENT_SIZE (16 * 1024) +/* max 5 slots, 10 channels, 2 channel in 1 slot */ +#define AUD_MAX_SLOTSEL 5 + /* * This is a selector for virtual register map of AIO. * @@ -322,9 +325,6 @@ int aio_chip_set_pll(struct uniphier_aio_chip *chip, int pll_id, void aio_chip_init(struct uniphier_aio_chip *chip); int aio_init(struct uniphier_aio_sub *sub); void aio_port_reset(struct uniphier_aio_sub *sub); -int aio_port_set_rate(struct uniphier_aio_sub *sub, int rate); -int aio_port_set_fmt(struct uniphier_aio_sub *sub); -int aio_port_set_clk(struct uniphier_aio_sub *sub); int aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through, const struct snd_pcm_hw_params *params); void aio_port_set_enable(struct uniphier_aio_sub *sub, int enable); diff --git a/sound/soc/zte/zx-tdm.c b/sound/soc/zte/zx-tdm.c index dc955272f58b..389272eeba9a 100644 --- a/sound/soc/zte/zx-tdm.c +++ b/sound/soc/zte/zx-tdm.c @@ -144,8 +144,8 @@ static void zx_tdm_rx_dma_en(struct zx_tdm_info *tdm, bool on) #define ZX_TDM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000) #define ZX_TDM_FMTBIT \ - (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_MU_LAW | \ - SNDRV_PCM_FORMAT_A_LAW) + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_MU_LAW | \ + SNDRV_PCM_FMTBIT_A_LAW) static int zx_tdm_dai_probe(struct snd_soc_dai *dai) { |