diff options
author | Takashi Iwai <tiwai@suse.de> | 2020-01-27 17:45:44 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2020-01-27 17:45:44 +0100 |
commit | 90fb04f890bcb7384b4d4c216dc2640b0a870df3 (patch) | |
tree | f4c35d625351cd8034bdd37bb3e19926bad674db /sound | |
parent | 9b132f2764e5a04bfb2e6c6f51cf5a729617d6a4 (diff) | |
parent | a7196caf83ea9e9b56c5c8c95fbfe0d45acec46b (diff) |
Merge tag 'asoc-v5.6' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v5.6
A pretty big release this time around, a lot of new drivers and both
Morimoto-san and Takashi were doing subsystem wide updates as well:
- Further big refactorings from Morimoto-san simplifying the core
interfaces and moving things to the component level.
- Transition of drivers to managed buffer allocation and removal of
redundant PCM ioctls.
- New driver support for Ingenic JZ4770, Mediatek MT6660, Qualcomm
WCD934x and WSA881x, and Realtek RT700, RT711, RT715, RT1011, RT1015
and RT1308.
Diffstat (limited to 'sound')
227 files changed, 23363 insertions, 4011 deletions
diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 4b9a27e25206..f54beb7f39a8 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -836,7 +836,6 @@ static int acp_dma_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - int status; uint64_t size; u32 val = 0; struct snd_pcm_runtime *runtime; @@ -967,35 +966,19 @@ static int acp_dma_hw_params(struct snd_soc_component *component, } size = params_buffer_bytes(params); - status = snd_pcm_lib_malloc_pages(substream, size); - if (status < 0) - return status; - - memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); - if (substream->dma_buffer.area) { - acp_set_sram_bank_state(rtd->acp_mmio, 0, true); - /* Save for runtime private data */ - rtd->dma_addr = substream->dma_buffer.addr; - rtd->order = get_order(size); + acp_set_sram_bank_state(rtd->acp_mmio, 0, true); + /* Save for runtime private data */ + rtd->dma_addr = substream->dma_buffer.addr; + rtd->order = get_order(size); - /* Fill the page table entries in ACP SRAM */ - rtd->size = size; - rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; - rtd->direction = substream->stream; + /* Fill the page table entries in ACP SRAM */ + rtd->size = size; + rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; + rtd->direction = substream->stream; - config_acp_dma(rtd->acp_mmio, rtd, adata->asic_type); - status = 0; - } else { - status = -ENOMEM; - } - return status; -} - -static int acp_dma_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); + config_acp_dma(rtd->acp_mmio, rtd, adata->asic_type); + return 0; } static u64 acp_get_byte_count(struct audio_substream_data *rtd) @@ -1142,18 +1125,18 @@ static int acp_dma_new(struct snd_soc_component *component, switch (adata->asic_type) { case CHIP_STONEY: - snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, - SNDRV_DMA_TYPE_DEV, - parent, - ST_MIN_BUFFER, - ST_MAX_BUFFER); + snd_pcm_set_managed_buffer_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, + parent, + ST_MIN_BUFFER, + ST_MAX_BUFFER); break; default: - snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, - SNDRV_DMA_TYPE_DEV, - parent, - MIN_BUFFER, - MAX_BUFFER); + snd_pcm_set_managed_buffer_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, + parent, + MIN_BUFFER, + MAX_BUFFER); break; } return 0; @@ -1219,9 +1202,7 @@ static const struct snd_soc_component_driver acp_asoc_platform = { .name = DRV_NAME, .open = acp_dma_open, .close = acp_dma_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = acp_dma_hw_params, - .hw_free = acp_dma_hw_free, .trigger = acp_dma_trigger, .pointer = acp_dma_pointer, .mmap = acp_dma_mmap, diff --git a/sound/soc/amd/raven/Makefile b/sound/soc/amd/raven/Makefile index 108d1acf189b..62c22b6ed95a 100644 --- a/sound/soc/amd/raven/Makefile +++ b/sound/soc/amd/raven/Makefile @@ -2,5 +2,7 @@ # Raven Ridge platform Support snd-pci-acp3x-objs := pci-acp3x.o snd-acp3x-pcm-dma-objs := acp3x-pcm-dma.o +snd-acp3x-i2s-objs := acp3x-i2s.o obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-pci-acp3x.o obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-acp3x-pcm-dma.o +obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-acp3x-i2s.o diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c new file mode 100644 index 000000000000..bf51cadf8682 --- /dev/null +++ b/sound/soc/amd/raven/acp3x-i2s.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// AMD ALSA SoC PCM Driver +// +//Copyright 2016 Advanced Micro Devices, Inc. + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/io.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <linux/dma-mapping.h> + +#include "acp3x.h" + +#define DRV_NAME "acp3x-i2s" + +static int acp3x_i2s_set_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct i2s_dev_data *adata; + int mode; + + adata = snd_soc_dai_get_drvdata(cpu_dai); + mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + switch (mode) { + case SND_SOC_DAIFMT_I2S: + adata->tdm_mode = TDM_DISABLE; + break; + case SND_SOC_DAIFMT_DSP_A: + adata->tdm_mode = TDM_ENABLE; + break; + default: + return -EINVAL; + } + return 0; +} + +static int acp3x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai, + u32 tx_mask, u32 rx_mask, int slots, int slot_width) +{ + struct i2s_dev_data *adata; + u32 val, reg_val, frmt_reg, frm_len; + u16 slot_len; + + adata = snd_soc_dai_get_drvdata(cpu_dai); + + /* These values are as per Hardware Spec */ + switch (slot_width) { + case SLOT_WIDTH_8: + slot_len = 8; + break; + case SLOT_WIDTH_16: + slot_len = 16; + break; + case SLOT_WIDTH_24: + slot_len = 24; + break; + case SLOT_WIDTH_32: + slot_len = 0; + break; + default: + return -EINVAL; + } + + /* Enable I2S/BT channels TDM, respective TX/RX frame lengths.*/ + + frm_len = FRM_LEN | (slots << 15) | (slot_len << 18); + if (adata->substream_type == SNDRV_PCM_STREAM_PLAYBACK) { + switch (adata->i2s_instance) { + case I2S_BT_INSTANCE: + reg_val = mmACP_BTTDM_ITER; + frmt_reg = mmACP_BTTDM_TXFRMT; + break; + case I2S_SP_INSTANCE: + default: + reg_val = mmACP_I2STDM_ITER; + frmt_reg = mmACP_I2STDM_TXFRMT; + } + } else { + switch (adata->i2s_instance) { + case I2S_BT_INSTANCE: + reg_val = mmACP_BTTDM_IRER; + frmt_reg = mmACP_BTTDM_RXFRMT; + break; + case I2S_SP_INSTANCE: + default: + reg_val = mmACP_I2STDM_IRER; + frmt_reg = mmACP_I2STDM_RXFRMT; + } + } + val = rv_readl(adata->acp3x_base + reg_val); + rv_writel(val | 0x2, adata->acp3x_base + reg_val); + rv_writel(frm_len, adata->acp3x_base + frmt_reg); + adata->tdm_fmt = frm_len; + return 0; +} + +static int acp3x_i2s_hwparams(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct i2s_stream_instance *rtd; + struct snd_soc_pcm_runtime *prtd; + struct snd_soc_card *card; + struct acp3x_platform_info *pinfo; + u32 val; + u32 reg_val; + + prtd = substream->private_data; + rtd = substream->runtime->private_data; + card = prtd->card; + pinfo = snd_soc_card_get_drvdata(card); + if (pinfo) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rtd->i2s_instance = pinfo->play_i2s_instance; + else + rtd->i2s_instance = pinfo->cap_i2s_instance; + } + + /* These values are as per Hardware Spec */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_U8: + case SNDRV_PCM_FORMAT_S8: + rtd->xfer_resolution = 0x0; + break; + case SNDRV_PCM_FORMAT_S16_LE: + rtd->xfer_resolution = 0x02; + break; + case SNDRV_PCM_FORMAT_S24_LE: + rtd->xfer_resolution = 0x04; + break; + case SNDRV_PCM_FORMAT_S32_LE: + rtd->xfer_resolution = 0x05; + break; + default: + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + reg_val = mmACP_BTTDM_ITER; + break; + case I2S_SP_INSTANCE: + default: + reg_val = mmACP_I2STDM_ITER; + } + } else { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + reg_val = mmACP_BTTDM_IRER; + break; + case I2S_SP_INSTANCE: + default: + reg_val = mmACP_I2STDM_IRER; + } + } + val = rv_readl(rtd->acp3x_base + reg_val); + val = val | (rtd->xfer_resolution << 3); + rv_writel(val, rtd->acp3x_base + reg_val); + return 0; +} + +static int acp3x_i2s_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct i2s_stream_instance *rtd; + struct snd_soc_pcm_runtime *prtd; + struct snd_soc_card *card; + struct acp3x_platform_info *pinfo; + u32 ret, val, period_bytes, reg_val, ier_val, water_val; + + prtd = substream->private_data; + rtd = substream->runtime->private_data; + card = prtd->card; + pinfo = snd_soc_card_get_drvdata(card); + if (pinfo) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rtd->i2s_instance = pinfo->play_i2s_instance; + else + rtd->i2s_instance = pinfo->cap_i2s_instance; + } + period_bytes = frames_to_bytes(substream->runtime, + substream->runtime->period_size); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + rtd->bytescount = acp_get_byte_count(rtd, + substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + water_val = + mmACP_BT_TX_INTR_WATERMARK_SIZE; + reg_val = mmACP_BTTDM_ITER; + ier_val = mmACP_BTTDM_IER; + break; + case I2S_SP_INSTANCE: + default: + water_val = + mmACP_I2S_TX_INTR_WATERMARK_SIZE; + reg_val = mmACP_I2STDM_ITER; + ier_val = mmACP_I2STDM_IER; + } + } else { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + water_val = + mmACP_BT_RX_INTR_WATERMARK_SIZE; + reg_val = mmACP_BTTDM_IRER; + ier_val = mmACP_BTTDM_IER; + break; + case I2S_SP_INSTANCE: + default: + water_val = + mmACP_I2S_RX_INTR_WATERMARK_SIZE; + reg_val = mmACP_I2STDM_IRER; + ier_val = mmACP_I2STDM_IER; + } + } + rv_writel(period_bytes, rtd->acp3x_base + water_val); + val = rv_readl(rtd->acp3x_base + reg_val); + val = val | BIT(0); + rv_writel(val, rtd->acp3x_base + reg_val); + rv_writel(1, rtd->acp3x_base + ier_val); + ret = 0; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + reg_val = mmACP_BTTDM_ITER; + ier_val = mmACP_BTTDM_IER; + break; + case I2S_SP_INSTANCE: + default: + reg_val = mmACP_I2STDM_ITER; + ier_val = mmACP_I2STDM_IER; + } + + } else { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + reg_val = mmACP_BTTDM_IRER; + ier_val = mmACP_BTTDM_IER; + break; + case I2S_SP_INSTANCE: + default: + reg_val = mmACP_I2STDM_IRER; + ier_val = mmACP_I2STDM_IER; + } + } + val = rv_readl(rtd->acp3x_base + reg_val); + val = val & ~BIT(0); + rv_writel(val, rtd->acp3x_base + reg_val); + rv_writel(0, rtd->acp3x_base + ier_val); + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static struct snd_soc_dai_ops acp3x_i2s_dai_ops = { + .hw_params = acp3x_i2s_hwparams, + .trigger = acp3x_i2s_trigger, + .set_fmt = acp3x_i2s_set_fmt, + .set_tdm_slot = acp3x_i2s_set_tdm_slot, +}; + +static const struct snd_soc_component_driver acp3x_dai_component = { + .name = "acp3x-i2s", +}; + +static struct snd_soc_dai_driver acp3x_i2s_dai = { + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &acp3x_i2s_dai_ops, +}; + +static int acp3x_dai_probe(struct platform_device *pdev) +{ + struct resource *res; + struct i2s_dev_data *adata; + int ret; + + adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data), + GFP_KERNEL); + if (!adata) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); + return -ENOMEM; + } + adata->acp3x_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!adata->acp3x_base) + return -ENOMEM; + + adata->i2s_irq = res->start; + dev_set_drvdata(&pdev->dev, adata); + ret = devm_snd_soc_register_component(&pdev->dev, + &acp3x_dai_component, &acp3x_i2s_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Fail to register acp i2s dai\n"); + return -ENODEV; + } + return 0; +} + +static int acp3x_dai_remove(struct platform_device *pdev) +{ + /* As we use devm_ memory alloc there is nothing TBD here */ + + return 0; +} + +static struct platform_driver acp3x_dai_driver = { + .probe = acp3x_dai_probe, + .remove = acp3x_dai_remove, + .driver = { + .name = "acp3x_i2s_playcap", + }, +}; + +module_platform_driver(acp3x_dai_driver); + +MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com"); +MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index 60709e3ba99d..5c3ec3c58e3b 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -9,7 +9,6 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/pm_runtime.h> -#include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-dai.h> @@ -18,28 +17,11 @@ #define DRV_NAME "acp3x-i2s-audio" -struct i2s_dev_data { - bool tdm_mode; - unsigned int i2s_irq; - u32 tdm_fmt; - void __iomem *acp3x_base; - struct snd_pcm_substream *play_stream; - struct snd_pcm_substream *capture_stream; -}; - -struct i2s_stream_instance { - u16 num_pages; - u16 channels; - u32 xfer_resolution; - u64 bytescount; - dma_addr_t dma_addr; - void __iomem *acp3x_base; -}; - static const struct snd_pcm_hardware acp3x_pcm_hardware_playback = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | @@ -60,7 +42,8 @@ static const struct snd_pcm_hardware acp3x_pcm_hardware_capture = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, @@ -76,112 +59,13 @@ static const struct snd_pcm_hardware acp3x_pcm_hardware_capture = { .periods_max = CAPTURE_MAX_NUM_PERIODS, }; -static int acp3x_power_on(void __iomem *acp3x_base, bool on) -{ - u16 val, mask; - u32 timeout; - - if (on == true) { - val = 1; - mask = ACP3x_POWER_ON; - } else { - val = 0; - mask = ACP3x_POWER_OFF; - } - - rv_writel(val, acp3x_base + mmACP_PGFSM_CONTROL); - timeout = 0; - while (true) { - val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS); - if ((val & ACP3x_POWER_OFF_IN_PROGRESS) == mask) - break; - if (timeout > 100) { - pr_err("ACP3x power state change failure\n"); - return -ENODEV; - } - timeout++; - cpu_relax(); - } - return 0; -} - -static int acp3x_reset(void __iomem *acp3x_base) -{ - u32 val, timeout; - - rv_writel(1, acp3x_base + mmACP_SOFT_RESET); - timeout = 0; - while (true) { - val = rv_readl(acp3x_base + mmACP_SOFT_RESET); - if ((val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK) || - timeout > 100) { - if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK) - break; - return -ENODEV; - } - timeout++; - cpu_relax(); - } - - rv_writel(0, acp3x_base + mmACP_SOFT_RESET); - timeout = 0; - while (true) { - val = rv_readl(acp3x_base + mmACP_SOFT_RESET); - if (!val || timeout > 100) { - if (!val) - break; - return -ENODEV; - } - timeout++; - cpu_relax(); - } - return 0; -} - -static int acp3x_init(void __iomem *acp3x_base) -{ - int ret; - - /* power on */ - ret = acp3x_power_on(acp3x_base, true); - if (ret) { - pr_err("ACP3x power on failed\n"); - return ret; - } - /* Reset */ - ret = acp3x_reset(acp3x_base); - if (ret) { - pr_err("ACP3x reset failed\n"); - return ret; - } - return 0; -} - -static int acp3x_deinit(void __iomem *acp3x_base) -{ - int ret; - - /* Reset */ - ret = acp3x_reset(acp3x_base); - if (ret) { - pr_err("ACP3x reset failed\n"); - return ret; - } - /* power off */ - ret = acp3x_power_on(acp3x_base, false); - if (ret) { - pr_err("ACP3x power off failed\n"); - return ret; - } - return 0; -} - static irqreturn_t i2s_irq_handler(int irq, void *dev_id) { + struct i2s_dev_data *rv_i2s_data; u16 play_flag, cap_flag; u32 val; - struct i2s_dev_data *rv_i2s_data = dev_id; + rv_i2s_data = dev_id; if (!rv_i2s_data) return IRQ_NONE; @@ -194,6 +78,13 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id) snd_pcm_period_elapsed(rv_i2s_data->play_stream); play_flag = 1; } + if ((val & BIT(I2S_TX_THRESHOLD)) && + rv_i2s_data->i2ssp_play_stream) { + rv_writel(BIT(I2S_TX_THRESHOLD), + rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT); + snd_pcm_period_elapsed(rv_i2s_data->i2ssp_play_stream); + play_flag = 1; + } if ((val & BIT(BT_RX_THRESHOLD)) && rv_i2s_data->capture_stream) { rv_writel(BIT(BT_RX_THRESHOLD), rv_i2s_data->acp3x_base + @@ -201,6 +92,13 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id) snd_pcm_period_elapsed(rv_i2s_data->capture_stream); cap_flag = 1; } + if ((val & BIT(I2S_RX_THRESHOLD)) && + rv_i2s_data->i2ssp_capture_stream) { + rv_writel(BIT(I2S_RX_THRESHOLD), + rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT); + snd_pcm_period_elapsed(rv_i2s_data->i2ssp_capture_stream); + cap_flag = 1; + } if (play_flag | cap_flag) return IRQ_HANDLED; @@ -211,15 +109,31 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id) static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction) { u16 page_idx; - u32 low, high, val, acp_fifo_addr; - dma_addr_t addr = rtd->dma_addr; + u32 low, high, val, acp_fifo_addr, reg_fifo_addr; + u32 reg_ringbuf_size, reg_dma_size, reg_fifo_size; + dma_addr_t addr; - /* 8 scratch registers used to map one 64 bit address */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - val = 0; - else - val = rtd->num_pages * 8; + addr = rtd->dma_addr; + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + val = ACP_SRAM_BT_PB_PTE_OFFSET; + break; + case I2S_SP_INSTANCE: + default: + val = ACP_SRAM_SP_PB_PTE_OFFSET; + } + } else { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + val = ACP_SRAM_BT_CP_PTE_OFFSET; + break; + case I2S_SP_INSTANCE: + default: + val = ACP_SRAM_SP_CP_PTE_OFFSET; + } + } /* Group Enable */ rv_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp3x_base + mmACPAXI2AXI_ATU_BASE_ADDR_GRP_1); @@ -241,48 +155,77 @@ static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction) } if (direction == SNDRV_PCM_STREAM_PLAYBACK) { - /* Config ringbuffer */ - rv_writel(MEM_WINDOW_START, rtd->acp3x_base + - mmACP_BT_TX_RINGBUFADDR); - rv_writel(MAX_BUFFER, rtd->acp3x_base + - mmACP_BT_TX_RINGBUFSIZE); - rv_writel(DMA_SIZE, rtd->acp3x_base + mmACP_BT_TX_DMA_SIZE); - - /* Config audio fifo */ - acp_fifo_addr = ACP_SRAM_PTE_OFFSET + (rtd->num_pages * 8) - + PLAYBACK_FIFO_ADDR_OFFSET; - rv_writel(acp_fifo_addr, rtd->acp3x_base + - mmACP_BT_TX_FIFOADDR); - rv_writel(FIFO_SIZE, rtd->acp3x_base + mmACP_BT_TX_FIFOSIZE); + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + reg_ringbuf_size = mmACP_BT_TX_RINGBUFSIZE; + reg_dma_size = mmACP_BT_TX_DMA_SIZE; + acp_fifo_addr = ACP_SRAM_PTE_OFFSET + + BT_PB_FIFO_ADDR_OFFSET; + reg_fifo_addr = mmACP_BT_TX_FIFOADDR; + reg_fifo_size = mmACP_BT_TX_FIFOSIZE; + rv_writel(I2S_BT_TX_MEM_WINDOW_START, + rtd->acp3x_base + mmACP_BT_TX_RINGBUFADDR); + break; + + case I2S_SP_INSTANCE: + default: + reg_ringbuf_size = mmACP_I2S_TX_RINGBUFSIZE; + reg_dma_size = mmACP_I2S_TX_DMA_SIZE; + acp_fifo_addr = ACP_SRAM_PTE_OFFSET + + SP_PB_FIFO_ADDR_OFFSET; + reg_fifo_addr = mmACP_I2S_TX_FIFOADDR; + reg_fifo_size = mmACP_I2S_TX_FIFOSIZE; + rv_writel(I2S_SP_TX_MEM_WINDOW_START, + rtd->acp3x_base + mmACP_I2S_TX_RINGBUFADDR); + } } else { - /* Config ringbuffer */ - rv_writel(MEM_WINDOW_START + MAX_BUFFER, rtd->acp3x_base + - mmACP_BT_RX_RINGBUFADDR); - rv_writel(MAX_BUFFER, rtd->acp3x_base + - mmACP_BT_RX_RINGBUFSIZE); - rv_writel(DMA_SIZE, rtd->acp3x_base + mmACP_BT_RX_DMA_SIZE); - - /* Config audio fifo */ - acp_fifo_addr = ACP_SRAM_PTE_OFFSET + - (rtd->num_pages * 8) + CAPTURE_FIFO_ADDR_OFFSET; - rv_writel(acp_fifo_addr, rtd->acp3x_base + - mmACP_BT_RX_FIFOADDR); - rv_writel(FIFO_SIZE, rtd->acp3x_base + mmACP_BT_RX_FIFOSIZE); - } + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + reg_ringbuf_size = mmACP_BT_RX_RINGBUFSIZE; + reg_dma_size = mmACP_BT_RX_DMA_SIZE; + acp_fifo_addr = ACP_SRAM_PTE_OFFSET + + BT_CAPT_FIFO_ADDR_OFFSET; + reg_fifo_addr = mmACP_BT_RX_FIFOADDR; + reg_fifo_size = mmACP_BT_RX_FIFOSIZE; + rv_writel(I2S_BT_RX_MEM_WINDOW_START, + rtd->acp3x_base + mmACP_BT_RX_RINGBUFADDR); + break; - /* Enable watermark/period interrupt to host */ - rv_writel(BIT(BT_TX_THRESHOLD) | BIT(BT_RX_THRESHOLD), - rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL); + case I2S_SP_INSTANCE: + default: + reg_ringbuf_size = mmACP_I2S_RX_RINGBUFSIZE; + reg_dma_size = mmACP_I2S_RX_DMA_SIZE; + acp_fifo_addr = ACP_SRAM_PTE_OFFSET + + SP_CAPT_FIFO_ADDR_OFFSET; + reg_fifo_addr = mmACP_I2S_RX_FIFOADDR; + reg_fifo_size = mmACP_I2S_RX_FIFOSIZE; + rv_writel(I2S_SP_RX_MEM_WINDOW_START, + rtd->acp3x_base + mmACP_I2S_RX_RINGBUFADDR); + } + } + rv_writel(MAX_BUFFER, rtd->acp3x_base + reg_ringbuf_size); + rv_writel(DMA_SIZE, rtd->acp3x_base + reg_dma_size); + rv_writel(acp_fifo_addr, rtd->acp3x_base + reg_fifo_addr); + rv_writel(FIFO_SIZE, rtd->acp3x_base + reg_fifo_size); + rv_writel(BIT(I2S_RX_THRESHOLD) | BIT(BT_RX_THRESHOLD) + | BIT(I2S_TX_THRESHOLD) | BIT(BT_TX_THRESHOLD), + rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL); } static int acp3x_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - int ret = 0; - struct snd_pcm_runtime *runtime = substream->runtime; - struct i2s_dev_data *adata = dev_get_drvdata(component->dev); - struct i2s_stream_instance *i2s_data = kzalloc(sizeof(struct i2s_stream_instance), - GFP_KERNEL); + struct snd_pcm_runtime *runtime; + struct snd_soc_pcm_runtime *prtd; + struct i2s_dev_data *adata; + struct i2s_stream_instance *i2s_data; + int ret; + + runtime = substream->runtime; + prtd = substream->private_data; + component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); + adata = dev_get_drvdata(component->dev); + i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL); if (!i2s_data) return -EINVAL; @@ -299,74 +242,77 @@ static int acp3x_dma_open(struct snd_soc_component *component, return ret; } - if (!adata->play_stream && !adata->capture_stream) + if (!adata->play_stream && !adata->capture_stream && + adata->i2ssp_play_stream && !adata->i2ssp_capture_stream) rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { adata->play_stream = substream; - else + adata->i2ssp_play_stream = substream; + } else { adata->capture_stream = substream; + adata->i2ssp_capture_stream = substream; + } i2s_data->acp3x_base = adata->acp3x_base; runtime->private_data = i2s_data; - return 0; + return ret; } -static u64 acp_get_byte_count(struct i2s_stream_instance *rtd, int direction) -{ - u64 byte_count; - - if (direction == SNDRV_PCM_STREAM_PLAYBACK) { - byte_count = rv_readl(rtd->acp3x_base + - mmACP_BT_TX_LINEARPOSITIONCNTR_HIGH); - byte_count |= rv_readl(rtd->acp3x_base + - mmACP_BT_TX_LINEARPOSITIONCNTR_LOW); - } else { - byte_count = rv_readl(rtd->acp3x_base + - mmACP_BT_RX_LINEARPOSITIONCNTR_HIGH); - byte_count |= rv_readl(rtd->acp3x_base + - mmACP_BT_RX_LINEARPOSITIONCNTR_LOW); - } - return byte_count; -} static int acp3x_dma_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - int status; + struct i2s_stream_instance *rtd; + struct snd_soc_pcm_runtime *prtd; + struct snd_soc_card *card; + struct acp3x_platform_info *pinfo; u64 size; - struct snd_pcm_runtime *runtime = substream->runtime; - struct i2s_stream_instance *rtd = runtime->private_data; + prtd = substream->private_data; + card = prtd->card; + pinfo = snd_soc_card_get_drvdata(card); + rtd = substream->runtime->private_data; if (!rtd) return -EINVAL; + if (pinfo) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rtd->i2s_instance = pinfo->play_i2s_instance; + else + rtd->i2s_instance = pinfo->cap_i2s_instance; + else + pr_err("pinfo failed\n"); + size = params_buffer_bytes(params); - status = snd_pcm_lib_malloc_pages(substream, size); - if (status < 0) - return status; - - memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); - if (substream->dma_buffer.area) { - rtd->dma_addr = substream->dma_buffer.addr; - rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); - config_acp3x_dma(rtd, substream->stream); - status = 0; - } else { - status = -ENOMEM; - } - return status; + rtd->dma_addr = substream->dma_buffer.addr; + rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); + config_acp3x_dma(rtd, substream->stream); + return 0; } static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - u32 pos = 0; - u32 buffersize = 0; - u64 bytescount = 0; - struct i2s_stream_instance *rtd = - substream->runtime->private_data; + struct snd_soc_pcm_runtime *prtd; + struct snd_soc_card *card; + struct acp3x_platform_info *pinfo; + struct i2s_stream_instance *rtd; + u32 pos; + u32 buffersize; + u64 bytescount; + + prtd = substream->private_data; + card = prtd->card; + rtd = substream->runtime->private_data; + pinfo = snd_soc_card_get_drvdata(card); + if (pinfo) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rtd->i2s_instance = pinfo->play_i2s_instance; + else + rtd->i2s_instance = pinfo->cap_i2s_instance; + } buffersize = frames_to_bytes(substream->runtime, substream->runtime->buffer_size); @@ -381,17 +327,11 @@ static int acp3x_dma_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { struct device *parent = component->dev->parent; - snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, - parent, MIN_BUFFER, MAX_BUFFER); + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, + parent, MIN_BUFFER, MAX_BUFFER); return 0; } -static int acp3x_dma_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); -} - static int acp3x_dma_mmap(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct vm_area_struct *vma) @@ -402,206 +342,35 @@ static int acp3x_dma_mmap(struct snd_soc_component *component, static int acp3x_dma_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct i2s_stream_instance *rtd = substream->runtime->private_data; - struct i2s_dev_data *adata = dev_get_drvdata(component->dev); + struct snd_soc_pcm_runtime *prtd; + struct i2s_dev_data *adata; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prtd = substream->private_data; + component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); + adata = dev_get_drvdata(component->dev); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { adata->play_stream = NULL; - else + adata->i2ssp_play_stream = NULL; + } else { adata->capture_stream = NULL; + adata->i2ssp_capture_stream = NULL; + } /* Disable ACP irq, when the current stream is being closed and * another stream is also not active. */ - if (!adata->play_stream && !adata->capture_stream) + if (!adata->play_stream && !adata->capture_stream && + !adata->i2ssp_play_stream && !adata->i2ssp_capture_stream) rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); - kfree(rtd); - return 0; -} - -static int acp3x_dai_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) -{ - - struct i2s_dev_data *adata = snd_soc_dai_get_drvdata(cpu_dai); - - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - adata->tdm_mode = false; - break; - case SND_SOC_DAIFMT_DSP_A: - adata->tdm_mode = true; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int acp3x_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, - u32 rx_mask, int slots, int slot_width) -{ - u32 val = 0; - u16 slot_len; - - struct i2s_dev_data *adata = snd_soc_dai_get_drvdata(cpu_dai); - - switch (slot_width) { - case SLOT_WIDTH_8: - slot_len = 8; - break; - case SLOT_WIDTH_16: - slot_len = 16; - break; - case SLOT_WIDTH_24: - slot_len = 24; - break; - case SLOT_WIDTH_32: - slot_len = 0; - break; - default: - return -EINVAL; - } - - val = rv_readl(adata->acp3x_base + mmACP_BTTDM_ITER); - rv_writel((val | 0x2), adata->acp3x_base + mmACP_BTTDM_ITER); - val = rv_readl(adata->acp3x_base + mmACP_BTTDM_IRER); - rv_writel((val | 0x2), adata->acp3x_base + mmACP_BTTDM_IRER); - - val = (FRM_LEN | (slots << 15) | (slot_len << 18)); - rv_writel(val, adata->acp3x_base + mmACP_BTTDM_TXFRMT); - rv_writel(val, adata->acp3x_base + mmACP_BTTDM_RXFRMT); - - adata->tdm_fmt = val; return 0; } -static int acp3x_dai_i2s_hwparams(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - u32 val = 0; - struct i2s_stream_instance *rtd = substream->runtime->private_data; - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_U8: - case SNDRV_PCM_FORMAT_S8: - rtd->xfer_resolution = 0x0; - break; - case SNDRV_PCM_FORMAT_S16_LE: - rtd->xfer_resolution = 0x02; - break; - case SNDRV_PCM_FORMAT_S24_LE: - rtd->xfer_resolution = 0x04; - break; - case SNDRV_PCM_FORMAT_S32_LE: - rtd->xfer_resolution = 0x05; - break; - default: - return -EINVAL; - } - val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER); - val = val | (rtd->xfer_resolution << 3); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_ITER); - else - rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_IRER); - - return 0; -} - -static int acp3x_dai_i2s_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) -{ - int ret = 0; - struct i2s_stream_instance *rtd = substream->runtime->private_data; - u32 val, period_bytes; - - period_bytes = frames_to_bytes(substream->runtime, - substream->runtime->period_size); - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - rtd->bytescount = acp_get_byte_count(rtd, substream->stream); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - rv_writel(period_bytes, rtd->acp3x_base + - mmACP_BT_TX_INTR_WATERMARK_SIZE); - val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER); - val = val | BIT(0); - rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_ITER); - } else { - rv_writel(period_bytes, rtd->acp3x_base + - mmACP_BT_RX_INTR_WATERMARK_SIZE); - val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER); - val = val | BIT(0); - rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_IRER); - } - rv_writel(1, rtd->acp3x_base + mmACP_BTTDM_IER); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER); - val = val & ~BIT(0); - rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_ITER); - } else { - val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER); - val = val & ~BIT(0); - rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_IRER); - } - rv_writel(0, rtd->acp3x_base + mmACP_BTTDM_IER); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static struct snd_soc_dai_ops acp3x_dai_i2s_ops = { - .hw_params = acp3x_dai_i2s_hwparams, - .trigger = acp3x_dai_i2s_trigger, - .set_fmt = acp3x_dai_i2s_set_fmt, - .set_tdm_slot = acp3x_dai_set_tdm_slot, -}; - -static struct snd_soc_dai_driver acp3x_i2s_dai_driver = { - .playback = { - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | - SNDRV_PCM_FMTBIT_U8 | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - .channels_min = 2, - .channels_max = 8, - - .rate_min = 8000, - .rate_max = 96000, - }, - .capture = { - .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | - SNDRV_PCM_FMTBIT_U8 | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - .channels_min = 2, - .channels_max = 2, - .rate_min = 8000, - .rate_max = 48000, - }, - .ops = &acp3x_dai_i2s_ops, -}; - static const struct snd_soc_component_driver acp3x_i2s_component = { .name = DRV_NAME, .open = acp3x_dma_open, .close = acp3x_dma_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = acp3x_dma_hw_params, - .hw_free = acp3x_dma_hw_free, .pointer = acp3x_dma_pointer, .mmap = acp3x_dma_mmap, .pcm_construct = acp3x_dma_new, @@ -609,10 +378,10 @@ static const struct snd_soc_component_driver acp3x_i2s_component = { static int acp3x_audio_probe(struct platform_device *pdev) { - int status; struct resource *res; struct i2s_dev_data *adata; unsigned int irqflags; + int status; if (!pdev->dev.platform_data) { dev_err(&pdev->dev, "platform_data not retrieved\n"); @@ -622,7 +391,7 @@ static int acp3x_audio_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { - dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); + dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); return -ENODEV; } @@ -632,6 +401,8 @@ static int acp3x_audio_probe(struct platform_device *pdev) adata->acp3x_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!adata->acp3x_base) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { @@ -640,97 +411,81 @@ static int acp3x_audio_probe(struct platform_device *pdev) } adata->i2s_irq = res->start; - adata->play_stream = NULL; - adata->capture_stream = NULL; dev_set_drvdata(&pdev->dev, adata); - /* Initialize ACP */ - status = acp3x_init(adata->acp3x_base); - if (status) - return -ENODEV; status = devm_snd_soc_register_component(&pdev->dev, &acp3x_i2s_component, - &acp3x_i2s_dai_driver, 1); + NULL, 0); if (status) { - dev_err(&pdev->dev, "Fail to register acp i2s dai\n"); - goto dev_err; + dev_err(&pdev->dev, "Fail to register acp i2s component\n"); + return -ENODEV; } status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler, irqflags, "ACP3x_I2S_IRQ", adata); if (status) { dev_err(&pdev->dev, "ACP3x I2S IRQ request failed\n"); - goto dev_err; + return -ENODEV; } - pm_runtime_set_autosuspend_delay(&pdev->dev, 10000); + pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_enable(&pdev->dev); + pm_runtime_allow(&pdev->dev); return 0; -dev_err: - status = acp3x_deinit(adata->acp3x_base); - if (status) - dev_err(&pdev->dev, "ACP de-init failed\n"); - else - dev_info(&pdev->dev, "ACP de-initialized\n"); - /*ignore device status and return driver probe error*/ - return -ENODEV; } static int acp3x_audio_remove(struct platform_device *pdev) { - int ret; - struct i2s_dev_data *adata = dev_get_drvdata(&pdev->dev); - - ret = acp3x_deinit(adata->acp3x_base); - if (ret) - dev_err(&pdev->dev, "ACP de-init failed\n"); - else - dev_info(&pdev->dev, "ACP de-initialized\n"); - pm_runtime_disable(&pdev->dev); return 0; } static int acp3x_resume(struct device *dev) { - int status; - u32 val; - struct i2s_dev_data *adata = dev_get_drvdata(dev); + struct i2s_dev_data *adata; + u32 val, reg_val, frmt_val; - status = acp3x_init(adata->acp3x_base); - if (status) - return -ENODEV; + reg_val = 0; + frmt_val = 0; + adata = dev_get_drvdata(dev); if (adata->play_stream && adata->play_stream->runtime) { struct i2s_stream_instance *rtd = adata->play_stream->runtime->private_data; config_acp3x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK); - rv_writel((rtd->xfer_resolution << 3), - rtd->acp3x_base + mmACP_BTTDM_ITER); - if (adata->tdm_mode == true) { - rv_writel(adata->tdm_fmt, adata->acp3x_base + - mmACP_BTTDM_TXFRMT); - val = rv_readl(adata->acp3x_base + mmACP_BTTDM_ITER); - rv_writel((val | 0x2), adata->acp3x_base + - mmACP_BTTDM_ITER); + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + reg_val = mmACP_BTTDM_ITER; + frmt_val = mmACP_BTTDM_TXFRMT; + break; + case I2S_SP_INSTANCE: + default: + reg_val = mmACP_I2STDM_ITER; + frmt_val = mmACP_I2STDM_TXFRMT; } + rv_writel((rtd->xfer_resolution << 3), rtd->acp3x_base + reg_val); } - if (adata->capture_stream && adata->capture_stream->runtime) { struct i2s_stream_instance *rtd = adata->capture_stream->runtime->private_data; config_acp3x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE); - rv_writel((rtd->xfer_resolution << 3), - rtd->acp3x_base + mmACP_BTTDM_IRER); - if (adata->tdm_mode == true) { - rv_writel(adata->tdm_fmt, adata->acp3x_base + - mmACP_BTTDM_RXFRMT); - val = rv_readl(adata->acp3x_base + mmACP_BTTDM_IRER); - rv_writel((val | 0x2), adata->acp3x_base + - mmACP_BTTDM_IRER); + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + reg_val = mmACP_BTTDM_IRER; + frmt_val = mmACP_BTTDM_RXFRMT; + break; + case I2S_SP_INSTANCE: + default: + reg_val = mmACP_I2STDM_IRER; + frmt_val = mmACP_I2STDM_RXFRMT; } + rv_writel((rtd->xfer_resolution << 3), rtd->acp3x_base + reg_val); + } + if (adata->tdm_mode == TDM_ENABLE) { + rv_writel(adata->tdm_fmt, adata->acp3x_base + frmt_val); + val = rv_readl(adata->acp3x_base + reg_val); + rv_writel(val | 0x2, adata->acp3x_base + reg_val); } - rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); return 0; } @@ -738,14 +493,9 @@ static int acp3x_resume(struct device *dev) static int acp3x_pcm_runtime_suspend(struct device *dev) { - int status; - struct i2s_dev_data *adata = dev_get_drvdata(dev); + struct i2s_dev_data *adata; - status = acp3x_deinit(adata->acp3x_base); - if (status) - dev_err(dev, "ACP de-init failed\n"); - else - dev_info(dev, "ACP de-initialized\n"); + adata = dev_get_drvdata(dev); rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); @@ -754,12 +504,10 @@ static int acp3x_pcm_runtime_suspend(struct device *dev) static int acp3x_pcm_runtime_resume(struct device *dev) { - int status; - struct i2s_dev_data *adata = dev_get_drvdata(dev); + struct i2s_dev_data *adata; + + adata = dev_get_drvdata(dev); - status = acp3x_init(adata->acp3x_base); - if (status) - return -ENODEV; rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); return 0; } @@ -774,13 +522,14 @@ static struct platform_driver acp3x_dma_driver = { .probe = acp3x_audio_probe, .remove = acp3x_audio_remove, .driver = { - .name = "acp3x_rv_i2s", + .name = "acp3x_rv_i2s_dma", .pm = &acp3x_pm_ops, }, }; module_platform_driver(acp3x_dma_driver); +MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com"); MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com"); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver"); diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h index 4f2cadd90a87..21e7ac017f2b 100644 --- a/sound/soc/amd/raven/acp3x.h +++ b/sound/soc/amd/raven/acp3x.h @@ -6,14 +6,28 @@ */ #include "chip_offset_byte.h" +#include <sound/pcm.h> +#define I2S_SP_INSTANCE 0x01 +#define I2S_BT_INSTANCE 0x02 +#define TDM_ENABLE 1 +#define TDM_DISABLE 0 + +#define ACP3x_DEVS 4 #define ACP3x_PHY_BASE_ADDRESS 0x1240000 #define ACP3x_I2S_MODE 0 #define ACP3x_REG_START 0x1240000 #define ACP3x_REG_END 0x1250200 +#define ACP3x_I2STDM_REG_START 0x1242400 +#define ACP3x_I2STDM_REG_END 0x1242410 +#define ACP3x_BT_TDM_REG_START 0x1242800 +#define ACP3x_BT_TDM_REG_END 0x1242810 #define I2S_MODE 0x04 +#define I2S_RX_THRESHOLD 27 +#define I2S_TX_THRESHOLD 28 #define BT_TX_THRESHOLD 26 #define BT_RX_THRESHOLD 25 +#define ACP_ERR_INTR_MASK 29 #define ACP3x_POWER_ON 0x00 #define ACP3x_POWER_ON_IN_PROGRESS 0x01 #define ACP3x_POWER_OFF 0x02 @@ -21,19 +35,28 @@ #define ACP3x_SOFT_RESET__SoftResetAudDone_MASK 0x00010001 #define ACP_SRAM_PTE_OFFSET 0x02050000 +#define ACP_SRAM_SP_PB_PTE_OFFSET 0x0 +#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100 +#define ACP_SRAM_BT_PB_PTE_OFFSET 0x200 +#define ACP_SRAM_BT_CP_PTE_OFFSET 0x300 #define PAGE_SIZE_4K_ENABLE 0x2 -#define MEM_WINDOW_START 0x4000000 -#define PLAYBACK_FIFO_ADDR_OFFSET 0x400 -#define CAPTURE_FIFO_ADDR_OFFSET 0x500 +#define I2S_SP_TX_MEM_WINDOW_START 0x4000000 +#define I2S_SP_RX_MEM_WINDOW_START 0x4020000 +#define I2S_BT_TX_MEM_WINDOW_START 0x4040000 +#define I2S_BT_RX_MEM_WINDOW_START 0x4060000 +#define SP_PB_FIFO_ADDR_OFFSET 0x500 +#define SP_CAPT_FIFO_ADDR_OFFSET 0x700 +#define BT_PB_FIFO_ADDR_OFFSET 0x900 +#define BT_CAPT_FIFO_ADDR_OFFSET 0xB00 #define PLAYBACK_MIN_NUM_PERIODS 2 #define PLAYBACK_MAX_NUM_PERIODS 8 -#define PLAYBACK_MAX_PERIOD_SIZE 16384 -#define PLAYBACK_MIN_PERIOD_SIZE 4096 +#define PLAYBACK_MAX_PERIOD_SIZE 8192 +#define PLAYBACK_MIN_PERIOD_SIZE 1024 #define CAPTURE_MIN_NUM_PERIODS 2 #define CAPTURE_MAX_NUM_PERIODS 8 -#define CAPTURE_MAX_PERIOD_SIZE 16384 -#define CAPTURE_MIN_PERIOD_SIZE 4096 +#define CAPTURE_MAX_PERIOD_SIZE 8192 +#define CAPTURE_MIN_PERIOD_SIZE 1024 #define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS) #define MIN_BUFFER MAX_BUFFER @@ -45,7 +68,45 @@ #define SLOT_WIDTH_16 0x10 #define SLOT_WIDTH_24 0x18 #define SLOT_WIDTH_32 0x20 +#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01 +#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00 +#define ACP_PGFSM_STATUS_MASK 0x03 +#define ACP_POWERED_ON 0x00 +#define ACP_POWER_ON_IN_PROGRESS 0x01 +#define ACP_POWERED_OFF 0x02 +#define ACP_POWER_OFF_IN_PROGRESS 0x03 + +struct acp3x_platform_info { + u16 play_i2s_instance; + u16 cap_i2s_instance; + u16 capture_channel; +}; +struct i2s_dev_data { + bool tdm_mode; + unsigned int i2s_irq; + u16 i2s_instance; + u32 tdm_fmt; + u32 substream_type; + void __iomem *acp3x_base; + struct snd_pcm_substream *play_stream; + struct snd_pcm_substream *capture_stream; + struct snd_pcm_substream *i2ssp_play_stream; + struct snd_pcm_substream *i2ssp_capture_stream; +}; + +struct i2s_stream_instance { + u16 num_pages; + u16 i2s_instance; + u16 capture_channel; + u16 direction; + u16 channels; + u32 xfer_resolution; + u32 val; + dma_addr_t dma_addr; + u64 bytescount; + void __iomem *acp3x_base; +}; static inline u32 rv_readl(void __iomem *base_addr) { @@ -56,3 +117,43 @@ static inline void rv_writel(u32 val, void __iomem *base_addr) { writel(val, base_addr - ACP3x_PHY_BASE_ADDRESS); } + +static inline u64 acp_get_byte_count(struct i2s_stream_instance *rtd, + int direction) +{ + u64 byte_count; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + byte_count = rv_readl(rtd->acp3x_base + + mmACP_BT_TX_LINEARPOSITIONCNTR_HIGH); + byte_count |= rv_readl(rtd->acp3x_base + + mmACP_BT_TX_LINEARPOSITIONCNTR_LOW); + break; + case I2S_SP_INSTANCE: + default: + byte_count = rv_readl(rtd->acp3x_base + + mmACP_I2S_TX_LINEARPOSITIONCNTR_HIGH); + byte_count |= rv_readl(rtd->acp3x_base + + mmACP_I2S_TX_LINEARPOSITIONCNTR_LOW); + } + + } else { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + byte_count = rv_readl(rtd->acp3x_base + + mmACP_BT_RX_LINEARPOSITIONCNTR_HIGH); + byte_count |= rv_readl(rtd->acp3x_base + + mmACP_BT_RX_LINEARPOSITIONCNTR_LOW); + break; + case I2S_SP_INSTANCE: + default: + byte_count = rv_readl(rtd->acp3x_base + + mmACP_I2S_RX_LINEARPOSITIONCNTR_HIGH); + byte_count |= rv_readl(rtd->acp3x_base + + mmACP_I2S_RX_LINEARPOSITIONCNTR_LOW); + } + } + return byte_count; +} diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c index facec2472b34..65330bb50e74 100644 --- a/sound/soc/amd/raven/pci-acp3x.c +++ b/sound/soc/amd/raven/pci-acp3x.c @@ -9,6 +9,8 @@ #include <linux/io.h> #include <linux/platform_device.h> #include <linux/interrupt.h> +#include <linux/pm_runtime.h> +#include <linux/delay.h> #include "acp3x.h" @@ -16,17 +18,120 @@ struct acp3x_dev_data { void __iomem *acp3x_base; bool acp3x_audio_mode; struct resource *res; - struct platform_device *pdev; + struct platform_device *pdev[ACP3x_DEVS]; }; +static int acp3x_power_on(void __iomem *acp3x_base) +{ + u32 val; + int timeout; + + val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS); + + if (val == 0) + return val; + + if (!((val & ACP_PGFSM_STATUS_MASK) == + ACP_POWER_ON_IN_PROGRESS)) + rv_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, + acp3x_base + mmACP_PGFSM_CONTROL); + timeout = 0; + while (++timeout < 500) { + val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS); + if (!val) + return 0; + udelay(1); + } + return -ETIMEDOUT; +} + +static int acp3x_power_off(void __iomem *acp3x_base) +{ + u32 val; + int timeout; + + rv_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK, + acp3x_base + mmACP_PGFSM_CONTROL); + timeout = 0; + while (++timeout < 500) { + val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS); + if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF) + return 0; + udelay(1); + } + return -ETIMEDOUT; +} + +static int acp3x_reset(void __iomem *acp3x_base) +{ + u32 val; + int timeout; + + rv_writel(1, acp3x_base + mmACP_SOFT_RESET); + timeout = 0; + while (++timeout < 500) { + val = rv_readl(acp3x_base + mmACP_SOFT_RESET); + if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK) + break; + cpu_relax(); + } + rv_writel(0, acp3x_base + mmACP_SOFT_RESET); + timeout = 0; + while (++timeout < 500) { + val = rv_readl(acp3x_base + mmACP_SOFT_RESET); + if (!val) + return 0; + cpu_relax(); + } + return -ETIMEDOUT; +} + +static int acp3x_init(void __iomem *acp3x_base) +{ + int ret; + + /* power on */ + ret = acp3x_power_on(acp3x_base); + if (ret) { + pr_err("ACP3x power on failed\n"); + return ret; + } + /* Reset */ + ret = acp3x_reset(acp3x_base); + if (ret) { + pr_err("ACP3x reset failed\n"); + return ret; + } + return 0; +} + +static int acp3x_deinit(void __iomem *acp3x_base) +{ + int ret; + + /* Reset */ + ret = acp3x_reset(acp3x_base); + if (ret) { + pr_err("ACP3x reset failed\n"); + return ret; + } + /* power off */ + ret = acp3x_power_off(acp3x_base); + if (ret) { + pr_err("ACP3x power off failed\n"); + return ret; + } + return 0; +} + static int snd_acp3x_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { - int ret; - u32 addr, val; struct acp3x_dev_data *adata; - struct platform_device_info pdevinfo; + struct platform_device_info pdevinfo[ACP3x_DEVS]; unsigned int irqflags; + int ret, i; + u32 addr, val; if (pci_enable_device(pci)) { dev_err(&pci->dev, "pci_enable_device failed\n"); @@ -56,23 +161,27 @@ static int snd_acp3x_probe(struct pci_dev *pci, irqflags = 0; addr = pci_resource_start(pci, 0); - adata->acp3x_base = ioremap(addr, pci_resource_len(pci, 0)); + adata->acp3x_base = devm_ioremap(&pci->dev, addr, + pci_resource_len(pci, 0)); if (!adata->acp3x_base) { ret = -ENOMEM; - goto release_regions; + goto disable_msi; } pci_set_master(pci); pci_set_drvdata(pci, adata); + ret = acp3x_init(adata->acp3x_base); + if (ret) + goto disable_msi; val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG); switch (val) { case I2S_MODE: adata->res = devm_kzalloc(&pci->dev, - sizeof(struct resource) * 2, + sizeof(struct resource) * 4, GFP_KERNEL); if (!adata->res) { ret = -ENOMEM; - goto unmap_mmio; + goto de_init; } adata->res[0].name = "acp3x_i2s_iomem"; @@ -80,40 +189,82 @@ static int snd_acp3x_probe(struct pci_dev *pci, adata->res[0].start = addr; adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START); - adata->res[1].name = "acp3x_i2s_irq"; - adata->res[1].flags = IORESOURCE_IRQ; - adata->res[1].start = pci->irq; - adata->res[1].end = pci->irq; + adata->res[1].name = "acp3x_i2s_sp"; + adata->res[1].flags = IORESOURCE_MEM; + adata->res[1].start = addr + ACP3x_I2STDM_REG_START; + adata->res[1].end = addr + ACP3x_I2STDM_REG_END; + + adata->res[2].name = "acp3x_i2s_bt"; + adata->res[2].flags = IORESOURCE_MEM; + adata->res[2].start = addr + ACP3x_BT_TDM_REG_START; + adata->res[2].end = addr + ACP3x_BT_TDM_REG_END; + + adata->res[3].name = "acp3x_i2s_irq"; + adata->res[3].flags = IORESOURCE_IRQ; + adata->res[3].start = pci->irq; + adata->res[3].end = adata->res[3].start; adata->acp3x_audio_mode = ACP3x_I2S_MODE; memset(&pdevinfo, 0, sizeof(pdevinfo)); - pdevinfo.name = "acp3x_rv_i2s"; - pdevinfo.id = 0; - pdevinfo.parent = &pci->dev; - pdevinfo.num_res = 2; - pdevinfo.res = adata->res; - pdevinfo.data = &irqflags; - pdevinfo.size_data = sizeof(irqflags); - - adata->pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(adata->pdev)) { - dev_err(&pci->dev, "cannot register %s device\n", - pdevinfo.name); - ret = PTR_ERR(adata->pdev); - goto unmap_mmio; + pdevinfo[0].name = "acp3x_rv_i2s_dma"; + pdevinfo[0].id = 0; + pdevinfo[0].parent = &pci->dev; + pdevinfo[0].num_res = 4; + pdevinfo[0].res = &adata->res[0]; + pdevinfo[0].data = &irqflags; + pdevinfo[0].size_data = sizeof(irqflags); + + pdevinfo[1].name = "acp3x_i2s_playcap"; + pdevinfo[1].id = 0; + pdevinfo[1].parent = &pci->dev; + pdevinfo[1].num_res = 1; + pdevinfo[1].res = &adata->res[1]; + + pdevinfo[2].name = "acp3x_i2s_playcap"; + pdevinfo[2].id = 1; + pdevinfo[2].parent = &pci->dev; + pdevinfo[2].num_res = 1; + pdevinfo[2].res = &adata->res[1]; + + pdevinfo[3].name = "acp3x_i2s_playcap"; + pdevinfo[3].id = 2; + pdevinfo[3].parent = &pci->dev; + pdevinfo[3].num_res = 1; + pdevinfo[3].res = &adata->res[2]; + for (i = 0; i < ACP3x_DEVS; i++) { + adata->pdev[i] = + platform_device_register_full(&pdevinfo[i]); + if (IS_ERR(adata->pdev[i])) { + dev_err(&pci->dev, "cannot register %s device\n", + pdevinfo[i].name); + ret = PTR_ERR(adata->pdev[i]); + goto unregister_devs; + } } break; default: dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val); ret = -ENODEV; - goto unmap_mmio; + goto disable_msi; } + pm_runtime_set_autosuspend_delay(&pci->dev, 2000); + pm_runtime_use_autosuspend(&pci->dev); + pm_runtime_set_active(&pci->dev); + pm_runtime_put_noidle(&pci->dev); + pm_runtime_enable(&pci->dev); + pm_runtime_allow(&pci->dev); return 0; -unmap_mmio: +unregister_devs: + if (val == I2S_MODE) + for (i = 0; i < ACP3x_DEVS; i++) + platform_device_unregister(adata->pdev[i]); +de_init: + if (acp3x_deinit(adata->acp3x_base)) + dev_err(&pci->dev, "ACP de-init failed\n"); +disable_msi: pci_disable_msi(pci); - iounmap(adata->acp3x_base); release_regions: pci_release_regions(pci); disable_pci: @@ -122,13 +273,56 @@ disable_pci: return ret; } -static void snd_acp3x_remove(struct pci_dev *pci) +static int snd_acp3x_suspend(struct device *dev) +{ + int ret; + struct acp3x_dev_data *adata; + + adata = dev_get_drvdata(dev); + ret = acp3x_deinit(adata->acp3x_base); + if (ret) + dev_err(dev, "ACP de-init failed\n"); + else + dev_dbg(dev, "ACP de-initialized\n"); + + return 0; +} + +static int snd_acp3x_resume(struct device *dev) { - struct acp3x_dev_data *adata = pci_get_drvdata(pci); + int ret; + struct acp3x_dev_data *adata; - platform_device_unregister(adata->pdev); - iounmap(adata->acp3x_base); + adata = dev_get_drvdata(dev); + ret = acp3x_init(adata->acp3x_base); + if (ret) { + dev_err(dev, "ACP init failed\n"); + return ret; + } + return 0; +} +static const struct dev_pm_ops acp3x_pm = { + .runtime_suspend = snd_acp3x_suspend, + .runtime_resume = snd_acp3x_resume, + .resume = snd_acp3x_resume, +}; + +static void snd_acp3x_remove(struct pci_dev *pci) +{ + struct acp3x_dev_data *adata; + int i, ret; + + adata = pci_get_drvdata(pci); + if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) { + for (i = 0; i < ACP3x_DEVS; i++) + platform_device_unregister(adata->pdev[i]); + } + ret = acp3x_deinit(adata->acp3x_base); + if (ret) + dev_err(&pci->dev, "ACP de-init failed\n"); + pm_runtime_disable(&pci->dev); + pm_runtime_get_noresume(&pci->dev); pci_disable_msi(pci); pci_release_regions(pci); pci_disable_device(pci); @@ -147,10 +341,14 @@ static struct pci_driver acp3x_driver = { .id_table = snd_acp3x_ids, .probe = snd_acp3x_probe, .remove = snd_acp3x_remove, + .driver = { + .pm = &acp3x_pm, + } }; module_pci_driver(acp3x_driver); +MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com"); MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com"); MODULE_DESCRIPTION("AMD ACP3x PCI driver"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index f118c229ed82..d1dc8e6366dc 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -19,6 +19,8 @@ config SND_ATMEL_SOC_DMA config SND_ATMEL_SOC_SSC tristate + select SND_ATMEL_SOC_DMA + select SND_ATMEL_SOC_PDC config SND_ATMEL_SOC_SSC_PDC tristate "SoC PCM DAI support for AT91 SSC controller using PDC" diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c index 18a2fd02fffe..59c1331a6984 100644 --- a/sound/soc/atmel/atmel-pcm-pdc.c +++ b/sound/soc/atmel/atmel-pcm-pdc.c @@ -379,7 +379,6 @@ static int atmel_pcm_close(struct snd_soc_component *component, static const struct snd_soc_component_driver atmel_soc_platform = { .open = atmel_pcm_open, .close = atmel_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = atmel_pcm_hw_params, .hw_free = atmel_pcm_hw_free, .prepare = atmel_pcm_prepare, diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index ca603397651c..1073f468f21f 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -760,12 +760,12 @@ static int atmel_ssc_trigger(struct snd_pcm_substream *substream, } #ifdef CONFIG_PM -static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai) +static int atmel_ssc_suspend(struct snd_soc_component *component) { struct atmel_ssc_info *ssc_p; - struct platform_device *pdev = to_platform_device(cpu_dai->dev); + struct platform_device *pdev = to_platform_device(component->dev); - if (!cpu_dai->active) + if (!component->active) return 0; ssc_p = &ssc_info[pdev->id]; @@ -787,15 +787,13 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai) return 0; } - - -static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) +static int atmel_ssc_resume(struct snd_soc_component *component) { struct atmel_ssc_info *ssc_p; - struct platform_device *pdev = to_platform_device(cpu_dai->dev); + struct platform_device *pdev = to_platform_device(component->dev); u32 cr; - if (!cpu_dai->active) + if (!component->active) return 0; ssc_p = &ssc_info[pdev->id]; @@ -839,8 +837,6 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { }; static struct snd_soc_dai_driver atmel_ssc_dai = { - .suspend = atmel_ssc_suspend, - .resume = atmel_ssc_resume, .playback = { .channels_min = 1, .channels_max = 2, @@ -860,6 +856,8 @@ static struct snd_soc_dai_driver atmel_ssc_dai = { static const struct snd_soc_component_driver atmel_ssc_component = { .name = "atmel-ssc", + .suspend = atmel_ssc_suspend, + .resume = atmel_ssc_resume, }; static int asoc_ssc_init(struct device *dev) diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c index 0792c40e6cc1..73c6a0edb3d8 100644 --- a/sound/soc/au1x/ac97c.c +++ b/sound/soc/au1x/ac97c.c @@ -206,7 +206,6 @@ static int au1xac97c_dai_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver au1xac97c_dai_driver = { .name = "alchemy-ac97c", - .bus_control = true, .probe = au1xac97c_dai_probe, .playback = { .rates = AC97_RATES, diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index 4553108ec92a..8f855644c6b4 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -197,10 +197,6 @@ static int au1xpsc_pcm_hw_params(struct snd_soc_component *component, struct au1xpsc_audio_dmadata *pcd; int stype, ret; - ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); - if (ret < 0) - goto out; - stype = substream->stream; pcd = to_dmadata(substream, component); @@ -232,13 +228,6 @@ out: return ret; } -static int au1xpsc_pcm_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - snd_pcm_lib_free_pages(substream); - return 0; -} - static int au1xpsc_pcm_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -315,7 +304,7 @@ static int au1xpsc_pcm_new(struct snd_soc_component *component, struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1); return 0; @@ -326,9 +315,7 @@ static struct snd_soc_component_driver au1xpsc_soc_component = { .name = DRV_NAME, .open = au1xpsc_pcm_open, .close = au1xpsc_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = au1xpsc_pcm_hw_params, - .hw_free = au1xpsc_pcm_hw_free, .prepare = au1xpsc_pcm_prepare, .trigger = au1xpsc_pcm_trigger, .pointer = au1xpsc_pcm_pointer, diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c index 054dfda89d3e..c9a038a5e2d3 100644 --- a/sound/soc/au1x/dma.c +++ b/sound/soc/au1x/dma.c @@ -231,19 +231,10 @@ static int alchemy_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_hw_params *hw_params) { struct audio_stream *stream = ss_to_as(substream, component); - int err; - - err = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; - err = au1000_setup_dma_link(stream, - params_period_bytes(hw_params), - params_periods(hw_params)); - if (err) - snd_pcm_lib_free_pages(substream); - return err; + return au1000_setup_dma_link(stream, + params_period_bytes(hw_params), + params_periods(hw_params)); } static int alchemy_pcm_hw_free(struct snd_soc_component *component, @@ -251,7 +242,7 @@ static int alchemy_pcm_hw_free(struct snd_soc_component *component, { struct audio_stream *stream = ss_to_as(substream, component); au1000_release_dma_link(stream); - return snd_pcm_lib_free_pages(substream); + return 0; } static int alchemy_pcm_trigger(struct snd_soc_component *component, @@ -292,8 +283,8 @@ static int alchemy_pcm_new(struct snd_soc_component *component, { struct snd_pcm *pcm = rtd->pcm; - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - NULL, 65536, (4096 * 1024) - 1); + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + NULL, 65536, (4096 * 1024) - 1); return 0; } @@ -302,7 +293,6 @@ static struct snd_soc_component_driver alchemy_pcm_soc_component = { .name = DRV_NAME, .open = alchemy_pcm_open, .close = alchemy_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = alchemy_pcm_hw_params, .hw_free = alchemy_pcm_hw_free, .trigger = alchemy_pcm_trigger, diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 08bc04e2da2a..0227993c5da8 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -339,7 +339,6 @@ static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { }; static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = { - .bus_control = true, .probe = au1xpsc_ac97_probe, .playback = { .rates = AC97_RATES, diff --git a/sound/soc/bcm/cygnus-pcm.c b/sound/soc/bcm/cygnus-pcm.c index c65408085c1d..3a80c613bc3f 100644 --- a/sound/soc/bcm/cygnus-pcm.c +++ b/sound/soc/bcm/cygnus-pcm.c @@ -818,7 +818,6 @@ static int cygnus_dma_new(struct snd_soc_component *component, static struct snd_soc_component_driver cygnus_soc_platform = { .open = cygnus_pcm_open, .close = cygnus_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = cygnus_pcm_hw_params, .hw_free = cygnus_pcm_hw_free, .prepare = cygnus_pcm_prepare, diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c index 2f9357d7da96..257f5048061e 100644 --- a/sound/soc/bcm/cygnus-ssp.c +++ b/sound/soc/bcm/cygnus-ssp.c @@ -1052,10 +1052,13 @@ static int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, } #ifdef CONFIG_PM_SLEEP -static int cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai) +static int __cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai) { struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); + if (!cpu_dai->active) + return 0; + if (!aio->is_slave) { u32 val; @@ -1078,11 +1081,25 @@ static int cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai) return 0; } -static int cygnus_ssp_resume(struct snd_soc_dai *cpu_dai) +static int cygnus_ssp_suspend(struct snd_soc_component *component) +{ + struct snd_soc_dai *dai; + int ret = 0; + + for_each_component_dais(component, dai) + ret |= __cygnus_ssp_suspend(dai); + + return ret; +} + +static int __cygnus_ssp_resume(struct snd_soc_dai *cpu_dai) { struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai); int error; + if (!cpu_dai->active) + return 0; + if (!aio->is_slave) { if (aio->clk_trace.cap_clk_en) { error = clk_prepare_enable(aio->cygaud-> @@ -1109,6 +1126,18 @@ static int cygnus_ssp_resume(struct snd_soc_dai *cpu_dai) return 0; } + +static int cygnus_ssp_resume(struct snd_soc_component *component) +{ + struct snd_soc_dai *dai; + int ret = 0; + + for_each_component_dais(component, dai) + ret |= __cygnus_ssp_resume(dai); + + return ret; +} + #else #define cygnus_ssp_suspend NULL #define cygnus_ssp_resume NULL @@ -1149,8 +1178,6 @@ static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = { SNDRV_PCM_FMTBIT_S32_LE, \ }, \ .ops = &cygnus_ssp_dai_ops, \ - .suspend = cygnus_ssp_suspend, \ - .resume = cygnus_ssp_resume, \ } static const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = { @@ -1169,14 +1196,14 @@ static const struct snd_soc_dai_driver cygnus_spdif_dai_info = { SNDRV_PCM_FMTBIT_S32_LE, }, .ops = &cygnus_spdif_dai_ops, - .suspend = cygnus_ssp_suspend, - .resume = cygnus_ssp_resume, }; static struct snd_soc_dai_driver cygnus_ssp_dai[CYGNUS_MAX_PORTS]; static const struct snd_soc_component_driver cygnus_ssp_component = { .name = "cygnus-audio", + .suspend = cygnus_ssp_suspend, + .resume = cygnus_ssp_resume, }; /* diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c index e21eaa1893d1..1c45fb9ff990 100644 --- a/sound/soc/cirrus/ep93xx-ac97.c +++ b/sound/soc/cirrus/ep93xx-ac97.c @@ -336,7 +336,6 @@ static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = { static struct snd_soc_dai_driver ep93xx_ac97_dai = { .name = "ep93xx-ac97", .id = 0, - .bus_control = true, .probe = ep93xx_ac97_dai_probe, .playback = { .stream_name = "AC97 Playback", diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 7d9cf67129d4..723f4cf19467 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -364,11 +364,11 @@ static int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, } #ifdef CONFIG_PM -static int ep93xx_i2s_suspend(struct snd_soc_dai *dai) +static int ep93xx_i2s_suspend(struct snd_soc_component *component) { - struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); + struct ep93xx_i2s_info *info = snd_soc_component_get_drvdata(component); - if (!dai->active) + if (!component->active) return 0; ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK); @@ -377,11 +377,11 @@ static int ep93xx_i2s_suspend(struct snd_soc_dai *dai) return 0; } -static int ep93xx_i2s_resume(struct snd_soc_dai *dai) +static int ep93xx_i2s_resume(struct snd_soc_component *component) { - struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); + struct ep93xx_i2s_info *info = snd_soc_component_get_drvdata(component); - if (!dai->active) + if (!component->active) return 0; ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK); @@ -406,8 +406,6 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { static struct snd_soc_dai_driver ep93xx_i2s_dai = { .symmetric_rates= 1, .probe = ep93xx_i2s_dai_probe, - .suspend = ep93xx_i2s_suspend, - .resume = ep93xx_i2s_resume, .playback = { .channels_min = 2, .channels_max = 2, @@ -425,6 +423,8 @@ static struct snd_soc_dai_driver ep93xx_i2s_dai = { static const struct snd_soc_component_driver ep93xx_i2s_component = { .name = "ep93xx-i2s", + .suspend = ep93xx_i2s_suspend, + .resume = ep93xx_i2s_resume, }; static int ep93xx_i2s_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 4abf37b5083f..c9eb683bd1b0 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -101,6 +101,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ISABELLE if I2C select SND_SOC_JZ4740_CODEC select SND_SOC_JZ4725B_CODEC + select SND_SOC_JZ4770_CODEC select SND_SOC_LM4857 if I2C select SND_SOC_LM49453 if I2C select SND_SOC_LOCHNAGAR_SC if MFD_LOCHNAGAR @@ -124,6 +125,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ML26124 if I2C select SND_SOC_MT6351 if MTK_PMIC_WRAP select SND_SOC_MT6358 if MTK_PMIC_WRAP + select SND_SOC_MT6660 if I2C select SND_SOC_NAU8540 if I2C select SND_SOC_NAU8810 if I2C select SND_SOC_NAU8822 if I2C @@ -149,6 +151,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_RT286 if I2C select SND_SOC_RT298 if I2C select SND_SOC_RT1011 if I2C + select SND_SOC_RT1015 if I2C select SND_SOC_RT1305 if I2C select SND_SOC_RT1308 if I2C select SND_SOC_RT5514 if I2C @@ -165,6 +168,10 @@ config SND_SOC_ALL_CODECS select SND_SOC_RT5670 if I2C select SND_SOC_RT5677 if I2C && SPI_MASTER select SND_SOC_RT5682 if I2C + select SND_SOC_RT700_SDW if SOUNDWIRE + select SND_SOC_RT711_SDW if SOUNDWIRE + select SND_SOC_RT715_SDW if SOUNDWIRE + select SND_SOC_RT1308_SDW if SOUNDWIRE select SND_SOC_SGTL5000 if I2C select SND_SOC_SI476X if MFD_SI476X_CORE select SND_SOC_SIMPLE_AMPLIFIER @@ -207,6 +214,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_UDA134X select SND_SOC_UDA1380 if I2C select SND_SOC_WCD9335 if SLIMBUS + select SND_SOC_WCD934X if MFD_WCD934X select SND_SOC_WL1273 if MFD_WL1273_CORE select SND_SOC_WM0010 if SPI_MASTER select SND_SOC_WM1250_EV1 if I2C @@ -261,6 +269,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) + select SND_SOC_WSA881X if SOUNDWIRE help Normally ASoC codec drivers are only built if a machine driver which uses them is also built since they are only usable with a machine @@ -672,6 +681,17 @@ config SND_SOC_JZ4725B_CODEC This driver can also be built as a module. If so, the module will be called snd-soc-jz4725b-codec. +config SND_SOC_JZ4770_CODEC + depends on MIPS || COMPILE_TEST + select REGMAP + tristate "Ingenic JZ4770 internal CODEC" + help + Enable support for the internal CODEC found in the JZ4770 SoC + from Ingenic. + + This driver can also be built as a module. If so, the module + will be called snd-soc-jz4770-codec. + config SND_SOC_L3 tristate @@ -951,6 +971,7 @@ config SND_SOC_RL6231 default y if SND_SOC_RT5677=y default y if SND_SOC_RT5682=y default y if SND_SOC_RT1011=y + default y if SND_SOC_RT1015=y default y if SND_SOC_RT1305=y default y if SND_SOC_RT1308=y default m if SND_SOC_RT5514=m @@ -967,6 +988,7 @@ config SND_SOC_RL6231 default m if SND_SOC_RT5677=m default m if SND_SOC_RT5682=m default m if SND_SOC_RT1011=m + default m if SND_SOC_RT1015=m default m if SND_SOC_RT1305=m default m if SND_SOC_RT1308=m @@ -994,12 +1016,20 @@ config SND_SOC_RT298 config SND_SOC_RT1011 tristate +config SND_SOC_RT1015 + tristate + config SND_SOC_RT1305 tristate config SND_SOC_RT1308 tristate +config SND_SOC_RT1308_SDW + tristate "Realtek RT1308 Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + config SND_SOC_RT5514 tristate @@ -1057,6 +1087,33 @@ config SND_SOC_RT5677_SPI config SND_SOC_RT5682 tristate +config SND_SOC_RT700 + tristate + +config SND_SOC_RT700_SDW + tristate "Realtek RT700 Codec - SDW" + depends on SOUNDWIRE + select SND_SOC_RT700 + select REGMAP_SOUNDWIRE + +config SND_SOC_RT711 + tristate + +config SND_SOC_RT711_SDW + tristate "Realtek RT711 Codec - SDW" + depends on SOUNDWIRE + select SND_SOC_RT711 + select REGMAP_SOUNDWIRE + +config SND_SOC_RT715 + tristate + +config SND_SOC_RT715_SDW + tristate "Realtek RT715 Codec - SDW" + depends on SOUNDWIRE + select SND_SOC_RT715 + select REGMAP_SOUNDWIRE + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" @@ -1275,6 +1332,13 @@ config SND_SOC_WCD9335 Qualcomm Technologies, Inc. (QTI) multimedia solutions, including the MSM8996, MSM8976, and MSM8956 chipsets. +config SND_SOC_WCD934X + tristate "WCD9340/WCD9341 Codec" + depends on MFD_WCD934X + help + The WCD9340/9341 is a audio codec IC Integrated in + Qualcomm SoCs like SDM845. + config SND_SOC_WL1273 tristate @@ -1473,6 +1537,15 @@ config SND_SOC_WM9713 select REGMAP_AC97 select AC97_BUS_COMPAT if AC97_BUS_NEW +config SND_SOC_WSA881X + tristate "WSA881X Codec" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + tristate + help + This enables support for Qualcomm WSA8810/WSA8815 Class-D + Smart Speaker Amplifier. + config SND_SOC_ZX_AUD96P22 tristate "ZTE ZX AUD96P22 CODEC" depends on I2C @@ -1507,6 +1580,15 @@ config SND_SOC_MT6358 Enable support for the platform which uses MT6358 as external codec device. +config SND_SOC_MT6660 + tristate "Mediatek MT6660 Speaker Amplifier" + depends on I2C + help + MediaTek MT6660 is a smart power amplifier which contain + speaker protection, multi-band DRC, equalizer functions. + Select N if you don't have MT6660 on board. + Select M to build this as module. + config SND_SOC_NAU8540 tristate "Nuvoton Technology Corporation NAU85L40 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index ddfd07071925..ba1b4b3fa2da 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -97,6 +97,7 @@ snd-soc-inno-rk3036-objs := inno_rk3036.o snd-soc-isabelle-objs := isabelle.o snd-soc-jz4740-codec-objs := jz4740.o snd-soc-jz4725b-codec-objs := jz4725b.o +snd-soc-jz4770-codec-objs := jz4770.o snd-soc-l3-objs := l3.o snd-soc-lm4857-objs := lm4857.o snd-soc-lm49453-objs := lm49453.o @@ -122,6 +123,7 @@ snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o snd-soc-mt6351-objs := mt6351.o snd-soc-mt6358-objs := mt6358.o +snd-soc-mt6660-objs := mt6660.o snd-soc-nau8540-objs := nau8540.o snd-soc-nau8810-objs := nau8810.o snd-soc-nau8822-objs := nau8822.o @@ -152,8 +154,10 @@ snd-soc-rk3328-objs := rk3328_codec.o snd-soc-rl6231-objs := rl6231.o snd-soc-rl6347a-objs := rl6347a.o snd-soc-rt1011-objs := rt1011.o +snd-soc-rt1015-objs := rt1015.o snd-soc-rt1305-objs := rt1305.o snd-soc-rt1308-objs := rt1308.o +snd-soc-rt1308-sdw-objs := rt1308-sdw.o snd-soc-rt274-objs := rt274.o snd-soc-rt286-objs := rt286.o snd-soc-rt298-objs := rt298.o @@ -173,6 +177,9 @@ 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-rt700-objs := rt700.o rt700-sdw.o +snd-soc-rt711-objs := rt711.o rt711-sdw.o +snd-soc-rt715-objs := rt715.o rt715-sdw.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -220,6 +227,7 @@ snd-soc-uda1334-objs := uda1334.o snd-soc-uda134x-objs := uda134x.o snd-soc-uda1380-objs := uda1380.o snd-soc-wcd9335-objs := wcd-clsh-v2.o wcd9335.o +snd-soc-wcd934x-objs := wcd-clsh-v2.o wcd934x.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o snd-soc-wm0010-objs := wm0010.o @@ -277,6 +285,7 @@ snd-soc-wm9705-objs := wm9705.o snd-soc-wm9712-objs := wm9712.o snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o +snd-soc-wsa881x-objs := wsa881x.o snd-soc-zx-aud96p22-objs := zx_aud96p22.o # Amp snd-soc-max9877-objs := max9877.o @@ -386,6 +395,7 @@ obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o +obj-$(CONFIG_SND_SOC_JZ4770_CODEC) += snd-soc-jz4770-codec.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o @@ -411,6 +421,7 @@ obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o obj-$(CONFIG_SND_SOC_MT6351) += snd-soc-mt6351.o obj-$(CONFIG_SND_SOC_MT6358) += snd-soc-mt6358.o +obj-$(CONFIG_SND_SOC_MT6660) += snd-soc-mt6660.o obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o @@ -441,8 +452,10 @@ obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o +obj-$(CONFIG_SND_SOC_RT1015) += snd-soc-rt1015.o obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o +obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o obj-$(CONFIG_SND_SOC_RT274) += snd-soc-rt274.o obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o @@ -463,6 +476,9 @@ 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_RT700) += snd-soc-rt700.o +obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o +obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.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 @@ -509,6 +525,7 @@ obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o +obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o @@ -566,6 +583,7 @@ obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o +obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o # Amp diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index ce3ed056ea8b..6a24f570c5e8 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -919,7 +919,7 @@ static int wov_pcm_hw_params(struct snd_soc_component *component, priv->wov_burst_read = true; mutex_unlock(&priv->wov_dma_lock); - return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); + return 0; } static int wov_pcm_hw_free(struct snd_soc_component *component, @@ -935,7 +935,7 @@ static int wov_pcm_hw_free(struct snd_soc_component *component, cancel_delayed_work_sync(&priv->wov_copy_work); - return snd_pcm_lib_free_pages(substream); + return 0; } static snd_pcm_uframes_t wov_pcm_pointer(struct snd_soc_component *component, @@ -951,8 +951,8 @@ static snd_pcm_uframes_t wov_pcm_pointer(struct snd_soc_component *component, static int wov_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { - snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC, - NULL, 0, 0); + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC, + NULL, 0, 0); return 0; } diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 55408c8fcb4e..e47758e4fb36 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -214,12 +214,10 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = { SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture", CS42L51_POWER_CTL1, 2, 1, cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), - SND_SOC_DAPM_DAC_E("Left DAC", "Left HiFi Playback", - CS42L51_POWER_CTL1, 5, 1, - cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), - SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback", - CS42L51_POWER_CTL1, 6, 1, - cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_DAC_E("Left DAC", NULL, CS42L51_POWER_CTL1, 5, 1, + cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_DAC_E("Right DAC", NULL, CS42L51_POWER_CTL1, 6, 1, + cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), /* analog/mic */ SND_SOC_DAPM_INPUT("AIN1L"), @@ -255,6 +253,12 @@ static const struct snd_soc_dapm_route cs42l51_routes[] = { {"HPL", NULL, "Left DAC"}, {"HPR", NULL, "Right DAC"}, + {"Right DAC", NULL, "DAC Mux"}, + {"Left DAC", NULL, "DAC Mux"}, + + {"DAC Mux", "Direct PCM", "Playback"}, + {"DAC Mux", "DSP PCM", "Playback"}, + {"Left ADC", NULL, "Left PGA"}, {"Right ADC", NULL, "Right PGA"}, diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c index ece1276f38eb..e8840dc142ef 100644 --- a/sound/soc/codecs/cs47l15.c +++ b/sound/soc/codecs/cs47l15.c @@ -438,11 +438,13 @@ static const struct snd_kcontrol_new cs47l15_aec_loopback_mux[] = { static const struct snd_soc_dapm_widget cs47l15_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT, 0, madera_sysclk_ev, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK, MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0), -SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, - MADERA_DSP_CLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT, + 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0), SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), @@ -529,6 +531,7 @@ SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), SND_SOC_DAPM_OUTPUT("DSP Trigger Out"), SND_SOC_DAPM_DEMUX("HPOUT1 Demux", SND_SOC_NOPM, 0, 0, &cs47l15_outdemux), +SND_SOC_DAPM_MUX("HPOUT1 Mono Mux", SND_SOC_NOPM, 0, 0, &cs47l15_outdemux), SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT, 0, NULL, 0), @@ -1084,6 +1087,9 @@ static const struct snd_soc_dapm_route cs47l15_dapm_routes[] = { { "AEC2 Loopback", "HPOUT1R", "OUT1R" }, { "HPOUT1 Demux", NULL, "OUT1L" }, { "HPOUT1 Demux", NULL, "OUT1R" }, + + { "OUT1R", NULL, "HPOUT1 Mono Mux" }, + { "HPOUTL", "HPOUT", "HPOUT1 Demux" }, { "HPOUTR", "HPOUT", "HPOUT1 Demux" }, { "EPOUTP", "EPOUT", "HPOUT1 Demux" }, @@ -1261,6 +1267,11 @@ static irqreturn_t cs47l15_adsp2_irq(int irq, void *data) return IRQ_HANDLED; } +static const struct snd_soc_dapm_route cs47l15_mono_routes[] = { + { "HPOUT1 Mono Mux", "HPOUT", "OUT1L" }, + { "HPOUT1 Mono Mux", "EPOUT", "OUT1L" }, +}; + static int cs47l15_component_probe(struct snd_soc_component *component) { struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); @@ -1277,7 +1288,9 @@ static int cs47l15_component_probe(struct snd_soc_component *component) if (ret) return ret; - ret = madera_init_outputs(component, CS47L15_MONO_OUTPUTS); + ret = madera_init_outputs(component, cs47l15_mono_routes, + ARRAY_SIZE(cs47l15_mono_routes), + CS47L15_MONO_OUTPUTS); if (ret) return ret; diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c index d396a8545d51..3d48a0d9ecc5 100644 --- a/sound/soc/codecs/cs47l35.c +++ b/sound/soc/codecs/cs47l35.c @@ -521,11 +521,13 @@ static const struct snd_kcontrol_new cs47l35_aec_loopback_mux[] = { static const struct snd_soc_dapm_widget cs47l35_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT, 0, madera_sysclk_ev, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK, MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT, - 0, NULL, 0), + 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0), @@ -631,6 +633,7 @@ SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), SND_SOC_DAPM_OUTPUT("DSP Trigger Out"), SND_SOC_DAPM_DEMUX("HPOUT1 Demux", SND_SOC_NOPM, 0, 0, &cs47l35_outdemux), +SND_SOC_DAPM_MUX("HPOUT1 Mono Mux", SND_SOC_NOPM, 0, 0, &cs47l35_outdemux), SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT, 0, NULL, 0), @@ -1309,6 +1312,8 @@ static const struct snd_soc_dapm_route cs47l35_dapm_routes[] = { { "SPKOUTN", NULL, "OUT4L" }, { "SPKOUTP", NULL, "OUT4L" }, + { "OUT1R", NULL, "HPOUT1 Mono Mux" }, + { "HPOUTL", "HPOUT", "HPOUT1 Demux" }, { "HPOUTR", "HPOUT", "HPOUT1 Demux" }, { "EPOUTP", "EPOUT", "HPOUT1 Demux" }, @@ -1552,6 +1557,11 @@ static irqreturn_t cs47l35_adsp2_irq(int irq, void *data) return IRQ_HANDLED; } +static const struct snd_soc_dapm_route cs47l35_mono_routes[] = { + { "HPOUT1 Mono Mux", "HPOUT", "OUT1L" }, + { "HPOUT1 Mono Mux", "EPOUT", "OUT1L" }, +}; + static int cs47l35_component_probe(struct snd_soc_component *component) { struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component); @@ -1568,7 +1578,9 @@ static int cs47l35_component_probe(struct snd_soc_component *component) if (ret) return ret; - ret = madera_init_outputs(component, CS47L35_MONO_OUTPUTS); + ret = madera_init_outputs(component, cs47l35_mono_routes, + ARRAY_SIZE(cs47l35_mono_routes), + CS47L35_MONO_OUTPUTS); if (ret) return ret; diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c index 32fe7ffb7526..bef3471f482d 100644 --- a/sound/soc/codecs/cs47l85.c +++ b/sound/soc/codecs/cs47l85.c @@ -790,15 +790,18 @@ static const struct snd_kcontrol_new cs47l85_output_anc_src[] = { static const struct snd_soc_dapm_widget cs47l85_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT, 0, madera_sysclk_ev, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1, - MADERA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), + MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK, MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK, MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), -SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, - MADERA_DSP_CLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT, + 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0), @@ -2005,12 +2008,18 @@ static const struct snd_soc_dapm_route cs47l85_dapm_routes[] = { { "IN3R", NULL, "IN3R Mode" }, { "IN4L", NULL, "DMICCLK4" }, + { "IN4L", NULL, "DMICDAT4" }, + { "IN4R", NULL, "DMICCLK4" }, { "IN4R", NULL, "DMICDAT4" }, { "IN5L", NULL, "DMICCLK5" }, + { "IN5L", NULL, "DMICDAT5" }, + { "IN5R", NULL, "DMICCLK5" }, { "IN5R", NULL, "DMICDAT5" }, { "IN6L", NULL, "DMICCLK6" }, + { "IN6L", NULL, "DMICDAT6" }, + { "IN6R", NULL, "DMICCLK6" }, { "IN6R", NULL, "DMICDAT6" }, MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"), @@ -2507,7 +2516,8 @@ static int cs47l85_component_probe(struct snd_soc_component *component) if (ret) return ret; - ret = madera_init_outputs(component, CS47L85_MONO_OUTPUTS); + ret = madera_init_outputs(component, NULL, CS47L85_MONO_OUTPUTS, + CS47L85_MONO_OUTPUTS); if (ret) return ret; diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c index 67cac60a859d..266eade82764 100644 --- a/sound/soc/codecs/cs47l90.c +++ b/sound/soc/codecs/cs47l90.c @@ -744,15 +744,18 @@ static const struct snd_kcontrol_new cs47l90_output_anc_src[] = { static const struct snd_soc_dapm_widget cs47l90_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT, 0, madera_sysclk_ev, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1, - MADERA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), + MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK, MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK, MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), -SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, - MADERA_DSP_CLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT, + 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0), @@ -1935,12 +1938,18 @@ static const struct snd_soc_dapm_route cs47l90_dapm_routes[] = { { "IN2R", NULL, "IN2R Mode" }, { "IN3L", NULL, "DMICCLK3" }, + { "IN3L", NULL, "DMICDAT3" }, + { "IN3R", NULL, "DMICCLK3" }, { "IN3R", NULL, "DMICDAT3" }, { "IN4L", NULL, "DMICCLK4" }, + { "IN4L", NULL, "DMICDAT4" }, + { "IN4R", NULL, "DMICCLK4" }, { "IN4R", NULL, "DMICDAT4" }, { "IN5L", NULL, "DMICCLK5" }, + { "IN5L", NULL, "DMICDAT5" }, + { "IN5R", NULL, "DMICCLK5" }, { "IN5R", NULL, "DMICDAT5" }, MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"), @@ -2418,7 +2427,8 @@ static int cs47l90_component_probe(struct snd_soc_component *component) if (ret) return ret; - ret = madera_init_outputs(component, CS47L90_MONO_OUTPUTS); + ret = madera_init_outputs(component, NULL, CS47L90_MONO_OUTPUTS, + CS47L90_MONO_OUTPUTS); if (ret) return ret; diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c index d50f75f3b3e4..942040fd354f 100644 --- a/sound/soc/codecs/cs47l92.c +++ b/sound/soc/codecs/cs47l92.c @@ -163,6 +163,51 @@ static int cs47l92_adsp_power_ev(struct snd_soc_dapm_widget *w, return wm_adsp_early_event(w, kcontrol, event); } +static int cs47l92_outclk_ev(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 cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); + struct madera_priv *priv = &cs47l92->core; + struct madera *madera = priv->madera; + unsigned int val; + int ret; + + ret = regmap_read(madera->regmap, MADERA_OUTPUT_RATE_1, &val); + if (ret) { + dev_err(madera->dev, "Failed to read OUTCLK source: %d\n", ret); + return ret; + } + + val &= MADERA_OUT_CLK_SRC_MASK; + + switch (val) { + case MADERA_OUTCLK_MCLK1: + case MADERA_OUTCLK_MCLK2: + case MADERA_OUTCLK_MCLK3: + val -= (MADERA_OUTCLK_MCLK1 - MADERA_MCLK1); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = clk_prepare_enable(madera->mclk[val].clk); + if (ret) + return ret; + break; + case SND_SOC_DAPM_POST_PMD: + clk_disable_unprepare(madera->mclk[val].clk); + break; + default: + break; + } + default: + break; + } + + return madera_domain_clk_ev(w, kcontrol, event); +} + #define CS47L92_NG_SRC(name, base) \ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ @@ -615,15 +660,18 @@ static const struct snd_kcontrol_new cs47l92_aec_loopback_mux = static const struct snd_soc_dapm_widget cs47l92_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT, 0, madera_sysclk_ev, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1, - MADERA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), + MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK, MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK, MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), -SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, - MADERA_DSP_CLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT, + 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0), SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0), @@ -666,7 +714,7 @@ SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM, MADERA_DOM_GRP_OUT, 0, - madera_domain_clk_ev, + cs47l92_outclk_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM, MADERA_DOM_GRP_SPD, 0, @@ -730,6 +778,7 @@ SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]), SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]), SND_SOC_DAPM_DEMUX("OUT3 Demux", SND_SOC_NOPM, 0, 0, &cs47l92_outdemux), +SND_SOC_DAPM_MUX("OUT3 Mono Mux", SND_SOC_NOPM, 0, 0, &cs47l92_outdemux), SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), @@ -1584,6 +1633,8 @@ static const struct snd_soc_dapm_route cs47l92_dapm_routes[] = { { "OUT3 Demux", NULL, "OUT3L" }, { "OUT3 Demux", NULL, "OUT3R" }, + { "OUT3R", NULL, "OUT3 Mono Mux" }, + { "HPOUT3L", "HPOUT3", "OUT3 Demux" }, { "HPOUT3R", "HPOUT3", "OUT3 Demux" }, { "HPOUT4L", "HPOUT4", "OUT3 Demux" }, @@ -1817,6 +1868,13 @@ static irqreturn_t cs47l92_adsp2_irq(int irq, void *data) return IRQ_HANDLED; } +static const struct snd_soc_dapm_route cs47l92_mono_routes[] = { + { "OUT1R", NULL, "OUT1L" }, + { "OUT2R", NULL, "OUT2L" }, + { "OUT3 Mono Mux", "HPOUT3", "OUT3L" }, + { "OUT3 Mono Mux", "HPOUT4", "OUT3L" }, +}; + static int cs47l92_component_probe(struct snd_soc_component *component) { struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); @@ -1833,7 +1891,9 @@ static int cs47l92_component_probe(struct snd_soc_component *component) if (ret) return ret; - ret = madera_init_outputs(component, CS47L92_MONO_OUTPUTS); + ret = madera_init_outputs(component, cs47l92_mono_routes, + ARRAY_SIZE(cs47l92_mono_routes), + CS47L92_MONO_OUTPUTS); if (ret) return ret; @@ -1959,10 +2019,8 @@ static int cs47l92_probe(struct platform_device *pdev) goto error_dsp_irq; ret = madera_init_bus_error_irq(&cs47l92->core, 0, wm_adsp2_bus_error); - if (ret != 0) { - wm_adsp2_remove(&cs47l92->core.adsp[0]); + if (ret != 0) goto error_adsp; - } madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1, &cs47l92->fll[0]); diff --git a/sound/soc/codecs/gtm601.c b/sound/soc/codecs/gtm601.c index d454294c8d06..ae9e1c70ca57 100644 --- a/sound/soc/codecs/gtm601.c +++ b/sound/soc/codecs/gtm601.c @@ -13,7 +13,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/kernel.h> -#include <linux/device.h> +#include <linux/of_device.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/initval.h> @@ -47,6 +47,24 @@ static struct snd_soc_dai_driver gtm601_dai = { }, }; +static struct snd_soc_dai_driver bm818_dai = { + .name = "bm818", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + static const struct snd_soc_component_driver soc_component_dev_gtm601 = { .dapm_widgets = gtm601_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(gtm601_dapm_widgets), @@ -60,17 +78,21 @@ static const struct snd_soc_component_driver soc_component_dev_gtm601 = { static int gtm601_platform_probe(struct platform_device *pdev) { + const struct snd_soc_dai_driver *dai_driver; + + dai_driver = of_device_get_match_data(&pdev->dev); + return devm_snd_soc_register_component(&pdev->dev, - &soc_component_dev_gtm601, >m601_dai, 1); + &soc_component_dev_gtm601, + (struct snd_soc_dai_driver *)dai_driver, 1); } -#if defined(CONFIG_OF) static const struct of_device_id gtm601_codec_of_match[] = { - { .compatible = "option,gtm601", }, + { .compatible = "option,gtm601", .data = (void *)>m601_dai }, + { .compatible = "broadmobi,bm818", .data = (void *)&bm818_dai }, {}, }; MODULE_DEVICE_TABLE(of, gtm601_codec_of_match); -#endif static struct platform_driver gtm601_codec_driver = { .driver = { diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 43110151e928..de003acb1951 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -164,6 +164,19 @@ static struct snd_soc_dai_driver hdac_hda_dais[] = { .sig_bits = 24, }, }, +{ + .id = HDAC_HDMI_3_DAI_ID, + .name = "intel-hdmi-hifi4", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "hifi4", + .channels_min = 1, + .channels_max = 32, + .rates = STUB_HDMI_RATES, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, }; @@ -346,6 +359,9 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, case HDAC_HDMI_2_DAI_ID: pcm_name = "HDMI 2"; break; + case HDAC_HDMI_3_DAI_ID: + pcm_name = "HDMI 3"; + break; default: dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id); return NULL; diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h index e145cec085b8..598b07d9b6fe 100644 --- a/sound/soc/codecs/hdac_hda.h +++ b/sound/soc/codecs/hdac_hda.h @@ -13,7 +13,8 @@ enum { HDAC_HDMI_0_DAI_ID, HDAC_HDMI_1_DAI_ID, HDAC_HDMI_2_DAI_ID, - HDAC_LAST_DAI_ID = HDAC_HDMI_2_DAI_ID, + HDAC_HDMI_3_DAI_ID, + HDAC_LAST_DAI_ID = HDAC_HDMI_3_DAI_ID, }; struct hdac_hda_pcm { diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 18c173e6a13b..e6558475e006 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -115,16 +115,8 @@ struct hdac_hdmi_dai_port_map { struct hdac_hdmi_cvt *cvt; }; -/* - * pin to port mapping table where the value indicate the pin number and - * the index indicate the port number with 1 base. - */ -static const int icl_pin2port_map[] = {0x4, 0x6, 0x8, 0xa, 0xb}; - struct hdac_hdmi_drv_data { unsigned int vendor_nid; - const int *port_map; /* pin to port mapping table */ - int port_num; }; struct hdac_hdmi_priv { @@ -1374,12 +1366,11 @@ static int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid) return 0; } -#define INTEL_VENDOR_NID_0x2 0x02 -#define INTEL_VENDOR_NID_0x8 0x08 -#define INTEL_VENDOR_NID_0xb 0x0b +#define INTEL_VENDOR_NID 0x08 +#define INTEL_GLK_VENDOR_NID 0x0b #define INTEL_GET_VENDOR_VERB 0xf81 #define INTEL_SET_VENDOR_VERB 0x781 -#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ +#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ #define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdev) @@ -1566,26 +1557,7 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev, static int hdac_hdmi_pin2port(void *aptr, int pin) { - struct hdac_device *hdev = aptr; - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); - const int *map = hdmi->drv_data->port_map; - int i; - - if (!hdmi->drv_data->port_num) - return pin - 4; /* map NID 0x05 -> port #1 */ - - /* - * looking for the pin number in the mapping table and return - * the index which indicate the port number - */ - for (i = 0; i < hdmi->drv_data->port_num; i++) { - if (pin == map[i]) - return i + 1; - } - - /* return -1 if pin number exceeds our expectation */ - dev_err(&hdev->dev, "Can't find the port for pin %d\n", pin); - return -1; + return pin - 4; /* map NID 0x05 -> port #1 */ } static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) @@ -1596,18 +1568,9 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) struct hdac_hdmi_port *hport = NULL; struct snd_soc_component *component = hdmi->component; int i; - hda_nid_t pin_nid; - - if (!hdmi->drv_data->port_num) { - /* for legacy platforms */ - pin_nid = port + 0x04; - } else if (port < hdmi->drv_data->port_num) { - /* get pin number from the pin2port mapping table */ - pin_nid = hdmi->drv_data->port_map[port - 1]; - } else { - dev_err(&hdev->dev, "Can't find the pin for port %d\n", port); - return; - } + + /* Don't know how this mapping is derived */ + hda_nid_t pin_nid = port + 0x04; dev_dbg(&hdev->dev, "%s: for pin:%d port=%d\n", __func__, pin_nid, pipe); @@ -2025,18 +1988,12 @@ static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx) return port->eld.info.spk_alloc; } -static struct hdac_hdmi_drv_data intel_icl_drv_data = { - .vendor_nid = INTEL_VENDOR_NID_0x2, - .port_map = icl_pin2port_map, - .port_num = ARRAY_SIZE(icl_pin2port_map), -}; - static struct hdac_hdmi_drv_data intel_glk_drv_data = { - .vendor_nid = INTEL_VENDOR_NID_0xb, + .vendor_nid = INTEL_GLK_VENDOR_NID, }; static struct hdac_hdmi_drv_data intel_drv_data = { - .vendor_nid = INTEL_VENDOR_NID_0x8, + .vendor_nid = INTEL_VENDOR_NID, }; static int hdac_hdmi_dev_probe(struct hdac_device *hdev) @@ -2216,8 +2173,6 @@ static const struct hda_device_id hdmi_list[] = { &intel_glk_drv_data), HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI", &intel_glk_drv_data), - HDA_CODEC_EXT_ENTRY(0x8086280f, 0x100000, "Icelake HDMI", - &intel_icl_drv_data), {} }; diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index f8b5b960e597..444cc4e3374e 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -274,7 +274,8 @@ struct hdmi_codec_priv { uint8_t eld[MAX_ELD_BYTES]; struct snd_pcm_chmap *chmap_info; unsigned int chmap_idx; - unsigned long busy; + struct mutex lock; + bool busy; struct snd_soc_jack *jack; unsigned int jack_status; }; @@ -292,7 +293,7 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = FIELD_SIZEOF(struct hdmi_codec_priv, eld); + uinfo->count = sizeof_field(struct hdmi_codec_priv, eld); return 0; } @@ -390,9 +391,10 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); int ret = 0; - ret = test_and_set_bit(0, &hcp->busy); - if (ret) { + mutex_lock(&hcp->lock); + if (hcp->busy) { dev_err(dai->dev, "Only one simultaneous stream supported!\n"); + mutex_unlock(&hcp->lock); return -EINVAL; } @@ -405,21 +407,21 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, if (hcp->hcd.ops->get_eld) { ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data, hcp->eld, sizeof(hcp->eld)); + if (ret) + goto err; + + ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld); + if (ret) + goto err; - if (!ret) { - ret = snd_pcm_hw_constraint_eld(substream->runtime, - hcp->eld); - if (ret) - goto err; - } /* Select chmap supported */ hdmi_codec_eld_chmap(hcp); } - return 0; + + hcp->busy = true; err: - /* Release the exclusive lock on error */ - clear_bit(0, &hcp->busy); + mutex_unlock(&hcp->lock); return ret; } @@ -431,7 +433,9 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); - clear_bit(0, &hcp->busy); + mutex_lock(&hcp->lock); + hcp->busy = false; + mutex_unlock(&hcp->lock); } static int hdmi_codec_hw_params(struct snd_pcm_substream *substream, @@ -811,6 +815,8 @@ static int hdmi_codec_probe(struct platform_device *pdev) return -ENOMEM; hcp->hcd = *hcd; + mutex_init(&hcp->lock); + daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL); if (!daidrv) return -ENOMEM; diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c new file mode 100644 index 000000000000..e7cf2c107607 --- /dev/null +++ b/sound/soc/codecs/jz4770.c @@ -0,0 +1,948 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Ingenic JZ4770 CODEC driver +// +// Copyright (C) 2012, Maarten ter Huurne <maarten@treewalker.org> +// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net> + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/time64.h> + +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +#define ICDC_RGADW_OFFSET 0x00 +#define ICDC_RGDATA_OFFSET 0x04 + +/* ICDC internal register access control register(RGADW) */ +#define ICDC_RGADW_RGWR BIT(16) + +#define ICDC_RGADW_RGADDR_OFFSET 8 +#define ICDC_RGADW_RGADDR_MASK GENMASK(14, ICDC_RGADW_RGADDR_OFFSET) + +#define ICDC_RGADW_RGDIN_OFFSET 0 +#define ICDC_RGADW_RGDIN_MASK GENMASK(7, ICDC_RGADW_RGDIN_OFFSET) + +/* ICDC internal register data output register (RGDATA)*/ +#define ICDC_RGDATA_IRQ BIT(8) + +#define ICDC_RGDATA_RGDOUT_OFFSET 0 +#define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET) + +/* Internal register space, accessed through regmap */ +enum { + JZ4770_CODEC_REG_SR, + JZ4770_CODEC_REG_AICR_DAC, + JZ4770_CODEC_REG_AICR_ADC, + JZ4770_CODEC_REG_CR_LO, + JZ4770_CODEC_REG_CR_HP, + + JZ4770_CODEC_REG_MISSING_REG1, + + JZ4770_CODEC_REG_CR_DAC, + JZ4770_CODEC_REG_CR_MIC, + JZ4770_CODEC_REG_CR_LI, + JZ4770_CODEC_REG_CR_ADC, + JZ4770_CODEC_REG_CR_MIX, + JZ4770_CODEC_REG_CR_VIC, + JZ4770_CODEC_REG_CCR, + JZ4770_CODEC_REG_FCR_DAC, + JZ4770_CODEC_REG_FCR_ADC, + JZ4770_CODEC_REG_ICR, + JZ4770_CODEC_REG_IMR, + JZ4770_CODEC_REG_IFR, + JZ4770_CODEC_REG_GCR_HPL, + JZ4770_CODEC_REG_GCR_HPR, + JZ4770_CODEC_REG_GCR_LIBYL, + JZ4770_CODEC_REG_GCR_LIBYR, + JZ4770_CODEC_REG_GCR_DACL, + JZ4770_CODEC_REG_GCR_DACR, + JZ4770_CODEC_REG_GCR_MIC1, + JZ4770_CODEC_REG_GCR_MIC2, + JZ4770_CODEC_REG_GCR_ADCL, + JZ4770_CODEC_REG_GCR_ADCR, + + JZ4770_CODEC_REG_MISSING_REG2, + + JZ4770_CODEC_REG_GCR_MIXADC, + JZ4770_CODEC_REG_GCR_MIXDAC, + JZ4770_CODEC_REG_AGC1, + JZ4770_CODEC_REG_AGC2, + JZ4770_CODEC_REG_AGC3, + JZ4770_CODEC_REG_AGC4, + JZ4770_CODEC_REG_AGC5, +}; + +#define REG_AICR_DAC_ADWL_OFFSET 6 +#define REG_AICR_DAC_ADWL_MASK (0x3 << REG_AICR_DAC_ADWL_OFFSET) +#define REG_AICR_DAC_SERIAL BIT(1) +#define REG_AICR_DAC_I2S BIT(0) + +#define REG_AICR_ADC_ADWL_OFFSET 6 +#define REG_AICR_ADC_ADWL_MASK (0x3 << REG_AICR_ADC_ADWL_OFFSET) +#define REG_AICR_ADC_SERIAL BIT(1) +#define REG_AICR_ADC_I2S BIT(0) + +#define REG_CR_LO_MUTE_OFFSET 7 +#define REG_CR_LO_SB_OFFSET 4 +#define REG_CR_LO_SEL_OFFSET 0 +#define REG_CR_LO_SEL_MASK (0x3 << REG_CR_LO_SEL_OFFSET) + +#define REG_CR_HP_MUTE BIT(7) +#define REG_CR_HP_LOAD BIT(6) +#define REG_CR_HP_SB_OFFSET 4 +#define REG_CR_HP_SB_HPCM BIT(3) +#define REG_CR_HP_SEL_OFFSET 0 +#define REG_CR_HP_SEL_MASK (0x3 << REG_CR_HP_SEL_OFFSET) + +#define REG_CR_DAC_MUTE BIT(7) +#define REG_CR_DAC_MONO BIT(6) +#define REG_CR_DAC_LEFT_ONLY BIT(5) +#define REG_CR_DAC_SB_OFFSET 4 +#define REG_CR_DAC_LRSWAP BIT(3) + +#define REG_CR_MIC_STEREO_OFFSET 7 +#define REG_CR_MIC_IDIFF_OFFSET 6 +#define REG_CR_MIC_SB_MIC2_OFFSET 5 +#define REG_CR_MIC_SB_MIC1_OFFSET 4 +#define REG_CR_MIC_BIAS_V0_OFFSET 1 +#define REG_CR_MIC_BIAS_SB_OFFSET 0 + +#define REG_CR_LI_LIBY_OFFSET 4 +#define REG_CR_LI_SB_OFFSET 0 + +#define REG_CR_ADC_DMIC_SEL BIT(7) +#define REG_CR_ADC_MONO BIT(6) +#define REG_CR_ADC_LEFT_ONLY BIT(5) +#define REG_CR_ADC_SB_OFFSET 4 +#define REG_CR_ADC_LRSWAP BIT(3) +#define REG_CR_ADC_IN_SEL_OFFSET 0 +#define REG_CR_ADC_IN_SEL_MASK (0x3 << REG_CR_ADC_IN_SEL_OFFSET) + +#define REG_CR_VIC_SB_SLEEP BIT(1) +#define REG_CR_VIC_SB BIT(0) + +#define REG_CCR_CRYSTAL_OFFSET 0 +#define REG_CCR_CRYSTAL_MASK (0xf << REG_CCR_CRYSTAL_OFFSET) + +#define REG_FCR_DAC_FREQ_OFFSET 0 +#define REG_FCR_DAC_FREQ_MASK (0xf << REG_FCR_DAC_FREQ_OFFSET) + +#define REG_FCR_ADC_FREQ_OFFSET 0 +#define REG_FCR_ADC_FREQ_MASK (0xf << REG_FCR_ADC_FREQ_OFFSET) + +#define REG_ICR_INT_FORM_OFFSET 6 +#define REG_ICR_INT_FORM_MASK (0x3 << REG_ICR_INT_FORM_OFFSET) + +#define REG_IMR_ALL_MASK (0x7f) +#define REG_IMR_SCLR_MASK BIT(6) +#define REG_IMR_JACK_MASK BIT(5) +#define REG_IMR_SCMC_MASK BIT(4) +#define REG_IMR_RUP_MASK BIT(3) +#define REG_IMR_RDO_MASK BIT(2) +#define REG_IMR_GUP_MASK BIT(1) +#define REG_IMR_GDO_MASK BIT(0) + +#define REG_IFR_ALL_MASK (0x7f) +#define REG_IFR_SCLR BIT(6) +#define REG_IFR_JACK BIT(5) +#define REG_IFR_SCMC BIT(4) +#define REG_IFR_RUP BIT(3) +#define REG_IFR_RDO BIT(2) +#define REG_IFR_GUP BIT(1) +#define REG_IFR_GDO BIT(0) + +#define REG_GCR_HPL_LRGO BIT(7) + +#define REG_GCR_DACL_RLGOD BIT(7) + +#define REG_GCR_GAIN_OFFSET 0 +#define REG_GCR_GAIN_MAX 0x1f + +#define REG_GCR_MIC_GAIN_OFFSET 0 +#define REG_GCR_MIC_GAIN_MAX 5 + +#define REG_GCR_ADC_GAIN_OFFSET 0 +#define REG_GCR_ADC_GAIN_MAX 23 + +#define REG_AGC1_EN BIT(7) + +/* codec private data */ +struct jz_codec { + struct device *dev; + struct regmap *regmap; + void __iomem *base; + struct clk *clk; +}; + +static int jz4770_codec_set_bias_level(struct snd_soc_component *codec, + enum snd_soc_bias_level level) +{ + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + struct regmap *regmap = jz_codec->regmap; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC, + REG_CR_VIC_SB, 0); + msleep(250); + regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC, + REG_CR_VIC_SB_SLEEP, 0); + msleep(400); + break; + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC, + REG_CR_VIC_SB_SLEEP, REG_CR_VIC_SB_SLEEP); + regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC, + REG_CR_VIC_SB, REG_CR_VIC_SB); + /* fall-through */ + default: + break; + } + + return 0; +} + +static int jz4770_codec_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec); + + /* + * SYSCLK output from the codec to the AIC is required to keep the + * DMA transfer going during playback when all audible outputs have + * been disabled. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dapm_force_enable_pin(dapm, "SYSCLK"); + + return 0; +} + +static void jz4770_codec_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dapm_disable_pin(dapm, "SYSCLK"); +} + + +static int jz4770_codec_pcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_component_force_bias_level(codec, + SND_SOC_BIAS_ON); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* do nothing */ + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int jz4770_codec_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_component *codec = dai->component; + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + unsigned int gain_bit = mute ? REG_IFR_GDO : REG_IFR_GUP; + unsigned int val; + int change, err; + + change = snd_soc_component_update_bits(codec, JZ4770_CODEC_REG_CR_DAC, + REG_CR_DAC_MUTE, + mute ? REG_CR_DAC_MUTE : 0); + if (change == 1) { + regmap_read(jz_codec->regmap, JZ4770_CODEC_REG_CR_DAC, &val); + + if (val & BIT(REG_CR_DAC_SB_OFFSET)) + return 1; + + err = regmap_read_poll_timeout(jz_codec->regmap, + JZ4770_CODEC_REG_IFR, + val, val & gain_bit, + 1000, 100 * USEC_PER_MSEC); + if (err) { + dev_err(jz_codec->dev, + "Timeout while setting digital mute: %d", err); + return err; + } + + /* clear GUP/GDO flag */ + regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, + gain_bit, gain_bit); + } + + return 0; +} + +/* unit: 0.01dB */ +static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 0); +static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 600); +static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 400, 0); +static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0); + +/* Unconditional controls. */ +static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = { + /* record gain control */ + SOC_DOUBLE_R_TLV("PCM Capture Volume", + JZ4770_CODEC_REG_GCR_ADCL, JZ4770_CODEC_REG_GCR_ADCR, + REG_GCR_ADC_GAIN_OFFSET, REG_GCR_ADC_GAIN_MAX, + 0, adc_tlv), + + SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume", + JZ4770_CODEC_REG_GCR_LIBYL, JZ4770_CODEC_REG_GCR_LIBYR, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv), +}; + +static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Volume", + .info = snd_soc_info_volsw, + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ + | SNDRV_CTL_ELEM_ACCESS_READWRITE, + .tlv.p = dac_tlv, + .get = snd_soc_dapm_get_volsw, + .put = snd_soc_dapm_put_volsw, + /* + * NOTE: DACR/DACL are inversed; the gain value written to DACR + * seems to affect the left channel, and the gain value written + * to DACL seems to affect the right channel. + */ + .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_DACR, + JZ4770_CODEC_REG_GCR_DACL, + REG_GCR_GAIN_OFFSET, + REG_GCR_GAIN_MAX, 1), + }, +}; + +static const struct snd_kcontrol_new jz4770_codec_hp_playback_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Volume", + .info = snd_soc_info_volsw, + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ + | SNDRV_CTL_ELEM_ACCESS_READWRITE, + .tlv.p = out_tlv, + .get = snd_soc_dapm_get_volsw, + .put = snd_soc_dapm_put_volsw, + /* HPR/HPL inversed for the same reason as above */ + .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_HPR, + JZ4770_CODEC_REG_GCR_HPL, + REG_GCR_GAIN_OFFSET, + REG_GCR_GAIN_MAX, 1), + }, +}; + +static int hpout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + unsigned int val; + int err; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* set cap-less, unmute HP */ + regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE, 0); + break; + + case SND_SOC_DAPM_POST_PMU: + /* wait for ramp-up complete (RUP) */ + err = regmap_read_poll_timeout(jz_codec->regmap, + JZ4770_CODEC_REG_IFR, + val, val & REG_IFR_RUP, + 1000, 100 * USEC_PER_MSEC); + if (err) { + dev_err(jz_codec->dev, "RUP timeout: %d", err); + return err; + } + + /* clear RUP flag */ + regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, + REG_IFR_RUP, REG_IFR_RUP); + + break; + + case SND_SOC_DAPM_POST_PMD: + /* set cap-couple, mute HP */ + regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE, + REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE); + + err = regmap_read_poll_timeout(jz_codec->regmap, + JZ4770_CODEC_REG_IFR, + val, val & REG_IFR_RDO, + 1000, 100 * USEC_PER_MSEC); + if (err) { + dev_err(jz_codec->dev, "RDO timeout: %d", err); + return err; + } + + /* clear RDO flag */ + regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, + REG_IFR_RDO, REG_IFR_RDO); + + break; + } + + return 0; +} + +static int adc_poweron_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (event == SND_SOC_DAPM_POST_PMU) + msleep(1000); + + return 0; +} + +static const char * const jz4770_codec_hp_texts[] = { + "PCM", "Line In", "Mic 1", "Mic 2" +}; +static const unsigned int jz4770_codec_hp_values[] = { 3, 2, 0, 1 }; +static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_hp_enum, + JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_SEL_OFFSET, + REG_CR_HP_SEL_MASK, + jz4770_codec_hp_texts, + jz4770_codec_hp_values); +static const struct snd_kcontrol_new jz4770_codec_hp_source = + SOC_DAPM_ENUM("Route", jz4770_codec_hp_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_lo_enum, + JZ4770_CODEC_REG_CR_LO, + REG_CR_LO_SEL_OFFSET, + REG_CR_LO_SEL_MASK, + jz4770_codec_hp_texts, + jz4770_codec_hp_values); +static const struct snd_kcontrol_new jz4770_codec_lo_source = + SOC_DAPM_ENUM("Route", jz4770_codec_lo_enum); + +static const char * const jz4770_codec_cap_texts[] = { + "Line In", "Mic 1", "Mic 2" +}; +static const unsigned int jz4770_codec_cap_values[] = { 2, 0, 1 }; +static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_cap_enum, + JZ4770_CODEC_REG_CR_ADC, + REG_CR_ADC_IN_SEL_OFFSET, + REG_CR_ADC_IN_SEL_MASK, + jz4770_codec_cap_texts, + jz4770_codec_cap_values); +static const struct snd_kcontrol_new jz4770_codec_cap_source = + SOC_DAPM_ENUM("Route", jz4770_codec_cap_enum); + +static const struct snd_kcontrol_new jz4770_codec_mic_controls[] = { + SOC_DAPM_SINGLE("Stereo Capture Switch", JZ4770_CODEC_REG_CR_MIC, + REG_CR_MIC_STEREO_OFFSET, 1, 0), +}; + +static const struct snd_soc_dapm_widget jz4770_codec_dapm_widgets[] = { + SND_SOC_DAPM_PGA_E("HP Out", JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_SB_OFFSET, 1, NULL, 0, hpout_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA("Line Out", JZ4770_CODEC_REG_CR_LO, + REG_CR_LO_SB_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Line Out Switch 2", JZ4770_CODEC_REG_CR_LO, + REG_CR_LO_MUTE_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Line In", JZ4770_CODEC_REG_CR_LI, + REG_CR_LI_SB_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_MUX("Headphones Source", SND_SOC_NOPM, 0, 0, + &jz4770_codec_hp_source), + SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0, + &jz4770_codec_cap_source), + SND_SOC_DAPM_MUX("Line Out Source", SND_SOC_NOPM, 0, 0, + &jz4770_codec_lo_source), + + SND_SOC_DAPM_PGA("Mic 1", JZ4770_CODEC_REG_CR_MIC, + REG_CR_MIC_SB_MIC1_OFFSET, 1, NULL, 0), + SND_SOC_DAPM_PGA("Mic 2", JZ4770_CODEC_REG_CR_MIC, + REG_CR_MIC_SB_MIC2_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Mic Diff", JZ4770_CODEC_REG_CR_MIC, + REG_CR_MIC_IDIFF_OFFSET, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Mic", SND_SOC_NOPM, 0, 0, + jz4770_codec_mic_controls, + ARRAY_SIZE(jz4770_codec_mic_controls)), + + SND_SOC_DAPM_PGA("Line In Bypass", JZ4770_CODEC_REG_CR_LI, + REG_CR_LI_LIBY_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_ADC_E("ADC", "HiFi Capture", JZ4770_CODEC_REG_CR_ADC, + REG_CR_ADC_SB_OFFSET, 1, adc_poweron_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_DAC("DAC", "HiFi Playback", JZ4770_CODEC_REG_CR_DAC, + REG_CR_DAC_SB_OFFSET, 1), + + SND_SOC_DAPM_MIXER("PCM Playback", SND_SOC_NOPM, 0, 0, + jz4770_codec_pcm_playback_controls, + ARRAY_SIZE(jz4770_codec_pcm_playback_controls)), + SND_SOC_DAPM_MIXER("Headphones Playback", SND_SOC_NOPM, 0, 0, + jz4770_codec_hp_playback_controls, + ARRAY_SIZE(jz4770_codec_hp_playback_controls)), + + SND_SOC_DAPM_SUPPLY("MICBIAS", JZ4770_CODEC_REG_CR_MIC, + REG_CR_MIC_BIAS_SB_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_INPUT("MIC1P"), + SND_SOC_DAPM_INPUT("MIC1N"), + SND_SOC_DAPM_INPUT("MIC2P"), + SND_SOC_DAPM_INPUT("MIC2N"), + + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("ROUT"), + + SND_SOC_DAPM_OUTPUT("LHPOUT"), + SND_SOC_DAPM_OUTPUT("RHPOUT"), + + SND_SOC_DAPM_INPUT("LLINEIN"), + SND_SOC_DAPM_INPUT("RLINEIN"), + + SND_SOC_DAPM_OUTPUT("SYSCLK"), +}; + +/* Unconditional routes. */ +static const struct snd_soc_dapm_route jz4770_codec_dapm_routes[] = { + { "Mic 1", NULL, "MIC1P" }, + { "Mic Diff", NULL, "MIC1N" }, + { "Mic 1", NULL, "Mic Diff" }, + { "Mic 2", NULL, "MIC2P" }, + { "Mic Diff", NULL, "MIC2N" }, + { "Mic 2", NULL, "Mic Diff" }, + + { "Line In", NULL, "LLINEIN" }, + { "Line In", NULL, "RLINEIN" }, + + { "Mic", "Stereo Capture Switch", "Mic 1" }, + { "Mic", "Stereo Capture Switch", "Mic 2" }, + { "Headphones Source", "Mic 1", "Mic" }, + { "Headphones Source", "Mic 2", "Mic" }, + { "Capture Source", "Mic 1", "Mic" }, + { "Capture Source", "Mic 2", "Mic" }, + + { "Headphones Source", "Mic 1", "Mic 1" }, + { "Headphones Source", "Mic 2", "Mic 2" }, + { "Headphones Source", "Line In", "Line In Bypass" }, + { "Headphones Source", "PCM", "Headphones Playback" }, + { "HP Out", NULL, "Headphones Source" }, + + { "Capture Source", "Line In", "Line In" }, + { "Capture Source", "Mic 1", "Mic 1" }, + { "Capture Source", "Mic 2", "Mic 2" }, + { "ADC", NULL, "Capture Source" }, + + { "Line In Bypass", NULL, "Line In" }, + { "Line Out Source", "Line In", "Line In Bypass" }, + { "Line Out Source", "PCM", "PCM Playback" }, + + { "LHPOUT", NULL, "HP Out"}, + { "RHPOUT", NULL, "HP Out"}, + + { "Line Out", NULL, "Line Out Source" }, + { "Line Out Switch 2", NULL, "Line Out" }, + + { "LOUT", NULL, "Line Out Switch 2"}, + { "ROUT", NULL, "Line Out Switch 2"}, + + { "PCM Playback", "Volume", "DAC" }, + { "Headphones Playback", "Volume", "PCM Playback" }, + + { "SYSCLK", NULL, "DAC" }, +}; + +static void jz4770_codec_codec_init_regs(struct snd_soc_component *codec) +{ + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + struct regmap *regmap = jz_codec->regmap; + + /* Collect updates for later sending. */ + regcache_cache_only(regmap, true); + + /* default HP output to PCM */ + regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_SEL_MASK, REG_CR_HP_SEL_MASK); + + /* default line output to PCM */ + regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_LO, + REG_CR_LO_SEL_MASK, REG_CR_LO_SEL_MASK); + + /* Disable stereo mic */ + regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_MIC, + BIT(REG_CR_MIC_STEREO_OFFSET), 0); + + /* Set mic 1 as default source for ADC */ + regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_ADC, + REG_CR_ADC_IN_SEL_MASK, 0); + + /* ADC/DAC: serial + i2s */ + regmap_update_bits(regmap, JZ4770_CODEC_REG_AICR_ADC, + REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S, + REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S); + regmap_update_bits(regmap, JZ4770_CODEC_REG_AICR_DAC, + REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S, + REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S); + + /* The generated IRQ is a high level */ + regmap_update_bits(regmap, JZ4770_CODEC_REG_ICR, + REG_ICR_INT_FORM_MASK, 0); + regmap_update_bits(regmap, JZ4770_CODEC_REG_IMR, REG_IMR_ALL_MASK, + REG_IMR_JACK_MASK | REG_IMR_RUP_MASK | + REG_IMR_RDO_MASK | REG_IMR_GUP_MASK | + REG_IMR_GDO_MASK); + + /* 12M oscillator */ + regmap_update_bits(regmap, JZ4770_CODEC_REG_CCR, + REG_CCR_CRYSTAL_MASK, 0); + + /* 0: 16ohm/220uF, 1: 10kohm/1uF */ + regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_LOAD, 0); + + /* disable automatic gain */ + regmap_update_bits(regmap, JZ4770_CODEC_REG_AGC1, REG_AGC1_EN, 0); + + /* Disable DAC lrswap */ + regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_DAC, + REG_CR_DAC_LRSWAP, REG_CR_DAC_LRSWAP); + + /* Independent L/R DAC gain control */ + regmap_update_bits(regmap, JZ4770_CODEC_REG_GCR_DACL, + REG_GCR_DACL_RLGOD, 0); + + /* Disable ADC lrswap */ + regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_ADC, + REG_CR_ADC_LRSWAP, REG_CR_ADC_LRSWAP); + + /* default to cap-less mode(0) */ + regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_SB_HPCM, 0); + + /* Send collected updates. */ + regcache_cache_only(regmap, false); + regcache_sync(regmap); + + /* Reset all interrupt flags. */ + regmap_write(regmap, JZ4770_CODEC_REG_IFR, REG_IFR_ALL_MASK); +} + +static int jz4770_codec_codec_probe(struct snd_soc_component *codec) +{ + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + + clk_prepare_enable(jz_codec->clk); + + jz4770_codec_codec_init_regs(codec); + + return 0; +} + +static void jz4770_codec_codec_remove(struct snd_soc_component *codec) +{ + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + + clk_disable_unprepare(jz_codec->clk); +} + +static const struct snd_soc_component_driver jz4770_codec_soc_codec_dev = { + .probe = jz4770_codec_codec_probe, + .remove = jz4770_codec_codec_remove, + .set_bias_level = jz4770_codec_set_bias_level, + .controls = jz4770_codec_snd_controls, + .num_controls = ARRAY_SIZE(jz4770_codec_snd_controls), + .dapm_widgets = jz4770_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(jz4770_codec_dapm_widgets), + .dapm_routes = jz4770_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(jz4770_codec_dapm_routes), + .suspend_bias_off = 1, + .use_pmdown_time = 1, +}; + +static const unsigned int jz4770_codec_sample_rates[] = { + 96000, 48000, 44100, 32000, + 24000, 22050, 16000, 12000, + 11025, 9600, 8000, +}; + +static int jz4770_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct jz_codec *codec = snd_soc_component_get_drvdata(dai->component); + unsigned int rate, bit_width; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + bit_width = 0; + break; + case SNDRV_PCM_FORMAT_S18_3LE: + bit_width = 1; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + bit_width = 2; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bit_width = 3; + break; + default: + return -EINVAL; + } + + for (rate = 0; rate < ARRAY_SIZE(jz4770_codec_sample_rates); rate++) { + if (jz4770_codec_sample_rates[rate] == params_rate(params)) + break; + } + + if (rate == ARRAY_SIZE(jz4770_codec_sample_rates)) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_DAC, + REG_AICR_DAC_ADWL_MASK, + bit_width << REG_AICR_DAC_ADWL_OFFSET); + regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_DAC, + REG_FCR_DAC_FREQ_MASK, + rate << REG_FCR_DAC_FREQ_OFFSET); + } else { + regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_ADC, + REG_AICR_ADC_ADWL_MASK, + bit_width << REG_AICR_ADC_ADWL_OFFSET); + regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_ADC, + REG_FCR_ADC_FREQ_MASK, + rate << REG_FCR_ADC_FREQ_OFFSET); + } + + return 0; +} + +static const struct snd_soc_dai_ops jz4770_codec_dai_ops = { + .startup = jz4770_codec_startup, + .shutdown = jz4770_codec_shutdown, + .hw_params = jz4770_codec_hw_params, + .trigger = jz4770_codec_pcm_trigger, + .digital_mute = jz4770_codec_digital_mute, +}; + +#define JZ_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE) + +static struct snd_soc_dai_driver jz4770_codec_dai = { + .name = "jz4770-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = JZ_CODEC_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = JZ_CODEC_FORMATS, + }, + .ops = &jz4770_codec_dai_ops, +}; + +static bool jz4770_codec_volatile(struct device *dev, unsigned int reg) +{ + return reg == JZ4770_CODEC_REG_SR || reg == JZ4770_CODEC_REG_IFR; +} + +static bool jz4770_codec_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case JZ4770_CODEC_REG_MISSING_REG1: + case JZ4770_CODEC_REG_MISSING_REG2: + return false; + default: + return true; + } +} + +static bool jz4770_codec_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case JZ4770_CODEC_REG_SR: + case JZ4770_CODEC_REG_MISSING_REG1: + case JZ4770_CODEC_REG_MISSING_REG2: + return false; + default: + return true; + } +} + +static int jz4770_codec_io_wait(struct jz_codec *codec) +{ + u32 reg; + + return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg, + !(reg & ICDC_RGADW_RGWR), + 1000, 10 * USEC_PER_MSEC); +} + +static int jz4770_codec_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct jz_codec *codec = context; + unsigned int i; + u32 tmp; + int ret; + + ret = jz4770_codec_io_wait(codec); + if (ret) + return ret; + + tmp = readl(codec->base + ICDC_RGADW_OFFSET); + tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK) + | (reg << ICDC_RGADW_RGADDR_OFFSET); + writel(tmp, codec->base + ICDC_RGADW_OFFSET); + + /* wait 6+ cycles */ + for (i = 0; i < 6; i++) + *val = readl(codec->base + ICDC_RGDATA_OFFSET) & + ICDC_RGDATA_RGDOUT_MASK; + + return 0; +} + +static int jz4770_codec_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct jz_codec *codec = context; + int ret; + + ret = jz4770_codec_io_wait(codec); + if (ret) + return ret; + + writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val, + codec->base + ICDC_RGADW_OFFSET); + + ret = jz4770_codec_io_wait(codec); + if (ret) + return ret; + + return 0; +} + +static const u8 jz4770_codec_reg_defaults[] = { + 0x00, 0xC3, 0xC3, 0x90, 0x98, 0xFF, 0x90, 0xB1, + 0x11, 0x10, 0x00, 0x03, 0x00, 0x00, 0x40, 0x00, + 0xFF, 0x00, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x34, + 0x07, 0x44, 0x1F, 0x00 +}; + +static struct regmap_config jz4770_codec_regmap_config = { + .reg_bits = 7, + .val_bits = 8, + + .max_register = JZ4770_CODEC_REG_AGC5, + .volatile_reg = jz4770_codec_volatile, + .readable_reg = jz4770_codec_readable, + .writeable_reg = jz4770_codec_writeable, + + .reg_read = jz4770_codec_reg_read, + .reg_write = jz4770_codec_reg_write, + + .reg_defaults_raw = jz4770_codec_reg_defaults, + .num_reg_defaults_raw = ARRAY_SIZE(jz4770_codec_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static int jz4770_codec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct jz_codec *codec; + int ret; + + codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + codec->dev = dev; + + codec->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(codec->base)) { + ret = PTR_ERR(codec->base); + dev_err(dev, "Failed to ioremap mmio memory: %d\n", ret); + return ret; + } + + codec->regmap = devm_regmap_init(dev, NULL, codec, + &jz4770_codec_regmap_config); + if (IS_ERR(codec->regmap)) + return PTR_ERR(codec->regmap); + + codec->clk = devm_clk_get(dev, "aic"); + if (IS_ERR(codec->clk)) + return PTR_ERR(codec->clk); + + platform_set_drvdata(pdev, codec); + + ret = devm_snd_soc_register_component(dev, &jz4770_codec_soc_codec_dev, + &jz4770_codec_dai, 1); + if (ret) { + dev_err(dev, "Failed to register codec: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct of_device_id jz4770_codec_of_matches[] = { + { .compatible = "ingenic,jz4770-codec", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jz4770_codec_of_matches); + +static struct platform_driver jz4770_codec_driver = { + .probe = jz4770_codec_probe, + .driver = { + .name = "jz4770-codec", + .of_match_table = of_match_ptr(jz4770_codec_of_matches), + }, +}; +module_platform_driver(jz4770_codec_driver); + +MODULE_DESCRIPTION("JZ4770 SoC internal codec driver"); +MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>"); +MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c index 52639811cc52..40de9d7811d1 100644 --- a/sound/soc/codecs/madera.c +++ b/sound/soc/codecs/madera.c @@ -163,6 +163,48 @@ static const int madera_dsp_bus_error_irqs[MADERA_MAX_ADSP] = { MADERA_IRQ_DSP7_BUS_ERR, }; +int madera_clk_ev(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 madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + unsigned int val; + int clk_idx; + int ret; + + ret = regmap_read(madera->regmap, w->reg, &val); + if (ret) { + dev_err(madera->dev, "Failed to check clock source: %d\n", ret); + return ret; + } + + switch ((val & MADERA_SYSCLK_SRC_MASK) >> MADERA_SYSCLK_SRC_SHIFT) { + case MADERA_CLK_SRC_MCLK1: + clk_idx = MADERA_MCLK1; + break; + case MADERA_CLK_SRC_MCLK2: + clk_idx = MADERA_MCLK2; + break; + case MADERA_CLK_SRC_MCLK3: + clk_idx = MADERA_MCLK3; + break; + default: + return 0; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return clk_prepare_enable(madera->mclk[clk_idx].clk); + case SND_SOC_DAPM_POST_PMD: + clk_disable_unprepare(madera->mclk[clk_idx].clk); + return 0; + default: + return 0; + } +} +EXPORT_SYMBOL_GPL(madera_clk_ev); + static void madera_spin_sysclk(struct madera_priv *priv) { struct madera *madera = priv->madera; @@ -193,9 +235,16 @@ int madera_sysclk_ev(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct madera_priv *priv = snd_soc_component_get_drvdata(component); - madera_spin_sysclk(priv); + switch (event) { + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_PRE_PMD: + madera_spin_sysclk(priv); + break; + default: + break; + } - return 0; + return madera_clk_ev(w, kcontrol, event); } EXPORT_SYMBOL_GPL(madera_sysclk_ev); @@ -526,6 +575,7 @@ int madera_out1_demux_put(struct snd_kcontrol *kcontrol, usleep_range(2000, 3000); /* wait for wseq to complete */ /* change demux setting */ + ret = 0; if (madera->out_clamp[0]) ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1, @@ -1162,7 +1212,9 @@ static const struct snd_soc_dapm_route madera_mono_routes[] = { { "OUT6R", NULL, "OUT6L" }, }; -int madera_init_outputs(struct snd_soc_component *component, int n_mono_routes) +int madera_init_outputs(struct snd_soc_component *component, + const struct snd_soc_dapm_route *routes, + int n_mono_routes, int n_real) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); @@ -1179,16 +1231,21 @@ int madera_init_outputs(struct snd_soc_component *component, int n_mono_routes) n_mono_routes = MADERA_MAX_OUTPUT; } + if (!routes) + routes = madera_mono_routes; + for (i = 0; i < n_mono_routes; i++) { /* Default is 0 so noop with defaults */ if (pdata->out_mono[i]) { val = MADERA_OUT1_MONO; - snd_soc_dapm_add_routes(dapm, - &madera_mono_routes[i], 1); + snd_soc_dapm_add_routes(dapm, &routes[i], 1); } else { val = 0; } + if (i >= n_real) + continue; + regmap_update_bits(madera->regmap, MADERA_OUTPUT_PATH_CONFIG_1L + (i * 8), MADERA_OUT1_MONO, val); @@ -3809,11 +3866,75 @@ static bool madera_set_fll_phase_integrator(struct madera_fll *fll, return reg_change; } +static int madera_set_fll_clks_reg(struct madera_fll *fll, bool ena, + unsigned int reg, unsigned int mask, + unsigned int shift) +{ + struct madera *madera = fll->madera; + unsigned int src; + struct clk *clk; + int ret; + + ret = regmap_read(madera->regmap, reg, &src); + if (ret != 0) { + madera_fll_err(fll, "Failed to read current source: %d\n", + ret); + return ret; + } + + src = (src & mask) >> shift; + + switch (src) { + case MADERA_FLL_SRC_MCLK1: + clk = madera->mclk[MADERA_MCLK1].clk; + break; + case MADERA_FLL_SRC_MCLK2: + clk = madera->mclk[MADERA_MCLK2].clk; + break; + case MADERA_FLL_SRC_MCLK3: + clk = madera->mclk[MADERA_MCLK3].clk; + break; + default: + return 0; + } + + if (ena) { + return clk_prepare_enable(clk); + } else { + clk_disable_unprepare(clk); + return 0; + } +} + +static inline int madera_set_fll_clks(struct madera_fll *fll, int base, bool ena) +{ + return madera_set_fll_clks_reg(fll, ena, + base + MADERA_FLL_CONTROL_6_OFFS, + MADERA_FLL1_REFCLK_SRC_MASK, + MADERA_FLL1_REFCLK_DIV_SHIFT); +} + +static inline int madera_set_fllao_clks(struct madera_fll *fll, int base, bool ena) +{ + return madera_set_fll_clks_reg(fll, ena, + base + MADERA_FLLAO_CONTROL_6_OFFS, + MADERA_FLL_AO_REFCLK_SRC_MASK, + MADERA_FLL_AO_REFCLK_SRC_SHIFT); +} + +static inline int madera_set_fllhj_clks(struct madera_fll *fll, int base, bool ena) +{ + return madera_set_fll_clks_reg(fll, ena, + base + MADERA_FLL_CONTROL_1_OFFS, + CS47L92_FLL1_REFCLK_SRC_MASK, + CS47L92_FLL1_REFCLK_SRC_SHIFT); +} + static void madera_disable_fll(struct madera_fll *fll) { struct madera *madera = fll->madera; unsigned int sync_base; - bool change; + bool ref_change, sync_change; switch (madera->type) { case CS47L35: @@ -3831,18 +3952,23 @@ static void madera_disable_fll(struct madera_fll *fll) MADERA_FLL1_FREERUN, MADERA_FLL1_FREERUN); regmap_update_bits_check(madera->regmap, fll->base + MADERA_FLL_CONTROL_1_OFFS, - MADERA_FLL1_ENA, 0, &change); - regmap_update_bits(madera->regmap, - sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS, - MADERA_FLL1_SYNC_ENA, 0); + MADERA_FLL1_ENA, 0, &ref_change); + regmap_update_bits_check(madera->regmap, + sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS, + MADERA_FLL1_SYNC_ENA, 0, &sync_change); regmap_update_bits(madera->regmap, fll->base + MADERA_FLL_CONTROL_1_OFFS, MADERA_FLL1_FREERUN, 0); madera_wait_for_fll(fll, false); - if (change) + if (sync_change) + madera_set_fll_clks(fll, sync_base, false); + + if (ref_change) { + madera_set_fll_clks(fll, fll->base, false); pm_runtime_put_autosuspend(madera->dev); + } } static int madera_enable_fll(struct madera_fll *fll) @@ -3898,6 +4024,10 @@ static int madera_enable_fll(struct madera_fll *fll) regmap_update_bits(fll->madera->regmap, fll->base + MADERA_FLL_CONTROL_7_OFFS, MADERA_FLL1_GAIN_MASK, 0); + + if (sync_enabled > 0) + madera_set_fll_clks(fll, sync_base, false); + madera_set_fll_clks(fll, fll->base, false); } /* Apply SYNCCLK setting */ @@ -3976,11 +4106,15 @@ static int madera_enable_fll(struct madera_fll *fll) if (!already_enabled) pm_runtime_get_sync(madera->dev); - if (have_sync) + if (have_sync) { + madera_set_fll_clks(fll, sync_base, true); regmap_update_bits(madera->regmap, sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS, MADERA_FLL1_SYNC_ENA, MADERA_FLL1_SYNC_ENA); + } + + madera_set_fll_clks(fll, fll->base, true); regmap_update_bits(madera->regmap, fll->base + MADERA_FLL_CONTROL_1_OFFS, MADERA_FLL1_ENA, MADERA_FLL1_ENA); @@ -4152,6 +4286,9 @@ static int madera_enable_fll_ao(struct madera_fll *fll, fll->base + MADERA_FLLAO_CONTROL_1_OFFS, MADERA_FLL_AO_HOLD, MADERA_FLL_AO_HOLD); + if (already_enabled) + madera_set_fllao_clks(fll, fll->base, false); + for (i = 0; i < patch_size; i++) { val = patch[i].def; @@ -4165,6 +4302,8 @@ static int madera_enable_fll_ao(struct madera_fll *fll, regmap_write(madera->regmap, patch[i].reg, val); } + madera_set_fllao_clks(fll, fll->base, true); + regmap_update_bits(madera->regmap, fll->base + MADERA_FLLAO_CONTROL_1_OFFS, MADERA_FLL_AO_ENA, MADERA_FLL_AO_ENA); @@ -4208,8 +4347,10 @@ static int madera_disable_fll_ao(struct madera_fll *fll) fll->base + MADERA_FLLAO_CONTROL_2_OFFS, MADERA_FLL_AO_CTRL_UPD_MASK, 0); - if (change) + if (change) { + madera_set_fllao_clks(fll, fll->base, false); pm_runtime_put_autosuspend(madera->dev); + } return 0; } @@ -4295,8 +4436,10 @@ static int madera_fllhj_disable(struct madera_fll *fll) fll->base + MADERA_FLL_CONTROL_2_OFFS, MADERA_FLL1_CTRL_UPD_MASK, 0); - if (change) + if (change) { + madera_set_fllhj_clks(fll, fll->base, false); pm_runtime_put_autosuspend(madera->dev); + } return 0; } @@ -4468,6 +4611,9 @@ static int madera_fllhj_enable(struct madera_fll *fll) MADERA_FLL1_HOLD_MASK, MADERA_FLL1_HOLD_MASK); + if (already_enabled) + madera_set_fllhj_clks(fll, fll->base, false); + /* Apply refclk */ ret = madera_fllhj_apply(fll, fll->ref_freq); if (ret) { @@ -4479,6 +4625,8 @@ static int madera_fllhj_enable(struct madera_fll *fll) CS47L92_FLL1_REFCLK_SRC_MASK, fll->ref_src << CS47L92_FLL1_REFCLK_SRC_SHIFT); + madera_set_fllhj_clks(fll, fll->base, true); + regmap_update_bits(madera->regmap, fll->base + MADERA_FLL_CONTROL_1_OFFS, MADERA_FLL1_ENA_MASK, @@ -4566,7 +4714,7 @@ EXPORT_SYMBOL_GPL(madera_fllhj_set_refclk); * * @component: Device to configure * @output: Output number - * @diff: True to set the output to differential mode + * @differential: True to set the output to differential mode * * Some systems use external analogue switches to connect more * analogue devices to the CODEC than are supported by the device. In diff --git a/sound/soc/codecs/madera.h b/sound/soc/codecs/madera.h index 6d8938a3fb64..e0c0be59e2ef 100644 --- a/sound/soc/codecs/madera.h +++ b/sound/soc/codecs/madera.h @@ -383,6 +383,8 @@ int madera_eq_coeff_put(struct snd_kcontrol *kcontrol, int madera_lhpf_coeff_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int madera_clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); int madera_sysclk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); int madera_spk_ev(struct snd_soc_dapm_widget *w, @@ -421,7 +423,9 @@ int madera_core_free(struct madera_priv *priv); int madera_init_overheat(struct madera_priv *priv); int madera_free_overheat(struct madera_priv *priv); int madera_init_inputs(struct snd_soc_component *component); -int madera_init_outputs(struct snd_soc_component *component, int n_mono_routes); +int madera_init_outputs(struct snd_soc_component *component, + const struct snd_soc_dapm_route *routes, + int n_mono_routes, int n_real); int madera_init_bus_error_irq(struct madera_priv *priv, int dsp_num, irq_handler_t handler); void madera_free_bus_error_irq(struct madera_priv *priv, int dsp_num); diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index e46b6ada13b1..0313e1183167 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -5,24 +5,149 @@ * Copyright 2011-2012 Maxim Integrated Products */ +#include <linux/acpi.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/of.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> -#include <linux/acpi.h> -#include <linux/clk.h> #include <sound/jack.h> +#include <sound/max98090.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/tlv.h> -#include <sound/max98090.h> #include "max98090.h" +static void max98090_shdn_save_locked(struct max98090_priv *max98090) +{ + int shdn = 0; + + /* saved_shdn, saved_count, SHDN are protected by card->dapm_mutex */ + regmap_read(max98090->regmap, M98090_REG_DEVICE_SHUTDOWN, &shdn); + max98090->saved_shdn |= shdn; + ++max98090->saved_count; + + if (shdn) + regmap_write(max98090->regmap, M98090_REG_DEVICE_SHUTDOWN, 0x0); +} + +static void max98090_shdn_restore_locked(struct max98090_priv *max98090) +{ + /* saved_shdn, saved_count, SHDN are protected by card->dapm_mutex */ + if (--max98090->saved_count == 0) { + if (max98090->saved_shdn) { + regmap_write(max98090->regmap, + M98090_REG_DEVICE_SHUTDOWN, + M98090_SHDNN_MASK); + max98090->saved_shdn = 0; + } + } +} + +static void max98090_shdn_save(struct max98090_priv *max98090) +{ + mutex_lock(&max98090->component->card->dapm_mutex); + max98090_shdn_save_locked(max98090); +} + +static void max98090_shdn_restore(struct max98090_priv *max98090) +{ + max98090_shdn_restore_locked(max98090); + mutex_unlock(&max98090->component->card->dapm_mutex); +} + +static int max98090_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct max98090_priv *max98090 = + snd_soc_component_get_drvdata(component); + int ret; + + max98090_shdn_save(max98090); + ret = snd_soc_put_volsw(kcontrol, ucontrol); + max98090_shdn_restore(max98090); + + return ret; +} + +static int max98090_dapm_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct max98090_priv *max98090 = + snd_soc_component_get_drvdata(component); + int ret; + + max98090_shdn_save(max98090); + ret = snd_soc_dapm_put_enum_double_locked(kcontrol, ucontrol); + max98090_shdn_restore(max98090); + + return ret; +} + +static int max98090_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct max98090_priv *max98090 = + snd_soc_component_get_drvdata(component); + int ret; + + max98090_shdn_save(max98090); + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + max98090_shdn_restore(max98090); + + return ret; +} + +static int max98090_bytes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct max98090_priv *max98090 = + snd_soc_component_get_drvdata(component); + int ret; + + max98090_shdn_save(max98090); + ret = snd_soc_bytes_put(kcontrol, ucontrol); + max98090_shdn_restore(max98090); + + return ret; +} + +static int max98090_dapm_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 max98090_priv *max98090 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_PRE_PMD: + max98090_shdn_save_locked(max98090); + break; + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_POST_PMD: + max98090_shdn_restore_locked(max98090); + break; + } + + return 0; +} + /* Allows for sparsely populated register maps */ static const struct reg_default max98090_reg[] = { { 0x00, 0x00 }, /* 00 Software Reset */ @@ -506,10 +631,13 @@ static SOC_ENUM_SINGLE_DECL(max98090_adchp_enum, max98090_pwr_perf_text); static const struct snd_kcontrol_new max98090_snd_controls[] = { - SOC_ENUM("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum), + SOC_ENUM_EXT("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum, + snd_soc_get_enum_double, max98090_put_enum_double), - SOC_SINGLE("DMIC MIC Comp Filter Config", M98090_REG_DIGITAL_MIC_CONFIG, - M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0), + SOC_SINGLE_EXT("DMIC MIC Comp Filter Config", + M98090_REG_DIGITAL_MIC_CONFIG, + M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), SOC_SINGLE_EXT_TLV("MIC1 Boost Volume", M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT, @@ -564,24 +692,34 @@ static const struct snd_kcontrol_new max98090_snd_controls[] = { M98090_AVR_SHIFT, M98090_AVR_NUM - 1, 1, max98090_av_tlv), - SOC_ENUM("ADC Oversampling Rate", max98090_osr128_enum), - SOC_SINGLE("ADC Quantizer Dither", M98090_REG_ADC_CONTROL, - M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0), - SOC_ENUM("ADC High Performance Mode", max98090_adchp_enum), - - SOC_SINGLE("DAC Mono Mode", M98090_REG_IO_CONFIGURATION, - M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0), - SOC_SINGLE("SDIN Mode", M98090_REG_IO_CONFIGURATION, - M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0), - SOC_SINGLE("SDOUT Mode", M98090_REG_IO_CONFIGURATION, - M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0), - SOC_SINGLE("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION, - M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1), - SOC_ENUM("Filter Mode", max98090_mode_enum), - SOC_SINGLE("Record Path DC Blocking", M98090_REG_FILTER_CONFIG, - M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0), - SOC_SINGLE("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG, - M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0), + SOC_ENUM_EXT("ADC Oversampling Rate", max98090_osr128_enum, + snd_soc_get_enum_double, max98090_put_enum_double), + SOC_SINGLE_EXT("ADC Quantizer Dither", M98090_REG_ADC_CONTROL, + M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), + SOC_ENUM_EXT("ADC High Performance Mode", max98090_adchp_enum, + snd_soc_get_enum_double, max98090_put_enum_double), + + SOC_SINGLE_EXT("DAC Mono Mode", M98090_REG_IO_CONFIGURATION, + M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), + SOC_SINGLE_EXT("SDIN Mode", M98090_REG_IO_CONFIGURATION, + M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), + SOC_SINGLE_EXT("SDOUT Mode", M98090_REG_IO_CONFIGURATION, + M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), + SOC_SINGLE_EXT("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION, + M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1, + snd_soc_get_volsw, max98090_put_volsw), + SOC_ENUM_EXT("Filter Mode", max98090_mode_enum, + snd_soc_get_enum_double, max98090_put_enum_double), + SOC_SINGLE_EXT("Record Path DC Blocking", M98090_REG_FILTER_CONFIG, + M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), + SOC_SINGLE_EXT("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG, + M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), SOC_SINGLE_TLV("Digital BQ Volume", M98090_REG_ADC_BIQUAD_LEVEL, M98090_AVBQ_SHIFT, M98090_AVBQ_NUM - 1, 1, max98090_dv_tlv), SOC_SINGLE_EXT_TLV("Digital Sidetone Volume", @@ -594,13 +732,17 @@ static const struct snd_kcontrol_new max98090_snd_controls[] = { SOC_SINGLE_TLV("Digital Volume", M98090_REG_DAI_PLAYBACK_LEVEL, M98090_DV_SHIFT, M98090_DV_NUM - 1, 1, max98090_dv_tlv), - SND_SOC_BYTES("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105), - SOC_SINGLE("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE, - M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0), - SOC_SINGLE("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE, - M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0), - SOC_SINGLE("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE, - M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0), + SND_SOC_BYTES_E("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105, + snd_soc_bytes_get, max98090_bytes_put), + SOC_SINGLE_EXT("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), + SOC_SINGLE_EXT("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), + SOC_SINGLE_EXT("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), SOC_SINGLE("Digital EQ Clipping Detection", M98090_REG_DAI_PLAYBACK_LEVEL_EQ, M98090_EQCLPN_SHIFT, M98090_EQCLPN_NUM - 1, 1), @@ -608,25 +750,34 @@ static const struct snd_kcontrol_new max98090_snd_controls[] = { M98090_DVEQ_SHIFT, M98090_DVEQ_NUM - 1, 1, max98090_dv_tlv), - SOC_SINGLE("ALC Enable", M98090_REG_DRC_TIMING, - M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0), - SOC_ENUM("ALC Attack Time", max98090_drcatk_enum), - SOC_ENUM("ALC Release Time", max98090_drcrls_enum), + SOC_SINGLE_EXT("ALC Enable", M98090_REG_DRC_TIMING, + M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), + SOC_ENUM_EXT("ALC Attack Time", max98090_drcatk_enum, + snd_soc_get_enum_double, max98090_put_enum_double), + SOC_ENUM_EXT("ALC Release Time", max98090_drcrls_enum, + snd_soc_get_enum_double, max98090_put_enum_double), SOC_SINGLE_TLV("ALC Make Up Volume", M98090_REG_DRC_GAIN, M98090_DRCG_SHIFT, M98090_DRCG_NUM - 1, 0, max98090_alcmakeup_tlv), - SOC_ENUM("ALC Compression Ratio", max98090_alccmp_enum), - SOC_ENUM("ALC Expansion Ratio", max98090_drcexp_enum), - SOC_SINGLE_TLV("ALC Compression Threshold Volume", + SOC_ENUM_EXT("ALC Compression Ratio", max98090_alccmp_enum, + snd_soc_get_enum_double, max98090_put_enum_double), + SOC_ENUM_EXT("ALC Expansion Ratio", max98090_drcexp_enum, + snd_soc_get_enum_double, max98090_put_enum_double), + SOC_SINGLE_EXT_TLV("ALC Compression Threshold Volume", M98090_REG_DRC_COMPRESSOR, M98090_DRCTHC_SHIFT, - M98090_DRCTHC_NUM - 1, 1, max98090_alccomp_tlv), - SOC_SINGLE_TLV("ALC Expansion Threshold Volume", + M98090_DRCTHC_NUM - 1, 1, + snd_soc_get_volsw, max98090_put_volsw, max98090_alccomp_tlv), + SOC_SINGLE_EXT_TLV("ALC Expansion Threshold Volume", M98090_REG_DRC_EXPANDER, M98090_DRCTHE_SHIFT, - M98090_DRCTHE_NUM - 1, 1, max98090_drcexp_tlv), + M98090_DRCTHE_NUM - 1, 1, + snd_soc_get_volsw, max98090_put_volsw, max98090_drcexp_tlv), - SOC_ENUM("DAC HP Playback Performance Mode", - max98090_dac_perfmode_enum), - SOC_ENUM("DAC High Performance Mode", max98090_dachp_enum), + SOC_ENUM_EXT("DAC HP Playback Performance Mode", + max98090_dac_perfmode_enum, + snd_soc_get_enum_double, max98090_put_enum_double), + SOC_ENUM_EXT("DAC High Performance Mode", max98090_dachp_enum, + snd_soc_get_enum_double, max98090_put_enum_double), SOC_SINGLE_TLV("Headphone Left Mixer Volume", M98090_REG_HP_CONTROL, M98090_MIXHPLG_SHIFT, @@ -684,9 +835,12 @@ static const struct snd_kcontrol_new max98090_snd_controls[] = { SOC_SINGLE("Volume Adjustment Smoothing", M98090_REG_LEVEL_CONTROL, M98090_VSENN_SHIFT, M98090_VSENN_NUM - 1, 1), - SND_SOC_BYTES("Biquad Coefficients", M98090_REG_RECORD_BIQUAD_BASE, 15), - SOC_SINGLE("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE, - M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0), + SND_SOC_BYTES_E("Biquad Coefficients", + M98090_REG_RECORD_BIQUAD_BASE, 15, + snd_soc_bytes_get, max98090_bytes_put), + SOC_SINGLE_EXT("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), }; static const struct snd_kcontrol_new max98091_snd_controls[] = { @@ -695,10 +849,12 @@ static const struct snd_kcontrol_new max98091_snd_controls[] = { M98090_DMIC34_ZEROPAD_SHIFT, M98090_DMIC34_ZEROPAD_NUM - 1, 0), - SOC_ENUM("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum), - SOC_SINGLE("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG, + SOC_ENUM_EXT("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum, + snd_soc_get_enum_double, max98090_put_enum_double), + SOC_SINGLE_EXT("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG, M98090_FLT_DMIC34HPF_SHIFT, - M98090_FLT_DMIC34HPF_NUM - 1, 0), + M98090_FLT_DMIC34HPF_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), SOC_SINGLE_TLV("DMIC3 Boost Volume", M98090_REG_DMIC3_VOLUME, M98090_DMIC_AV3G_SHIFT, M98090_DMIC_AV3G_NUM - 1, 0, @@ -716,8 +872,9 @@ static const struct snd_kcontrol_new max98091_snd_controls[] = { SND_SOC_BYTES("DMIC34 Biquad Coefficients", M98090_REG_DMIC34_BIQUAD_BASE, 15), - SOC_SINGLE("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE, - M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0), + SOC_SINGLE_EXT("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0, + snd_soc_get_volsw, max98090_put_volsw), SOC_SINGLE_TLV("DMIC34 BQ PreAttenuation Volume", M98090_REG_DMIC34_BQ_PREATTEN, M98090_AV34BQ_SHIFT, @@ -771,19 +928,6 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w, return 0; } -static int max98090_shdn_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 max98090_priv *max98090 = snd_soc_component_get_drvdata(component); - - if (event & SND_SOC_DAPM_POST_PMU) - max98090->shdn_pending = true; - - return 0; - -} - static const char *mic1_mux_text[] = { "IN12", "IN56" }; static SOC_ENUM_SINGLE_DECL(mic1_mux_enum, @@ -884,10 +1028,14 @@ static SOC_ENUM_SINGLE_DECL(ltenr_mux_enum, lten_mux_text); static const struct snd_kcontrol_new max98090_ltenl_mux = - SOC_DAPM_ENUM("LTENL Mux", ltenl_mux_enum); + SOC_DAPM_ENUM_EXT("LTENL Mux", ltenl_mux_enum, + snd_soc_dapm_get_enum_double, + max98090_dapm_put_enum_double); static const struct snd_kcontrol_new max98090_ltenr_mux = - SOC_DAPM_ENUM("LTENR Mux", ltenr_mux_enum); + SOC_DAPM_ENUM_EXT("LTENR Mux", ltenr_mux_enum, + snd_soc_dapm_get_enum_double, + max98090_dapm_put_enum_double); static const char *lben_mux_text[] = { "Normal", "Loopback" }; @@ -902,10 +1050,14 @@ static SOC_ENUM_SINGLE_DECL(lbenr_mux_enum, lben_mux_text); static const struct snd_kcontrol_new max98090_lbenl_mux = - SOC_DAPM_ENUM("LBENL Mux", lbenl_mux_enum); + SOC_DAPM_ENUM_EXT("LBENL Mux", lbenl_mux_enum, + snd_soc_dapm_get_enum_double, + max98090_dapm_put_enum_double); static const struct snd_kcontrol_new max98090_lbenr_mux = - SOC_DAPM_ENUM("LBENR Mux", lbenr_mux_enum); + SOC_DAPM_ENUM_EXT("LBENR Mux", lbenr_mux_enum, + snd_soc_dapm_get_enum_double, + max98090_dapm_put_enum_double); static const char *stenl_mux_text[] = { "Normal", "Sidetone Left" }; @@ -1072,21 +1224,25 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { SND_SOC_DAPM_INPUT("IN56"), SND_SOC_DAPM_SUPPLY("MICBIAS", M98090_REG_INPUT_ENABLE, - M98090_MBEN_SHIFT, 0, NULL, 0), + M98090_MBEN_SHIFT, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_SUPPLY("SHDN", M98090_REG_DEVICE_SHUTDOWN, M98090_SHDNN_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("SDIEN", M98090_REG_IO_CONFIGURATION, - M98090_SDIEN_SHIFT, 0, NULL, 0), + M98090_SDIEN_SHIFT, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_SUPPLY("SDOEN", M98090_REG_IO_CONFIGURATION, - M98090_SDOEN_SHIFT, 0, NULL, 0), + M98090_SDOEN_SHIFT, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_SUPPLY("DMICL_ENA", M98090_REG_DIGITAL_MIC_ENABLE, - M98090_DIGMICL_SHIFT, 0, max98090_shdn_event, - SND_SOC_DAPM_POST_PMU), + M98090_DIGMICL_SHIFT, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_SUPPLY("DMICR_ENA", M98090_REG_DIGITAL_MIC_ENABLE, - M98090_DIGMICR_SHIFT, 0, max98090_shdn_event, - SND_SOC_DAPM_POST_PMU), + M98090_DIGMICR_SHIFT, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_SUPPLY("AHPF", M98090_REG_FILTER_CONFIG, - M98090_AHPF_SHIFT, 0, NULL, 0), + M98090_AHPF_SHIFT, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), /* * Note: Sysclk and misc power supplies are taken care of by SHDN @@ -1116,10 +1272,12 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { &max98090_lineb_mixer_controls[0], ARRAY_SIZE(max98090_lineb_mixer_controls)), - SND_SOC_DAPM_PGA("LINEA Input", M98090_REG_INPUT_ENABLE, - M98090_LINEAEN_SHIFT, 0, NULL, 0), - SND_SOC_DAPM_PGA("LINEB Input", M98090_REG_INPUT_ENABLE, - M98090_LINEBEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("LINEA Input", M98090_REG_INPUT_ENABLE, + M98090_LINEAEN_SHIFT, 0, NULL, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEB Input", M98090_REG_INPUT_ENABLE, + M98090_LINEBEN_SHIFT, 0, NULL, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0, &max98090_left_adc_mixer_controls[0], @@ -1130,11 +1288,11 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { ARRAY_SIZE(max98090_right_adc_mixer_controls)), SND_SOC_DAPM_ADC_E("ADCL", NULL, M98090_REG_INPUT_ENABLE, - M98090_ADLEN_SHIFT, 0, max98090_shdn_event, - SND_SOC_DAPM_POST_PMU), + M98090_ADLEN_SHIFT, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_ADC_E("ADCR", NULL, M98090_REG_INPUT_ENABLE, - M98090_ADREN_SHIFT, 0, max98090_shdn_event, - SND_SOC_DAPM_POST_PMU), + M98090_ADREN_SHIFT, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_AIF_OUT("AIFOUTL", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0), @@ -1162,10 +1320,12 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_DAC("DACL", NULL, M98090_REG_OUTPUT_ENABLE, - M98090_DALEN_SHIFT, 0), - SND_SOC_DAPM_DAC("DACR", NULL, M98090_REG_OUTPUT_ENABLE, - M98090_DAREN_SHIFT, 0), + SND_SOC_DAPM_DAC_E("DACL", NULL, M98090_REG_OUTPUT_ENABLE, + M98090_DALEN_SHIFT, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_DAC_E("DACR", NULL, M98090_REG_OUTPUT_ENABLE, + M98090_DAREN_SHIFT, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0, &max98090_left_hp_mixer_controls[0], @@ -1200,20 +1360,26 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { SND_SOC_DAPM_MUX("MIXHPRSEL Mux", SND_SOC_NOPM, 0, 0, &max98090_mixhprsel_mux), - SND_SOC_DAPM_PGA("HP Left Out", M98090_REG_OUTPUT_ENABLE, - M98090_HPLEN_SHIFT, 0, NULL, 0), - SND_SOC_DAPM_PGA("HP Right Out", M98090_REG_OUTPUT_ENABLE, - M98090_HPREN_SHIFT, 0, NULL, 0), - - SND_SOC_DAPM_PGA("SPK Left Out", M98090_REG_OUTPUT_ENABLE, - M98090_SPLEN_SHIFT, 0, NULL, 0), - SND_SOC_DAPM_PGA("SPK Right Out", M98090_REG_OUTPUT_ENABLE, - M98090_SPREN_SHIFT, 0, NULL, 0), - - SND_SOC_DAPM_PGA("RCV Left Out", M98090_REG_OUTPUT_ENABLE, - M98090_RCVLEN_SHIFT, 0, NULL, 0), - SND_SOC_DAPM_PGA("RCV Right Out", M98090_REG_OUTPUT_ENABLE, - M98090_RCVREN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("HP Left Out", M98090_REG_OUTPUT_ENABLE, + M98090_HPLEN_SHIFT, 0, NULL, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_PGA_E("HP Right Out", M98090_REG_OUTPUT_ENABLE, + M98090_HPREN_SHIFT, 0, NULL, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), + + SND_SOC_DAPM_PGA_E("SPK Left Out", M98090_REG_OUTPUT_ENABLE, + M98090_SPLEN_SHIFT, 0, NULL, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_PGA_E("SPK Right Out", M98090_REG_OUTPUT_ENABLE, + M98090_SPREN_SHIFT, 0, NULL, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), + + SND_SOC_DAPM_PGA_E("RCV Left Out", M98090_REG_OUTPUT_ENABLE, + M98090_RCVLEN_SHIFT, 0, NULL, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_PGA_E("RCV Right Out", M98090_REG_OUTPUT_ENABLE, + M98090_RCVREN_SHIFT, 0, NULL, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_OUTPUT("HPL"), SND_SOC_DAPM_OUTPUT("HPR"), @@ -1228,9 +1394,11 @@ static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = { SND_SOC_DAPM_INPUT("DMIC4"), SND_SOC_DAPM_SUPPLY("DMIC3_ENA", M98090_REG_DIGITAL_MIC_ENABLE, - M98090_DIGMIC3_SHIFT, 0, NULL, 0), + M98090_DIGMIC3_SHIFT, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_SUPPLY("DMIC4_ENA", M98090_REG_DIGITAL_MIC_ENABLE, - M98090_DIGMIC4_SHIFT, 0, NULL, 0), + M98090_DIGMIC4_SHIFT, 0, max98090_dapm_event, + SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD), }; static const struct snd_soc_dapm_route max98090_dapm_routes[] = { @@ -1501,6 +1669,11 @@ static void max98090_configure_bclk(struct snd_soc_component *component) return; } + /* + * Master mode: no need to save and restore SHDN for the following + * sensitive registers. + */ + /* Check for supported PCLK to LRCLK ratios */ for (i = 0; i < ARRAY_SIZE(pclk_rates); i++) { if ((pclk_rates[i] == max98090->sysclk) && @@ -1587,12 +1760,14 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: /* Set to slave mode PLL - MAS mode off */ + max98090_shdn_save(max98090); snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_MSB, 0x00); snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_LSB, 0x00); snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE, M98090_USE_M1_MASK, 0); + max98090_shdn_restore(max98090); max98090->master = false; break; case SND_SOC_DAIFMT_CBM_CFM: @@ -1618,7 +1793,9 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, dev_err(component->dev, "DAI clock mode unsupported"); return -EINVAL; } + max98090_shdn_save(max98090); snd_soc_component_write(component, M98090_REG_MASTER_MODE, regval); + max98090_shdn_restore(max98090); regval = 0; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -1663,8 +1840,10 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, if (max98090->tdm_slots > 1) regval ^= M98090_BCI_MASK; + max98090_shdn_save(max98090); snd_soc_component_write(component, M98090_REG_INTERFACE_FORMAT, regval); + max98090_shdn_restore(max98090); } return 0; @@ -1676,6 +1855,7 @@ static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai, struct snd_soc_component *component = codec_dai->component; struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); struct max98090_cdata *cdata; + cdata = &max98090->dai[0]; if (slots < 0 || slots > 4) @@ -1685,6 +1865,7 @@ static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai, max98090->tdm_width = slot_width; if (max98090->tdm_slots > 1) { + max98090_shdn_save(max98090); /* SLOTL SLOTR SLOTDLY */ snd_soc_component_write(component, M98090_REG_TDM_FORMAT, 0 << M98090_TDM_SLOTL_SHIFT | @@ -1695,6 +1876,7 @@ static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai, snd_soc_component_update_bits(component, M98090_REG_TDM_CONTROL, M98090_TDM_MASK, M98090_TDM_MASK); + max98090_shdn_restore(max98090); } /* @@ -1894,6 +2076,7 @@ static int max98090_configure_dmic(struct max98090_priv *max98090, dmic_freq = dmic_table[pclk_index].settings[micclk_index].freq; dmic_comp = dmic_table[pclk_index].settings[micclk_index].comp[i]; + max98090_shdn_save(max98090); regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_ENABLE, M98090_MICCLK_MASK, micclk_index << M98090_MICCLK_SHIFT); @@ -1902,6 +2085,7 @@ static int max98090_configure_dmic(struct max98090_priv *max98090, M98090_DMIC_COMP_MASK | M98090_DMIC_FREQ_MASK, dmic_comp << M98090_DMIC_COMP_SHIFT | dmic_freq << M98090_DMIC_FREQ_SHIFT); + max98090_shdn_restore(max98090); return 0; } @@ -1938,8 +2122,10 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream, switch (params_width(params)) { case 16: + max98090_shdn_save(max98090); snd_soc_component_update_bits(component, M98090_REG_INTERFACE_FORMAT, M98090_WS_MASK, 0); + max98090_shdn_restore(max98090); break; default: return -EINVAL; @@ -1950,6 +2136,7 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream, cdata->rate = max98090->lrclk; + max98090_shdn_save(max98090); /* Update filter mode */ if (max98090->lrclk < 24000) snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG, @@ -1965,6 +2152,7 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream, else snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG, M98090_DHF_MASK, M98090_DHF_MASK); + max98090_shdn_restore(max98090); max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk, max98090->lrclk); @@ -1995,6 +2183,7 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai, * 0x02 (when master clk is 20MHz to 40MHz).. * 0x03 (when master clk is 40MHz to 60MHz).. */ + max98090_shdn_save(max98090); if ((freq >= 10000000) && (freq <= 20000000)) { snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK, M98090_PSCLK_DIV1); @@ -2009,8 +2198,10 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai, max98090->pclk = freq >> 2; } else { dev_err(component->dev, "Invalid master clock frequency\n"); + max98090_shdn_restore(max98090); return -EINVAL; } + max98090_shdn_restore(max98090); max98090->sysclk = freq; @@ -2122,10 +2313,12 @@ static void max98090_pll_work(struct max98090_priv *max98090) */ /* Toggle shutdown OFF then ON */ + mutex_lock(&component->card->dapm_mutex); snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN, M98090_SHDNN_MASK, 0); snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN, M98090_SHDNN_MASK, M98090_SHDNN_MASK); + mutex_unlock(&component->card->dapm_mutex); for (i = 0; i < 10; ++i) { /* Give PLL time to lock */ @@ -2448,7 +2641,12 @@ static int max98090_probe(struct snd_soc_component *component) */ snd_soc_component_read32(component, M98090_REG_DEVICE_STATUS); - /* High Performance is default */ + /* + * SHDN should be 0 at the point, no need to save/restore for the + * following registers. + * + * High Performance is default + */ snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL, M98090_DACHP_MASK, 1 << M98090_DACHP_SHIFT); @@ -2459,7 +2657,12 @@ static int max98090_probe(struct snd_soc_component *component) M98090_ADCHP_MASK, 1 << M98090_ADCHP_SHIFT); - /* Turn on VCM bandgap reference */ + /* + * SHDN should be 0 at the point, no need to save/restore for the + * following registers. + * + * Turn on VCM bandgap reference + */ snd_soc_component_write(component, M98090_REG_BIAS_CONTROL, M98090_VCM_MODE_MASK); @@ -2491,25 +2694,9 @@ static void max98090_remove(struct snd_soc_component *component) max98090->component = NULL; } -static void max98090_seq_notifier(struct snd_soc_component *component, - enum snd_soc_dapm_type event, int subseq) -{ - struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); - - if (max98090->shdn_pending) { - snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN, - M98090_SHDNN_MASK, 0); - msleep(40); - snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN, - M98090_SHDNN_MASK, M98090_SHDNN_MASK); - max98090->shdn_pending = false; - } -} - static const struct snd_soc_component_driver soc_component_dev_max98090 = { .probe = max98090_probe, .remove = max98090_remove, - .seq_notifier = max98090_seq_notifier, .set_bias_level = max98090_set_bias_level, .idle_bias_on = 1, .use_pmdown_time = 1, @@ -2651,17 +2838,12 @@ static int max98090_resume(struct device *dev) return 0; } - -static int max98090_suspend(struct device *dev) -{ - return 0; -} #endif static const struct dev_pm_ops max98090_pm = { SET_RUNTIME_PM_OPS(max98090_runtime_suspend, max98090_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(max98090_suspend, max98090_resume) + SET_SYSTEM_SLEEP_PM_OPS(NULL, max98090_resume) }; static const struct i2c_device_id max98090_i2c_id[] = { diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h index a197114b0dad..0a31708b7df7 100644 --- a/sound/soc/codecs/max98090.h +++ b/sound/soc/codecs/max98090.h @@ -1539,7 +1539,8 @@ struct max98090_priv { unsigned int pa2en; unsigned int sidetone; bool master; - bool shdn_pending; + int saved_count; + int saved_shdn; }; int max98090_mic_detect(struct snd_soc_component *component, diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index 1f7964beb20c..85bc7ae4d267 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -374,9 +374,8 @@ static void pm8916_wcd_analog_micbias_enable(struct snd_soc_component *component } -static int pm8916_wcd_analog_enable_micbias_ext(struct snd_soc_component - *component, int event, - int reg, unsigned int cap_mode) +static int pm8916_wcd_analog_enable_micbias(struct snd_soc_component *component, + int event, unsigned int cap_mode) { switch (event) { case SND_SOC_DAPM_POST_PMU: @@ -389,72 +388,44 @@ static int pm8916_wcd_analog_enable_micbias_ext(struct snd_soc_component return 0; } -static int pm8916_wcd_analog_enable_micbias_int(struct snd_soc_component - *component, int event, - int reg, u32 cap_mode) +static int pm8916_wcd_analog_enable_micbias_int(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_update_bits(component, reg, MICB_1_EN_PULL_DOWN_EN_MASK, 0); snd_soc_component_update_bits(component, CDC_A_MICB_1_EN, MICB_1_EN_OPA_STG2_TAIL_CURR_MASK, MICB_1_EN_OPA_STG2_TAIL_CURR_1_60UA); - - break; - case SND_SOC_DAPM_POST_PMU: - pm8916_wcd_analog_micbias_enable(component); - snd_soc_component_update_bits(component, CDC_A_MICB_1_EN, - MICB_1_EN_BYP_CAP_MASK, cap_mode); break; } return 0; } -static int pm8916_wcd_analog_enable_micbias_ext1(struct - snd_soc_dapm_widget - *w, struct snd_kcontrol - *kcontrol, int event) +static int pm8916_wcd_analog_enable_micbias1(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 pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component); - return pm8916_wcd_analog_enable_micbias_ext(component, event, w->reg, - wcd->micbias1_cap_mode); + return pm8916_wcd_analog_enable_micbias(component, event, + wcd->micbias1_cap_mode); } -static int pm8916_wcd_analog_enable_micbias_ext2(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 pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component); - - return pm8916_wcd_analog_enable_micbias_ext(component, event, w->reg, - wcd->micbias2_cap_mode); - -} - -static int pm8916_wcd_analog_enable_micbias_int1(struct - snd_soc_dapm_widget - *w, struct snd_kcontrol - *kcontrol, int event) +static int pm8916_wcd_analog_enable_micbias2(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 pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component); - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - snd_soc_component_update_bits(component, CDC_A_MICB_1_INT_RBIAS, - MICB_1_INT_TX1_INT_RBIAS_EN_MASK, - MICB_1_INT_TX1_INT_RBIAS_EN_ENABLE); - break; - } + return pm8916_wcd_analog_enable_micbias(component, event, + wcd->micbias2_cap_mode); - return pm8916_wcd_analog_enable_micbias_int(component, event, w->reg, - wcd->micbias1_cap_mode); } static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv, @@ -564,9 +535,8 @@ static int pm8916_wcd_analog_enable_micbias_int2(struct switch (event) { case SND_SOC_DAPM_PRE_PMU: - snd_soc_component_update_bits(component, CDC_A_MICB_1_INT_RBIAS, - MICB_1_INT_TX2_INT_RBIAS_EN_MASK, - MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE); + snd_soc_component_update_bits(component, CDC_A_MICB_2_EN, + CDC_A_MICB_2_PULL_DOWN_EN_MASK, 0); break; case SND_SOC_DAPM_POST_PMU: pm8916_mbhc_configure_bias(wcd, true); @@ -576,8 +546,7 @@ static int pm8916_wcd_analog_enable_micbias_int2(struct break; } - return pm8916_wcd_analog_enable_micbias_int(component, event, w->reg, - wcd->micbias2_cap_mode); + return pm8916_wcd_analog_enable_micbias_int(w, kcontrol, event); } static int pm8916_wcd_analog_enable_adc(struct snd_soc_dapm_widget *w, @@ -878,14 +847,16 @@ static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = { {"SPK PA", NULL, "SPK DAC"}, {"SPK DAC", "Switch", "PDM_RX3"}, - {"MIC BIAS Internal1", NULL, "INT_LDO_H"}, - {"MIC BIAS Internal2", NULL, "INT_LDO_H"}, - {"MIC BIAS External1", NULL, "INT_LDO_H"}, - {"MIC BIAS External2", NULL, "INT_LDO_H"}, - {"MIC BIAS Internal1", NULL, "vdd-micbias"}, - {"MIC BIAS Internal2", NULL, "vdd-micbias"}, - {"MIC BIAS External1", NULL, "vdd-micbias"}, - {"MIC BIAS External2", NULL, "vdd-micbias"}, + {"MIC_BIAS1", NULL, "INT_LDO_H"}, + {"MIC_BIAS2", NULL, "INT_LDO_H"}, + {"MIC_BIAS1", NULL, "vdd-micbias"}, + {"MIC_BIAS2", NULL, "vdd-micbias"}, + + {"MIC BIAS External1", NULL, "MIC_BIAS1"}, + {"MIC BIAS Internal1", NULL, "MIC_BIAS1"}, + {"MIC BIAS External2", NULL, "MIC_BIAS2"}, + {"MIC BIAS Internal2", NULL, "MIC_BIAS2"}, + {"MIC BIAS Internal3", NULL, "MIC_BIAS1"}, }; static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { @@ -937,21 +908,26 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("RX_BIAS", CDC_A_RX_COM_BIAS_DAC, 7, 0, NULL, 0), /* TX */ - SND_SOC_DAPM_SUPPLY("MIC BIAS Internal1", CDC_A_MICB_1_EN, 7, 0, - pm8916_wcd_analog_enable_micbias_int1, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY("MIC BIAS Internal2", CDC_A_MICB_2_EN, 7, 0, + SND_SOC_DAPM_SUPPLY("MIC_BIAS1", CDC_A_MICB_1_EN, 7, 0, + pm8916_wcd_analog_enable_micbias1, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("MIC_BIAS2", CDC_A_MICB_2_EN, 7, 0, + pm8916_wcd_analog_enable_micbias2, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("MIC BIAS External1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIC BIAS External2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal1", CDC_A_MICB_1_INT_RBIAS, 7, 0, + pm8916_wcd_analog_enable_micbias_int, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal2", CDC_A_MICB_1_INT_RBIAS, 4, 0, pm8916_wcd_analog_enable_micbias_int2, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_SUPPLY("MIC BIAS External1", CDC_A_MICB_1_EN, 7, 0, - pm8916_wcd_analog_enable_micbias_ext1, - SND_SOC_DAPM_POST_PMU), - SND_SOC_DAPM_SUPPLY("MIC BIAS External2", CDC_A_MICB_2_EN, 7, 0, - pm8916_wcd_analog_enable_micbias_ext2, - SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal3", CDC_A_MICB_1_INT_RBIAS, 1, 0, + pm8916_wcd_analog_enable_micbias_int, + SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_ADC_E("ADC1", NULL, CDC_A_TX_1_EN, 7, 0, pm8916_wcd_analog_enable_adc, diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c new file mode 100644 index 000000000000..a36c416caad4 --- /dev/null +++ b/sound/soc/codecs/mt6660.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0 // + +// Copyright (c) 2019 MediaTek Inc. + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/pm_runtime.h> +#include <linux/delay.h> +#include <linux/debugfs.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <sound/pcm_params.h> + +#include "mt6660.h" + +struct reg_size_table { + u32 addr; + u8 size; +}; + +static const struct reg_size_table mt6660_reg_size_table[] = { + { MT6660_REG_HPF1_COEF, 4 }, + { MT6660_REG_HPF2_COEF, 4 }, + { MT6660_REG_TDM_CFG3, 2 }, + { MT6660_REG_RESV17, 2 }, + { MT6660_REG_RESV23, 2 }, + { MT6660_REG_SIGMAX, 2 }, + { MT6660_REG_DEVID, 2 }, + { MT6660_REG_HCLIP_CTRL, 2 }, + { MT6660_REG_DA_GAIN, 2 }, +}; + +static int mt6660_get_reg_size(uint32_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt6660_reg_size_table); i++) { + if (mt6660_reg_size_table[i].addr == addr) + return mt6660_reg_size_table[i].size; + } + return 1; +} + +static int mt6660_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct mt6660_chip *chip = context; + int size = mt6660_get_reg_size(reg); + u8 reg_data[4]; + int i, ret; + + for (i = 0; i < size; i++) + reg_data[size - i - 1] = (val >> (8 * i)) & 0xff; + + ret = i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data); + return ret; +} + +static int mt6660_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct mt6660_chip *chip = context; + int size = mt6660_get_reg_size(reg); + int i, ret; + u8 data[4]; + u32 reg_data = 0; + + ret = i2c_smbus_read_i2c_block_data(chip->i2c, reg, size, data); + if (ret < 0) + return ret; + for (i = 0; i < size; i++) { + reg_data <<= 8; + reg_data |= data[i]; + } + *val = reg_data; + return 0; +} + +static const struct regmap_config mt6660_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .reg_write = mt6660_reg_write, + .reg_read = mt6660_reg_read, +}; + +static int mt6660_codec_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (event == SND_SOC_DAPM_POST_PMU) + usleep_range(1000, 1100); + return 0; +} + +static int mt6660_codec_classd_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); + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(component->dev, + "%s: before classd turn on\n", __func__); + /* config to adaptive mode */ + ret = snd_soc_component_update_bits(component, + MT6660_REG_BST_CTRL, 0x03, 0x03); + if (ret < 0) { + dev_err(component->dev, "config mode adaptive fail\n"); + return ret; + } + break; + case SND_SOC_DAPM_POST_PMU: + /* voltage sensing enable */ + ret = snd_soc_component_update_bits(component, + MT6660_REG_RESV7, 0x04, 0x04); + if (ret < 0) { + dev_err(component->dev, + "enable voltage sensing fail\n"); + return ret; + } + dev_dbg(component->dev, "Amp on\n"); + break; + case SND_SOC_DAPM_PRE_PMD: + dev_dbg(component->dev, "Amp off\n"); + /* voltage sensing disable */ + ret = snd_soc_component_update_bits(component, + MT6660_REG_RESV7, 0x04, 0x00); + if (ret < 0) { + dev_err(component->dev, + "disable voltage sensing fail\n"); + return ret; + } + /* pop-noise improvement 1 */ + ret = snd_soc_component_update_bits(component, + MT6660_REG_RESV10, 0x10, 0x10); + if (ret < 0) { + dev_err(component->dev, + "pop-noise improvement 1 fail\n"); + return ret; + } + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(component->dev, + "%s: after classd turn off\n", __func__); + /* pop-noise improvement 2 */ + ret = snd_soc_component_update_bits(component, + MT6660_REG_RESV10, 0x10, 0x00); + if (ret < 0) { + dev_err(component->dev, + "pop-noise improvement 2 fail\n"); + return ret; + } + /* config to off mode */ + ret = snd_soc_component_update_bits(component, + MT6660_REG_BST_CTRL, 0x03, 0x00); + if (ret < 0) { + dev_err(component->dev, "config mode off fail\n"); + return ret; + } + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget mt6660_component_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("DAC", NULL, MT6660_REG_PLL_CFG1, + 0, 1, mt6660_codec_dac_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_ADC("VI ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_PGA("PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV_E("ClassD", MT6660_REG_SYSTEM_CTRL, 2, 0, + NULL, 0, mt6660_codec_classd_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("OUTP"), + SND_SOC_DAPM_OUTPUT("OUTN"), +}; + +static const struct snd_soc_dapm_route mt6660_component_dapm_routes[] = { + { "DAC", NULL, "aif_playback" }, + { "PGA", NULL, "DAC" }, + { "ClassD", NULL, "PGA" }, + { "OUTP", NULL, "ClassD" }, + { "OUTN", NULL, "ClassD" }, + { "VI ADC", NULL, "ClassD" }, + { "aif_capture", NULL, "VI ADC" }, +}; + +static int mt6660_component_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct mt6660_chip *chip = (struct mt6660_chip *) + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = chip->chip_rev & 0x0f; + return 0; +} + +static const DECLARE_TLV_DB_SCALE(vol_ctl_tlv, -1155, 5, 0); + +static const struct snd_kcontrol_new mt6660_component_snd_controls[] = { + SOC_SINGLE_TLV("Digital Volume", MT6660_REG_VOL_CTRL, 0, 255, + 1, vol_ctl_tlv), + SOC_SINGLE("Hard Clip Switch", MT6660_REG_HCLIP_CTRL, 8, 1, 0), + SOC_SINGLE("Clip Switch", MT6660_REG_SPS_CTRL, 0, 1, 0), + SOC_SINGLE("Boost Mode", MT6660_REG_BST_CTRL, 0, 3, 0), + SOC_SINGLE("DRE Switch", MT6660_REG_DRE_CTRL, 0, 1, 0), + SOC_SINGLE("DC Protect Switch", MT6660_REG_DC_PROTECT_CTRL, 3, 1, 0), + SOC_SINGLE("Data Output Left Channel Selection", + MT6660_REG_DATAO_SEL, 3, 7, 0), + SOC_SINGLE("Data Output Right Channel Selection", + MT6660_REG_DATAO_SEL, 0, 7, 0), + SOC_SINGLE_EXT("T0 SEL", MT6660_REG_CALI_T0, 0, 7, 0, + snd_soc_get_volsw, NULL), + SOC_SINGLE_EXT("Chip Rev", MT6660_REG_DEVID, 8, 15, 0, + mt6660_component_get_volsw, NULL), +}; + +static int _mt6660_chip_power_on(struct mt6660_chip *chip, int on_off) +{ + return regmap_write_bits(chip->regmap, MT6660_REG_SYSTEM_CTRL, + 0x01, on_off ? 0x00 : 0x01); +} + +static int mt6660_component_probe(struct snd_soc_component *component) +{ + struct mt6660_chip *chip = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s\n", __func__); + snd_soc_component_init_regmap(component, chip->regmap); + + return 0; +} + +static void mt6660_component_remove(struct snd_soc_component *component) +{ + dev_dbg(component->dev, "%s\n", __func__); + snd_soc_component_exit_regmap(component); +} + +static const struct snd_soc_component_driver mt6660_component_driver = { + .probe = mt6660_component_probe, + .remove = mt6660_component_remove, + + .controls = mt6660_component_snd_controls, + .num_controls = ARRAY_SIZE(mt6660_component_snd_controls), + .dapm_widgets = mt6660_component_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt6660_component_dapm_widgets), + .dapm_routes = mt6660_component_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(mt6660_component_dapm_routes), + + .idle_bias_on = false, /* idle_bias_off = true */ +}; + +static int mt6660_component_aif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) +{ + int word_len = params_physical_width(hw_params); + int aud_bit = params_width(hw_params); + u16 reg_data = 0; + int ret; + + dev_dbg(dai->dev, "%s: ++\n", __func__); + dev_dbg(dai->dev, "format: 0x%08x\n", params_format(hw_params)); + dev_dbg(dai->dev, "rate: 0x%08x\n", params_rate(hw_params)); + dev_dbg(dai->dev, "word_len: %d, aud_bit: %d\n", word_len, aud_bit); + if (word_len > 32 || word_len < 16) { + dev_err(dai->dev, "not supported word length\n"); + return -ENOTSUPP; + } + switch (aud_bit) { + case 16: + reg_data = 3; + break; + case 18: + reg_data = 2; + break; + case 20: + reg_data = 1; + break; + case 24: + case 32: + reg_data = 0; + break; + default: + return -ENOTSUPP; + } + ret = snd_soc_component_update_bits(dai->component, + MT6660_REG_SERIAL_CFG1, 0xc0, (reg_data << 6)); + if (ret < 0) { + dev_err(dai->dev, "config aud bit fail\n"); + return ret; + } + ret = snd_soc_component_update_bits(dai->component, + MT6660_REG_TDM_CFG3, 0x3f0, word_len << 4); + if (ret < 0) { + dev_err(dai->dev, "config word len fail\n"); + return ret; + } + dev_dbg(dai->dev, "%s: --\n", __func__); + return 0; +} + +static const struct snd_soc_dai_ops mt6660_component_aif_ops = { + .hw_params = mt6660_component_aif_hw_params, +}; + +#define STUB_RATES SNDRV_PCM_RATE_8000_192000 +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_U32_LE) + +static struct snd_soc_dai_driver mt6660_codec_dai = { + .name = "mt6660-aif", + .playback = { + .stream_name = "aif_playback", + .channels_min = 1, + .channels_max = 2, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, + .capture = { + .stream_name = "aif_capture", + .channels_min = 1, + .channels_max = 2, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, + /* dai properties */ + .symmetric_rates = 1, + .symmetric_channels = 1, + .symmetric_samplebits = 1, + /* dai operations */ + .ops = &mt6660_component_aif_ops, +}; + +static int _mt6660_chip_id_check(struct mt6660_chip *chip) +{ + int ret; + unsigned int val; + + ret = regmap_read(chip->regmap, MT6660_REG_DEVID, &val); + if (ret < 0) + return ret; + val &= 0x0ff0; + if (val != 0x00e0 && val != 0x01e0) { + dev_err(chip->dev, "%s id(%x) not match\n", __func__, val); + return -ENODEV; + } + return 0; +} + +static int _mt6660_chip_sw_reset(struct mt6660_chip *chip) +{ + int ret; + + /* turn on main pll first, then trigger reset */ + ret = regmap_write(chip->regmap, MT6660_REG_SYSTEM_CTRL, 0x00); + if (ret < 0) + return ret; + ret = regmap_write(chip->regmap, MT6660_REG_SYSTEM_CTRL, 0x80); + if (ret < 0) + return ret; + msleep(30); + return 0; +} + +static int _mt6660_read_chip_revision(struct mt6660_chip *chip) +{ + int ret; + unsigned int val; + + ret = regmap_read(chip->regmap, MT6660_REG_DEVID, &val); + if (ret < 0) { + dev_err(chip->dev, "get chip revision fail\n"); + return ret; + } + chip->chip_rev = val&0xff; + dev_info(chip->dev, "%s chip_rev = %x\n", __func__, chip->chip_rev); + return 0; +} + +static int mt6660_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mt6660_chip *chip = NULL; + int ret; + + dev_dbg(&client->dev, "%s\n", __func__); + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + chip->i2c = client; + chip->dev = &client->dev; + mutex_init(&chip->io_lock); + i2c_set_clientdata(client, chip); + + chip->regmap = devm_regmap_init(&client->dev, + NULL, chip, &mt6660_regmap_config); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + dev_err(&client->dev, "failed to initialise regmap: %d\n", ret); + return ret; + } + + /* chip reset first */ + ret = _mt6660_chip_sw_reset(chip); + if (ret < 0) { + dev_err(chip->dev, "chip reset fail\n"); + goto probe_fail; + } + /* chip power on */ + ret = _mt6660_chip_power_on(chip, 1); + if (ret < 0) { + dev_err(chip->dev, "chip power on 2 fail\n"); + goto probe_fail; + } + /* chip devid check */ + ret = _mt6660_chip_id_check(chip); + if (ret < 0) { + dev_err(chip->dev, "chip id check fail\n"); + goto probe_fail; + } + /* chip revision get */ + ret = _mt6660_read_chip_revision(chip); + if (ret < 0) { + dev_err(chip->dev, "read chip revision fail\n"); + goto probe_fail; + } + pm_runtime_set_active(chip->dev); + pm_runtime_enable(chip->dev); + + ret = devm_snd_soc_register_component(chip->dev, + &mt6660_component_driver, + &mt6660_codec_dai, 1); + return ret; +probe_fail: + _mt6660_chip_power_on(chip, 0); + mutex_destroy(&chip->io_lock); + return ret; +} + +static int mt6660_i2c_remove(struct i2c_client *client) +{ + struct mt6660_chip *chip = i2c_get_clientdata(client); + + pm_runtime_disable(chip->dev); + pm_runtime_set_suspended(chip->dev); + mutex_destroy(&chip->io_lock); + return 0; +} + +static int __maybe_unused mt6660_i2c_runtime_suspend(struct device *dev) +{ + struct mt6660_chip *chip = dev_get_drvdata(dev); + + dev_dbg(dev, "enter low power mode\n"); + return regmap_update_bits(chip->regmap, + MT6660_REG_SYSTEM_CTRL, 0x01, 0x01); +} + +static int __maybe_unused mt6660_i2c_runtime_resume(struct device *dev) +{ + struct mt6660_chip *chip = dev_get_drvdata(dev); + + dev_dbg(dev, "exit low power mode\n"); + return regmap_update_bits(chip->regmap, + MT6660_REG_SYSTEM_CTRL, 0x01, 0x00); +} + +static const struct dev_pm_ops mt6660_dev_pm_ops = { + SET_RUNTIME_PM_OPS(mt6660_i2c_runtime_suspend, + mt6660_i2c_runtime_resume, NULL) +}; + +static const struct of_device_id __maybe_unused mt6660_of_id[] = { + { .compatible = "mediatek,mt6660",}, + {}, +}; +MODULE_DEVICE_TABLE(of, mt6660_of_id); + +static const struct i2c_device_id mt6660_i2c_id[] = { + {"mt6660", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, mt6660_i2c_id); + +static struct i2c_driver mt6660_i2c_driver = { + .driver = { + .name = "mt6660", + .of_match_table = of_match_ptr(mt6660_of_id), + .pm = &mt6660_dev_pm_ops, + }, + .probe = mt6660_i2c_probe, + .remove = mt6660_i2c_remove, + .id_table = mt6660_i2c_id, +}; +module_i2c_driver(mt6660_i2c_driver); + +MODULE_AUTHOR("Jeff Chang <jeff_chang@richtek.com>"); +MODULE_DESCRIPTION("MT6660 SPKAMP Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0.7_G"); diff --git a/sound/soc/codecs/mt6660.h b/sound/soc/codecs/mt6660.h new file mode 100644 index 000000000000..054a3c56ec1f --- /dev/null +++ b/sound/soc/codecs/mt6660.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 MediaTek Inc. + */ + +#ifndef __SND_SOC_MT6660_H +#define __SND_SOC_MT6660_H + +#include <linux/mutex.h> +#include <linux/regmap.h> + +#pragma pack(push, 1) +struct mt6660_platform_data { + u8 init_setting_num; + u32 *init_setting_addr; + u32 *init_setting_mask; + u32 *init_setting_val; +}; + +struct mt6660_chip { + struct i2c_client *i2c; + struct device *dev; + struct platform_device *param_dev; + struct mt6660_platform_data plat_data; + struct mutex io_lock; + struct regmap *regmap; + u16 chip_rev; +}; +#pragma pack(pop) + +#define MT6660_REG_DEVID (0x00) +#define MT6660_REG_SYSTEM_CTRL (0x03) +#define MT6660_REG_IRQ_STATUS1 (0x05) +#define MT6660_REG_ADDA_CLOCK (0x07) +#define MT6660_REG_SERIAL_CFG1 (0x10) +#define MT6660_REG_DATAO_SEL (0x12) +#define MT6660_REG_TDM_CFG3 (0x15) +#define MT6660_REG_HPF_CTRL (0x18) +#define MT6660_REG_HPF1_COEF (0x1A) +#define MT6660_REG_HPF2_COEF (0x1B) +#define MT6660_REG_PATH_BYPASS (0x1E) +#define MT6660_REG_WDT_CTRL (0x20) +#define MT6660_REG_HCLIP_CTRL (0x24) +#define MT6660_REG_VOL_CTRL (0x29) +#define MT6660_REG_SPS_CTRL (0x30) +#define MT6660_REG_SIGMAX (0x33) +#define MT6660_REG_CALI_T0 (0x3F) +#define MT6660_REG_BST_CTRL (0x40) +#define MT6660_REG_PROTECTION_CFG (0x46) +#define MT6660_REG_DA_GAIN (0x4c) +#define MT6660_REG_AUDIO_IN2_SEL (0x50) +#define MT6660_REG_SIG_GAIN (0x51) +#define MT6660_REG_PLL_CFG1 (0x60) +#define MT6660_REG_DRE_CTRL (0x68) +#define MT6660_REG_DRE_THDMODE (0x69) +#define MT6660_REG_DRE_CORASE (0x6B) +#define MT6660_REG_PWM_CTRL (0x70) +#define MT6660_REG_DC_PROTECT_CTRL (0x74) +#define MT6660_REG_ADC_USB_MODE (0x7c) +#define MT6660_REG_INTERNAL_CFG (0x88) +#define MT6660_REG_RESV0 (0x98) +#define MT6660_REG_RESV1 (0x99) +#define MT6660_REG_RESV2 (0x9A) +#define MT6660_REG_RESV3 (0x9B) +#define MT6660_REG_RESV6 (0xA2) +#define MT6660_REG_RESV7 (0xA3) +#define MT6660_REG_RESV10 (0xB0) +#define MT6660_REG_RESV11 (0xB1) +#define MT6660_REG_RESV16 (0xB6) +#define MT6660_REG_RESV17 (0xB7) +#define MT6660_REG_RESV19 (0xB9) +#define MT6660_REG_RESV21 (0xBB) +#define MT6660_REG_RESV23 (0xBD) +#define MT6660_REG_RESV31 (0xD3) +#define MT6660_REG_RESV40 (0xE0) + +#endif /* __SND_SOC_MT6660_H */ diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c index 2552073e54ce..dec5638060c3 100644 --- a/sound/soc/codecs/rt1011.c +++ b/sound/soc/codecs/rt1011.c @@ -40,7 +40,6 @@ static const struct reg_sequence init_list[] = { { RT1011_ADC_SET_5, 0x0a20 }, { RT1011_DAC_SET_2, 0xa032 }, - { RT1011_ADC_SET_1, 0x2925 }, { RT1011_SPK_PRO_DC_DET_1, 0xb00c }, { RT1011_SPK_PRO_DC_DET_2, 0xcccc }, @@ -2186,7 +2185,6 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag) /* ADC/DAC setting */ regmap_write(rt1011->regmap, RT1011_ADC_SET_5, 0x0a20); regmap_write(rt1011->regmap, RT1011_DAC_SET_2, 0xe232); - regmap_write(rt1011->regmap, RT1011_ADC_SET_1, 0x2925); regmap_write(rt1011->regmap, RT1011_ADC_SET_4, 0xc000); /* DC detection */ @@ -2235,8 +2233,18 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag) dc_offset |= (value & 0xffff); dev_info(dev, "Gain1 offset=0x%x\n", dc_offset); + /* check the package info. */ + regmap_read(rt1011->regmap, RT1011_EFUSE_MATCH_DONE, &value); + if (value & 0x4) + rt1011->pack_id = 1; if (cali_flag) { + + if (rt1011->pack_id) + regmap_write(rt1011->regmap, RT1011_ADC_SET_1, 0x292c); + else + regmap_write(rt1011->regmap, RT1011_ADC_SET_1, 0x2925); + /* Class D on */ regmap_write(rt1011->regmap, RT1011_CLASS_D_POS, 0x010e); regmap_write(rt1011->regmap, @@ -2361,6 +2369,11 @@ static void rt1011_calibration_work(struct work_struct *work) rt1011_r0_load(rt1011); } + + if (rt1011->pack_id) + snd_soc_component_write(component, RT1011_ADC_SET_1, 0x292c); + else + snd_soc_component_write(component, RT1011_ADC_SET_1, 0x2925); } static int rt1011_parse_dp(struct rt1011_priv *rt1011, struct device *dev) diff --git a/sound/soc/codecs/rt1011.h b/sound/soc/codecs/rt1011.h index 68fadc15fa8c..f3a9a96640f1 100644 --- a/sound/soc/codecs/rt1011.h +++ b/sound/soc/codecs/rt1011.h @@ -692,6 +692,7 @@ struct rt1011_priv { unsigned int r0_reg, cali_done; unsigned int r0_calib, temperature_calib; int recv_spk_mode; + unsigned int pack_id; /* 0: WLCSP; 1: QFN */ }; #endif /* end of _RT1011_H_ */ diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c new file mode 100644 index 000000000000..4a9c5b54008f --- /dev/null +++ b/sound/soc/codecs/rt1015.c @@ -0,0 +1,993 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt1015.c -- RT1015 ALSA SoC audio amplifier driver +// +// Copyright 2019 Realtek Semiconductor Corp. +// +// Author: Jack Yu <jack.yu@realtek.com> +// +// + +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "rl6231.h" +#include "rt1015.h" + +static const struct reg_default rt1015_reg[] = { + { 0x0000, 0x0000 }, + { 0x0004, 0xa000 }, + { 0x0006, 0x0003 }, + { 0x000a, 0x0802 }, + { 0x000c, 0x0020 }, + { 0x000e, 0x0000 }, + { 0x0010, 0x0000 }, + { 0x0012, 0x0000 }, + { 0x0020, 0x8000 }, + { 0x0022, 0x471b }, + { 0x006a, 0x0000 }, + { 0x006c, 0x4020 }, + { 0x0076, 0x0000 }, + { 0x0078, 0x0000 }, + { 0x007a, 0x0000 }, + { 0x007c, 0x10ec }, + { 0x007d, 0x1015 }, + { 0x00f0, 0x5000 }, + { 0x00f2, 0x0774 }, + { 0x00f3, 0x8400 }, + { 0x00f4, 0x0000 }, + { 0x0100, 0x0028 }, + { 0x0102, 0xff02 }, + { 0x0104, 0x8232 }, + { 0x0106, 0x200c }, + { 0x010c, 0x002f }, + { 0x010e, 0xc000 }, + { 0x0111, 0x0200 }, + { 0x0112, 0x0400 }, + { 0x0114, 0x0022 }, + { 0x0116, 0x0000 }, + { 0x0118, 0x0000 }, + { 0x011a, 0x0123 }, + { 0x011c, 0x4567 }, + { 0x0300, 0xdddd }, + { 0x0302, 0x0000 }, + { 0x0311, 0x9330 }, + { 0x0313, 0x0000 }, + { 0x0314, 0x0000 }, + { 0x031a, 0x00a0 }, + { 0x031c, 0x001f }, + { 0x031d, 0xffff }, + { 0x031e, 0x0000 }, + { 0x031f, 0x0000 }, + { 0x0321, 0x0000 }, + { 0x0322, 0x0000 }, + { 0x0328, 0x0000 }, + { 0x0329, 0x0000 }, + { 0x032a, 0x0000 }, + { 0x032b, 0x0000 }, + { 0x032c, 0x0000 }, + { 0x032d, 0x0000 }, + { 0x032e, 0x030e }, + { 0x0330, 0x0080 }, + { 0x0332, 0x0034 }, + { 0x0334, 0x0000 }, + { 0x0336, 0x0000 }, + { 0x0506, 0x04ff }, + { 0x0508, 0x0030 }, + { 0x050a, 0x0018 }, + { 0x0519, 0x307f }, + { 0x051a, 0xffff }, + { 0x051b, 0x4000 }, + { 0x051d, 0x0000 }, + { 0x051f, 0x0000 }, + { 0x0536, 0x1000 }, + { 0x0538, 0x0000 }, + { 0x053a, 0x0000 }, + { 0x053c, 0x0000 }, + { 0x053d, 0x0000 }, + { 0x053e, 0x0000 }, + { 0x053f, 0x0000 }, + { 0x0540, 0x0000 }, + { 0x0541, 0x0000 }, + { 0x0542, 0x0000 }, + { 0x0543, 0x0000 }, + { 0x0544, 0x0000 }, + { 0x0568, 0x0000 }, + { 0x056a, 0x0000 }, + { 0x1000, 0x0000 }, + { 0x1002, 0x6505 }, + { 0x1006, 0x5515 }, + { 0x1007, 0x003f }, + { 0x1009, 0x770f }, + { 0x100a, 0x01ff }, + { 0x100c, 0x0000 }, + { 0x100d, 0x0003 }, + { 0x1010, 0xa433 }, + { 0x1020, 0x0000 }, + { 0x1200, 0x3d02 }, + { 0x1202, 0x0813 }, + { 0x1204, 0x0211 }, + { 0x1206, 0x0000 }, + { 0x1208, 0x0000 }, + { 0x120a, 0x0000 }, + { 0x120c, 0x0000 }, + { 0x120e, 0x0000 }, + { 0x1210, 0x0000 }, + { 0x1212, 0x0000 }, + { 0x1300, 0x0701 }, + { 0x1302, 0x12f9 }, + { 0x1304, 0x3405 }, + { 0x1305, 0x0844 }, + { 0x1306, 0x1611 }, + { 0x1308, 0x555e }, + { 0x130a, 0x0000 }, + { 0x130c, 0x2400}, + { 0x130e, 0x7700 }, + { 0x130f, 0x0000 }, + { 0x1310, 0x0000 }, + { 0x1312, 0x0000 }, + { 0x1314, 0x0000 }, + { 0x1316, 0x0000 }, + { 0x1318, 0x0000 }, + { 0x131a, 0x0000 }, + { 0x1322, 0x0029 }, + { 0x1323, 0x4a52 }, + { 0x1324, 0x002c }, + { 0x1325, 0x0b02 }, + { 0x1326, 0x002d }, + { 0x1327, 0x6b5a }, + { 0x1328, 0x002e }, + { 0x1329, 0xcbb2 }, + { 0x132a, 0x0030 }, + { 0x132b, 0x2c0b }, + { 0x1330, 0x0031 }, + { 0x1331, 0x8c63 }, + { 0x1332, 0x0032 }, + { 0x1333, 0xecbb }, + { 0x1334, 0x0034 }, + { 0x1335, 0x4d13 }, + { 0x1336, 0x0037 }, + { 0x1337, 0x0dc3 }, + { 0x1338, 0x003d }, + { 0x1339, 0xef7b }, + { 0x133a, 0x0044 }, + { 0x133b, 0xd134 }, + { 0x133c, 0x0047 }, + { 0x133d, 0x91e4 }, + { 0x133e, 0x004d }, + { 0x133f, 0xc370 }, + { 0x1340, 0x0053 }, + { 0x1341, 0xf4fd }, + { 0x1342, 0x0060 }, + { 0x1343, 0x5816 }, + { 0x1344, 0x006c }, + { 0x1345, 0xbb2e }, + { 0x1346, 0x0072 }, + { 0x1347, 0xecbb }, + { 0x1348, 0x0076 }, + { 0x1349, 0x5d97 }, +}; + +static bool rt1015_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT1015_RESET: + case RT1015_CLK_DET: + case RT1015_SIL_DET: + case RT1015_VER_ID: + case RT1015_VENDOR_ID: + case RT1015_DEVICE_ID: + case RT1015_PRO_ALT: + case RT1015_DAC3: + case RT1015_VBAT_TEST_OUT1: + case RT1015_VBAT_TEST_OUT2: + case RT1015_VBAT_PROT_ATT: + case RT1015_VBAT_DET_CODE: + case RT1015_SMART_BST_CTRL1: + case RT1015_SPK_DC_DETECT1: + case RT1015_SPK_DC_DETECT4: + case RT1015_SPK_DC_DETECT5: + case RT1015_DC_CALIB_CLSD1: + case RT1015_DC_CALIB_CLSD5: + case RT1015_DC_CALIB_CLSD6: + case RT1015_DC_CALIB_CLSD7: + case RT1015_DC_CALIB_CLSD8: + case RT1015_S_BST_TIMING_INTER1: + return true; + + default: + return false; + } +} + +static bool rt1015_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT1015_RESET: + case RT1015_CLK2: + case RT1015_CLK3: + case RT1015_PLL1: + case RT1015_PLL2: + case RT1015_CLK_DET: + case RT1015_SIL_DET: + case RT1015_CUSTOMER_ID: + case RT1015_PCODE_FWVER: + case RT1015_VER_ID: + case RT1015_VENDOR_ID: + case RT1015_DEVICE_ID: + case RT1015_PAD_DRV1: + case RT1015_PAD_DRV2: + case RT1015_GAT_BOOST: + case RT1015_PRO_ALT: + case RT1015_MAN_I2C: + case RT1015_DAC1: + case RT1015_DAC2: + case RT1015_DAC3: + case RT1015_ADC1: + case RT1015_ADC2: + case RT1015_TDM_MASTER: + case RT1015_TDM_TCON: + case RT1015_TDM1_1: + case RT1015_TDM1_2: + case RT1015_TDM1_3: + case RT1015_TDM1_4: + case RT1015_TDM1_5: + case RT1015_MIXER1: + case RT1015_MIXER2: + case RT1015_ANA_PROTECT1: + case RT1015_ANA_CTRL_SEQ1: + case RT1015_ANA_CTRL_SEQ2: + case RT1015_VBAT_DET_DEB: + case RT1015_VBAT_VOLT_DET1: + case RT1015_VBAT_VOLT_DET2: + case RT1015_VBAT_TEST_OUT1: + case RT1015_VBAT_TEST_OUT2: + case RT1015_VBAT_PROT_ATT: + case RT1015_VBAT_DET_CODE: + case RT1015_PWR1: + case RT1015_PWR4: + case RT1015_PWR5: + case RT1015_PWR6: + case RT1015_PWR7: + case RT1015_PWR8: + case RT1015_PWR9: + case RT1015_CLASSD_SEQ: + case RT1015_SMART_BST_CTRL1: + case RT1015_SMART_BST_CTRL2: + case RT1015_ANA_CTRL1: + case RT1015_ANA_CTRL2: + case RT1015_SPK_VOL: + case RT1015_SHORT_DETTOP1: + case RT1015_SHORT_DETTOP2: + case RT1015_SPK_DC_DETECT1: + case RT1015_SPK_DC_DETECT2: + case RT1015_SPK_DC_DETECT3: + case RT1015_SPK_DC_DETECT4: + case RT1015_SPK_DC_DETECT5: + case RT1015_BAT_RPO_STEP1: + case RT1015_BAT_RPO_STEP2: + case RT1015_BAT_RPO_STEP3: + case RT1015_BAT_RPO_STEP4: + case RT1015_BAT_RPO_STEP5: + case RT1015_BAT_RPO_STEP6: + case RT1015_BAT_RPO_STEP7: + case RT1015_BAT_RPO_STEP8: + case RT1015_BAT_RPO_STEP9: + case RT1015_BAT_RPO_STEP10: + case RT1015_BAT_RPO_STEP11: + case RT1015_BAT_RPO_STEP12: + case RT1015_SPREAD_SPEC1: + case RT1015_SPREAD_SPEC2: + case RT1015_PAD_STATUS: + case RT1015_PADS_PULLING_CTRL1: + case RT1015_PADS_DRIVING: + case RT1015_SYS_RST1: + case RT1015_SYS_RST2: + case RT1015_SYS_GATING1: + case RT1015_TEST_MODE1: + case RT1015_TEST_MODE2: + case RT1015_TIMING_CTRL1: + case RT1015_PLL_INT: + case RT1015_TEST_OUT1: + case RT1015_DC_CALIB_CLSD1: + case RT1015_DC_CALIB_CLSD2: + case RT1015_DC_CALIB_CLSD3: + case RT1015_DC_CALIB_CLSD4: + case RT1015_DC_CALIB_CLSD5: + case RT1015_DC_CALIB_CLSD6: + case RT1015_DC_CALIB_CLSD7: + case RT1015_DC_CALIB_CLSD8: + case RT1015_DC_CALIB_CLSD9: + case RT1015_DC_CALIB_CLSD10: + case RT1015_CLSD_INTERNAL1: + case RT1015_CLSD_INTERNAL2: + case RT1015_CLSD_INTERNAL3: + case RT1015_CLSD_INTERNAL4: + case RT1015_CLSD_INTERNAL5: + case RT1015_CLSD_INTERNAL6: + case RT1015_CLSD_INTERNAL7: + case RT1015_CLSD_INTERNAL8: + case RT1015_CLSD_INTERNAL9: + case RT1015_CLSD_OCP_CTRL: + case RT1015_VREF_LV: + case RT1015_MBIAS1: + case RT1015_MBIAS2: + case RT1015_MBIAS3: + case RT1015_MBIAS4: + case RT1015_VREF_LV1: + case RT1015_S_BST_TIMING_INTER1: + case RT1015_S_BST_TIMING_INTER2: + case RT1015_S_BST_TIMING_INTER3: + case RT1015_S_BST_TIMING_INTER4: + case RT1015_S_BST_TIMING_INTER5: + case RT1015_S_BST_TIMING_INTER6: + case RT1015_S_BST_TIMING_INTER7: + case RT1015_S_BST_TIMING_INTER8: + case RT1015_S_BST_TIMING_INTER9: + case RT1015_S_BST_TIMING_INTER10: + case RT1015_S_BST_TIMING_INTER11: + case RT1015_S_BST_TIMING_INTER12: + case RT1015_S_BST_TIMING_INTER13: + case RT1015_S_BST_TIMING_INTER14: + case RT1015_S_BST_TIMING_INTER15: + case RT1015_S_BST_TIMING_INTER16: + case RT1015_S_BST_TIMING_INTER17: + case RT1015_S_BST_TIMING_INTER18: + case RT1015_S_BST_TIMING_INTER19: + case RT1015_S_BST_TIMING_INTER20: + case RT1015_S_BST_TIMING_INTER21: + case RT1015_S_BST_TIMING_INTER22: + case RT1015_S_BST_TIMING_INTER23: + case RT1015_S_BST_TIMING_INTER24: + case RT1015_S_BST_TIMING_INTER25: + case RT1015_S_BST_TIMING_INTER26: + case RT1015_S_BST_TIMING_INTER27: + case RT1015_S_BST_TIMING_INTER28: + case RT1015_S_BST_TIMING_INTER29: + case RT1015_S_BST_TIMING_INTER30: + case RT1015_S_BST_TIMING_INTER31: + case RT1015_S_BST_TIMING_INTER32: + case RT1015_S_BST_TIMING_INTER33: + case RT1015_S_BST_TIMING_INTER34: + case RT1015_S_BST_TIMING_INTER35: + case RT1015_S_BST_TIMING_INTER36: + return true; + + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9525, 75, 0); + +static const char * const rt1015_din_source_select[] = { + "Left", + "Right", + "Left + Right average", +}; + +static SOC_ENUM_SINGLE_DECL(rt1015_mono_lr_sel, RT1015_PAD_DRV2, 4, + rt1015_din_source_select); + +static const char * const rt1015_boost_mode[] = { + "Bypass", "Adaptive", "Fixed Adaptive" +}; + +static const SOC_ENUM_SINGLE_DECL(rt1015_boost_mode_enum, 0, 0, + rt1015_boost_mode); + +static int rt1015_boost_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1015_priv *rt1015 = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1015->boost_mode; + + return 0; +} + +static int rt1015_boost_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1015_priv *rt1015 = + snd_soc_component_get_drvdata(component); + + rt1015->boost_mode = ucontrol->value.integer.value[0]; + + switch (rt1015->boost_mode) { + case BYPASS: + snd_soc_component_update_bits(component, + RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK | + RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK, + RT1015_ABST_REG_MODE | RT1015_ABST_FIX_TGT_DIS | + RT1015_BYPASS_SWRREG_BYPASS); + break; + case ADAPTIVE: + snd_soc_component_update_bits(component, + RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK | + RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK, + RT1015_ABST_AUTO_MODE | RT1015_ABST_FIX_TGT_DIS | + RT1015_BYPASS_SWRREG_PASS); + break; + case FIXED_ADAPTIVE: + snd_soc_component_update_bits(component, + RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK | + RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK, + RT1015_ABST_AUTO_MODE | RT1015_ABST_FIX_TGT_EN | + RT1015_BYPASS_SWRREG_PASS); + break; + default: + dev_err(component->dev, "Unknown boost control.\n"); + } + + return 0; +} + +static int rt5518_bypass_boost_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1015_priv *rt1015 = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1015->bypass_boost; + + return 0; +} + +static int rt5518_bypass_boost_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1015_priv *rt1015 = + snd_soc_component_get_drvdata(component); + + if (!rt1015->dac_is_used) { + rt1015->bypass_boost = ucontrol->value.integer.value[0]; + if (rt1015->bypass_boost == 1) { + snd_soc_component_write(component, + RT1015_PWR4, 0x00b2); + snd_soc_component_write(component, + RT1015_CLSD_INTERNAL8, 0x2008); + snd_soc_component_write(component, + RT1015_CLSD_INTERNAL9, 0x0140); + snd_soc_component_write(component, + RT1015_GAT_BOOST, 0x00fe); + snd_soc_component_write(component, + RT1015_PWR_STATE_CTRL, 0x000d); + msleep(500); + snd_soc_component_write(component, + RT1015_PWR_STATE_CTRL, 0x000e); + } + } else + dev_err(component->dev, "DAC is being used!\n"); + + return 0; +} + +static const struct snd_kcontrol_new rt1015_snd_controls[] = { + SOC_SINGLE_TLV("DAC Playback Volume", RT1015_DAC1, RT1015_DAC_VOL_SFT, + 127, 0, dac_vol_tlv), + SOC_DOUBLE("DAC Playback Switch", RT1015_DAC3, + RT1015_DA_MUTE_SFT, RT1015_DVOL_MUTE_FLAG_SFT, 1, 1), + SOC_ENUM_EXT("Boost Mode", rt1015_boost_mode_enum, + rt1015_boost_mode_get, rt1015_boost_mode_put), + SOC_ENUM("Mono LR Select", rt1015_mono_lr_sel), + SOC_SINGLE_EXT("Bypass Boost", SND_SOC_NOPM, 0, 1, 0, + rt5518_bypass_boost_get, rt5518_bypass_boost_put), +}; + +static int rt1015_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(source->dapm); + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + + if (rt1015->sysclk_src == RT1015_SCLK_S_PLL) + return 1; + else + return 0; +} + +static int r1015_dac_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 rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rt1015->dac_is_used = 1; + if (rt1015->bypass_boost == 0) { + snd_soc_component_write(component, + RT1015_SYS_RST1, 0x05f7); + snd_soc_component_write(component, + RT1015_GAT_BOOST, 0xacfe); + snd_soc_component_write(component, + RT1015_PWR9, 0xaa00); + snd_soc_component_write(component, + RT1015_GAT_BOOST, 0xecfe); + } else { + snd_soc_component_write(component, + RT1015_SYS_RST1, 0x05f7); + snd_soc_component_write(component, + RT1015_PWR_STATE_CTRL, 0x026e); + } + break; + + case SND_SOC_DAPM_POST_PMD: + if (rt1015->bypass_boost == 0) { + snd_soc_component_write(component, + RT1015_PWR9, 0xa800); + snd_soc_component_write(component, + RT1015_SYS_RST1, 0x05f5); + } else { + snd_soc_component_write(component, + RT1015_PWR_STATE_CTRL, 0x0268); + snd_soc_component_write(component, + RT1015_SYS_RST1, 0x05f5); + } + rt1015->dac_is_used = 0; + break; + + default: + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt1015_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT1015_PWR1, RT1015_PWR_LDO2_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("INT RC CLK", RT1015_PWR1, RT1015_PWR_INTCLK_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ISENSE", RT1015_PWR1, RT1015_PWR_ISENSE_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("VSENSE", RT1015_PWR1, RT1015_PWR_VSENSE_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL", RT1015_PWR1, RT1015_PWR_PLL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BG1 BG2", RT1015_PWR1, RT1015_PWR_BG_1_2_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS BG", RT1015_PWR1, RT1015_PWR_MBIAS_BG_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("VBAT", RT1015_PWR1, RT1015_PWR_VBAT_BIT, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY("MBIAS", RT1015_PWR1, RT1015_PWR_MBIAS_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCV", RT1015_PWR1, RT1015_PWR_ADCV_BIT, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY("MIXERV", RT1015_PWR1, RT1015_PWR_MIXERV_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("SUMV", RT1015_PWR1, RT1015_PWR_SUMV_BIT, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY("VREFLV", RT1015_PWR1, RT1015_PWR_VREFLV_BIT, 0, + NULL, 0), + + SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("DAC", NULL, RT1015_PWR1, RT1015_PWR_DAC_BIT, 0, + r1015_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("SPO"), +}; + +static const struct snd_soc_dapm_route rt1015_dapm_routes[] = { + { "DAC", NULL, "AIFRX" }, + { "DAC", NULL, "LDO2" }, + { "DAC", NULL, "PLL", rt1015_is_sys_clk_from_pll}, + { "DAC", NULL, "INT RC CLK" }, + { "DAC", NULL, "ISENSE" }, + { "DAC", NULL, "VSENSE" }, + { "DAC", NULL, "BG1 BG2" }, + { "DAC", NULL, "MBIAS BG" }, + { "DAC", NULL, "VBAT" }, + { "DAC", NULL, "MBIAS" }, + { "DAC", NULL, "ADCV" }, + { "DAC", NULL, "MIXERV" }, + { "DAC", NULL, "SUMV" }, + { "DAC", NULL, "VREFLV" }, + { "SPO", NULL, "DAC" }, +}; + +static int rt1015_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 rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + int pre_div, bclk_ms, frame_size; + unsigned int val_len = 0; + + rt1015->lrck = params_rate(params); + pre_div = rl6231_get_clk_info(rt1015->sysclk, rt1015->lrck); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock rate\n"); + return -EINVAL; + } + + 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; + } + + bclk_ms = frame_size > 32; + rt1015->bclk = rt1015->lrck * (32 << bclk_ms); + + dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt1015->lrck, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len = RT1015_I2S_DL_20; + break; + case 24: + val_len = RT1015_I2S_DL_24; + break; + case 8: + val_len = RT1015_I2S_DL_8; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT1015_TDM_MASTER, + RT1015_I2S_DL_MASK, val_len); + snd_soc_component_update_bits(component, RT1015_CLK2, + RT1015_FS_PD_MASK, pre_div); + + return 0; +} + +static int rt1015_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + unsigned int reg_val = 0, reg_val2 = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + reg_val |= RT1015_TCON_TDM_MS_M; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT1015_TCON_TDM_MS_S; + 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_val2 |= RT1015_TDM_INV_BCLK; + 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 |= RT1015_I2S_M_DF_LEFT; + break; + + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT1015_I2S_M_DF_PCM_A; + break; + + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT1015_I2S_M_DF_PCM_B; + break; + + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT1015_TDM_MASTER, + RT1015_TCON_TDM_MS_MASK | RT1015_I2S_M_DF_MASK, + reg_val); + snd_soc_component_update_bits(component, RT1015_TDM1_1, + RT1015_TDM_INV_BCLK_MASK, reg_val2); + + return 0; +} + +static int rt1015_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt1015->sysclk && clk_id == rt1015->sysclk_src) + return 0; + + switch (clk_id) { + case RT1015_SCLK_S_MCLK: + reg_val |= RT1015_CLK_SYS_PRE_SEL_MCLK; + break; + + case RT1015_SCLK_S_PLL: + reg_val |= RT1015_CLK_SYS_PRE_SEL_PLL; + break; + + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + rt1015->sysclk = freq; + rt1015->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + snd_soc_component_update_bits(component, RT1015_CLK2, + RT1015_CLK_SYS_PRE_SEL_MASK, reg_val); + + return 0; +} + +static int rt1015_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt1015->pll_in = 0; + rt1015->pll_out = 0; + + return 0; + } + + if (source == rt1015->pll_src && freq_in == rt1015->pll_in && + freq_out == rt1015->pll_out) + return 0; + + switch (source) { + case RT1015_PLL_S_MCLK: + snd_soc_component_update_bits(component, RT1015_CLK2, + RT1015_PLL_SEL_MASK, RT1015_PLL_SEL_PLL_SRC2); + break; + + case RT1015_PLL_S_BCLK: + snd_soc_component_update_bits(component, RT1015_CLK2, + RT1015_PLL_SEL_MASK, RT1015_PLL_SEL_BCLK); + 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, RT1015_PLL1, + (pll_code.m_bp ? 0 : pll_code.m_code) << RT1015_PLL_M_SFT | + pll_code.m_bp << RT1015_PLL_M_BP_SFT | pll_code.n_code); + snd_soc_component_write(component, RT1015_PLL2, + pll_code.k_code); + + rt1015->pll_in = freq_in; + rt1015->pll_out = freq_out; + rt1015->pll_src = source; + + return 0; +} + +static int rt1015_probe(struct snd_soc_component *component) +{ + struct rt1015_priv *rt1015 = + snd_soc_component_get_drvdata(component); + + rt1015->component = component; + snd_soc_component_write(component, RT1015_BAT_RPO_STEP1, 0x061c); + + return 0; +} + +static void rt1015_remove(struct snd_soc_component *component) +{ + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + + regmap_write(rt1015->regmap, RT1015_RESET, 0); +} + +#define RT1015_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT1015_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +struct snd_soc_dai_ops rt1015_aif_dai_ops = { + .hw_params = rt1015_hw_params, + .set_fmt = rt1015_set_dai_fmt, +}; + +struct snd_soc_dai_driver rt1015_dai[] = { + { + .name = "rt1015-aif", + .id = 0, + .playback = { + .stream_name = "AIF Playback", + .channels_min = 1, + .channels_max = 4, + .rates = RT1015_STEREO_RATES, + .formats = RT1015_FORMATS, + }, + } +}; + +#ifdef CONFIG_PM +static int rt1015_suspend(struct snd_soc_component *component) +{ + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1015->regmap, true); + regcache_mark_dirty(rt1015->regmap); + + return 0; +} + +static int rt1015_resume(struct snd_soc_component *component) +{ + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1015->regmap, false); + regcache_sync(rt1015->regmap); + return 0; +} +#else +#define rt1015_suspend NULL +#define rt1015_resume NULL +#endif + +static const struct snd_soc_component_driver soc_component_dev_rt1015 = { + .probe = rt1015_probe, + .remove = rt1015_remove, + .suspend = rt1015_suspend, + .resume = rt1015_resume, + .controls = rt1015_snd_controls, + .num_controls = ARRAY_SIZE(rt1015_snd_controls), + .dapm_widgets = rt1015_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1015_dapm_widgets), + .dapm_routes = rt1015_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1015_dapm_routes), + .set_sysclk = rt1015_set_component_sysclk, + .set_pll = rt1015_set_component_pll, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config rt1015_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = RT1015_S_BST_TIMING_INTER36, + .volatile_reg = rt1015_volatile_register, + .readable_reg = rt1015_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt1015_reg, + .num_reg_defaults = ARRAY_SIZE(rt1015_reg), +}; + +static const struct i2c_device_id rt1015_i2c_id[] = { + { "rt1015", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt1015_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id rt1015_of_match[] = { + { .compatible = "realtek,rt1015", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt1015_of_match); +#endif + +#ifdef CONFIG_ACPI +static struct acpi_device_id rt1015_acpi_match[] = { + {"10EC1015", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt1015_acpi_match); +#endif + +static int rt1015_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt1015_priv *rt1015; + int ret; + unsigned int val; + + rt1015 = devm_kzalloc(&i2c->dev, sizeof(struct rt1015_priv), + GFP_KERNEL); + if (rt1015 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt1015); + + rt1015->regmap = devm_regmap_init_i2c(i2c, &rt1015_regmap); + if (IS_ERR(rt1015->regmap)) { + ret = PTR_ERR(rt1015->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt1015->regmap, RT1015_DEVICE_ID, &val); + if ((val != RT1015_DEVICE_ID_VAL) && (val != RT1015_DEVICE_ID_VAL2)) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt1015\n", val); + return -ENODEV; + } + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt1015, + rt1015_dai, ARRAY_SIZE(rt1015_dai)); +} + +static void rt1015_i2c_shutdown(struct i2c_client *client) +{ + struct rt1015_priv *rt1015 = i2c_get_clientdata(client); + + regmap_write(rt1015->regmap, RT1015_RESET, 0); +} + +static struct i2c_driver rt1015_i2c_driver = { + .driver = { + .name = "rt1015", + .of_match_table = of_match_ptr(rt1015_of_match), + .acpi_match_table = ACPI_PTR(rt1015_acpi_match), + }, + .probe = rt1015_i2c_probe, + .shutdown = rt1015_i2c_shutdown, + .id_table = rt1015_i2c_id, +}; +module_i2c_driver(rt1015_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT1015 driver"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt1015.h b/sound/soc/codecs/rt1015.h new file mode 100644 index 000000000000..ef3745a4faae --- /dev/null +++ b/sound/soc/codecs/rt1015.h @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt1015.h -- RT1015 ALSA SoC audio amplifier driver +// +// Copyright 2019 Realtek Semiconductor Corp. +// Author: Jack Yu <jack.yu@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 __RT1015_H__ +#define __RT1015_H__ + +#define RT1015_DEVICE_ID_VAL 0x1011 +#define RT1015_DEVICE_ID_VAL2 0x1015 + +#define RT1015_RESET 0x0000 +#define RT1015_CLK2 0x0004 +#define RT1015_CLK3 0x0006 +#define RT1015_PLL1 0x000a +#define RT1015_PLL2 0x000c +#define RT1015_CLK_DET 0x0020 +#define RT1015_SIL_DET 0x0022 +#define RT1015_CUSTOMER_ID 0x0076 +#define RT1015_PCODE_FWVER 0x0078 +#define RT1015_VER_ID 0x007a +#define RT1015_VENDOR_ID 0x007c +#define RT1015_DEVICE_ID 0x007d +#define RT1015_PAD_DRV1 0x00f0 +#define RT1015_PAD_DRV2 0x00f2 +#define RT1015_GAT_BOOST 0x00f3 +#define RT1015_PRO_ALT 0x00f4 +#define RT1015_MAN_I2C 0x0100 +#define RT1015_DAC1 0x0102 +#define RT1015_DAC2 0x0104 +#define RT1015_DAC3 0x0106 +#define RT1015_ADC1 0x010c +#define RT1015_ADC2 0x010e +#define RT1015_TDM_MASTER 0x0111 +#define RT1015_TDM_TCON 0x0112 +#define RT1015_TDM1_1 0x0114 +#define RT1015_TDM1_2 0x0116 +#define RT1015_TDM1_3 0x0118 +#define RT1015_TDM1_4 0x011a +#define RT1015_TDM1_5 0x011c +#define RT1015_MIXER1 0x0300 +#define RT1015_MIXER2 0x0302 +#define RT1015_ANA_PROTECT1 0x0311 +#define RT1015_ANA_CTRL_SEQ1 0x0313 +#define RT1015_ANA_CTRL_SEQ2 0x0314 +#define RT1015_VBAT_DET_DEB 0x031a +#define RT1015_VBAT_VOLT_DET1 0x031c +#define RT1015_VBAT_VOLT_DET2 0x031d +#define RT1015_VBAT_TEST_OUT1 0x031e +#define RT1015_VBAT_TEST_OUT2 0x031f +#define RT1015_VBAT_PROT_ATT 0x0320 +#define RT1015_VBAT_DET_CODE 0x0321 +#define RT1015_PWR1 0x0322 +#define RT1015_PWR4 0x0328 +#define RT1015_PWR5 0x0329 +#define RT1015_PWR6 0x032a +#define RT1015_PWR7 0x032b +#define RT1015_PWR8 0x032c +#define RT1015_PWR9 0x032d +#define RT1015_CLASSD_SEQ 0x032e +#define RT1015_SMART_BST_CTRL1 0x0330 +#define RT1015_SMART_BST_CTRL2 0x0332 +#define RT1015_ANA_CTRL1 0x0334 +#define RT1015_ANA_CTRL2 0x0336 +#define RT1015_PWR_STATE_CTRL 0x0338 +#define RT1015_SPK_VOL 0x0506 +#define RT1015_SHORT_DETTOP1 0x0508 +#define RT1015_SHORT_DETTOP2 0x050a +#define RT1015_SPK_DC_DETECT1 0x0519 +#define RT1015_SPK_DC_DETECT2 0x051a +#define RT1015_SPK_DC_DETECT3 0x051b +#define RT1015_SPK_DC_DETECT4 0x051d +#define RT1015_SPK_DC_DETECT5 0x051f +#define RT1015_BAT_RPO_STEP1 0x0536 +#define RT1015_BAT_RPO_STEP2 0x0538 +#define RT1015_BAT_RPO_STEP3 0x053a +#define RT1015_BAT_RPO_STEP4 0x053c +#define RT1015_BAT_RPO_STEP5 0x053d +#define RT1015_BAT_RPO_STEP6 0x053e +#define RT1015_BAT_RPO_STEP7 0x053f +#define RT1015_BAT_RPO_STEP8 0x0540 +#define RT1015_BAT_RPO_STEP9 0x0541 +#define RT1015_BAT_RPO_STEP10 0x0542 +#define RT1015_BAT_RPO_STEP11 0x0543 +#define RT1015_BAT_RPO_STEP12 0x0544 +#define RT1015_SPREAD_SPEC1 0x0568 +#define RT1015_SPREAD_SPEC2 0x056a +#define RT1015_PAD_STATUS 0x1000 +#define RT1015_PADS_PULLING_CTRL1 0x1002 +#define RT1015_PADS_DRIVING 0x1006 +#define RT1015_SYS_RST1 0x1007 +#define RT1015_SYS_RST2 0x1009 +#define RT1015_SYS_GATING1 0x100a +#define RT1015_TEST_MODE1 0x100c +#define RT1015_TEST_MODE2 0x100d +#define RT1015_TIMING_CTRL1 0x100e +#define RT1015_PLL_INT 0x1010 +#define RT1015_TEST_OUT1 0x1020 +#define RT1015_DC_CALIB_CLSD1 0x1200 +#define RT1015_DC_CALIB_CLSD2 0x1202 +#define RT1015_DC_CALIB_CLSD3 0x1204 +#define RT1015_DC_CALIB_CLSD4 0x1206 +#define RT1015_DC_CALIB_CLSD5 0x1208 +#define RT1015_DC_CALIB_CLSD6 0x120a +#define RT1015_DC_CALIB_CLSD7 0x120c +#define RT1015_DC_CALIB_CLSD8 0x120e +#define RT1015_DC_CALIB_CLSD9 0x1210 +#define RT1015_DC_CALIB_CLSD10 0x1212 +#define RT1015_CLSD_INTERNAL1 0x1300 +#define RT1015_CLSD_INTERNAL2 0x1302 +#define RT1015_CLSD_INTERNAL3 0x1304 +#define RT1015_CLSD_INTERNAL4 0x1305 +#define RT1015_CLSD_INTERNAL5 0x1306 +#define RT1015_CLSD_INTERNAL6 0x1308 +#define RT1015_CLSD_INTERNAL7 0x130a +#define RT1015_CLSD_INTERNAL8 0x130c +#define RT1015_CLSD_INTERNAL9 0x130e +#define RT1015_CLSD_OCP_CTRL 0x130f +#define RT1015_VREF_LV 0x1310 +#define RT1015_MBIAS1 0x1312 +#define RT1015_MBIAS2 0x1314 +#define RT1015_MBIAS3 0x1316 +#define RT1015_MBIAS4 0x1318 +#define RT1015_VREF_LV1 0x131a +#define RT1015_S_BST_TIMING_INTER1 0x1322 +#define RT1015_S_BST_TIMING_INTER2 0x1323 +#define RT1015_S_BST_TIMING_INTER3 0x1324 +#define RT1015_S_BST_TIMING_INTER4 0x1325 +#define RT1015_S_BST_TIMING_INTER5 0x1326 +#define RT1015_S_BST_TIMING_INTER6 0x1327 +#define RT1015_S_BST_TIMING_INTER7 0x1328 +#define RT1015_S_BST_TIMING_INTER8 0x1329 +#define RT1015_S_BST_TIMING_INTER9 0x132a +#define RT1015_S_BST_TIMING_INTER10 0x132b +#define RT1015_S_BST_TIMING_INTER11 0x1330 +#define RT1015_S_BST_TIMING_INTER12 0x1331 +#define RT1015_S_BST_TIMING_INTER13 0x1332 +#define RT1015_S_BST_TIMING_INTER14 0x1333 +#define RT1015_S_BST_TIMING_INTER15 0x1334 +#define RT1015_S_BST_TIMING_INTER16 0x1335 +#define RT1015_S_BST_TIMING_INTER17 0x1336 +#define RT1015_S_BST_TIMING_INTER18 0x1337 +#define RT1015_S_BST_TIMING_INTER19 0x1338 +#define RT1015_S_BST_TIMING_INTER20 0x1339 +#define RT1015_S_BST_TIMING_INTER21 0x133a +#define RT1015_S_BST_TIMING_INTER22 0x133b +#define RT1015_S_BST_TIMING_INTER23 0x133c +#define RT1015_S_BST_TIMING_INTER24 0x133d +#define RT1015_S_BST_TIMING_INTER25 0x133e +#define RT1015_S_BST_TIMING_INTER26 0x133f +#define RT1015_S_BST_TIMING_INTER27 0x1340 +#define RT1015_S_BST_TIMING_INTER28 0x1341 +#define RT1015_S_BST_TIMING_INTER29 0x1342 +#define RT1015_S_BST_TIMING_INTER30 0x1343 +#define RT1015_S_BST_TIMING_INTER31 0x1344 +#define RT1015_S_BST_TIMING_INTER32 0x1345 +#define RT1015_S_BST_TIMING_INTER33 0x1346 +#define RT1015_S_BST_TIMING_INTER34 0x1347 +#define RT1015_S_BST_TIMING_INTER35 0x1348 +#define RT1015_S_BST_TIMING_INTER36 0x1349 + +/* 0x0004 */ +#define RT1015_CLK_SYS_PRE_SEL_MASK (0x3 << 14) +#define RT1015_CLK_SYS_PRE_SEL_SFT 14 +#define RT1015_CLK_SYS_PRE_SEL_MCLK (0x0 << 14) +#define RT1015_CLK_SYS_PRE_SEL_PLL (0x2 << 14) +#define RT1015_PLL_SEL_MASK (0x1 << 13) +#define RT1015_PLL_SEL_SFT 13 +#define RT1015_PLL_SEL_PLL_SRC2 (0x0 << 13) +#define RT1015_PLL_SEL_BCLK (0x1 << 13) +#define RT1015_FS_PD_MASK (0x7 << 4) +#define RT1015_FS_PD_SFT 4 + +/* 0x000a */ +#define RT1015_PLL_M_MAX 0xf +#define RT1015_PLL_M_MASK (RT1015_PLL_M_MAX << 12) +#define RT1015_PLL_M_SFT 12 +#define RT1015_PLL_M_BP (0x1 << 11) +#define RT1015_PLL_M_BP_SFT 11 +#define RT1015_PLL_N_MAX 0x1ff +#define RT1015_PLL_N_MASK (RT1015_PLL_N_MAX << 0) +#define RT1015_PLL_N_SFT 0 + +/* 0x000c */ +#define RT1015_PLL_BPK_MASK (0x1 << 5) +#define RT1015_PLL_BPK (0x0 << 5) +#define RT1015_PLL_K_MAX 0x1f +#define RT1015_PLL_K_MASK (RT1015_PLL_K_MAX) +#define RT1015_PLL_K_SFT 0 + +/* 0x007a */ +#define RT1015_ID_MASK 0xff +#define RT1015_ID_VERA 0x0 +#define RT1015_ID_VERB 0x1 + +/* 0x0102 */ +#define RT1015_DAC_VOL_MASK (0x7f << 9) +#define RT1015_DAC_VOL_SFT 9 + +/* 0x0104 */ +#define RT1015_DAC_CLK (0x1 << 13) +#define RT1015_DAC_CLK_BIT 13 + +/* 0x0106 */ +#define RT1015_DAC_MUTE_MASK (0x1 << 15) +#define RT1015_DA_MUTE_SFT 15 +#define RT1015_DVOL_MUTE_FLAG_SFT 12 + +/* 0x0111 */ +#define RT1015_TCON_TDM_MS_MASK (0x1 << 14) +#define RT1015_TCON_TDM_MS_SFT 14 +#define RT1015_TCON_TDM_MS_S (0x0 << 14) +#define RT1015_TCON_TDM_MS_M (0x1 << 14) +#define RT1015_I2S_DL_MASK (0x7 << 8) +#define RT1015_I2S_DL_SFT 8 +#define RT1015_I2S_DL_16 (0x0 << 8) +#define RT1015_I2S_DL_20 (0x1 << 8) +#define RT1015_I2S_DL_24 (0x2 << 8) +#define RT1015_I2S_DL_8 (0x3 << 8) +#define RT1015_I2S_M_DF_MASK (0x7 << 0) +#define RT1015_I2S_M_DF_SFT 0 +#define RT1015_I2S_M_DF_I2S (0x0) +#define RT1015_I2S_M_DF_LEFT (0x1) +#define RT1015_I2S_M_DF_PCM_A (0x2) +#define RT1015_I2S_M_DF_PCM_B (0x3) +#define RT1015_I2S_M_DF_PCM_A_N (0x6) +#define RT1015_I2S_M_DF_PCM_B_N (0x7) + +/* TDM_tcon Setting (0x0112) */ +#define RT1015_I2S_TCON_DF_MASK (0x7 << 13) +#define RT1015_I2S_TCON_DF_SFT 13 +#define RT1015_I2S_TCON_DF_I2S (0x0 << 13) +#define RT1015_I2S_TCON_DF_LEFT (0x1 << 13) +#define RT1015_I2S_TCON_DF_PCM_A (0x2 << 13) +#define RT1015_I2S_TCON_DF_PCM_B (0x3 << 13) +#define RT1015_I2S_TCON_DF_PCM_A_N (0x6 << 13) +#define RT1015_I2S_TCON_DF_PCM_B_N (0x7 << 13) +#define RT1015_TCON_BCLK_SEL_MASK (0x3 << 10) +#define RT1015_TCON_BCLK_SEL_SFT 10 +#define RT1015_TCON_BCLK_SEL_32FS (0x0 << 10) +#define RT1015_TCON_BCLK_SEL_64FS (0x1 << 10) +#define RT1015_TCON_BCLK_SEL_128FS (0x2 << 10) +#define RT1015_TCON_BCLK_SEL_256FS (0x3 << 10) +#define RT1015_TCON_CH_LEN_MASK (0x3 << 5) +#define RT1015_TCON_CH_LEN_SFT 5 +#define RT1015_TCON_CH_LEN_16B (0x0 << 5) +#define RT1015_TCON_CH_LEN_20B (0x1 << 5) +#define RT1015_TCON_CH_LEN_24B (0x2 << 5) +#define RT1015_TCON_CH_LEN_32B (0x3 << 5) +#define RT1015_TCON_BCLK_MST_MASK (0x1 << 4) +#define RT1015_TCON_BCLK_MST_SFT 4 +#define RT1015_TCON_BCLK_MST_INV (0x1 << 4) + +/* TDM1 Setting-1 (0x0114) */ +#define RT1015_TDM_INV_BCLK_MASK (0x1 << 15) +#define RT1015_TDM_INV_BCLK_SFT 15 +#define RT1015_TDM_INV_BCLK (0x1 << 15) + +/* 0x0330 */ +#define RT1015_ABST_AUTO_EN_MASK (0x1 << 13) +#define RT1015_ABST_AUTO_MODE (0x1 << 13) +#define RT1015_ABST_REG_MODE (0x0 << 13) +#define RT1015_ABST_FIX_TGT_MASK (0x1 << 12) +#define RT1015_ABST_FIX_TGT_EN (0x1 << 12) +#define RT1015_ABST_FIX_TGT_DIS (0x0 << 12) +#define RT1015_BYPASS_SWR_REG_MASK (0x1 << 7) +#define RT1015_BYPASS_SWRREG_BYPASS (0x1 << 7) +#define RT1015_BYPASS_SWRREG_PASS (0x0 << 7) + +/* 0x0322 */ +#define RT1015_PWR_LDO2 (0x1 << 15) +#define RT1015_PWR_LDO2_BIT 15 +#define RT1015_PWR_DAC (0x1 << 14) +#define RT1015_PWR_DAC_BIT 14 +#define RT1015_PWR_INTCLK (0x1 << 13) +#define RT1015_PWR_INTCLK_BIT 13 +#define RT1015_PWR_ISENSE (0x1 << 12) +#define RT1015_PWR_ISENSE_BIT 12 +#define RT1015_PWR_VSENSE (0x1 << 10) +#define RT1015_PWR_VSENSE_BIT 10 +#define RT1015_PWR_PLL (0x1 << 9) +#define RT1015_PWR_PLL_BIT 9 +#define RT1015_PWR_BG_1_2 (0x1 << 8) +#define RT1015_PWR_BG_1_2_BIT 8 +#define RT1015_PWR_MBIAS_BG (0x1 << 7) +#define RT1015_PWR_MBIAS_BG_BIT 7 +#define RT1015_PWR_VBAT (0x1 << 6) +#define RT1015_PWR_VBAT_BIT 6 +#define RT1015_PWR_MBIAS (0x1 << 4) +#define RT1015_PWR_MBIAS_BIT 4 +#define RT1015_PWR_ADCV (0x1 << 3) +#define RT1015_PWR_ADCV_BIT 3 +#define RT1015_PWR_MIXERV (0x1 << 2) +#define RT1015_PWR_MIXERV_BIT 2 +#define RT1015_PWR_SUMV (0x1 << 1) +#define RT1015_PWR_SUMV_BIT 1 +#define RT1015_PWR_VREFLV (0x1 << 0) +#define RT1015_PWR_VREFLV_BIT 0 + +/* 0x0324 */ +#define RT1015_PWR_BASIC (0x1 << 15) +#define RT1015_PWR_BASIC_BIT 15 +#define RT1015_PWR_SD (0x1 << 14) +#define RT1015_PWR_SD_BIT 14 +#define RT1015_PWR_IBIAS (0x1 << 13) +#define RT1015_PWR_IBIAS_BIT 13 +#define RT1015_PWR_VCM (0x1 << 11) +#define RT1015_PWR_VCM_BIT 11 + +/* 0x0328 */ +#define RT1015_PWR_SWR (0x1 << 12) +#define RT1015_PWR_SWR_BIT 12 + +/* 0x1300 */ +#define RT1015_PWR_CLSD (0x1 << 12) +#define RT1015_PWR_CLSD_BIT 12 + +/* 0x007a */ +#define RT1015_ID_MASK 0xff +#define RT1015_ID_VERA 0x0 +#define RT1015_ID_VERB 0x1 + +/* System Clock Source */ +enum { + RT1015_SCLK_S_MCLK, + RT1015_SCLK_S_PLL, +}; + +/* PLL1 Source */ +enum { + RT1015_PLL_S_MCLK, + RT1015_PLL_S_BCLK, +}; + +enum { + RT1015_AIF1, + RT1015_AIFS, +}; + +enum { + RT1015_VERA, + RT1015_VERB, +}; + +enum { + BYPASS, + ADAPTIVE, + FIXED_ADAPTIVE, +}; + +struct rt1015_priv { + struct snd_soc_component *component; + struct regmap *regmap; + int sysclk; + int sysclk_src; + int lrck; + int bclk; + int id; + int pll_src; + int pll_in; + int pll_out; + int boost_mode; + int bypass_boost; + int amp_ver; + int dac_is_used; +}; + +#endif /* __RT1015_H__ */ diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c new file mode 100644 index 000000000000..8a03dbfe7906 --- /dev/null +++ b/sound/soc/codecs/rt1308-sdw.c @@ -0,0 +1,736 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt1308-sdw.c -- rt1308 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/pm_runtime.h> +#include <linux/mod_devicetable.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> + +#include "rt1308.h" +#include "rt1308-sdw.h" + +static bool rt1308_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0: + case 0x00f0: + case 0x2f01 ... 0x2f07: + case 0x3000 ... 0x3001: + case 0x3004 ... 0x3005: + case 0x3008: + case 0x300a: + case 0xc000 ... 0xcff3: + return true; + default: + return false; + } +} + +static bool rt1308_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f01 ... 0x2f07: + case 0x3000 ... 0x3001: + case 0x3004 ... 0x3005: + case 0x3008: + case 0x300a: + case 0xc000: + return true; + default: + return false; + } +} + +static const struct regmap_config rt1308_sdw_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt1308_readable_register, + .volatile_reg = rt1308_volatile_register, + .max_register = 0xcfff, + .reg_defaults = rt1308_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt1308_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +/* Bus clock frequency */ +#define RT1308_CLK_FREQ_9600000HZ 9600000 +#define RT1308_CLK_FREQ_12000000HZ 12000000 +#define RT1308_CLK_FREQ_6000000HZ 6000000 +#define RT1308_CLK_FREQ_4800000HZ 4800000 +#define RT1308_CLK_FREQ_2400000HZ 2400000 +#define RT1308_CLK_FREQ_12288000HZ 12288000 + +static int rt1308_clock_config(struct device *dev) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt1308->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT1308_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT1308_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT1308_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT1308_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT1308_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT1308_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt1308->regmap, 0xe0, value); + regmap_write(rt1308->regmap, 0xf0, value); + + dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq); + + return 0; +} + +static int rt1308_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i, num_of_ports = 1; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x00; /* BITMAP: 00010100 (not enable yet) */ + prop->sink_ports = 0x2; /* BITMAP: 00000010 */ + + /* for sink */ + nval = hweight32(prop->sink_ports); + num_of_ports += nval; + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* Allocate port_ready based on num_of_ports */ + slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, + sizeof(*slave->port_ready), + GFP_KERNEL); + if (!slave->port_ready) + return -ENOMEM; + + /* Initialize completion */ + for (i = 0; i < num_of_ports; i++) + init_completion(&slave->port_ready[i]); + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + dev_dbg(&slave->dev, "%s\n", __func__); + + return 0; +} + +static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); + int ret = 0; + unsigned int efuse_m_btl_l, efuse_m_btl_r, tmp; + unsigned int efuse_c_btl_l, efuse_c_btl_r; + + if (rt1308->hw_init) + return 0; + + ret = rt1308_read_prop(slave); + if (ret < 0) + goto _io_init_err_; + + if (rt1308->first_hw_init) { + regcache_cache_only(rt1308->regmap, false); + regcache_cache_bypass(rt1308->regmap, true); + } + + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + if (!rt1308->first_hw_init) { + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + /* sw reset */ + regmap_write(rt1308->regmap, RT1308_SDW_RESET, 0); + + /* read efuse */ + regmap_write(rt1308->regmap, 0xc360, 0x01); + regmap_write(rt1308->regmap, 0xc361, 0x80); + regmap_write(rt1308->regmap, 0xc7f0, 0x04); + regmap_write(rt1308->regmap, 0xc7f1, 0xfe); + msleep(100); + regmap_write(rt1308->regmap, 0xc7f0, 0x44); + msleep(20); + regmap_write(rt1308->regmap, 0xc240, 0x10); + + regmap_read(rt1308->regmap, 0xc861, &tmp); + efuse_m_btl_l = tmp; + regmap_read(rt1308->regmap, 0xc860, &tmp); + efuse_m_btl_l = efuse_m_btl_l | (tmp << 8); + regmap_read(rt1308->regmap, 0xc863, &tmp); + efuse_c_btl_l = tmp; + regmap_read(rt1308->regmap, 0xc862, &tmp); + efuse_c_btl_l = efuse_c_btl_l | (tmp << 8); + regmap_read(rt1308->regmap, 0xc871, &tmp); + efuse_m_btl_r = tmp; + regmap_read(rt1308->regmap, 0xc870, &tmp); + efuse_m_btl_r = efuse_m_btl_r | (tmp << 8); + regmap_read(rt1308->regmap, 0xc873, &tmp); + efuse_c_btl_r = tmp; + regmap_read(rt1308->regmap, 0xc872, &tmp); + efuse_c_btl_r = efuse_c_btl_r | (tmp << 8); + dev_info(&slave->dev, "%s m_btl_l=0x%x, m_btl_r=0x%x\n", __func__, + efuse_m_btl_l, efuse_m_btl_r); + dev_info(&slave->dev, "%s c_btl_l=0x%x, c_btl_r=0x%x\n", __func__, + efuse_c_btl_l, efuse_c_btl_r); + + /* initial settings */ + regmap_write(rt1308->regmap, 0xc103, 0xc0); + regmap_write(rt1308->regmap, 0xc030, 0x17); + regmap_write(rt1308->regmap, 0xc031, 0x81); + regmap_write(rt1308->regmap, 0xc032, 0x26); + regmap_write(rt1308->regmap, 0xc040, 0x80); + regmap_write(rt1308->regmap, 0xc041, 0x80); + regmap_write(rt1308->regmap, 0xc042, 0x06); + regmap_write(rt1308->regmap, 0xc052, 0x0a); + regmap_write(rt1308->regmap, 0xc080, 0x0a); + regmap_write(rt1308->regmap, 0xc060, 0x02); + regmap_write(rt1308->regmap, 0xc061, 0x75); + regmap_write(rt1308->regmap, 0xc062, 0x05); + regmap_write(rt1308->regmap, 0xc171, 0x07); + regmap_write(rt1308->regmap, 0xc173, 0x0d); + regmap_write(rt1308->regmap, 0xc311, 0x7f); + regmap_write(rt1308->regmap, 0xc900, 0x90); + regmap_write(rt1308->regmap, 0xc1a0, 0x84); + regmap_write(rt1308->regmap, 0xc1a1, 0x01); + regmap_write(rt1308->regmap, 0xc360, 0x78); + regmap_write(rt1308->regmap, 0xc361, 0x87); + regmap_write(rt1308->regmap, 0xc0a1, 0x71); + regmap_write(rt1308->regmap, 0xc210, 0x00); + regmap_write(rt1308->regmap, 0xc070, 0x00); + regmap_write(rt1308->regmap, 0xc100, 0xd7); + regmap_write(rt1308->regmap, 0xc101, 0xd7); + regmap_write(rt1308->regmap, 0xc300, 0x09); + + if (rt1308->first_hw_init) { + regcache_cache_bypass(rt1308->regmap, false); + regcache_mark_dirty(rt1308->regmap); + } else + rt1308->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt1308->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + +_io_init_err_: + return ret; +} + +static int rt1308_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); + + /* Update the status */ + rt1308->status = status; + + if (status == SDW_SLAVE_UNATTACHED) + rt1308->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt1308->hw_init || rt1308->status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt1308_io_init(&slave->dev, slave); +} + +static int rt1308_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt1308->params, params, sizeof(*params)); + + ret = rt1308_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return ret; +} + +static int rt1308_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + + return 0; +} + +static int rt1308_classd_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_POST_PMU: + msleep(30); + snd_soc_component_update_bits(component, + RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4), + 0x3, 0x3); + msleep(40); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, + RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4), + 0x3, 0); + usleep_range(150000, 200000); + break; + + default: + break; + } + + return 0; +} + +static const char * const rt1308_rx_data_ch_select[] = { + "LR", + "LL", + "RL", + "RR", +}; + +static SOC_ENUM_SINGLE_DECL(rt1308_rx_data_ch_enum, + RT1308_SDW_OFFSET | (RT1308_DATA_PATH << 4), 0, + rt1308_rx_data_ch_select); + +static const struct snd_kcontrol_new rt1308_snd_controls[] = { + + /* I2S Data Channel Selection */ + SOC_ENUM("RX Channel Select", rt1308_rx_data_ch_enum), +}; + +static const struct snd_kcontrol_new rt1308_sto_dac_l = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + RT1308_SDW_OFFSET_BYTE3 | (RT1308_DAC_SET << 4), + RT1308_DVOL_MUTE_L_EN_SFT, 1, 1); + +static const struct snd_kcontrol_new rt1308_sto_dac_r = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + RT1308_SDW_OFFSET_BYTE3 | (RT1308_DAC_SET << 4), + RT1308_DVOL_MUTE_R_EN_SFT, 1, 1); + +static const struct snd_soc_dapm_widget rt1308_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Supply Widgets */ + SND_SOC_DAPM_SUPPLY("MBIAS20U", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ALDO", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DBG", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DACL", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLK25M", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_R", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_L", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Power", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 3, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DLDO", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIXER_R", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIXER_L", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS4U", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("PLL2_LDO", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F2", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B2", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 0, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_l), + SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_r), + + /* Output Lines */ + SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1308_classd_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), +}; + +static const struct snd_soc_dapm_route rt1308_dapm_routes[] = { + + { "DAC", NULL, "AIF1RX" }, + + { "DAC", NULL, "MBIAS20U" }, + { "DAC", NULL, "ALDO" }, + { "DAC", NULL, "DBG" }, + { "DAC", NULL, "DACL" }, + { "DAC", NULL, "CLK25M" }, + { "DAC", NULL, "ADC_R" }, + { "DAC", NULL, "ADC_L" }, + { "DAC", NULL, "DLDO" }, + { "DAC", NULL, "VREF" }, + { "DAC", NULL, "MIXER_R" }, + { "DAC", NULL, "MIXER_L" }, + { "DAC", NULL, "MBIAS4U" }, + { "DAC", NULL, "PLL2_LDO" }, + { "DAC", NULL, "PLL2B" }, + { "DAC", NULL, "PLL2F" }, + { "DAC", NULL, "PLL2F2" }, + { "DAC", NULL, "PLL2B2" }, + + { "DAC L", "Switch", "DAC" }, + { "DAC R", "Switch", "DAC" }, + { "DAC L", NULL, "DAC Power" }, + { "DAC R", NULL, "DAC Power" }, + + { "CLASS D", NULL, "DAC L" }, + { "CLASS D", NULL, "DAC R" }, + { "SPOL", NULL, "CLASS D" }, + { "SPOR", NULL, "CLASS D" }, +}; + +static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + struct sdw_stream_data *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + + /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = stream; + else + dai->capture_dma_data = stream; + + return 0; +} + +static void rt1308_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_stream_data *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(stream); +} + +static int rt1308_sdw_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 rt1308_sdw_priv *rt1308 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_data *stream; + int retval, port, num_channels; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!stream) + return -EINVAL; + + if (!rt1308->sdw_slave) + return -EINVAL; + + /* SoundWire specific configuration */ + /* port 1 for playback */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + port = 1; + } else { + return -EINVAL; + } + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = (1 << (num_channels)) - 1; + port_config.num = port; + + retval = sdw_stream_add_slave(rt1308->sdw_slave, &stream_config, + &port_config, 1, stream->sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + return retval; +} + +static int rt1308_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1308_sdw_priv *rt1308 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt1308->sdw_slave) + return -EINVAL; + + sdw_stream_remove_slave(rt1308->sdw_slave, stream->sdw_stream); + return 0; +} + +/* + * slave_ops: callbacks for get_clock_stop_mode, clock_stop and + * port_prep are not defined for now + */ +static struct sdw_slave_ops rt1308_slave_ops = { + .read_prop = rt1308_read_prop, + .interrupt_callback = rt1308_interrupt_callback, + .update_status = rt1308_update_status, + .bus_config = rt1308_bus_config, +}; + +static const struct snd_soc_component_driver soc_component_sdw_rt1308 = { + .controls = rt1308_snd_controls, + .num_controls = ARRAY_SIZE(rt1308_snd_controls), + .dapm_widgets = rt1308_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets), + .dapm_routes = rt1308_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes), +}; + +static const struct snd_soc_dai_ops rt1308_aif_dai_ops = { + .hw_params = rt1308_sdw_hw_params, + .hw_free = rt1308_sdw_pcm_hw_free, + .set_sdw_stream = rt1308_set_sdw_stream, + .shutdown = rt1308_sdw_shutdown, +}; + +#define RT1308_STEREO_RATES SNDRV_PCM_RATE_48000 +#define RT1308_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver rt1308_sdw_dai[] = { + { + .name = "rt1308-aif", + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1308_STEREO_RATES, + .formats = RT1308_FORMATS, + }, + .ops = &rt1308_aif_dai_ops, + }, +}; + +static int rt1308_sdw_init(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave) +{ + struct rt1308_sdw_priv *rt1308; + int ret; + + rt1308 = devm_kzalloc(dev, sizeof(*rt1308), GFP_KERNEL); + if (!rt1308) + return -ENOMEM; + + dev_set_drvdata(dev, rt1308); + rt1308->sdw_slave = slave; + rt1308->regmap = regmap; + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt1308->hw_init = false; + rt1308->first_hw_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_component_sdw_rt1308, + rt1308_sdw_dai, + ARRAY_SIZE(rt1308_sdw_dai)); + + dev_dbg(&slave->dev, "%s\n", __func__); + + return ret; +} + +static int rt1308_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Assign ops */ + slave->ops = &rt1308_slave_ops; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw(slave, &rt1308_sdw_regmap); + if (!regmap) + return -EINVAL; + + rt1308_sdw_init(&slave->dev, regmap, slave); + + return 0; +} + +static const struct sdw_device_id rt1308_id[] = { + SDW_SLAVE_ENTRY(0x025d, 0x1308, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt1308_id); + +static int rt1308_dev_suspend(struct device *dev) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); + + if (!rt1308->hw_init) + return 0; + + regcache_cache_only(rt1308->regmap, true); + + return 0; +} + +#define RT1308_PROBE_TIMEOUT 2000 + +static int rt1308_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt1308->hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT1308_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt1308->regmap, false); + regcache_sync_region(rt1308->regmap, 0xc000, 0xcfff); + + return 0; +} + +static const struct dev_pm_ops rt1308_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume) + SET_RUNTIME_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume, NULL) +}; + +static struct sdw_driver rt1308_sdw_driver = { + .driver = { + .name = "rt1308", + .owner = THIS_MODULE, + .pm = &rt1308_pm, + }, + .probe = rt1308_sdw_probe, + .ops = &rt1308_slave_ops, + .id_table = rt1308_id, +}; +module_sdw_driver(rt1308_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT1308 driver SDW"); +MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h new file mode 100644 index 000000000000..c9341e70d6cf --- /dev/null +++ b/sound/soc/codecs/rt1308-sdw.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt1308-sdw.h -- RT1308 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT1308_SDW_H__ +#define __RT1308_SDW_H__ + +static const struct reg_default rt1308_reg_defaults[] = { + { 0x0000, 0x00 }, + { 0x0001, 0x00 }, + { 0x0002, 0x00 }, + { 0x0003, 0x00 }, + { 0x0004, 0x00 }, + { 0x0005, 0x01 }, + { 0x0020, 0x00 }, + { 0x0022, 0x00 }, + { 0x0023, 0x00 }, + { 0x0024, 0x00 }, + { 0x0025, 0x00 }, + { 0x0026, 0x00 }, + { 0x0030, 0x00 }, + { 0x0032, 0x00 }, + { 0x0033, 0x00 }, + { 0x0034, 0x00 }, + { 0x0035, 0x00 }, + { 0x0036, 0x00 }, + { 0x0040, 0x00 }, + { 0x0041, 0x00 }, + { 0x0042, 0x00 }, + { 0x0043, 0x00 }, + { 0x0044, 0x20 }, + { 0x0045, 0x01 }, + { 0x0046, 0x01 }, + { 0x0048, 0x00 }, + { 0x0049, 0x00 }, + { 0x0050, 0x20 }, + { 0x0051, 0x02 }, + { 0x0052, 0x5D }, + { 0x0053, 0x13 }, + { 0x0054, 0x08 }, + { 0x0055, 0x00 }, + { 0x0060, 0x00 }, + { 0x0070, 0x00 }, + { 0x00E0, 0x00 }, + { 0x00F0, 0x00 }, + { 0x0100, 0x00 }, + { 0x0101, 0x00 }, + { 0x0102, 0x20 }, + { 0x0103, 0x00 }, + { 0x0104, 0x00 }, + { 0x0105, 0x03 }, + { 0x0120, 0x00 }, + { 0x0122, 0x00 }, + { 0x0123, 0x00 }, + { 0x0124, 0x00 }, + { 0x0125, 0x00 }, + { 0x0126, 0x00 }, + { 0x0127, 0x00 }, + { 0x0130, 0x00 }, + { 0x0132, 0x00 }, + { 0x0133, 0x00 }, + { 0x0134, 0x00 }, + { 0x0135, 0x00 }, + { 0x0136, 0x00 }, + { 0x0137, 0x00 }, + { 0x0200, 0x00 }, + { 0x0201, 0x00 }, + { 0x0202, 0x00 }, + { 0x0203, 0x00 }, + { 0x0204, 0x00 }, + { 0x0205, 0x03 }, + { 0x0220, 0x00 }, + { 0x0222, 0x00 }, + { 0x0223, 0x00 }, + { 0x0224, 0x00 }, + { 0x0225, 0x00 }, + { 0x0226, 0x00 }, + { 0x0227, 0x00 }, + { 0x0230, 0x00 }, + { 0x0232, 0x00 }, + { 0x0233, 0x00 }, + { 0x0234, 0x00 }, + { 0x0235, 0x00 }, + { 0x0236, 0x00 }, + { 0x0237, 0x00 }, + { 0x0400, 0x00 }, + { 0x0401, 0x00 }, + { 0x0402, 0x00 }, + { 0x0403, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x03 }, + { 0x0420, 0x00 }, + { 0x0422, 0x00 }, + { 0x0423, 0x00 }, + { 0x0424, 0x00 }, + { 0x0425, 0x00 }, + { 0x0426, 0x00 }, + { 0x0427, 0x00 }, + { 0x0430, 0x00 }, + { 0x0432, 0x00 }, + { 0x0433, 0x00 }, + { 0x0434, 0x00 }, + { 0x0435, 0x00 }, + { 0x0436, 0x00 }, + { 0x0437, 0x00 }, + { 0x0f00, 0x00 }, + { 0x0f01, 0x00 }, + { 0x0f02, 0x00 }, + { 0x0f03, 0x00 }, + { 0x0f04, 0x00 }, + { 0x0f05, 0x00 }, + { 0x0f20, 0x00 }, + { 0x0f22, 0x00 }, + { 0x0f23, 0x00 }, + { 0x0f24, 0x00 }, + { 0x0f25, 0x00 }, + { 0x0f26, 0x00 }, + { 0x0f27, 0x00 }, + { 0x0f30, 0x00 }, + { 0x0f32, 0x00 }, + { 0x0f33, 0x00 }, + { 0x0f34, 0x00 }, + { 0x0f35, 0x00 }, + { 0x0f36, 0x00 }, + { 0x0f37, 0x00 }, + { 0x2f01, 0x01 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x0f }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f07, 0x8e }, + { 0x3000, 0x00 }, + { 0x3001, 0x00 }, + { 0x3004, 0x01 }, + { 0x3005, 0x23 }, + { 0x3008, 0x02 }, + { 0x300a, 0x00 }, + { 0xc003 | (RT1308_DAC_SET << 4), 0x00 }, + { 0xc001 | (RT1308_POWER << 4), 0x00 }, + { 0xc002 | (RT1308_POWER << 4), 0x00 }, +}; + +#define RT1308_SDW_OFFSET 0xc000 +#define RT1308_SDW_OFFSET_BYTE0 0xc000 +#define RT1308_SDW_OFFSET_BYTE1 0xc001 +#define RT1308_SDW_OFFSET_BYTE2 0xc002 +#define RT1308_SDW_OFFSET_BYTE3 0xc003 + +#define RT1308_SDW_RESET (RT1308_SDW_OFFSET | (RT1308_RESET << 4)) + +struct rt1308_sdw_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct sdw_slave *sdw_slave; + enum sdw_slave_status status; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; +}; + +struct sdw_stream_data { + struct sdw_stream_runtime *sdw_stream; +}; + +#endif /* __RT1308_SDW_H__ */ diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index f1b7b947ecbd..1a25a3787935 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -215,11 +215,9 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, { struct rt5514_dsp *rt5514_dsp = snd_soc_component_get_drvdata(component); - int ret; u8 buf[8]; mutex_lock(&rt5514_dsp->dma_lock); - ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); rt5514_dsp->substream = substream; rt5514_dsp->dma_offset = 0; @@ -230,7 +228,7 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, mutex_unlock(&rt5514_dsp->dma_lock); - return ret; + return 0; } static int rt5514_spi_hw_free(struct snd_soc_component *component, @@ -245,7 +243,7 @@ static int rt5514_spi_hw_free(struct snd_soc_component *component, cancel_delayed_work_sync(&rt5514_dsp->copy_work); - return snd_pcm_lib_free_pages(substream); + return 0; } static snd_pcm_uframes_t rt5514_spi_pcm_pointer( @@ -294,8 +292,8 @@ static int rt5514_spi_pcm_probe(struct snd_soc_component *component) static int rt5514_spi_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { - snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC, - NULL, 0, 0); + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC, + NULL, 0, 0); return 0; } diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c index 7810b1d7de32..3f40d2751833 100644 --- a/sound/soc/codecs/rt5677-spi.c +++ b/sound/soc/codecs/rt5677-spi.c @@ -132,14 +132,12 @@ static int rt5677_spi_hw_params( { struct rt5677_dsp *rt5677_dsp = snd_soc_component_get_drvdata(component); - int ret; mutex_lock(&rt5677_dsp->dma_lock); - ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); rt5677_dsp->substream = substream; mutex_unlock(&rt5677_dsp->dma_lock); - return ret; + return 0; } static int rt5677_spi_hw_free( @@ -153,7 +151,7 @@ static int rt5677_spi_hw_free( rt5677_dsp->substream = NULL; mutex_unlock(&rt5677_dsp->dma_lock); - return snd_pcm_lib_free_pages(substream); + return 0; } static int rt5677_spi_prepare( @@ -376,8 +374,8 @@ done: static int rt5677_spi_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { - snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC, - NULL, 0, 0); + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC, + NULL, 0, 0); return 0; } diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c new file mode 100644 index 000000000000..a4b95425886f --- /dev/null +++ b/sound/soc/codecs/rt700-sdw.c @@ -0,0 +1,551 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt700-sdw.c -- rt700 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/mod_devicetable.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "rt700.h" +#include "rt700-sdw.h" + +static bool rt700_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0: + case 0x00f0: + case 0x2000 ... 0x200e: + case 0x2012 ... 0x2016: + case 0x201a ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2200 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2220 ... 0x2223: + case 0x2230 ... 0x2231: + case 0x3000 ... 0x3fff: + case 0x7000 ... 0x7fff: + case 0x8300 ... 0x83ff: + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0x75201a: + case 0x752045: + case 0x752046: + case 0x752048: + case 0x75204a: + case 0x75206b: + case 0x752080: + case 0x752081: + return true; + default: + return false; + } +} + +static bool rt700_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2009: + case 0x2016: + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2021: + case 0x2023: + case 0x2230: + case 0x200b ... 0x200e: /* i2c read */ + case 0x2012 ... 0x2015: /* HD-A read */ + case 0x202d ... 0x202f: /* BRA */ + case 0x2201 ... 0x2212: /* i2c debug */ + case 0x2220 ... 0x2223: /* decoded HD-A */ + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0xff01: + case 0x75201a: + case 0x752046: + case 0x752080: + case 0x752081: + return true; + default: + return false; + } +} + +static int rt700_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2; + unsigned int is_hda_reg = 1, is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xff; + reg = reg >> 8; + nid = reg & 0xff; + ret = regmap_write(rt700->sdw_regmap, reg, 0); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, val2); + if (ret < 0) + return ret; + + reg3 = RT700_PRIV_DATA_R_H | nid; + ret = regmap_write(rt700->sdw_regmap, + reg3, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg4, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x3000) { + reg += 0x8000; + ret = regmap_write(rt700->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + reg += 0x2000; + reg |= 0x800; + ret = regmap_write(rt700->sdw_regmap, + reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt700->sdw_regmap, + reg2, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt700->sdw_regmap, reg, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x9000) { + ret = regmap_write(rt700->sdw_regmap, + reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0xb000) { + ret = regmap_write(rt700->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else { + ret = regmap_read(rt700->sdw_regmap, reg, val); + if (ret < 0) + return ret; + is_hda_reg = 0; + } + + if (is_hda_reg || is_index_reg) { + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + ret = regmap_read(rt700->sdw_regmap, + RT700_READ_HDA_3, &sdw_data_3); + if (ret < 0) + return ret; + ret = regmap_read(rt700->sdw_regmap, + RT700_READ_HDA_2, &sdw_data_2); + if (ret < 0) + return ret; + ret = regmap_read(rt700->sdw_regmap, + RT700_READ_HDA_1, &sdw_data_1); + if (ret < 0) + return ret; + ret = regmap_read(rt700->sdw_regmap, + RT700_READ_HDA_0, &sdw_data_0); + if (ret < 0) + return ret; + *val = ((sdw_data_3 & 0xff) << 24) | + ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + } + + if (is_hda_reg == 0) + dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", + __func__, reg, reg2, reg3, reg4, *val); + else + dev_dbg(dev, "[%s] %04x %04x => %08x\n", + __func__, reg, reg2, *val); + + return 0; +} + +static int rt700_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int reg2 = 0, reg3, reg4, nid, mask, val2; + unsigned int is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xff; + reg = reg >> 8; + nid = reg & 0xff; + ret = regmap_write(rt700->sdw_regmap, reg, 0); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, val2); + if (ret < 0) + return ret; + + reg3 = RT700_PRIV_DATA_W_H | nid; + ret = regmap_write(rt700->sdw_regmap, + reg3, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg4, (val & 0xff)); + if (ret < 0) + return ret; + is_index_reg = 1; + } else if (reg < 0x4fff) { + ret = regmap_write(rt700->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (reg == 0xff01) { + ret = regmap_write(rt700->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + ret = regmap_write(rt700->sdw_regmap, + reg, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, (val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt700->sdw_regmap, + reg2, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt700->sdw_regmap, reg, (val & 0xff)); + if (ret < 0) + return ret; + } + + if (reg2 == 0) + dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", + __func__, reg, reg2, reg3, reg4, val2, val); + else + dev_dbg(dev, "[%s] %04x %04x <= %04x\n", + __func__, reg, reg2, val); + + return 0; +} + +static const struct regmap_config rt700_regmap = { + .reg_bits = 24, + .val_bits = 32, + .readable_reg = rt700_readable_register, + .volatile_reg = rt700_volatile_register, + .max_register = 0x755800, + .reg_defaults = rt700_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt700_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, + .reg_read = rt700_sdw_read, + .reg_write = rt700_sdw_write, +}; + +static const struct regmap_config rt700_sdw_regmap = { + .name = "sdw", + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt700_readable_register, + .max_register = 0xff01, + .cache_type = REGCACHE_NONE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt700_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); + + /* Update the status */ + rt700->status = status; + + if (status == SDW_SLAVE_UNATTACHED) + rt700->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt700->hw_init || rt700->status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt700_io_init(&slave->dev, slave); +} + +static int rt700_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i, num_of_ports = 1; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = false; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x14; /* BITMAP: 00010100 */ + prop->sink_ports = 0xA; /* BITMAP: 00001010 */ + + nval = hweight32(prop->source_ports); + num_of_ports += nval; + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + num_of_ports += nval; + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* Allocate port_ready based on num_of_ports */ + slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, + sizeof(*slave->port_ready), + GFP_KERNEL); + if (!slave->port_ready) + return -ENOMEM; + + /* Initialize completion */ + for (i = 0; i < num_of_ports; i++) + init_completion(&slave->port_ready[i]); + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int rt700_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt700->params, params, sizeof(*params)); + + ret = rt700_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return ret; +} + +static int rt700_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); + + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + + if (status->control_port & 0x4) { + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_detect_work, msecs_to_jiffies(250)); + } + + return 0; +} + +/* + * slave_ops: callbacks for get_clock_stop_mode, clock_stop and + * port_prep are not defined for now + */ +static struct sdw_slave_ops rt700_slave_ops = { + .read_prop = rt700_read_prop, + .interrupt_callback = rt700_interrupt_callback, + .update_status = rt700_update_status, + .bus_config = rt700_bus_config, +}; + +static int rt700_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *sdw_regmap, *regmap; + + /* Assign ops */ + slave->ops = &rt700_slave_ops; + + /* Regmap Initialization */ + sdw_regmap = devm_regmap_init_sdw(slave, &rt700_sdw_regmap); + if (!sdw_regmap) + return -EINVAL; + + regmap = devm_regmap_init(&slave->dev, NULL, + &slave->dev, &rt700_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + rt700_init(&slave->dev, sdw_regmap, regmap, slave); + + return 0; +} + +static int rt700_sdw_remove(struct sdw_slave *slave) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); + + if (rt700 && rt700->hw_init) { + cancel_delayed_work(&rt700->jack_detect_work); + cancel_delayed_work(&rt700->jack_btn_check_work); + } + + return 0; +} + +static const struct sdw_device_id rt700_id[] = { + SDW_SLAVE_ENTRY(0x025d, 0x700, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt700_id); + +static int rt700_dev_suspend(struct device *dev) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + + if (!rt700->hw_init) + return 0; + + regcache_cache_only(rt700->regmap, true); + + return 0; +} + +#define RT700_PROBE_TIMEOUT 2000 + +static int rt700_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt700->hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT700_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt700->regmap, false); + regcache_sync_region(rt700->regmap, 0x3000, 0x8fff); + regcache_sync_region(rt700->regmap, 0x752010, 0x75206b); + + return 0; +} + +static const struct dev_pm_ops rt700_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt700_dev_suspend, rt700_dev_resume) + SET_RUNTIME_PM_OPS(rt700_dev_suspend, rt700_dev_resume, NULL) +}; + +static struct sdw_driver rt700_sdw_driver = { + .driver = { + .name = "rt700", + .owner = THIS_MODULE, + .pm = &rt700_pm, + }, + .probe = rt700_sdw_probe, + .remove = rt700_sdw_remove, + .ops = &rt700_slave_ops, + .id_table = rt700_id, +}; +module_sdw_driver(rt700_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT700 driver SDW"); +MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt700-sdw.h b/sound/soc/codecs/rt700-sdw.h new file mode 100644 index 000000000000..4ad0dcfd16fd --- /dev/null +++ b/sound/soc/codecs/rt700-sdw.h @@ -0,0 +1,335 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt700-sdw.h -- RT700 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT700_SDW_H__ +#define __RT700_SDW_H__ + +static const struct reg_default rt700_reg_defaults[] = { + { 0x0000, 0x0000 }, + { 0x0001, 0x0000 }, + { 0x0002, 0x0000 }, + { 0x0003, 0x0000 }, + { 0x0004, 0x0000 }, + { 0x0005, 0x0001 }, + { 0x0020, 0x0000 }, + { 0x0022, 0x0000 }, + { 0x0023, 0x0000 }, + { 0x0024, 0x0000 }, + { 0x0025, 0x0000 }, + { 0x0026, 0x0000 }, + { 0x0030, 0x0000 }, + { 0x0032, 0x0000 }, + { 0x0033, 0x0000 }, + { 0x0034, 0x0000 }, + { 0x0035, 0x0000 }, + { 0x0036, 0x0000 }, + { 0x0040, 0x0000 }, + { 0x0041, 0x0000 }, + { 0x0042, 0x0000 }, + { 0x0043, 0x0000 }, + { 0x0044, 0x0020 }, + { 0x0045, 0x0001 }, + { 0x0046, 0x0000 }, + { 0x0050, 0x0000 }, + { 0x0051, 0x0000 }, + { 0x0052, 0x0000 }, + { 0x0053, 0x0000 }, + { 0x0054, 0x0000 }, + { 0x0055, 0x0000 }, + { 0x0060, 0x0000 }, + { 0x0070, 0x0000 }, + { 0x00e0, 0x0000 }, + { 0x00f0, 0x0000 }, + { 0x0100, 0x0000 }, + { 0x0101, 0x0000 }, + { 0x0102, 0x0000 }, + { 0x0103, 0x0000 }, + { 0x0104, 0x0000 }, + { 0x0105, 0x0000 }, + { 0x0120, 0x0000 }, + { 0x0121, 0x0000 }, + { 0x0122, 0x0000 }, + { 0x0123, 0x0000 }, + { 0x0124, 0x0000 }, + { 0x0125, 0x0000 }, + { 0x0126, 0x0000 }, + { 0x0127, 0x0000 }, + { 0x0130, 0x0000 }, + { 0x0131, 0x0000 }, + { 0x0132, 0x0000 }, + { 0x0133, 0x0000 }, + { 0x0134, 0x0000 }, + { 0x0135, 0x0000 }, + { 0x0136, 0x0000 }, + { 0x0137, 0x0000 }, + { 0x0200, 0x0000 }, + { 0x0201, 0x0000 }, + { 0x0202, 0x0000 }, + { 0x0203, 0x0000 }, + { 0x0204, 0x0000 }, + { 0x0205, 0x0000 }, + { 0x0220, 0x0000 }, + { 0x0221, 0x0000 }, + { 0x0222, 0x0000 }, + { 0x0223, 0x0000 }, + { 0x0224, 0x0000 }, + { 0x0225, 0x0000 }, + { 0x0226, 0x0000 }, + { 0x0227, 0x0000 }, + { 0x0230, 0x0000 }, + { 0x0231, 0x0000 }, + { 0x0232, 0x0000 }, + { 0x0233, 0x0000 }, + { 0x0234, 0x0000 }, + { 0x0235, 0x0000 }, + { 0x0236, 0x0000 }, + { 0x0237, 0x0000 }, + { 0x0300, 0x0000 }, + { 0x0301, 0x0000 }, + { 0x0302, 0x0000 }, + { 0x0303, 0x0000 }, + { 0x0304, 0x0000 }, + { 0x0305, 0x0000 }, + { 0x0320, 0x0000 }, + { 0x0321, 0x0000 }, + { 0x0322, 0x0000 }, + { 0x0323, 0x0000 }, + { 0x0324, 0x0000 }, + { 0x0325, 0x0000 }, + { 0x0326, 0x0000 }, + { 0x0327, 0x0000 }, + { 0x0330, 0x0000 }, + { 0x0331, 0x0000 }, + { 0x0332, 0x0000 }, + { 0x0333, 0x0000 }, + { 0x0334, 0x0000 }, + { 0x0335, 0x0000 }, + { 0x0336, 0x0000 }, + { 0x0337, 0x0000 }, + { 0x0400, 0x0000 }, + { 0x0401, 0x0000 }, + { 0x0402, 0x0000 }, + { 0x0403, 0x0000 }, + { 0x0404, 0x0000 }, + { 0x0405, 0x0000 }, + { 0x0420, 0x0000 }, + { 0x0421, 0x0000 }, + { 0x0422, 0x0000 }, + { 0x0423, 0x0000 }, + { 0x0424, 0x0000 }, + { 0x0425, 0x0000 }, + { 0x0426, 0x0000 }, + { 0x0427, 0x0000 }, + { 0x0430, 0x0000 }, + { 0x0431, 0x0000 }, + { 0x0432, 0x0000 }, + { 0x0433, 0x0000 }, + { 0x0434, 0x0000 }, + { 0x0435, 0x0000 }, + { 0x0436, 0x0000 }, + { 0x0437, 0x0000 }, + { 0x0500, 0x0000 }, + { 0x0501, 0x0000 }, + { 0x0502, 0x0000 }, + { 0x0503, 0x0000 }, + { 0x0504, 0x0000 }, + { 0x0505, 0x0000 }, + { 0x0520, 0x0000 }, + { 0x0521, 0x0000 }, + { 0x0522, 0x0000 }, + { 0x0523, 0x0000 }, + { 0x0524, 0x0000 }, + { 0x0525, 0x0000 }, + { 0x0526, 0x0000 }, + { 0x0527, 0x0000 }, + { 0x0530, 0x0000 }, + { 0x0531, 0x0000 }, + { 0x0532, 0x0000 }, + { 0x0533, 0x0000 }, + { 0x0534, 0x0000 }, + { 0x0535, 0x0000 }, + { 0x0536, 0x0000 }, + { 0x0537, 0x0000 }, + { 0x0600, 0x0000 }, + { 0x0601, 0x0000 }, + { 0x0602, 0x0000 }, + { 0x0603, 0x0000 }, + { 0x0604, 0x0000 }, + { 0x0605, 0x0000 }, + { 0x0620, 0x0000 }, + { 0x0621, 0x0000 }, + { 0x0622, 0x0000 }, + { 0x0623, 0x0000 }, + { 0x0624, 0x0000 }, + { 0x0625, 0x0000 }, + { 0x0626, 0x0000 }, + { 0x0627, 0x0000 }, + { 0x0630, 0x0000 }, + { 0x0631, 0x0000 }, + { 0x0632, 0x0000 }, + { 0x0633, 0x0000 }, + { 0x0634, 0x0000 }, + { 0x0635, 0x0000 }, + { 0x0636, 0x0000 }, + { 0x0637, 0x0000 }, + { 0x0700, 0x0000 }, + { 0x0701, 0x0000 }, + { 0x0702, 0x0000 }, + { 0x0703, 0x0000 }, + { 0x0704, 0x0000 }, + { 0x0705, 0x0000 }, + { 0x0720, 0x0000 }, + { 0x0721, 0x0000 }, + { 0x0722, 0x0000 }, + { 0x0723, 0x0000 }, + { 0x0724, 0x0000 }, + { 0x0725, 0x0000 }, + { 0x0726, 0x0000 }, + { 0x0727, 0x0000 }, + { 0x0730, 0x0000 }, + { 0x0731, 0x0000 }, + { 0x0732, 0x0000 }, + { 0x0733, 0x0000 }, + { 0x0734, 0x0000 }, + { 0x0735, 0x0000 }, + { 0x0736, 0x0000 }, + { 0x0737, 0x0000 }, + { 0x0800, 0x0000 }, + { 0x0801, 0x0000 }, + { 0x0802, 0x0000 }, + { 0x0803, 0x0000 }, + { 0x0804, 0x0000 }, + { 0x0805, 0x0000 }, + { 0x0820, 0x0000 }, + { 0x0821, 0x0000 }, + { 0x0822, 0x0000 }, + { 0x0823, 0x0000 }, + { 0x0824, 0x0000 }, + { 0x0825, 0x0000 }, + { 0x0826, 0x0000 }, + { 0x0827, 0x0000 }, + { 0x0830, 0x0000 }, + { 0x0831, 0x0000 }, + { 0x0832, 0x0000 }, + { 0x0833, 0x0000 }, + { 0x0834, 0x0000 }, + { 0x0835, 0x0000 }, + { 0x0836, 0x0000 }, + { 0x0837, 0x0000 }, + { 0x0f00, 0x0000 }, + { 0x0f01, 0x0000 }, + { 0x0f02, 0x0000 }, + { 0x0f03, 0x0000 }, + { 0x0f04, 0x0000 }, + { 0x0f05, 0x0000 }, + { 0x0f20, 0x0000 }, + { 0x0f21, 0x0000 }, + { 0x0f22, 0x0000 }, + { 0x0f23, 0x0000 }, + { 0x0f24, 0x0000 }, + { 0x0f25, 0x0000 }, + { 0x0f26, 0x0000 }, + { 0x0f27, 0x0000 }, + { 0x0f30, 0x0000 }, + { 0x0f31, 0x0000 }, + { 0x0f32, 0x0000 }, + { 0x0f33, 0x0000 }, + { 0x0f34, 0x0000 }, + { 0x0f35, 0x0000 }, + { 0x0f36, 0x0000 }, + { 0x0f37, 0x0000 }, + { 0x2000, 0x0000 }, + { 0x2001, 0x0000 }, + { 0x2002, 0x0000 }, + { 0x2003, 0x0000 }, + { 0x2004, 0x0000 }, + { 0x2005, 0x0000 }, + { 0x2006, 0x0000 }, + { 0x2007, 0x0000 }, + { 0x2008, 0x0000 }, + { 0x2009, 0x0003 }, + { 0x200a, 0x0003 }, + { 0x200b, 0x0000 }, + { 0x200c, 0x0000 }, + { 0x200d, 0x0000 }, + { 0x200e, 0x0000 }, + { 0x2012, 0x0000 }, + { 0x2013, 0x0000 }, + { 0x2014, 0x0000 }, + { 0x2015, 0x0000 }, + { 0x2016, 0x0000 }, + { 0x201a, 0x0000 }, + { 0x201b, 0x0000 }, + { 0x201c, 0x0000 }, + { 0x201d, 0x0000 }, + { 0x201e, 0x0000 }, + { 0x201f, 0x0000 }, + { 0x2020, 0x0000 }, + { 0x2021, 0x0000 }, + { 0x2022, 0x0000 }, + { 0x2023, 0x0000 }, + { 0x2024, 0x0000 }, + { 0x2025, 0x0002 }, + { 0x2026, 0x0000 }, + { 0x2027, 0x0000 }, + { 0x2029, 0x0000 }, + { 0x202a, 0x0000 }, + { 0x202d, 0x0000 }, + { 0x202e, 0x0000 }, + { 0x202f, 0x0000 }, + { 0x2030, 0x0000 }, + { 0x2031, 0x0000 }, + { 0x2032, 0x0000 }, + { 0x2033, 0x0000 }, + { 0x2034, 0x0000 }, + { 0x2200, 0x0000 }, + { 0x2201, 0x0000 }, + { 0x2202, 0x0000 }, + { 0x2203, 0x0000 }, + { 0x2204, 0x0000 }, + { 0x2206, 0x0000 }, + { 0x2207, 0x0000 }, + { 0x2208, 0x0000 }, + { 0x2209, 0x0000 }, + { 0x220a, 0x0000 }, + { 0x220b, 0x0000 }, + { 0x220c, 0x0000 }, + { 0x220d, 0x0000 }, + { 0x220e, 0x0000 }, + { 0x220f, 0x0000 }, + { 0x2211, 0x0000 }, + { 0x2212, 0x0000 }, + { 0x2220, 0x0000 }, + { 0x2221, 0x0000 }, + { 0x2222, 0x0000 }, + { 0x2223, 0x0000 }, + { 0x2230, 0x0000 }, + { 0x2231, 0x0000 }, + { 0x3121, 0x0001 }, + { 0x3122, 0x0000 }, + { 0x3123, 0x0000 }, + { 0x7303, 0x0057 }, + { 0x7303, 0x0057 }, + { 0x8383, 0x0057 }, + { 0x7308, 0x0097 }, + { 0x8388, 0x0097 }, + { 0x7309, 0x0097 }, + { 0x8389, 0x0097 }, + { 0x7312, 0x0000 }, + { 0x8392, 0x0000 }, + { 0x7313, 0x0000 }, + { 0x8393, 0x0000 }, + { 0x7319, 0x0000 }, + { 0x8399, 0x0000 }, + { 0x75201a, 0x8003 }, + { 0x752045, 0x5289 }, + { 0x752048, 0xd049 }, + { 0x75204a, 0xa83b }, + { 0x75206b, 0x5064 }, +}; + +#endif /* __RT700_H__ */ diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c new file mode 100644 index 000000000000..ff68f0e4f629 --- /dev/null +++ b/sound/soc/codecs/rt700.c @@ -0,0 +1,1237 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt700.c -- rt700 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm_runtime.h> +#include <linux/pm.h> +#include <linux/soundwire/sdw.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/hda_verbs.h> +#include <sound/jack.h> + +#include "rt700.h" + +static int rt700_index_write(struct regmap *regmap, + unsigned int reg, unsigned int value) +{ + int ret; + unsigned int addr = (RT700_PRIV_INDEX_W_H << 8) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + pr_err("Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +static int rt700_index_read(struct regmap *regmap, + unsigned int reg, unsigned int *value) +{ + int ret; + unsigned int addr = (RT700_PRIV_INDEX_W_H << 8) | reg; + + *value = 0; + ret = regmap_read(regmap, addr, value); + if (ret < 0) + pr_err("Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static unsigned int rt700_button_detect(struct rt700_priv *rt700) +{ + unsigned int btn_type = 0, val80, val81; + int ret; + + ret = rt700_index_read(rt700->regmap, RT700_IRQ_FLAG_TABLE1, &val80); + if (ret < 0) + goto read_error; + ret = rt700_index_read(rt700->regmap, RT700_IRQ_FLAG_TABLE2, &val81); + if (ret < 0) + goto read_error; + + val80 &= 0x0381; + val81 &= 0xff00; + + switch (val80) { + case 0x0200: + case 0x0100: + case 0x0080: + btn_type |= SND_JACK_BTN_0; + break; + case 0x0001: + btn_type |= SND_JACK_BTN_3; + break; + } + switch (val81) { + case 0x8000: + case 0x4000: + case 0x2000: + btn_type |= SND_JACK_BTN_1; + break; + case 0x1000: + case 0x0800: + case 0x0400: + btn_type |= SND_JACK_BTN_2; + break; + case 0x0200: + case 0x0100: + btn_type |= SND_JACK_BTN_3; + break; + } +read_error: + return btn_type; +} + +static int rt700_headset_detect(struct rt700_priv *rt700) +{ + unsigned int buf, loop = 0; + int ret; + unsigned int jack_status = 0, reg; + + ret = rt700_index_read(rt700->regmap, + RT700_COMBO_JACK_AUTO_CTL2, &buf); + if (ret < 0) + goto io_error; + + while (loop < 500 && + (buf & RT700_COMBOJACK_AUTO_DET_STATUS) == 0) { + loop++; + + usleep_range(9000, 10000); + ret = rt700_index_read(rt700->regmap, + RT700_COMBO_JACK_AUTO_CTL2, &buf); + if (ret < 0) + goto io_error; + + reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT; + ret = regmap_read(rt700->regmap, reg, &jack_status); + if ((jack_status & (1 << 31)) == 0) + goto remove_error; + } + + if (loop >= 500) + goto to_error; + + if (buf & RT700_COMBOJACK_AUTO_DET_TRS) + rt700->jack_type = SND_JACK_HEADPHONE; + else if ((buf & RT700_COMBOJACK_AUTO_DET_CTIA) || + (buf & RT700_COMBOJACK_AUTO_DET_OMTP)) + rt700->jack_type = SND_JACK_HEADSET; + + return 0; + +to_error: + ret = -ETIMEDOUT; + pr_err_ratelimited("Time-out error in %s\n", __func__); + return ret; +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +remove_error: + pr_err_ratelimited("Jack removal in %s\n", __func__); + return -ENODEV; +} + +static void rt700_jack_detect_handler(struct work_struct *work) +{ + struct rt700_priv *rt700 = + container_of(work, struct rt700_priv, jack_detect_work.work); + int btn_type = 0, ret; + unsigned int jack_status = 0, reg; + + if (!rt700->hs_jack) + return; + + if (!rt700->component->card->instantiated) + return; + + reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT; + ret = regmap_read(rt700->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (jack_status & (1 << 31)) { + /* jack in */ + if (rt700->jack_type == 0) { + ret = rt700_headset_detect(rt700); + if (ret < 0) + return; + if (rt700->jack_type == SND_JACK_HEADSET) + btn_type = rt700_button_detect(rt700); + } else if (rt700->jack_type == SND_JACK_HEADSET) { + /* jack is already in, report button event */ + btn_type = rt700_button_detect(rt700); + } + } else { + /* jack out */ + rt700->jack_type = 0; + } + + dev_dbg(&rt700->slave->dev, + "in %s, jack_type=0x%x\n", __func__, rt700->jack_type); + dev_dbg(&rt700->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + + snd_soc_jack_report(rt700->hs_jack, rt700->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt700->hs_jack, rt700->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt700_btn_check_handler(struct work_struct *work) +{ + struct rt700_priv *rt700 = container_of(work, struct rt700_priv, + jack_btn_check_work.work); + int btn_type = 0, ret; + unsigned int jack_status = 0, reg; + + reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT; + ret = regmap_read(rt700->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (jack_status & (1 << 31)) { + if (rt700->jack_type == SND_JACK_HEADSET) { + /* jack is already in, report button event */ + btn_type = rt700_button_detect(rt700); + } + } else { + rt700->jack_type = 0; + } + + /* cbj comparator */ + ret = rt700_index_read(rt700->regmap, RT700_COMBO_JACK_AUTO_CTL2, ®); + if (ret < 0) + goto io_error; + + if ((reg & 0xf0) == 0xf0) + btn_type = 0; + + dev_dbg(&rt700->slave->dev, + "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt700->hs_jack, rt700->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt700->hs_jack, rt700->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt700_jack_init(struct rt700_priv *rt700) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(rt700->component); + + /* power on */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + if (rt700->hs_jack) { + /* Enable Jack Detection */ + regmap_write(rt700->regmap, + RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + regmap_write(rt700->regmap, + RT700_SET_HP_UNSOLICITED_ENABLE, 0x81); + regmap_write(rt700->regmap, + RT700_SET_INLINE_UNSOLICITED_ENABLE, 0x83); + rt700_index_write(rt700->regmap, 0x10, 0x2420); + rt700_index_write(rt700->regmap, 0x19, 0x2e11); + + dev_dbg(&rt700->slave->dev, "in %s enable\n", __func__); + + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_detect_work, msecs_to_jiffies(250)); + } else { + regmap_write(rt700->regmap, + RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x00); + regmap_write(rt700->regmap, + RT700_SET_HP_UNSOLICITED_ENABLE, 0x00); + regmap_write(rt700->regmap, + RT700_SET_INLINE_UNSOLICITED_ENABLE, 0x00); + + dev_dbg(&rt700->slave->dev, "in %s disable\n", __func__); + } + + /* power off */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); +} + +static int rt700_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + rt700->hs_jack = hs_jack; + + if (!rt700->hw_init) { + dev_dbg(&rt700->slave->dev, + "%s hw_init not ready yet\n", __func__); + return 0; + } + + rt700_jack_init(rt700); + + return 0; +} + +static void rt700_get_gain(struct rt700_priv *rt700, unsigned int addr_h, + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) +{ + /* R Channel */ + *r_val = (val_h << 8); + regmap_read(rt700->regmap, addr_l, r_val); + + /* L Channel */ + val_h |= 0x20; + *l_val = (val_h << 8); + regmap_read(rt700->regmap, addr_h, l_val); +} + +/* For Verb-Set Amplifier Gain (Verb ID = 3h) */ +static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + 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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int addr_h, addr_l, val_h, val_ll, val_lr; + unsigned int read_ll, read_rl; + int i; + + /* Can't use update bit function, so read the original value first */ + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll); + + /* L Channel */ + if (mc->invert) { + /* for mute */ + val_ll = (mc->max - ucontrol->value.integer.value[0]) << 7; + /* keep gain */ + read_ll = read_ll & 0x7f; + val_ll |= read_ll; + } else { + /* for gain */ + val_ll = ((ucontrol->value.integer.value[0]) & 0x7f); + if (val_ll > mc->max) + val_ll = mc->max; + /* keep mute status */ + read_ll = read_ll & 0x80; + val_ll |= read_ll; + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + /* R Channel */ + if (mc->invert) { + /* for mute */ + val_lr = (mc->max - ucontrol->value.integer.value[1]) << 7; + /* keep gain */ + read_rl = read_rl & 0x7f; + val_lr |= read_rl; + } else { + /* for gain */ + val_lr = ((ucontrol->value.integer.value[1]) & 0x7f); + if (val_lr > mc->max) + val_lr = mc->max; + /* keep mute status */ + read_rl = read_rl & 0x80; + val_lr |= read_rl; + } + + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + if (val_ll == val_lr) { + /* Set both L/R channels at the same time */ + val_h = (1 << mc->shift) | (3 << 4); + regmap_write(rt700->regmap, + addr_h, (val_h << 8 | val_ll)); + regmap_write(rt700->regmap, + addr_l, (val_h << 8 | val_ll)); + } else { + /* Lch*/ + val_h = (1 << mc->shift) | (1 << 5); + regmap_write(rt700->regmap, + addr_h, (val_h << 8 | val_ll)); + + /* Rch */ + val_h = (1 << mc->shift) | (1 << 4); + regmap_write(rt700->regmap, + addr_l, (val_h << 8 | val_lr)); + } + /* check result */ + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt700_get_gain(rt700, addr_h, addr_l, val_h, + &read_rl, &read_ll); + if (read_rl == val_lr && read_ll == val_ll) + break; + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + return 0; +} + +static int rt700_set_amp_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int addr_h, addr_l, val_h; + unsigned int read_ll, read_rl; + + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll); + + if (mc->invert) { + /* for mute status */ + read_ll = !((read_ll & 0x80) >> RT700_MUTE_SFT); + read_rl = !((read_rl & 0x80) >> RT700_MUTE_SFT); + } else { + /* for gain */ + read_ll = read_ll & 0x7f; + read_rl = read_rl & 0x7f; + } + ucontrol->value.integer.value[0] = read_ll; + ucontrol->value.integer.value[1] = read_rl; + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt700_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("DAC Front Playback Volume", + RT700_SET_GAIN_DAC1_H, RT700_SET_GAIN_DAC1_L, + RT700_DIR_OUT_SFT, 0x57, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, out_vol_tlv), + SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", + RT700_SET_GAIN_ADC2_H, RT700_SET_GAIN_ADC2_L, + RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", + RT700_SET_GAIN_ADC1_H, RT700_SET_GAIN_ADC1_L, + RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", + RT700_SET_GAIN_ADC2_H, RT700_SET_GAIN_ADC2_L, + RT700_DIR_IN_SFT, 0x3f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", + RT700_SET_GAIN_ADC1_H, RT700_SET_GAIN_ADC1_L, + RT700_DIR_IN_SFT, 0x3f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("AMIC Volume", + RT700_SET_GAIN_AMIC_H, RT700_SET_GAIN_AMIC_L, + RT700_DIR_IN_SFT, 3, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, mic_vol_tlv), +}; + +static int rt700_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int reg, val = 0, nid; + int ret; + + if (strstr(ucontrol->id.name, "HPO Mux")) + nid = RT700_HP_OUT; + else if (strstr(ucontrol->id.name, "ADC 22 Mux")) + nid = RT700_MIXER_IN1; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + nid = RT700_MIXER_IN2; + else + return -EINVAL; + + /* vid = 0xf01 */ + reg = RT700_VERB_SET_CONNECT_SEL | nid; + ret = regmap_read(rt700->regmap, reg, &val); + if (ret < 0) + return ret; + + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt700_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, reg, nid; + int ret; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "HPO Mux")) + nid = RT700_HP_OUT; + else if (strstr(ucontrol->id.name, "ADC 22 Mux")) + nid = RT700_MIXER_IN1; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + nid = RT700_MIXER_IN2; + else + return -EINVAL; + + /* Verb ID = 0x701h */ + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + reg = RT700_VERB_SET_CONNECT_SEL | nid; + ret = regmap_read(rt700->regmap, reg, &val2); + if (ret < 0) + return ret; + + if (val == val2) + change = 0; + else + change = 1; + + if (change) { + reg = RT700_VERB_SET_CONNECT_SEL | nid; + regmap_write(rt700->regmap, reg, val); + } + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", + "DMIC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt700_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt700_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static const struct snd_kcontrol_new rt700_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt700_adc22_enum, + rt700_mux_get, rt700_mux_put); + +static const struct snd_kcontrol_new rt700_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt700_adc23_enum, + rt700_mux_get, rt700_mux_put); + +static const char * const out_mux_text[] = { + "Front", + "Surround", +}; + +static SOC_ENUM_SINGLE_DECL( + rt700_hp_enum, SND_SOC_NOPM, 0, out_mux_text); + +static const struct snd_kcontrol_new rt700_hp_mux = + SOC_DAPM_ENUM_EXT("HP Mux", rt700_hp_enum, + rt700_mux_get, rt700_mux_put); + +static int rt700_dac_front_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_DAC1, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_DAC1, 0x00); + break; + } + return 0; +} + +static int rt700_dac_surround_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_DAC2, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_DAC2, 0x00); + break; + } + return 0; +} + +static int rt700_adc_09_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_ADC1, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_ADC1, 0x00); + break; + } + return 0; +} + +static int rt700_adc_08_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_ADC2, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_ADC2, 0x00); + break; + } + return 0; +} + +static int rt700_hpo_mux_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int val_h = (1 << RT700_DIR_OUT_SFT) | (0x3 << 4); + unsigned int val_l; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val_l = 0x00; + regmap_write(rt700->regmap, + RT700_SET_GAIN_HP_H, (val_h << 8 | val_l)); + break; + case SND_SOC_DAPM_PRE_PMD: + val_l = (1 << RT700_MUTE_SFT); + regmap_write(rt700->regmap, + RT700_SET_GAIN_HP_H, (val_h << 8 | val_l)); + usleep_range(50000, 55000); + break; + } + return 0; +} + +static int rt700_spk_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int val_h = (1 << RT700_DIR_OUT_SFT) | (0x3 << 4); + unsigned int val_l; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val_l = 0x00; + regmap_write(rt700->regmap, + RT700_SET_GAIN_SPK_H, (val_h << 8 | val_l)); + break; + case SND_SOC_DAPM_PRE_PMD: + val_l = (1 << RT700_MUTE_SFT); + regmap_write(rt700->regmap, + RT700_SET_GAIN_SPK_H, (val_h << 8 | val_l)); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt700_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + SND_SOC_DAPM_DAC_E("DAC Front", NULL, SND_SOC_NOPM, 0, 0, + rt700_dac_front_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("DAC Surround", NULL, SND_SOC_NOPM, 0, 0, + rt700_dac_surround_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX_E("HPO Mux", SND_SOC_NOPM, 0, 0, &rt700_hp_mux, + rt700_hpo_mux_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("SPK PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + rt700_spk_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC 09", NULL, SND_SOC_NOPM, 0, 0, + rt700_adc_09_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC 08", NULL, SND_SOC_NOPM, 0, 0, + rt700_adc_08_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt700_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt700_adc23_mux), + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt700_audio_map[] = { + {"DAC Front", NULL, "DP1RX"}, + {"DAC Surround", NULL, "DP3RX"}, + {"DP2TX", NULL, "ADC 09"}, + {"DP4TX", NULL, "ADC 08"}, + {"ADC 09", NULL, "ADC 22 Mux"}, + {"ADC 08", NULL, "ADC 23 Mux"}, + {"ADC 22 Mux", "DMIC", "DMIC1"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "DMIC", "DMIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1"}, + {"ADC 23 Mux", "LINE2", "LINE2"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + {"HPO Mux", "Front", "DAC Front"}, + {"HPO Mux", "Surround", "DAC Surround"}, + {"HP", NULL, "HPO Mux"}, + {"SPK PGA", NULL, "DAC Front"}, + {"SPK", NULL, "SPK PGA"}, +}; + +static int rt700_probe(struct snd_soc_component *component) +{ + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + rt700->component = component; + + return 0; +} + +static int rt700_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, + AC_PWRST_D0); + } + break; + + case SND_SOC_BIAS_STANDBY: + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, + AC_PWRST_D3); + break; + + default: + break; + } + dapm->bias_level = level; + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_rt700 = { + .probe = rt700_probe, + .set_bias_level = rt700_set_bias_level, + .controls = rt700_snd_controls, + .num_controls = ARRAY_SIZE(rt700_snd_controls), + .dapm_widgets = rt700_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt700_dapm_widgets), + .dapm_routes = rt700_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt700_audio_map), + .set_jack = rt700_set_jack_detect, +}; + +static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + struct sdw_stream_data *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + + /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = stream; + else + dai->capture_dma_data = stream; + + return 0; +} + +static void rt700_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_stream_data *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(stream); +} + +static int rt700_pcm_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 rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_data *stream; + int retval, port, num_channels; + unsigned int val = 0; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!stream) + return -EINVAL; + + if (!rt700->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + /* This code assumes port 1 for playback and port 2 for capture */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + port = 1; + } else { + direction = SDW_DATA_DIR_TX; + port = 2; + } + + switch (dai->id) { + case RT700_AIF1: + break; + case RT700_AIF2: + port += 2; + break; + default: + dev_err(component->dev, "Invalid DAI id %d\n", dai->id); + return -EINVAL; + } + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = (1 << (num_channels)) - 1; + port_config.num = port; + + retval = sdw_stream_add_slave(rt700->slave, &stream_config, + &port_config, 1, stream->sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 8: + break; + case 16: + val |= (0x1 << 4); + break; + case 20: + val |= (0x2 << 4); + break; + case 24: + val |= (0x3 << 4); + break; + case 32: + val |= (0x4 << 4); + break; + default: + return -EINVAL; + } + + /* 48Khz */ + regmap_write(rt700->regmap, RT700_DAC_FORMAT_H, val); + regmap_write(rt700->regmap, RT700_ADC_FORMAT_H, val); + + return retval; +} + +static int rt700_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt700->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt700->slave, stream->sdw_stream); + return 0; +} + +#define RT700_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT700_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static struct snd_soc_dai_ops rt700_ops = { + .hw_params = rt700_pcm_hw_params, + .hw_free = rt700_pcm_hw_free, + .set_sdw_stream = rt700_set_sdw_stream, + .shutdown = rt700_shutdown, +}; + +static struct snd_soc_dai_driver rt700_dai[] = { + { + .name = "rt700-aif1", + .id = RT700_AIF1, + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, + { + .name = "rt700-aif2", + .id = RT700_AIF2, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, +}; + +/* Bus clock frequency */ +#define RT700_CLK_FREQ_9600000HZ 9600000 +#define RT700_CLK_FREQ_12000000HZ 12000000 +#define RT700_CLK_FREQ_6000000HZ 6000000 +#define RT700_CLK_FREQ_4800000HZ 4800000 +#define RT700_CLK_FREQ_2400000HZ 2400000 +#define RT700_CLK_FREQ_12288000HZ 12288000 + +int rt700_clock_config(struct device *dev) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt700->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT700_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT700_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT700_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT700_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT700_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT700_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt700->regmap, 0xe0, value); + regmap_write(rt700->regmap, 0xf0, value); + + dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq); + + return 0; +} + +int rt700_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave) + +{ + struct rt700_priv *rt700; + int ret; + + rt700 = devm_kzalloc(dev, sizeof(*rt700), GFP_KERNEL); + if (!rt700) + return -ENOMEM; + + dev_set_drvdata(dev, rt700); + rt700->slave = slave; + rt700->sdw_regmap = sdw_regmap; + rt700->regmap = regmap; + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt700->hw_init = false; + rt700->first_hw_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_codec_dev_rt700, + rt700_dai, + ARRAY_SIZE(rt700_dai)); + + dev_dbg(&slave->dev, "%s\n", __func__); + + return ret; +} + +int rt700_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + + if (rt700->hw_init) + return 0; + + if (rt700->first_hw_init) { + regcache_cache_only(rt700->regmap, false); + regcache_cache_bypass(rt700->regmap, true); + } + + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + if (!rt700->first_hw_init) { + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + /* reset */ + regmap_write(rt700->regmap, 0xff01, 0x0000); + regmap_write(rt700->regmap, 0x7520, 0x001a); + regmap_write(rt700->regmap, 0x7420, 0xc003); + + /* power on */ + regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + /* Set Pin Widget */ + regmap_write(rt700->regmap, RT700_SET_PIN_HP, 0x40); + regmap_write(rt700->regmap, RT700_SET_PIN_SPK, 0x40); + regmap_write(rt700->regmap, RT700_SET_EAPD_SPK, RT700_EAPD_HIGH); + regmap_write(rt700->regmap, RT700_SET_PIN_DMIC1, 0x20); + regmap_write(rt700->regmap, RT700_SET_PIN_DMIC2, 0x20); + regmap_write(rt700->regmap, RT700_SET_PIN_MIC2, 0x20); + + /* Set Configuration Default */ + regmap_write(rt700->regmap, 0x4f12, 0x91); + regmap_write(rt700->regmap, 0x4e12, 0xd6); + regmap_write(rt700->regmap, 0x4d12, 0x11); + regmap_write(rt700->regmap, 0x4c12, 0x20); + regmap_write(rt700->regmap, 0x4f13, 0x91); + regmap_write(rt700->regmap, 0x4e13, 0xd6); + regmap_write(rt700->regmap, 0x4d13, 0x11); + regmap_write(rt700->regmap, 0x4c13, 0x21); + + regmap_write(rt700->regmap, 0x4f19, 0x02); + regmap_write(rt700->regmap, 0x4e19, 0xa1); + regmap_write(rt700->regmap, 0x4d19, 0x90); + regmap_write(rt700->regmap, 0x4c19, 0x80); + + /* Enable Line2 */ + regmap_write(rt700->regmap, 0x371b, 0x40); + regmap_write(rt700->regmap, 0x731b, 0xb0); + regmap_write(rt700->regmap, 0x839b, 0x00); + + /* Set index */ + rt700_index_write(rt700->regmap, 0x4a, 0x201b); + rt700_index_write(rt700->regmap, 0x45, 0x5089); + rt700_index_write(rt700->regmap, 0x6b, 0x5064); + rt700_index_write(rt700->regmap, 0x48, 0xd249); + + /* Finish Initial Settings, set power to D3 */ + regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + if (!rt700->first_hw_init) { + INIT_DELAYED_WORK(&rt700->jack_detect_work, + rt700_jack_detect_handler); + INIT_DELAYED_WORK(&rt700->jack_btn_check_work, + rt700_btn_check_handler); + } + + /* + * if set_jack callback occurred early than io_init, + * we set up the jack detection function now + */ + if (rt700->hs_jack) + rt700_jack_init(rt700); + + if (rt700->first_hw_init) { + regcache_cache_bypass(rt700->regmap, false); + regcache_mark_dirty(rt700->regmap); + } else + rt700->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt700->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + + return 0; +} + +MODULE_DESCRIPTION("ASoC RT700 driver SDW"); +MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h new file mode 100644 index 000000000000..794ee2e29051 --- /dev/null +++ b/sound/soc/codecs/rt700.h @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt700.h -- RT700 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT700_H__ +#define __RT700_H__ + +extern const struct dev_pm_ops rt700_runtime_pm; + +struct rt700_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct regmap *sdw_regmap; + struct sdw_slave *slave; + enum sdw_slave_status status; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + int jack_type; +}; + +struct sdw_stream_data { + struct sdw_stream_runtime *sdw_stream; +}; + +/* NID */ +#define RT700_AUDIO_FUNCTION_GROUP 0x01 +#define RT700_DAC_OUT1 0x02 +#define RT700_DAC_OUT2 0x03 +#define RT700_ADC_IN1 0x09 +#define RT700_ADC_IN2 0x08 +#define RT700_DMIC1 0x12 +#define RT700_DMIC2 0x13 +#define RT700_SPK_OUT 0x14 +#define RT700_MIC2 0x19 +#define RT700_LINE1 0x1a +#define RT700_LINE2 0x1b +#define RT700_BEEP 0x1d +#define RT700_SPDIF 0x1e +#define RT700_VENDOR_REGISTERS 0x20 +#define RT700_HP_OUT 0x21 +#define RT700_MIXER_IN1 0x22 +#define RT700_MIXER_IN2 0x23 +#define RT700_INLINE_CMD 0x55 + +/* Index (NID:20h) */ +#define RT700_DAC_DC_CALI_CTL1 0x00 +#define RT700_PARA_VERB_CTL 0x1a +#define RT700_COMBO_JACK_AUTO_CTL1 0x45 +#define RT700_COMBO_JACK_AUTO_CTL2 0x46 +#define RT700_INLINE_CMD_CTL 0x48 +#define RT700_DIGITAL_MISC_CTRL4 0x4a +#define RT700_VREFOUT_CTL 0x6b +#define RT700_FSM_CTL 0x6f +#define RT700_IRQ_FLAG_TABLE1 0x80 +#define RT700_IRQ_FLAG_TABLE2 0x81 +#define RT700_IRQ_FLAG_TABLE3 0x82 + +/* Verb */ +#define RT700_VERB_SET_CONNECT_SEL 0x3100 +#define RT700_VERB_SET_EAPD_BTLENABLE 0x3c00 +#define RT700_VERB_GET_CONNECT_SEL 0xb100 +#define RT700_VERB_SET_POWER_STATE 0x3500 +#define RT700_VERB_SET_CHANNEL_STREAMID 0x3600 +#define RT700_VERB_SET_PIN_WIDGET_CONTROL 0x3700 +#define RT700_VERB_SET_UNSOLICITED_ENABLE 0x3800 +#define RT700_SET_AMP_GAIN_MUTE_H 0x7300 +#define RT700_SET_AMP_GAIN_MUTE_L 0x8380 +#define RT700_VERB_GET_PIN_SENSE 0xb900 + +#define RT700_READ_HDA_3 0x2012 +#define RT700_READ_HDA_2 0x2013 +#define RT700_READ_HDA_1 0x2014 +#define RT700_READ_HDA_0 0x2015 +#define RT700_PRIV_INDEX_W_H 0x7520 +#define RT700_PRIV_INDEX_W_L 0x85a0 +#define RT700_PRIV_DATA_W_H 0x7420 +#define RT700_PRIV_DATA_W_L 0x84a0 +#define RT700_PRIV_INDEX_R_H 0x9d20 +#define RT700_PRIV_INDEX_R_L 0xada0 +#define RT700_PRIV_DATA_R_H 0x9c20 +#define RT700_PRIV_DATA_R_L 0xaca0 +#define RT700_DAC_FORMAT_H 0x7203 +#define RT700_DAC_FORMAT_L 0x8283 +#define RT700_ADC_FORMAT_H 0x7209 +#define RT700_ADC_FORMAT_L 0x8289 +#define RT700_SET_AUDIO_POWER_STATE\ + (RT700_VERB_SET_POWER_STATE | RT700_AUDIO_FUNCTION_GROUP) +#define RT700_SET_PIN_DMIC1\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC1) +#define RT700_SET_PIN_DMIC2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC2) +#define RT700_SET_PIN_SPK\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_SPK_OUT) +#define RT700_SET_PIN_HP\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_HP_OUT) +#define RT700_SET_PIN_MIC2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_MIC2) +#define RT700_SET_PIN_LINE1\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE1) +#define RT700_SET_PIN_LINE2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE2) +#define RT700_SET_MIC2_UNSOLICITED_ENABLE\ + (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_MIC2) +#define RT700_SET_HP_UNSOLICITED_ENABLE\ + (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_HP_OUT) +#define RT700_SET_INLINE_UNSOLICITED_ENABLE\ + (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_INLINE_CMD) +#define RT700_SET_STREAMID_DAC1\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT1) +#define RT700_SET_STREAMID_DAC2\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT2) +#define RT700_SET_STREAMID_ADC1\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN1) +#define RT700_SET_STREAMID_ADC2\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN2) +#define RT700_SET_GAIN_DAC1_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_DAC_OUT1) +#define RT700_SET_GAIN_DAC1_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_DAC_OUT1) +#define RT700_SET_GAIN_ADC1_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN1) +#define RT700_SET_GAIN_ADC1_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN1) +#define RT700_SET_GAIN_ADC2_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN2) +#define RT700_SET_GAIN_ADC2_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN2) +#define RT700_SET_GAIN_AMIC_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_MIC2) +#define RT700_SET_GAIN_AMIC_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_MIC2) +#define RT700_SET_GAIN_HP_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_HP_OUT) +#define RT700_SET_GAIN_HP_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_HP_OUT) +#define RT700_SET_GAIN_SPK_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_SPK_OUT) +#define RT700_SET_GAIN_SPK_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_SPK_OUT) +#define RT700_SET_EAPD_SPK\ + (RT700_VERB_SET_EAPD_BTLENABLE | RT700_SPK_OUT) + +/* combo jack auto switch control 2 (0x46)(NID:20h) */ +#define RT700_COMBOJACK_AUTO_DET_STATUS (0x1 << 11) +#define RT700_COMBOJACK_AUTO_DET_TRS (0x1 << 10) +#define RT700_COMBOJACK_AUTO_DET_CTIA (0x1 << 9) +#define RT700_COMBOJACK_AUTO_DET_OMTP (0x1 << 8) + +#define RT700_EAPD_HIGH 0x2 +#define RT700_EAPD_LOW 0x0 +#define RT700_MUTE_SFT 7 +#define RT700_DIR_IN_SFT 6 +#define RT700_DIR_OUT_SFT 7 + +enum { + RT700_AIF1, + RT700_AIF2, + RT700_AIFS, +}; + +int rt700_io_init(struct device *dev, struct sdw_slave *slave); +int rt700_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave); + +int rt700_jack_detect(struct rt700_priv *rt700, bool *hp, bool *mic); +int rt700_clock_config(struct device *dev); +#endif /* __RT700_H__ */ diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c new file mode 100644 index 000000000000..85e62e1059cd --- /dev/null +++ b/sound/soc/codecs/rt711-sdw.c @@ -0,0 +1,552 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt711-sdw.c -- rt711 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/mod_devicetable.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "rt711.h" +#include "rt711-sdw.h" + +static bool rt711_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0: + case 0x00f0: + case 0x2012 ... 0x2016: + case 0x201a ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2201 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2220 ... 0x2223: + case 0x2230 ... 0x2239: + case 0x2f01 ... 0x2f0f: + case 0x3000 ... 0x3fff: + case 0x7000 ... 0x7fff: + case 0x8300 ... 0x83ff: + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0x752009: + case 0x752011: + case 0x75201a: + case 0x752045: + case 0x752046: + case 0x752048: + case 0x75204a: + case 0x75206b: + case 0x75206f: + case 0x752080: + case 0x752081: + case 0x752091: + case 0x755800: + return true; + default: + return false; + } +} + +static bool rt711_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2016: + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2021: + case 0x2023: + case 0x2230: + case 0x2012 ... 0x2015: /* HD-A read */ + case 0x202d ... 0x202f: /* BRA */ + case 0x2201 ... 0x2212: /* i2c debug */ + case 0x2220 ... 0x2223: /* decoded HD-A */ + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0xff01: + case 0x75201a: + case 0x752046: + case 0x752080: + case 0x752081: + case 0x755800: + return true; + default: + return false; + } +} + +static int rt711_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct rt711_priv *rt711 = dev_get_drvdata(dev); + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2; + unsigned int is_hda_reg = 1, is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xff; + reg = reg >> 8; + nid = reg & 0xff; + ret = regmap_write(rt711->sdw_regmap, reg, 0); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, val2); + if (ret < 0) + return ret; + + reg3 = RT711_PRIV_DATA_R_H | nid; + ret = regmap_write(rt711->sdw_regmap, + reg3, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg4, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x3000) { + reg += 0x8000; + ret = regmap_write(rt711->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + reg += 0x2000; + reg |= 0x800; + ret = regmap_write(rt711->sdw_regmap, + reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt711->sdw_regmap, + reg2, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt711->sdw_regmap, reg, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x9000) { + ret = regmap_write(rt711->sdw_regmap, + reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0xb000) { + ret = regmap_write(rt711->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else { + ret = regmap_read(rt711->sdw_regmap, reg, val); + if (ret < 0) + return ret; + is_hda_reg = 0; + } + + if (is_hda_reg || is_index_reg) { + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + ret = regmap_read(rt711->sdw_regmap, + RT711_READ_HDA_3, &sdw_data_3); + if (ret < 0) + return ret; + ret = regmap_read(rt711->sdw_regmap, + RT711_READ_HDA_2, &sdw_data_2); + if (ret < 0) + return ret; + ret = regmap_read(rt711->sdw_regmap, + RT711_READ_HDA_1, &sdw_data_1); + if (ret < 0) + return ret; + ret = regmap_read(rt711->sdw_regmap, + RT711_READ_HDA_0, &sdw_data_0); + if (ret < 0) + return ret; + *val = ((sdw_data_3 & 0xff) << 24) | + ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + } + + if (is_hda_reg == 0) + dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", + __func__, reg, reg2, reg3, reg4, *val); + else + dev_dbg(dev, "[%s] %04x %04x => %08x\n", + __func__, reg, reg2, *val); + + return 0; +} + +static int rt711_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct rt711_priv *rt711 = dev_get_drvdata(dev); + unsigned int reg2 = 0, reg3, reg4, nid, mask, val2; + unsigned int is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xff; + reg = reg >> 8; + nid = reg & 0xff; + ret = regmap_write(rt711->sdw_regmap, reg, 0); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, val2); + if (ret < 0) + return ret; + + reg3 = RT711_PRIV_DATA_W_H | nid; + ret = regmap_write(rt711->sdw_regmap, + reg3, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg4, (val & 0xff)); + if (ret < 0) + return ret; + is_index_reg = 1; + } else if (reg < 0x4fff) { + ret = regmap_write(rt711->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (reg == RT711_FUNC_RESET) { + ret = regmap_write(rt711->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + ret = regmap_write(rt711->sdw_regmap, + reg, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, (val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt711->sdw_regmap, + reg2, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt711->sdw_regmap, reg, (val & 0xff)); + if (ret < 0) + return ret; + } + + if (reg2 == 0) + dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", + __func__, reg, reg2, reg3, reg4, val2, val); + else + dev_dbg(dev, "[%s] %04x %04x <= %04x\n", + __func__, reg, reg2, val); + + return 0; +} + +static const struct regmap_config rt711_regmap = { + .reg_bits = 24, + .val_bits = 32, + .readable_reg = rt711_readable_register, + .volatile_reg = rt711_volatile_register, + .max_register = 0x755800, + .reg_defaults = rt711_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt711_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, + .reg_read = rt711_sdw_read, + .reg_write = rt711_sdw_write, +}; + +static const struct regmap_config rt711_sdw_regmap = { + .name = "sdw", + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt711_readable_register, + .max_register = 0xff01, + .cache_type = REGCACHE_NONE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt711_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); + + /* Update the status */ + rt711->status = status; + + if (status == SDW_SLAVE_UNATTACHED) + rt711->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt711->hw_init || rt711->status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt711_io_init(&slave->dev, slave); +} + +static int rt711_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i, num_of_ports = 1; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = false; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x14; /* BITMAP: 00010100 */ + prop->sink_ports = 0x8; /* BITMAP: 00001000 */ + + nval = hweight32(prop->source_ports); + num_of_ports += nval; + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + num_of_ports += nval; + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* Allocate port_ready based on num_of_ports */ + slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, + sizeof(*slave->port_ready), + GFP_KERNEL); + if (!slave->port_ready) + return -ENOMEM; + + /* Initialize completion */ + for (i = 0; i < num_of_ports; i++) + init_completion(&slave->port_ready[i]); + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int rt711_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt711->params, params, sizeof(*params)); + + ret = rt711_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return ret; +} + +static int rt711_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); + + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + + if (status->control_port & 0x4) { + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_detect_work, msecs_to_jiffies(250)); + } + + return 0; +} + +static struct sdw_slave_ops rt711_slave_ops = { + .read_prop = rt711_read_prop, + .interrupt_callback = rt711_interrupt_callback, + .update_status = rt711_update_status, + .bus_config = rt711_bus_config, +}; + +static int rt711_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *sdw_regmap, *regmap; + + /* Assign ops */ + slave->ops = &rt711_slave_ops; + + /* Regmap Initialization */ + sdw_regmap = devm_regmap_init_sdw(slave, &rt711_sdw_regmap); + if (!sdw_regmap) + return -EINVAL; + + regmap = devm_regmap_init(&slave->dev, NULL, + &slave->dev, &rt711_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + rt711_init(&slave->dev, sdw_regmap, regmap, slave); + + return 0; +} + +static int rt711_sdw_remove(struct sdw_slave *slave) +{ + struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); + + if (rt711 && rt711->hw_init) { + cancel_delayed_work(&rt711->jack_detect_work); + cancel_delayed_work(&rt711->jack_btn_check_work); + cancel_work_sync(&rt711->calibration_work); + } + + return 0; +} + +static const struct sdw_device_id rt711_id[] = { + SDW_SLAVE_ENTRY(0x025d, 0x711, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt711_id); + +static int rt711_dev_suspend(struct device *dev) +{ + struct rt711_priv *rt711 = dev_get_drvdata(dev); + + if (!rt711->hw_init) + return 0; + + regcache_cache_only(rt711->regmap, true); + + return 0; +} + +#define RT711_PROBE_TIMEOUT 2000 + +static int rt711_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt711_priv *rt711 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt711->hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT711_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt711->regmap, false); + regcache_sync_region(rt711->regmap, 0x3000, 0x8fff); + regcache_sync_region(rt711->regmap, 0x752009, 0x752091); + + return 0; +} + +static const struct dev_pm_ops rt711_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt711_dev_suspend, rt711_dev_resume) + SET_RUNTIME_PM_OPS(rt711_dev_suspend, rt711_dev_resume, NULL) +}; + +static struct sdw_driver rt711_sdw_driver = { + .driver = { + .name = "rt711", + .owner = THIS_MODULE, + .pm = &rt711_pm, + }, + .probe = rt711_sdw_probe, + .remove = rt711_sdw_remove, + .ops = &rt711_slave_ops, + .id_table = rt711_id, +}; +module_sdw_driver(rt711_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT711 SDW driver"); +MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt711-sdw.h b/sound/soc/codecs/rt711-sdw.h new file mode 100644 index 000000000000..43b2b984b29c --- /dev/null +++ b/sound/soc/codecs/rt711-sdw.h @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt711-sdw.h -- RT711 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT711_SDW_H__ +#define __RT711_SDW_H__ + +static const struct reg_default rt711_reg_defaults[] = { + { 0x0000, 0x00 }, + { 0x0001, 0x00 }, + { 0x0002, 0x00 }, + { 0x0003, 0x00 }, + { 0x0004, 0x00 }, + { 0x0005, 0x01 }, + { 0x0020, 0x00 }, + { 0x0022, 0x00 }, + { 0x0023, 0x00 }, + { 0x0024, 0x00 }, + { 0x0025, 0x00 }, + { 0x0026, 0x00 }, + { 0x0030, 0x00 }, + { 0x0032, 0x00 }, + { 0x0033, 0x00 }, + { 0x0034, 0x00 }, + { 0x0035, 0x00 }, + { 0x0036, 0x00 }, + { 0x0040, 0x00 }, + { 0x0041, 0x00 }, + { 0x0042, 0x00 }, + { 0x0043, 0x00 }, + { 0x0044, 0x20 }, + { 0x0045, 0x01 }, + { 0x0046, 0x01 }, + { 0x0050, 0x20 }, + { 0x0051, 0x02 }, + { 0x0052, 0x5d }, + { 0x0053, 0x07 }, + { 0x0054, 0x11 }, + { 0x0055, 0x00 }, + { 0x0060, 0x00 }, + { 0x0070, 0x00 }, + { 0x0080, 0xc0 }, + { 0x0088, 0x00 }, + { 0x00e0, 0x00 }, + { 0x00e1, 0x00 }, + { 0x00e2, 0x00 }, + { 0x00e3, 0x00 }, + { 0x00e5, 0x00 }, + { 0x00ee, 0x00 }, + { 0x00ef, 0x00 }, + { 0x00f0, 0x00 }, + { 0x00f1, 0x00 }, + { 0x00f2, 0x00 }, + { 0x00f3, 0x00 }, + { 0x00f4, 0x00 }, + { 0x00f5, 0x00 }, + { 0x00fe, 0x00 }, + { 0x00ff, 0x00 }, + { 0x0100, 0x00 }, + { 0x0101, 0x00 }, + { 0x0102, 0x00 }, + { 0x0103, 0x00 }, + { 0x0104, 0x00 }, + { 0x0105, 0x00 }, + { 0x0120, 0x00 }, + { 0x0122, 0x00 }, + { 0x0123, 0x00 }, + { 0x0124, 0x00 }, + { 0x0125, 0x00 }, + { 0x0126, 0x00 }, + { 0x0127, 0x00 }, + { 0x0130, 0x00 }, + { 0x0132, 0x00 }, + { 0x0133, 0x00 }, + { 0x0134, 0x00 }, + { 0x0135, 0x00 }, + { 0x0136, 0x00 }, + { 0x0137, 0x00 }, + { 0x0200, 0x00 }, + { 0x0201, 0x00 }, + { 0x0202, 0x00 }, + { 0x0203, 0x00 }, + { 0x0204, 0x00 }, + { 0x0205, 0x03 }, + { 0x0220, 0x00 }, + { 0x0222, 0x00 }, + { 0x0223, 0x00 }, + { 0x0224, 0x00 }, + { 0x0225, 0x00 }, + { 0x0226, 0x00 }, + { 0x0227, 0x00 }, + { 0x0230, 0x00 }, + { 0x0232, 0x00 }, + { 0x0233, 0x00 }, + { 0x0234, 0x00 }, + { 0x0235, 0x00 }, + { 0x0236, 0x00 }, + { 0x0237, 0x00 }, + { 0x0300, 0x00 }, + { 0x0301, 0x00 }, + { 0x0302, 0x20 }, + { 0x0303, 0x00 }, + { 0x0304, 0x00 }, + { 0x0305, 0x03 }, + { 0x0320, 0x00 }, + { 0x0322, 0x00 }, + { 0x0323, 0x00 }, + { 0x0324, 0x00 }, + { 0x0325, 0x00 }, + { 0x0326, 0x00 }, + { 0x0327, 0x00 }, + { 0x0330, 0x00 }, + { 0x0332, 0x00 }, + { 0x0333, 0x00 }, + { 0x0334, 0x00 }, + { 0x0335, 0x00 }, + { 0x0336, 0x00 }, + { 0x0337, 0x00 }, + { 0x0400, 0x00 }, + { 0x0401, 0x00 }, + { 0x0402, 0x00 }, + { 0x0403, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x03 }, + { 0x0420, 0x00 }, + { 0x0422, 0x00 }, + { 0x0423, 0x00 }, + { 0x0424, 0x00 }, + { 0x0425, 0x00 }, + { 0x0426, 0x00 }, + { 0x0427, 0x00 }, + { 0x0430, 0x00 }, + { 0x0432, 0x00 }, + { 0x0433, 0x00 }, + { 0x0434, 0x00 }, + { 0x0435, 0x00 }, + { 0x0436, 0x00 }, + { 0x0437, 0x00 }, + { 0x0f00, 0x00 }, + { 0x0f01, 0x00 }, + { 0x0f02, 0x20 }, + { 0x0f03, 0x00 }, + { 0x0f04, 0x00 }, + { 0x0f05, 0x03 }, + { 0x0f06, 0x00 }, + { 0x0f07, 0x00 }, + { 0x0f08, 0x00 }, + { 0x0f09, 0x00 }, + { 0x0f10, 0x00 }, + { 0x0f11, 0x00 }, + { 0x0f12, 0x00 }, + { 0x0f13, 0x00 }, + { 0x0f14, 0x00 }, + { 0x0f15, 0x00 }, + { 0x0f16, 0x00 }, + { 0x0f17, 0x00 }, + { 0x0f18, 0x00 }, + { 0x0f19, 0x00 }, + { 0x0f1a, 0x00 }, + { 0x0f1b, 0x00 }, + { 0x0f1c, 0x00 }, + { 0x0f1d, 0x00 }, + { 0x0f1e, 0x00 }, + { 0x0f1f, 0x00 }, + { 0x0f20, 0x00 }, + { 0x0f22, 0x00 }, + { 0x0f23, 0x00 }, + { 0x0f24, 0x00 }, + { 0x0f25, 0x00 }, + { 0x0f26, 0x00 }, + { 0x0f27, 0x00 }, + { 0x0f30, 0x00 }, + { 0x0f32, 0x00 }, + { 0x0f33, 0x00 }, + { 0x0f34, 0x00 }, + { 0x0f35, 0x00 }, + { 0x0f36, 0x00 }, + { 0x0f37, 0x00 }, + { 0x2012, 0x00 }, + { 0x2013, 0x00 }, + { 0x2014, 0x00 }, + { 0x2015, 0x00 }, + { 0x2016, 0x00 }, + { 0x201a, 0x00 }, + { 0x201b, 0x00 }, + { 0x201c, 0x0c }, + { 0x201d, 0x00 }, + { 0x201e, 0x00 }, + { 0x201f, 0x00 }, + { 0x2020, 0x00 }, + { 0x2021, 0x00 }, + { 0x2022, 0x00 }, + { 0x2023, 0x00 }, + { 0x2024, 0x00 }, + { 0x2025, 0x01 }, + { 0x2026, 0x00 }, + { 0x2027, 0x00 }, + { 0x2029, 0x00 }, + { 0x202a, 0x00 }, + { 0x202d, 0x00 }, + { 0x202e, 0x00 }, + { 0x202f, 0x00 }, + { 0x2030, 0x00 }, + { 0x2031, 0x00 }, + { 0x2032, 0x00 }, + { 0x2033, 0x00 }, + { 0x2034, 0x00 }, + { 0x2201, 0xc7 }, + { 0x2202, 0x0c }, + { 0x2203, 0x22 }, + { 0x2204, 0x04 }, + { 0x2206, 0x00 }, + { 0x2207, 0x00 }, + { 0x2208, 0x00 }, + { 0x2209, 0x00 }, + { 0x220a, 0x00 }, + { 0x220b, 0x00 }, + { 0x220c, 0x00 }, + { 0x220d, 0x04 }, + { 0x220e, 0x00 }, + { 0x220f, 0x00 }, + { 0x2211, 0x01 }, + { 0x2212, 0x00 }, + { 0x2220, 0x00 }, + { 0x2221, 0x00 }, + { 0x2222, 0x00 }, + { 0x2223, 0x00 }, + { 0x2230, 0x00 }, + { 0x2231, 0x2f }, + { 0x2232, 0x80 }, + { 0x2233, 0x00 }, + { 0x2234, 0x00 }, + { 0x2235, 0x00 }, + { 0x2236, 0x00 }, + { 0x2237, 0x00 }, + { 0x2238, 0x00 }, + { 0x2239, 0x00 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f07, 0xcf }, + { 0x2f08, 0x00 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x00 }, + { 0x2f0b, 0x00 }, + { 0x2f0c, 0x00 }, + { 0x2f0d, 0x00 }, + { 0x2f0e, 0x00 }, + { 0x2f0f, 0x00 }, + { 0x3122, 0x00 }, + { 0x3123, 0x00 }, + { 0x7303, 0x57 }, + { 0x8383, 0x57 }, + { 0x7308, 0x97 }, + { 0x8388, 0x97 }, + { 0x7309, 0x97 }, + { 0x8389, 0x97 }, + { 0x7312, 0x00 }, + { 0x8392, 0x00 }, + { 0x7313, 0x00 }, + { 0x8393, 0x00 }, + { 0x7319, 0x00 }, + { 0x8399, 0x00 }, + { 0x752009, 0x1029 }, + { 0x752011, 0x007a }, + { 0x75201a, 0x8003 }, + { 0x752045, 0x5289 }, + { 0x752048, 0xd049 }, + { 0x75204a, 0xa83b }, + { 0x75206b, 0x5064 }, + { 0x75206f, 0x058b }, + { 0x752091, 0x0000 }, +}; + +#endif /* __RT711_SDW_H__ */ diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c new file mode 100644 index 000000000000..2daed7692a3b --- /dev/null +++ b/sound/soc/codecs/rt711.c @@ -0,0 +1,1292 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt711.c -- rt711 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm_runtime.h> +#include <linux/pm.h> +#include <linux/soundwire/sdw.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/hda_verbs.h> +#include <sound/jack.h> + +#include "rt711.h" + +static int rt711_index_write(struct regmap *regmap, + unsigned int nid, unsigned int reg, unsigned int value) +{ + int ret; + unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 8) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + pr_err("Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +static int rt711_index_read(struct regmap *regmap, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 8) | reg; + + *value = 0; + ret = regmap_read(regmap, addr, value); + if (ret < 0) + pr_err("Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt711_index_update_bits(struct regmap *regmap, unsigned int nid, + unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp, orig; + int ret; + + ret = rt711_index_read(regmap, nid, reg, &orig); + if (ret < 0) + return ret; + + tmp = orig & ~mask; + tmp |= val & mask; + + return rt711_index_write(regmap, nid, reg, tmp); +} + +static void rt711_reset(struct regmap *regmap) +{ + regmap_write(regmap, RT711_FUNC_RESET, 0); + rt711_index_update_bits(regmap, RT711_VENDOR_REG, + RT711_PARA_VERB_CTL, RT711_HIDDEN_REG_SW_RESET, + RT711_HIDDEN_REG_SW_RESET); +} + +static int rt711_calibration(struct rt711_priv *rt711) +{ + unsigned int val, loop = 0; + struct device *dev; + struct regmap *regmap = rt711->regmap; + int ret = 0; + + mutex_lock(&rt711->calibrate_mutex); + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + dev = regmap_get_device(regmap); + + /* Calibration manual mode */ + rt711_index_update_bits(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, + 0xf, 0x0); + + /* trigger */ + rt711_index_update_bits(regmap, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, + RT711_DAC_DC_CALI_TRIGGER); + + /* wait for calibration process */ + rt711_index_read(regmap, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, &val); + + while (val & RT711_DAC_DC_CALI_TRIGGER) { + if (loop >= 500) { + pr_err("%s, calibration time-out!\n", + __func__); + ret = -ETIMEDOUT; + break; + } + loop++; + + usleep_range(10000, 11000); + rt711_index_read(regmap, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, &val); + } + + /* depop mode */ + rt711_index_update_bits(regmap, RT711_VENDOR_REG, + RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); + + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + mutex_unlock(&rt711->calibrate_mutex); + + dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret); + return ret; +} + +static unsigned int rt711_button_detect(struct rt711_priv *rt711) +{ + unsigned int btn_type = 0, val80, val81; + int ret; + + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_IRQ_FLAG_TABLE1, &val80); + if (ret < 0) + goto read_error; + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_IRQ_FLAG_TABLE2, &val81); + if (ret < 0) + goto read_error; + + val80 &= 0x0381; + val81 &= 0xff00; + + switch (val80) { + case 0x0200: + case 0x0100: + case 0x0080: + btn_type |= SND_JACK_BTN_0; + break; + case 0x0001: + btn_type |= SND_JACK_BTN_3; + break; + } + switch (val81) { + case 0x8000: + case 0x4000: + case 0x2000: + btn_type |= SND_JACK_BTN_1; + break; + case 0x1000: + case 0x0800: + case 0x0400: + btn_type |= SND_JACK_BTN_2; + break; + case 0x0200: + case 0x0100: + btn_type |= SND_JACK_BTN_3; + break; + } +read_error: + return btn_type; +} + +static int rt711_headset_detect(struct rt711_priv *rt711) +{ + unsigned int buf, loop = 0; + int ret; + unsigned int jack_status = 0, reg; + + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL2, &buf); + if (ret < 0) + goto io_error; + + while (loop < 500 && + (buf & RT711_COMBOJACK_AUTO_DET_STATUS) == 0) { + loop++; + + usleep_range(9000, 10000); + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL2, &buf); + if (ret < 0) + goto io_error; + + reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT; + ret = regmap_read(rt711->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + if ((jack_status & (1 << 31)) == 0) + goto remove_error; + } + + if (loop >= 500) + goto to_error; + + if (buf & RT711_COMBOJACK_AUTO_DET_TRS) + rt711->jack_type = SND_JACK_HEADPHONE; + else if ((buf & RT711_COMBOJACK_AUTO_DET_CTIA) || + (buf & RT711_COMBOJACK_AUTO_DET_OMTP)) + rt711->jack_type = SND_JACK_HEADSET; + + return 0; + +to_error: + ret = -ETIMEDOUT; + pr_err_ratelimited("Time-out error in %s\n", __func__); + return ret; +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +remove_error: + pr_err_ratelimited("Jack removal in %s\n", __func__); + return -ENODEV; +} + +static void rt711_jack_detect_handler(struct work_struct *work) +{ + struct rt711_priv *rt711 = + container_of(work, struct rt711_priv, jack_detect_work.work); + int btn_type = 0, ret; + unsigned int jack_status = 0, reg; + + if (!rt711->hs_jack) + return; + + if (!rt711->component->card->instantiated) + return; + + reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT; + ret = regmap_read(rt711->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (jack_status & (1 << 31)) { + /* jack in */ + if (rt711->jack_type == 0) { + ret = rt711_headset_detect(rt711); + if (ret < 0) + return; + if (rt711->jack_type == SND_JACK_HEADSET) + btn_type = rt711_button_detect(rt711); + } else if (rt711->jack_type == SND_JACK_HEADSET) { + /* jack is already in, report button event */ + btn_type = rt711_button_detect(rt711); + } + } else { + /* jack out */ + rt711->jack_type = 0; + } + + dev_dbg(&rt711->slave->dev, + "in %s, jack_type=0x%x\n", __func__, rt711->jack_type); + dev_dbg(&rt711->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt711_btn_check_handler(struct work_struct *work) +{ + struct rt711_priv *rt711 = container_of(work, struct rt711_priv, + jack_btn_check_work.work); + int btn_type = 0, ret; + unsigned int jack_status = 0, reg; + + reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT; + ret = regmap_read(rt711->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (jack_status & (1 << 31)) { + if (rt711->jack_type == SND_JACK_HEADSET) { + /* jack is already in, report button event */ + btn_type = rt711_button_detect(rt711); + } + } else { + rt711->jack_type = 0; + } + + /* cbj comparator */ + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL2, ®); + if (ret < 0) + goto io_error; + + if ((reg & 0xf0) == 0xf0) + btn_type = 0; + + dev_dbg(&rt711->slave->dev, + "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt711_jack_init(struct rt711_priv *rt711) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(rt711->component); + + mutex_lock(&rt711->calibrate_mutex); + /* power on */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + if (rt711->hs_jack) { + /* unsolicited response & IRQ control */ + regmap_write(rt711->regmap, + RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + regmap_write(rt711->regmap, + RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); + regmap_write(rt711->regmap, + RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + 0x10, 0x2420); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + 0x19, 0x2e11); + + switch (rt711->jd_src) { + case RT711_JD1: + /* default settings was already for JD1 */ + break; + case RT711_JD2: + rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG, + RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP | + RT711_HP_JD_SEL_JD2, + RT711_JD2_2PORT_200K_DECODE_HP | + RT711_HP_JD_SEL_JD2); + rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG, + RT711_CC_DET1, + RT711_HP_JD_FINAL_RESULT_CTL_JD12, + RT711_HP_JD_FINAL_RESULT_CTL_JD12); + break; + default: + dev_warn(rt711->component->dev, "Wrong JD source\n"); + break; + } + + dev_dbg(&rt711->slave->dev, "in %s enable\n", __func__); + + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_detect_work, msecs_to_jiffies(250)); + } else { + regmap_write(rt711->regmap, + RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x00); + regmap_write(rt711->regmap, + RT711_SET_HP_UNSOLICITED_ENABLE, 0x00); + regmap_write(rt711->regmap, + RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x00); + + dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__); + } + + /* power off */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + mutex_unlock(&rt711->calibrate_mutex); +} + +static int rt711_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + + rt711->hs_jack = hs_jack; + + if (!rt711->hw_init) { + dev_dbg(&rt711->slave->dev, + "%s hw_init not ready yet\n", __func__); + return 0; + } + + rt711_jack_init(rt711); + + return 0; +} + +static void rt711_get_gain(struct rt711_priv *rt711, unsigned int addr_h, + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) +{ + /* R Channel */ + *r_val = (val_h << 8); + regmap_read(rt711->regmap, addr_l, r_val); + + /* L Channel */ + val_h |= 0x20; + *l_val = (val_h << 8); + regmap_read(rt711->regmap, addr_h, l_val); +} + +/* For Verb-Set Amplifier Gain (Verb ID = 3h) */ +static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + 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 rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int addr_h, addr_l, val_h, val_ll, val_lr; + unsigned int read_ll, read_rl; + int i; + + /* Can't use update bit function, so read the original value first */ + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT711_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt711_get_gain(rt711, addr_h, addr_l, val_h, &read_rl, &read_ll); + + /* L Channel */ + if (mc->invert) { + /* for mute/unmute */ + val_ll = (mc->max - ucontrol->value.integer.value[0]) + << RT711_MUTE_SFT; + /* keep gain */ + read_ll = read_ll & 0x7f; + val_ll |= read_ll; + } else { + /* for gain */ + val_ll = ((ucontrol->value.integer.value[0]) & 0x7f); + if (val_ll > mc->max) + val_ll = mc->max; + /* keep mute status */ + read_ll = read_ll & (1 << RT711_MUTE_SFT); + val_ll |= read_ll; + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + /* R Channel */ + if (mc->invert) { + /* for mute/unmute */ + val_lr = (mc->max - ucontrol->value.integer.value[1]) + << RT711_MUTE_SFT; + /* keep gain */ + read_rl = read_rl & 0x7f; + val_lr |= read_rl; + } else { + /* for gain */ + val_lr = ((ucontrol->value.integer.value[1]) & 0x7f); + if (val_lr > mc->max) + val_lr = mc->max; + /* keep mute status */ + read_rl = read_rl & (1 << RT711_MUTE_SFT); + val_lr |= read_rl; + } + + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + + if (val_ll == val_lr) { + /* Set both L/R channels at the same time */ + val_h = (1 << mc->shift) | (3 << 4); + regmap_write(rt711->regmap, + addr_h, (val_h << 8 | val_ll)); + regmap_write(rt711->regmap, + addr_l, (val_h << 8 | val_ll)); + } else { + /* Lch*/ + val_h = (1 << mc->shift) | (1 << 5); + regmap_write(rt711->regmap, + addr_h, (val_h << 8 | val_ll)); + + /* Rch */ + val_h = (1 << mc->shift) | (1 << 4); + regmap_write(rt711->regmap, + addr_l, (val_h << 8 | val_lr)); + } + /* check result */ + if (mc->shift == RT711_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt711_get_gain(rt711, addr_h, addr_l, val_h, + &read_rl, &read_ll); + if (read_rl == val_lr && read_ll == val_ll) + break; + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + return 0; +} + +static int rt711_set_amp_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int addr_h, addr_l, val_h; + unsigned int read_ll, read_rl; + + /* switch to get command */ + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT711_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt711_get_gain(rt711, addr_h, addr_l, val_h, &read_rl, &read_ll); + + if (mc->invert) { + /* mute/unmute for switch controls */ + read_ll = !((read_ll & 0x80) >> RT711_MUTE_SFT); + read_rl = !((read_rl & 0x80) >> RT711_MUTE_SFT); + } else { + /* for gain volume controls */ + read_ll = read_ll & 0x7f; + read_rl = read_rl & 0x7f; + } + ucontrol->value.integer.value[0] = read_ll; + ucontrol->value.integer.value[1] = read_rl; + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt711_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("DAC Surr Playback Volume", + RT711_SET_GAIN_DAC2_H, RT711_SET_GAIN_DAC2_L, + RT711_DIR_OUT_SFT, 0x57, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, out_vol_tlv), + SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", + RT711_SET_GAIN_ADC2_H, RT711_SET_GAIN_ADC2_L, + RT711_DIR_IN_SFT, 1, 1, + rt711_set_amp_gain_get, rt711_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", + RT711_SET_GAIN_ADC1_H, RT711_SET_GAIN_ADC1_L, + RT711_DIR_IN_SFT, 1, 1, + rt711_set_amp_gain_get, rt711_set_amp_gain_put), + SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", + RT711_SET_GAIN_ADC2_H, RT711_SET_GAIN_ADC2_L, + RT711_DIR_IN_SFT, 0x3f, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", + RT711_SET_GAIN_ADC1_H, RT711_SET_GAIN_ADC1_L, + RT711_DIR_IN_SFT, 0x3f, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("AMIC Volume", + RT711_SET_GAIN_AMIC_H, RT711_SET_GAIN_AMIC_L, + RT711_DIR_IN_SFT, 3, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC1 Volume", + RT711_SET_GAIN_DMIC1_H, RT711_SET_GAIN_DMIC1_L, + RT711_DIR_IN_SFT, 3, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC2 Volume", + RT711_SET_GAIN_DMIC2_H, RT711_SET_GAIN_DMIC2_L, + RT711_DIR_IN_SFT, 3, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv), +}; + +static int rt711_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int reg, val = 0, nid; + int ret; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + nid = RT711_MIXER_IN1; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + nid = RT711_MIXER_IN2; + else + return -EINVAL; + + /* vid = 0xf01 */ + reg = RT711_VERB_SET_CONNECT_SEL | nid; + ret = regmap_read(rt711->regmap, reg, &val); + if (ret < 0) { + dev_err(component->dev, "%s: sdw read failed: %d\n", + __func__, ret); + return ret; + } + + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt711_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, reg, nid; + int ret; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + nid = RT711_MIXER_IN1; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + nid = RT711_MIXER_IN2; + else + return -EINVAL; + + /* Verb ID = 0x701h */ + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + reg = RT711_VERB_SET_CONNECT_SEL | nid; + ret = regmap_read(rt711->regmap, reg, &val2); + if (ret < 0) { + dev_err(component->dev, "%s: sdw read failed: %d\n", + __func__, ret); + return ret; + } + + if (val == val2) + change = 0; + else + change = 1; + + if (change) { + reg = RT711_VERB_SET_CONNECT_SEL | nid; + regmap_write(rt711->regmap, reg, val); + } + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", + "DMIC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt711_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt711_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static const struct snd_kcontrol_new rt711_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt711_adc22_enum, + rt711_mux_get, rt711_mux_put); + +static const struct snd_kcontrol_new rt711_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt711_adc23_enum, + rt711_mux_get, rt711_mux_put); + +static int rt711_dac_surround_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 rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int val_h = (1 << RT711_DIR_OUT_SFT) | (0x3 << 4); + unsigned int val_l; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_DAC2, 0x10); + + val_l = 0x00; + regmap_write(rt711->regmap, + RT711_SET_GAIN_HP_H, (val_h << 8 | val_l)); + break; + case SND_SOC_DAPM_PRE_PMD: + val_l = (1 << RT711_MUTE_SFT); + regmap_write(rt711->regmap, + RT711_SET_GAIN_HP_H, (val_h << 8 | val_l)); + usleep_range(50000, 55000); + + regmap_write(rt711->regmap, + RT711_SET_STREAMID_DAC2, 0x00); + break; + } + return 0; +} + +static int rt711_adc_09_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 rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_ADC1, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_ADC1, 0x00); + break; + } + return 0; +} + +static int rt711_adc_08_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 rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_ADC2, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_ADC2, 0x00); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt711_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + + SND_SOC_DAPM_DAC_E("DAC Surround", NULL, SND_SOC_NOPM, 0, 0, + rt711_dac_surround_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC 09", NULL, SND_SOC_NOPM, 0, 0, + rt711_adc_09_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC 08", NULL, SND_SOC_NOPM, 0, 0, + rt711_adc_08_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt711_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt711_adc23_mux), + + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt711_audio_map[] = { + {"DAC Surround", NULL, "DP3RX"}, + {"DP2TX", NULL, "ADC 09"}, + {"DP4TX", NULL, "ADC 08"}, + + {"ADC 09", NULL, "ADC 22 Mux"}, + {"ADC 08", NULL, "ADC 23 Mux"}, + {"ADC 22 Mux", "DMIC", "DMIC1"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "DMIC", "DMIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1"}, + {"ADC 23 Mux", "LINE2", "LINE2"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + + {"HP", NULL, "DAC Surround"}, +}; + +static int rt711_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, + AC_PWRST_D0); + } + break; + + case SND_SOC_BIAS_STANDBY: + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, + AC_PWRST_D3); + break; + + default: + break; + } + + return 0; +} + +static int rt711_parse_dt(struct rt711_priv *rt711, struct device *dev) +{ + device_property_read_u32(dev, "realtek,jd-src", + &rt711->jd_src); + + return 0; +} + +static int rt711_probe(struct snd_soc_component *component) +{ + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + + rt711_parse_dt(rt711, &rt711->slave->dev); + rt711->component = component; + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_rt711 = { + .probe = rt711_probe, + .set_bias_level = rt711_set_bias_level, + .controls = rt711_snd_controls, + .num_controls = ARRAY_SIZE(rt711_snd_controls), + .dapm_widgets = rt711_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt711_dapm_widgets), + .dapm_routes = rt711_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt711_audio_map), + .set_jack = rt711_set_jack_detect, +}; + +static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + struct sdw_stream_data *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + + /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = stream; + else + dai->capture_dma_data = stream; + + return 0; +} + +static void rt711_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_stream_data *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(stream); +} + +static int rt711_pcm_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 rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_data *stream; + int retval, port, num_channels; + unsigned int val = 0; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!stream) + return -EINVAL; + + if (!rt711->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + port = 3; + } else { + direction = SDW_DATA_DIR_TX; + if (dai->id == RT711_AIF1) + port = 4; + else if (dai->id == RT711_AIF2) + port = 2; + else + return -EINVAL; + } + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = (1 << (num_channels)) - 1; + port_config.num = port; + + retval = sdw_stream_add_slave(rt711->slave, &stream_config, + &port_config, 1, stream->sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 8: + break; + case 16: + val |= (0x1 << 4); + break; + case 20: + val |= (0x2 << 4); + break; + case 24: + val |= (0x3 << 4); + break; + case 32: + val |= (0x4 << 4); + break; + default: + return -EINVAL; + } + + /* 48Khz */ + regmap_write(rt711->regmap, RT711_DAC_FORMAT_H, val); + regmap_write(rt711->regmap, RT711_ADC1_FORMAT_H, val); + regmap_write(rt711->regmap, RT711_ADC2_FORMAT_H, val); + + return retval; +} + +static int rt711_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt711->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt711->slave, stream->sdw_stream); + return 0; +} + +#define RT711_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static struct snd_soc_dai_ops rt711_ops = { + .hw_params = rt711_pcm_hw_params, + .hw_free = rt711_pcm_hw_free, + .set_sdw_stream = rt711_set_sdw_stream, + .shutdown = rt711_shutdown, +}; + +static struct snd_soc_dai_driver rt711_dai[] = { + { + .name = "rt711-aif1", + .id = RT711_AIF1, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT711_STEREO_RATES, + .formats = RT711_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT711_STEREO_RATES, + .formats = RT711_FORMATS, + }, + .ops = &rt711_ops, + }, + { + .name = "rt711-aif2", + .id = RT711_AIF2, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT711_STEREO_RATES, + .formats = RT711_FORMATS, + }, + .ops = &rt711_ops, + } +}; + +/* Bus clock frequency */ +#define RT711_CLK_FREQ_9600000HZ 9600000 +#define RT711_CLK_FREQ_12000000HZ 12000000 +#define RT711_CLK_FREQ_6000000HZ 6000000 +#define RT711_CLK_FREQ_4800000HZ 4800000 +#define RT711_CLK_FREQ_2400000HZ 2400000 +#define RT711_CLK_FREQ_12288000HZ 12288000 + +int rt711_clock_config(struct device *dev) +{ + struct rt711_priv *rt711 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt711->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT711_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT711_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT711_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT711_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT711_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT711_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt711->regmap, 0xe0, value); + regmap_write(rt711->regmap, 0xf0, value); + + dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq); + + return 0; +} + +static void rt711_calibration_work(struct work_struct *work) +{ + struct rt711_priv *rt711 = + container_of(work, struct rt711_priv, calibration_work); + + rt711_calibration(rt711); +} + +int rt711_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave) +{ + struct rt711_priv *rt711; + int ret; + + rt711 = devm_kzalloc(dev, sizeof(*rt711), GFP_KERNEL); + if (!rt711) + return -ENOMEM; + + dev_set_drvdata(dev, rt711); + rt711->slave = slave; + rt711->sdw_regmap = sdw_regmap; + rt711->regmap = regmap; + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt711->hw_init = false; + rt711->first_hw_init = false; + + /* JD source uses JD2 in default */ + rt711->jd_src = RT711_JD2; + + ret = devm_snd_soc_register_component(dev, + &soc_codec_dev_rt711, + rt711_dai, + ARRAY_SIZE(rt711_dai)); + + dev_dbg(&slave->dev, "%s\n", __func__); + + return ret; +} + +int rt711_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt711_priv *rt711 = dev_get_drvdata(dev); + + if (rt711->hw_init) + return 0; + + if (rt711->first_hw_init) { + regcache_cache_only(rt711->regmap, false); + regcache_cache_bypass(rt711->regmap, true); + } + + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + if (!rt711->first_hw_init) { + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + rt711_reset(rt711->regmap); + + /* power on */ + regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + /* Set Pin Widget */ + regmap_write(rt711->regmap, RT711_SET_PIN_MIC2, 0x25); + regmap_write(rt711->regmap, RT711_SET_PIN_HP, 0xc0); + regmap_write(rt711->regmap, RT711_SET_PIN_DMIC1, 0x20); + regmap_write(rt711->regmap, RT711_SET_PIN_DMIC2, 0x20); + regmap_write(rt711->regmap, RT711_SET_PIN_LINE1, 0x20); + regmap_write(rt711->regmap, RT711_SET_PIN_LINE2, 0x20); + + /* Mute HP/ADC1/ADC2 */ + regmap_write(rt711->regmap, RT711_SET_GAIN_HP_H, 0xa080); + regmap_write(rt711->regmap, RT711_SET_GAIN_HP_H, 0x9080); + regmap_write(rt711->regmap, RT711_SET_GAIN_ADC2_H, 0x6080); + regmap_write(rt711->regmap, RT711_SET_GAIN_ADC2_H, 0x5080); + regmap_write(rt711->regmap, RT711_SET_GAIN_ADC1_H, 0x6080); + regmap_write(rt711->regmap, RT711_SET_GAIN_ADC1_H, 0x5080); + + /* Set Configuration Default */ + regmap_write(rt711->regmap, 0x4f12, 0x91); + regmap_write(rt711->regmap, 0x4e12, 0xd6); + regmap_write(rt711->regmap, 0x4d12, 0x11); + regmap_write(rt711->regmap, 0x4c12, 0x20); + regmap_write(rt711->regmap, 0x4f13, 0x91); + regmap_write(rt711->regmap, 0x4e13, 0xd6); + regmap_write(rt711->regmap, 0x4d13, 0x11); + regmap_write(rt711->regmap, 0x4c13, 0x21); + regmap_write(rt711->regmap, 0x4c21, 0xf0); + regmap_write(rt711->regmap, 0x4d21, 0x11); + regmap_write(rt711->regmap, 0x4e21, 0x11); + regmap_write(rt711->regmap, 0x4f21, 0x01); + + /* Data port arrangement */ + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_TX_RX_MUX_CTL, 0x0154); + + /* Set index */ + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_DIGITAL_MISC_CTRL4, 0x201b); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL1, 0x5089); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_VREFOUT_CTL, 0x5064); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_INLINE_CMD_CTL, 0xd249); + + /* Finish Initial Settings, set power to D3 */ + regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + if (rt711->first_hw_init) + rt711_calibration(rt711); + else { + INIT_DELAYED_WORK(&rt711->jack_detect_work, + rt711_jack_detect_handler); + INIT_DELAYED_WORK(&rt711->jack_btn_check_work, + rt711_btn_check_handler); + mutex_init(&rt711->calibrate_mutex); + INIT_WORK(&rt711->calibration_work, rt711_calibration_work); + schedule_work(&rt711->calibration_work); + } + + /* + * if set_jack callback occurred early than io_init, + * we set up the jack detection function now + */ + if (rt711->hs_jack) + rt711_jack_init(rt711); + + if (rt711->first_hw_init) { + regcache_cache_bypass(rt711->regmap, false); + regcache_mark_dirty(rt711->regmap); + } else + rt711->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt711->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +MODULE_DESCRIPTION("ASoC RT711 SDW driver"); +MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt711.h b/sound/soc/codecs/rt711.h new file mode 100644 index 000000000000..ca0f581feec7 --- /dev/null +++ b/sound/soc/codecs/rt711.h @@ -0,0 +1,227 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt711.h -- RT711 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT711_H__ +#define __RT711_H__ + +extern const struct dev_pm_ops rt711_runtime_pm; + +struct rt711_priv { + struct regmap *regmap; + struct regmap *sdw_regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + enum sdw_slave_status status; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + struct work_struct calibration_work; + struct mutex calibrate_mutex; /* for headset calibration */ + int jack_type, jd_src; +}; + +struct sdw_stream_data { + struct sdw_stream_runtime *sdw_stream; +}; + +/* NID */ +#define RT711_AUDIO_FUNCTION_GROUP 0x01 +#define RT711_DAC_OUT2 0x03 +#define RT711_ADC_IN1 0x09 +#define RT711_ADC_IN2 0x08 +#define RT711_DMIC1 0x12 +#define RT711_DMIC2 0x13 +#define RT711_MIC2 0x19 +#define RT711_LINE1 0x1a +#define RT711_LINE2 0x1b +#define RT711_BEEP 0x1d +#define RT711_VENDOR_REG 0x20 +#define RT711_HP_OUT 0x21 +#define RT711_MIXER_IN1 0x22 +#define RT711_MIXER_IN2 0x23 +#define RT711_INLINE_CMD 0x55 +#define RT711_VENDOR_CALI 0x58 +#define RT711_VENDOR_IMS_DRE 0x5b + +/* Index (NID:20h) */ +#define RT711_DAC_DC_CALI_CTL1 0x00 +#define RT711_JD_CTL2 0x09 +#define RT711_CC_DET1 0x11 +#define RT711_PARA_VERB_CTL 0x1a +#define RT711_COMBO_JACK_AUTO_CTL1 0x45 +#define RT711_COMBO_JACK_AUTO_CTL2 0x46 +#define RT711_INLINE_CMD_CTL 0x48 +#define RT711_DIGITAL_MISC_CTRL4 0x4a +#define RT711_VREFOUT_CTL 0x6b +#define RT711_FSM_CTL 0x6f +#define RT711_IRQ_FLAG_TABLE1 0x80 +#define RT711_IRQ_FLAG_TABLE2 0x81 +#define RT711_IRQ_FLAG_TABLE3 0x82 +#define RT711_TX_RX_MUX_CTL 0x91 + +/* Index (NID:5bh) */ +#define RT711_IMS_DIGITAL_CTL1 0x00 +#define RT711_HP_IMS_RESULT_L 0x20 +#define RT711_HP_IMS_RESULT_R 0x21 + +/* Verb */ +#define RT711_VERB_SET_CONNECT_SEL 0x3100 +#define RT711_VERB_SET_EAPD_BTLENABLE 0x3c00 +#define RT711_VERB_GET_CONNECT_SEL 0xb100 +#define RT711_VERB_SET_POWER_STATE 0x3500 +#define RT711_VERB_SET_CHANNEL_STREAMID 0x3600 +#define RT711_VERB_SET_PIN_WIDGET_CONTROL 0x3700 +#define RT711_VERB_SET_UNSOLICITED_ENABLE 0x3800 +#define RT711_SET_AMP_GAIN_MUTE_H 0x7300 +#define RT711_SET_AMP_GAIN_MUTE_L 0x8380 +#define RT711_VERB_GET_POWER_STATE 0xb500 +#define RT711_VERB_GET_CHANNEL_STREAMID 0xb600 +#define RT711_VERB_GET_PIN_SENSE 0xb900 +#define RT711_FUNC_RESET 0xff01 + +#define RT711_READ_HDA_3 0x2012 +#define RT711_READ_HDA_2 0x2013 +#define RT711_READ_HDA_1 0x2014 +#define RT711_READ_HDA_0 0x2015 +#define RT711_PRIV_INDEX_W_H 0x7500 +#define RT711_PRIV_INDEX_W_L 0x8580 +#define RT711_PRIV_DATA_W_H 0x7400 +#define RT711_PRIV_DATA_W_L 0x8480 +#define RT711_PRIV_INDEX_R_H 0x9d00 +#define RT711_PRIV_INDEX_R_L 0xad80 +#define RT711_PRIV_DATA_R_H 0x9c00 +#define RT711_PRIV_DATA_R_L 0xac80 +#define RT711_DAC_FORMAT_H 0x7203 +#define RT711_DAC_FORMAT_L 0x8283 +#define RT711_ADC1_FORMAT_H 0x7209 +#define RT711_ADC1_FORMAT_L 0x8289 +#define RT711_ADC2_FORMAT_H 0x7208 +#define RT711_ADC2_FORMAT_L 0x8288 + +#define RT711_SET_AUDIO_POWER_STATE\ + (RT711_VERB_SET_POWER_STATE | RT711_AUDIO_FUNCTION_GROUP) +#define RT711_GET_AUDIO_POWER_STATE\ + (RT711_VERB_GET_POWER_STATE | RT711_AUDIO_FUNCTION_GROUP) +#define RT711_SET_PIN_DMIC1\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_DMIC1) +#define RT711_SET_PIN_DMIC2\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_DMIC2) +#define RT711_SET_PIN_HP\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_HP_OUT) +#define RT711_SET_PIN_MIC2\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_MIC2) +#define RT711_SET_PIN_LINE1\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_LINE1) +#define RT711_SET_PIN_LINE2\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_LINE2) +#define RT711_SET_MIC2_UNSOLICITED_ENABLE\ + (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_MIC2) +#define RT711_SET_HP_UNSOLICITED_ENABLE\ + (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_HP_OUT) +#define RT711_SET_INLINE_UNSOLICITED_ENABLE\ + (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_INLINE_CMD) +#define RT711_SET_STREAMID_DAC2\ + (RT711_VERB_SET_CHANNEL_STREAMID | RT711_DAC_OUT2) +#define RT711_SET_STREAMID_ADC1\ + (RT711_VERB_SET_CHANNEL_STREAMID | RT711_ADC_IN1) +#define RT711_SET_STREAMID_ADC2\ + (RT711_VERB_SET_CHANNEL_STREAMID | RT711_ADC_IN2) +#define RT711_GET_STREAMID_DAC2\ + (RT711_VERB_GET_CHANNEL_STREAMID | RT711_DAC_OUT2) +#define RT711_GET_STREAMID_ADC1\ + (RT711_VERB_GET_CHANNEL_STREAMID | RT711_ADC_IN1) +#define RT711_GET_STREAMID_ADC2\ + (RT711_VERB_GET_CHANNEL_STREAMID | RT711_ADC_IN2) +#define RT711_SET_GAIN_DAC2_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_DAC_OUT2) +#define RT711_SET_GAIN_DAC2_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_DAC_OUT2) +#define RT711_SET_GAIN_ADC1_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_ADC_IN1) +#define RT711_SET_GAIN_ADC1_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_ADC_IN1) +#define RT711_SET_GAIN_ADC2_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_ADC_IN2) +#define RT711_SET_GAIN_ADC2_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_ADC_IN2) +#define RT711_SET_GAIN_AMIC_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_MIC2) +#define RT711_SET_GAIN_AMIC_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_MIC2) +#define RT711_SET_GAIN_DMIC1_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_DMIC1) +#define RT711_SET_GAIN_DMIC1_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_DMIC1) +#define RT711_SET_GAIN_DMIC2_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_DMIC2) +#define RT711_SET_GAIN_DMIC2_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_DMIC2) +#define RT711_SET_GAIN_HP_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_HP_OUT) +#define RT711_SET_GAIN_HP_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_HP_OUT) + +/* DAC DC offset calibration control-1 (0x00)(NID:20h) */ +#define RT711_DAC_DC_CALI_TRIGGER (0x1 << 15) + +/* jack detect control 2 (0x09)(NID:20h) */ +#define RT711_JD2_2PORT_200K_DECODE_HP (0x1 << 13) +#define RT711_HP_JD_SEL_JD1 (0x0 << 1) +#define RT711_HP_JD_SEL_JD2 (0x1 << 1) + +/* CC DET1 (0x11)(NID:20h) */ +#define RT711_HP_JD_FINAL_RESULT_CTL_JD12 (0x1 << 10) +#define RT711_HP_JD_FINAL_RESULT_CTL_CCDET (0x0 << 10) + +/* Parameter & Verb control (0x1a)(NID:20h) */ +#define RT711_HIDDEN_REG_SW_RESET (0x1 << 14) + +/* combo jack auto switch control 2 (0x46)(NID:20h) */ +#define RT711_COMBOJACK_AUTO_DET_STATUS (0x1 << 11) +#define RT711_COMBOJACK_AUTO_DET_TRS (0x1 << 10) +#define RT711_COMBOJACK_AUTO_DET_CTIA (0x1 << 9) +#define RT711_COMBOJACK_AUTO_DET_OMTP (0x1 << 8) + +/* FSM control (0x6f)(NID:20h) */ +#define RT711_CALI_CTL (0x0 << 0) +#define RT711_COMBOJACK_CTL (0x1 << 0) +#define RT711_IMS_CTL (0x2 << 0) +#define RT711_DEPOP_CTL (0x3 << 0) + +/* Impedance Sense Digital Control 1 (0x00)(NID:5bh) */ +#define RT711_TRIGGER_IMS (0x1 << 15) +#define RT711_IMS_EN (0x1 << 6) + +#define RT711_EAPD_HIGH 0x2 +#define RT711_EAPD_LOW 0x0 +#define RT711_MUTE_SFT 7 +/* set input/output mapping to payload[14][15] separately */ +#define RT711_DIR_IN_SFT 6 +#define RT711_DIR_OUT_SFT 7 + +enum { + RT711_AIF1, + RT711_AIF2, + RT711_AIFS, +}; + +enum rt711_jd_src { + RT711_JD_NULL, + RT711_JD1, + RT711_JD2 +}; + +int rt711_io_init(struct device *dev, struct sdw_slave *slave); +int rt711_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave); + +int rt711_jack_detect(struct rt711_priv *rt711, bool *hp, bool *mic); +int rt711_clock_config(struct device *dev); +#endif /* __RT711_H__ */ diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c new file mode 100644 index 000000000000..6d892c44c522 --- /dev/null +++ b/sound/soc/codecs/rt715-sdw.c @@ -0,0 +1,613 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * rt715-sdw.c -- rt715 ALSA SoC audio driver + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + * + * ALC715 ASoC Codec Driver based Intel Dummy SdW codec driver + * + */ +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/mod_devicetable.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "rt715.h" +#include "rt715-sdw.h" + +static bool rt715_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0 ... 0x00e5: + case 0x00ee ... 0x00ef: + case 0x00f0 ... 0x00f5: + case 0x00fe ... 0x00ff: + case 0x02e0: + case 0x02f0: + case 0x04e0: + case 0x04f0: + case 0x06e0: + case 0x06f0: + case 0x2000 ... 0x2016: + case 0x201a ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2200 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2220 ... 0x2223: + case 0x2230 ... 0x2239: + case 0x22f0 ... 0x22f3: + case 0x3122: + case 0x3123: + case 0x3124: + case 0x3125: + case 0x3607: + case 0x3608: + case 0x3609: + case 0x3610: + case 0x3611: + case 0x3627: + case 0x3712: + case 0x3713: + case 0x3718: + case 0x3719: + case 0x371a: + case 0x371b: + case 0x371d: + case 0x3729: + case 0x385e: + case 0x3859: + case 0x4c12: + case 0x4c13: + case 0x4c1d: + case 0x4c29: + case 0x4d12: + case 0x4d13: + case 0x4d1d: + case 0x4d29: + case 0x4e12: + case 0x4e13: + case 0x4e1d: + case 0x4e29: + case 0x4f12: + case 0x4f13: + case 0x4f1d: + case 0x4f29: + case 0x7207: + case 0x7208: + case 0x7209: + case 0x7227: + case 0x7307: + case 0x7308: + case 0x7309: + case 0x7312: + case 0x7313: + case 0x7318: + case 0x7319: + case 0x731a: + case 0x731b: + case 0x731d: + case 0x7327: + case 0x7329: + case 0x8287: + case 0x8288: + case 0x8289: + case 0x82a7: + case 0x8387: + case 0x8388: + case 0x8389: + case 0x8392: + case 0x8393: + case 0x8398: + case 0x8399: + case 0x839a: + case 0x839b: + case 0x839d: + case 0x83a7: + case 0x83a9: + case 0x752039: + return true; + default: + return false; + } +} + +static bool rt715_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e5: + case 0x00f0: + case 0x00f3: + case 0x00f5: + case 0x2009: + case 0x2016: + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2023: + case 0x2230: + case 0x200b ... 0x200e: /* i2c read */ + case 0x2012 ... 0x2015: /* HD-A read */ + case 0x202d ... 0x202f: /* BRA */ + case 0x2201 ... 0x2212: /* i2c debug */ + case 0x2220 ... 0x2223: /* decoded HD-A */ + return true; + default: + return false; + } +} + +static int rt715_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct rt715_priv *rt715 = dev_get_drvdata(dev); + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2; + unsigned int is_hda_reg = 1, is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xff; + reg = reg >> 8; + nid = reg & 0xff; + ret = regmap_write(rt715->sdw_regmap, reg, 0); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, val2); + if (ret < 0) + return ret; + + reg3 = RT715_PRIV_DATA_R_H | nid; + ret = regmap_write(rt715->sdw_regmap, reg3, + ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg4, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x3000) { + reg += 0x8000; + ret = regmap_write(rt715->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + reg += 0x2000; + reg |= 0x800; + ret = regmap_write(rt715->sdw_regmap, reg, + ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, + ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt715->sdw_regmap, reg, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x9000) { + ret = regmap_write(rt715->sdw_regmap, reg, + ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0xb000) { + ret = regmap_write(rt715->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else { + ret = regmap_read(rt715->sdw_regmap, reg, val); + if (ret < 0) + return ret; + is_hda_reg = 0; + } + + if (is_hda_reg || is_index_reg) { + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_3, + &sdw_data_3); + if (ret < 0) + return ret; + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_2, + &sdw_data_2); + if (ret < 0) + return ret; + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_1, + &sdw_data_1); + if (ret < 0) + return ret; + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_0, + &sdw_data_0); + if (ret < 0) + return ret; + *val = ((sdw_data_3 & 0xff) << 24) | + ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + } + + if (is_hda_reg == 0) + dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", __func__, + reg, reg2, reg3, reg4, *val); + else + dev_dbg(dev, "[%s] %04x %04x => %08x\n", + __func__, reg, reg2, *val); + + return 0; +} + +static int rt715_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct rt715_priv *rt715 = dev_get_drvdata(dev); + unsigned int reg2 = 0, reg3, reg4, nid, mask, val2; + unsigned int is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xff; + reg = reg >> 8; + nid = reg & 0xff; + ret = regmap_write(rt715->sdw_regmap, reg, 0); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, val2); + if (ret < 0) + return ret; + + reg3 = RT715_PRIV_DATA_W_H | nid; + ret = regmap_write(rt715->sdw_regmap, reg3, + ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg4, (val & 0xff)); + if (ret < 0) + return ret; + is_index_reg = 1; + } else if (reg < 0x4fff) { + ret = regmap_write(rt715->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (reg == RT715_FUNC_RESET) { + ret = regmap_write(rt715->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + ret = regmap_write(rt715->sdw_regmap, reg, + ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, (val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, + ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt715->sdw_regmap, reg, (val & 0xff)); + if (ret < 0) + return ret; + } + + if (reg2 == 0) + dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", + __func__, reg, reg2, reg3, reg4, val2, val); + else + dev_dbg(dev, "[%s] %04x %04x <= %04x\n", + __func__, reg, reg2, val); + + return 0; +} + +static const struct regmap_config rt715_regmap = { + .reg_bits = 24, + .val_bits = 32, + .readable_reg = rt715_readable_register, /* Readable registers */ + .volatile_reg = rt715_volatile_register, /* volatile register */ + .max_register = 0x752039, /* Maximum number of register */ + .reg_defaults = rt715_reg_defaults, /* Defaults */ + .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, + .reg_read = rt715_sdw_read, + .reg_write = rt715_sdw_write, +}; + +static const struct regmap_config rt715_sdw_regmap = { + .name = "sdw", + .reg_bits = 32, /* Total register space for SDW */ + .val_bits = 8, /* Total number of bits in register */ + .max_register = 0xff01, /* Maximum number of register */ + .cache_type = REGCACHE_NONE, + .use_single_read = true, + .use_single_write = true, +}; + +int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, + unsigned int *sdw_addr_h, unsigned int *sdw_data_h, + unsigned int *sdw_addr_l, unsigned int *sdw_data_l) +{ + unsigned int offset_h, offset_l, e_verb; + + if (((verb & 0xff) != 0) || verb == 0xf00) { /* 12 bits command */ + if (verb == 0x7ff) /* special case */ + offset_h = 0; + else + offset_h = 0x3000; + + if (verb & 0x800) /* get command */ + e_verb = (verb - 0xf00) | 0x80; + else /* set command */ + e_verb = (verb - 0x700); + + *sdw_data_h = payload; /* 7 bits payload */ + *sdw_addr_l = *sdw_data_l = 0; + } else { /* 4 bits command */ + if ((verb & 0x800) == 0x800) { /* read */ + offset_h = 0x9000; + offset_l = 0xa000; + } else { /* write */ + offset_h = 0x7000; + offset_l = 0x8000; + } + e_verb = verb >> 8; + *sdw_data_h = (payload >> 8); /* 16 bits payload [15:8] */ + *sdw_addr_l = (e_verb << 8) | nid | 0x80; /* 0x80: valid bit */ + *sdw_addr_l += offset_l; + *sdw_data_l = payload & 0xff; + } + + *sdw_addr_h = (e_verb << 8) | nid; + *sdw_addr_h += offset_h; + + return 0; +} +EXPORT_SYMBOL(hda_to_sdw); + +static int rt715_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev); + + /* Update the status */ + rt715->status = status; + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt715_io_init(&slave->dev, slave); +} + +static int rt715_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i, num_of_ports = 1; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = false; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x50;/* BITMAP: 01010000 */ + prop->sink_ports = 0x0; /* BITMAP: 00000000 */ + + nval = hweight32(prop->source_ports); + num_of_ports += nval; + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + dpn = prop->src_dpn_prop; + i = 0; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + num_of_ports += nval; + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + dpn = prop->sink_dpn_prop; + i = 0; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* Allocate port_ready based on num_of_ports */ + slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, + sizeof(*slave->port_ready), + GFP_KERNEL); + if (!slave->port_ready) + return -ENOMEM; + + /* Initialize completion */ + for (i = 0; i < num_of_ports; i++) + init_completion(&slave->port_ready[i]); + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int rt715_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt715->params, params, sizeof(*params)); + + ret = rt715_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return 0; +} + +static struct sdw_slave_ops rt715_slave_ops = { + .read_prop = rt715_read_prop, + .update_status = rt715_update_status, + .bus_config = rt715_bus_config, +}; + +static int rt715_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *sdw_regmap, *regmap; + + /* Assign ops */ + slave->ops = &rt715_slave_ops; + + /* Regmap Initialization */ + sdw_regmap = devm_regmap_init_sdw(slave, &rt715_sdw_regmap); + if (!sdw_regmap) + return -EINVAL; + + regmap = devm_regmap_init(&slave->dev, NULL, &slave->dev, + &rt715_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + rt715_init(&slave->dev, sdw_regmap, regmap, slave); + + return 0; +} + +static const struct sdw_device_id rt715_id[] = { + SDW_SLAVE_ENTRY(0x025d, 0x715, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt715_id); + +static int rt715_dev_suspend(struct device *dev) +{ + struct rt715_priv *rt715 = dev_get_drvdata(dev); + + if (!rt715->hw_init) + return 0; + + regcache_cache_only(rt715->regmap, true); + + return 0; +} + +#define RT715_PROBE_TIMEOUT 2000 + +static int rt715_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt715_priv *rt715 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt715->hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT715_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt715->regmap, false); + regcache_sync_region(rt715->regmap, 0x3000, 0x8fff); + regcache_sync_region(rt715->regmap, 0x752039, 0x752039); + + return 0; +} + +static const struct dev_pm_ops rt715_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume) + SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL) +}; + +static struct sdw_driver rt715_sdw_driver = { + .driver = { + .name = "rt715", + .owner = THIS_MODULE, + .pm = &rt715_pm, + }, + .probe = rt715_sdw_probe, + .ops = &rt715_slave_ops, + .id_table = rt715_id, +}; +module_sdw_driver(rt715_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT715 driver SDW"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715-sdw.h b/sound/soc/codecs/rt715-sdw.h new file mode 100644 index 000000000000..5d7661e335ae --- /dev/null +++ b/sound/soc/codecs/rt715-sdw.h @@ -0,0 +1,337 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt715-sdw.h -- RT715 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT715_SDW_H__ +#define __RT715_SDW_H__ + +static const struct reg_default rt715_reg_defaults[] = { + { 0x0000, 0x00 }, + { 0x0001, 0x00 }, + { 0x0002, 0x00 }, + { 0x0003, 0x00 }, + { 0x0004, 0x00 }, + { 0x0005, 0x01 }, + { 0x0020, 0x00 }, + { 0x0022, 0x00 }, + { 0x0023, 0x00 }, + { 0x0024, 0x00 }, + { 0x0025, 0x00 }, + { 0x0026, 0x00 }, + { 0x0030, 0x00 }, + { 0x0032, 0x00 }, + { 0x0033, 0x00 }, + { 0x0034, 0x00 }, + { 0x0035, 0x00 }, + { 0x0036, 0x00 }, + { 0x0040, 0x00 }, + { 0x0041, 0x00 }, + { 0x0042, 0x00 }, + { 0x0043, 0x00 }, + { 0x0044, 0x20 }, + { 0x0045, 0x01 }, + { 0x0046, 0x00 }, + { 0x0050, 0x20 }, + { 0x0051, 0x02 }, + { 0x0052, 0x5d }, + { 0x0053, 0x07 }, + { 0x0054, 0x15 }, + { 0x0055, 0x00 }, + { 0x0060, 0x00 }, + { 0x0070, 0x00 }, + { 0x0080, 0x00 }, + { 0x0088, 0x10 }, + { 0x00e0, 0x00 }, + { 0x00e1, 0x00 }, + { 0x00e2, 0x00 }, + { 0x00e3, 0x00 }, + { 0x00e4, 0x00 }, + { 0x00e5, 0x00 }, + { 0x00ee, 0x00 }, + { 0x00ef, 0x00 }, + { 0x00f0, 0x00 }, + { 0x00f1, 0x00 }, + { 0x00f2, 0x00 }, + { 0x00f3, 0x00 }, + { 0x00f4, 0x00 }, + { 0x00f5, 0x00 }, + { 0x00fe, 0x00 }, + { 0x00ff, 0x00 }, + { 0x0200, 0x00 }, + { 0x0201, 0x00 }, + { 0x0202, 0x20 }, + { 0x0203, 0x00 }, + { 0x0204, 0x00 }, + { 0x0205, 0x03 }, + { 0x0220, 0x00 }, + { 0x0221, 0x00 }, + { 0x0222, 0x00 }, + { 0x0223, 0x00 }, + { 0x0224, 0x00 }, + { 0x0225, 0x00 }, + { 0x0226, 0x00 }, + { 0x0227, 0x00 }, + { 0x0230, 0x00 }, + { 0x0231, 0x00 }, + { 0x0232, 0x00 }, + { 0x0233, 0x00 }, + { 0x0234, 0x00 }, + { 0x0235, 0x00 }, + { 0x0236, 0x00 }, + { 0x0237, 0x00 }, + { 0x02e0, 0x00 }, + { 0x02f0, 0x00 }, + { 0x0400, 0x00 }, + { 0x0401, 0x00 }, + { 0x0402, 0x20 }, + { 0x0403, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x0f }, + { 0x0420, 0x00 }, + { 0x0421, 0x00 }, + { 0x0422, 0x00 }, + { 0x0423, 0x00 }, + { 0x0424, 0x00 }, + { 0x0425, 0x00 }, + { 0x0426, 0x00 }, + { 0x0427, 0x00 }, + { 0x0430, 0x00 }, + { 0x0431, 0x00 }, + { 0x0432, 0x00 }, + { 0x0433, 0x00 }, + { 0x0434, 0x00 }, + { 0x0435, 0x00 }, + { 0x0436, 0x00 }, + { 0x0437, 0x00 }, + { 0x04e0, 0x00 }, + { 0x04f0, 0x00 }, + { 0x0600, 0x00 }, + { 0x0601, 0x00 }, + { 0x0602, 0x20 }, + { 0x0603, 0x00 }, + { 0x0604, 0x00 }, + { 0x0605, 0xff }, + { 0x0620, 0x00 }, + { 0x0621, 0x00 }, + { 0x0622, 0x00 }, + { 0x0623, 0x00 }, + { 0x0624, 0x00 }, + { 0x0625, 0x00 }, + { 0x0626, 0x00 }, + { 0x0627, 0x00 }, + { 0x0630, 0x00 }, + { 0x0631, 0x00 }, + { 0x0632, 0x00 }, + { 0x0633, 0x00 }, + { 0x0634, 0x00 }, + { 0x0635, 0x00 }, + { 0x0636, 0x00 }, + { 0x0637, 0x00 }, + { 0x06e0, 0x00 }, + { 0x06f0, 0x00 }, + { 0x0f00, 0x00 }, + { 0x0f01, 0x00 }, + { 0x0f02, 0x00 }, + { 0x0f03, 0x00 }, + { 0x0f04, 0x00 }, + { 0x0f05, 0xff }, + { 0x0f06, 0x00 }, + { 0x0f07, 0x00 }, + { 0x0f08, 0x00 }, + { 0x0f09, 0x00 }, + { 0x0f0a, 0x00 }, + { 0x0f0b, 0x00 }, + { 0x0f0c, 0x00 }, + { 0x0f0d, 0x00 }, + { 0x0f0e, 0x00 }, + { 0x0f0f, 0x00 }, + { 0x0f10, 0x00 }, + { 0x0f11, 0x00 }, + { 0x0f12, 0x00 }, + { 0x0f13, 0x00 }, + { 0x0f14, 0x00 }, + { 0x0f15, 0x00 }, + { 0x0f16, 0x00 }, + { 0x0f17, 0x00 }, + { 0x0f18, 0x00 }, + { 0x0f19, 0x00 }, + { 0x0f1a, 0x00 }, + { 0x0f1b, 0x00 }, + { 0x0f1c, 0x00 }, + { 0x0f1d, 0x00 }, + { 0x0f1e, 0x00 }, + { 0x0f1f, 0x00 }, + { 0x0f20, 0x00 }, + { 0x0f21, 0x00 }, + { 0x0f22, 0x00 }, + { 0x0f23, 0x00 }, + { 0x0f24, 0x00 }, + { 0x0f25, 0x00 }, + { 0x0f26, 0x00 }, + { 0x0f27, 0x00 }, + { 0x0f30, 0x00 }, + { 0x0f31, 0x00 }, + { 0x0f32, 0x00 }, + { 0x0f33, 0x00 }, + { 0x0f34, 0x00 }, + { 0x0f35, 0x00 }, + { 0x0f36, 0x00 }, + { 0x0f37, 0x00 }, + { 0x2000, 0x00 }, + { 0x2001, 0x00 }, + { 0x2002, 0x00 }, + { 0x2003, 0x00 }, + { 0x2004, 0x00 }, + { 0x2005, 0x00 }, + { 0x2006, 0x00 }, + { 0x2007, 0x00 }, + { 0x2008, 0x00 }, + { 0x2009, 0x03 }, + { 0x200a, 0x00 }, + { 0x200b, 0x00 }, + { 0x200c, 0x00 }, + { 0x200d, 0x00 }, + { 0x200e, 0x00 }, + { 0x200f, 0x10 }, + { 0x2010, 0x00 }, + { 0x2011, 0x00 }, + { 0x2012, 0x00 }, + { 0x2013, 0x00 }, + { 0x2014, 0x00 }, + { 0x2015, 0x00 }, + { 0x2016, 0x00 }, + { 0x201a, 0x00 }, + { 0x201b, 0x00 }, + { 0x201c, 0x00 }, + { 0x201d, 0x00 }, + { 0x201e, 0x00 }, + { 0x201f, 0x00 }, + { 0x2020, 0x00 }, + { 0x2021, 0x00 }, + { 0x2022, 0x00 }, + { 0x2023, 0x00 }, + { 0x2024, 0x00 }, + { 0x2025, 0x01 }, + { 0x2026, 0x00 }, + { 0x2027, 0x00 }, + { 0x2029, 0x00 }, + { 0x202a, 0x00 }, + { 0x202d, 0x00 }, + { 0x202e, 0x00 }, + { 0x202f, 0x00 }, + { 0x2030, 0x00 }, + { 0x2031, 0x00 }, + { 0x2032, 0x00 }, + { 0x2033, 0x00 }, + { 0x2034, 0x00 }, + { 0x2200, 0x00 }, + { 0x2201, 0x00 }, + { 0x2202, 0x00 }, + { 0x2203, 0x00 }, + { 0x2204, 0x00 }, + { 0x2206, 0x00 }, + { 0x2207, 0x00 }, + { 0x2208, 0x00 }, + { 0x2209, 0x00 }, + { 0x220a, 0x00 }, + { 0x220b, 0x00 }, + { 0x220c, 0x00 }, + { 0x220d, 0x00 }, + { 0x220e, 0x00 }, + { 0x220f, 0x00 }, + { 0x2210, 0x00 }, + { 0x2211, 0x00 }, + { 0x2212, 0x00 }, + { 0x2220, 0x00 }, + { 0x2221, 0x00 }, + { 0x2222, 0x00 }, + { 0x2223, 0x00 }, + { 0x2230, 0x00 }, + { 0x2231, 0x0f }, + { 0x2232, 0x00 }, + { 0x2233, 0x00 }, + { 0x2234, 0x00 }, + { 0x2235, 0x00 }, + { 0x2236, 0x00 }, + { 0x2237, 0x00 }, + { 0x2238, 0x00 }, + { 0x2239, 0x00 }, + { 0x22f0, 0x00 }, + { 0x22f1, 0x00 }, + { 0x22f2, 0x00 }, + { 0x22f3, 0x00 }, + { 0x3122, 0x02 }, + { 0x3123, 0x03 }, + { 0x3124, 0x00 }, + { 0x3125, 0x01 }, + { 0x3607, 0x00 }, + { 0x3608, 0x00 }, + { 0x3609, 0x00 }, + { 0x3610, 0x00 }, + { 0x3611, 0x00 }, + { 0x3627, 0x00 }, + { 0x3712, 0x00 }, + { 0x3713, 0x00 }, + { 0x3718, 0x00 }, + { 0x3719, 0x00 }, + { 0x371a, 0x00 }, + { 0x371b, 0x00 }, + { 0x371d, 0x00 }, + { 0x3729, 0x00 }, + { 0x385e, 0x00 }, + { 0x3859, 0x00 }, + { 0x4c12, 0x411111f0 }, + { 0x4c13, 0x411111f0 }, + { 0x4c1d, 0x411111f0 }, + { 0x4c29, 0x411111f0 }, + { 0x4d12, 0x411111f0 }, + { 0x4d13, 0x411111f0 }, + { 0x4d1d, 0x411111f0 }, + { 0x4d29, 0x411111f0 }, + { 0x4e12, 0x411111f0 }, + { 0x4e13, 0x411111f0 }, + { 0x4e1d, 0x411111f0 }, + { 0x4e29, 0x411111f0 }, + { 0x4f12, 0x411111f0 }, + { 0x4f13, 0x411111f0 }, + { 0x4f1d, 0x411111f0 }, + { 0x4f29, 0x411111f0 }, + { 0x7207, 0x00 }, + { 0x8287, 0x00 }, + { 0x7208, 0x00 }, + { 0x8288, 0x00 }, + { 0x7209, 0x00 }, + { 0x8289, 0x00 }, + { 0x7227, 0x00 }, + { 0x82a7, 0x00 }, + { 0x7307, 0x97 }, + { 0x8387, 0x97 }, + { 0x7308, 0x97 }, + { 0x8388, 0x97 }, + { 0x7309, 0x97 }, + { 0x8389, 0x97 }, + { 0x7312, 0x00 }, + { 0x8392, 0x00 }, + { 0x7313, 0x00 }, + { 0x8393, 0x00 }, + { 0x7318, 0x00 }, + { 0x8398, 0x00 }, + { 0x7319, 0x00 }, + { 0x8399, 0x00 }, + { 0x731a, 0x00 }, + { 0x839a, 0x00 }, + { 0x731b, 0x00 }, + { 0x839b, 0x00 }, + { 0x731d, 0x00 }, + { 0x839d, 0x00 }, + { 0x7327, 0x97 }, + { 0x83a7, 0x97 }, + { 0x7329, 0x00 }, + { 0x83a9, 0x00 }, + { 0x752039, 0xa500 }, +}; + +#endif /* __RT715_H__ */ diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c new file mode 100644 index 000000000000..2cbc57b16b13 --- /dev/null +++ b/sound/soc/codecs/rt715.c @@ -0,0 +1,872 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * rt715.c -- rt715 ALSA SoC audio driver + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + * + * ALC715 ASoC Codec Driver based Intel Dummy SdW codec driver + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/pm_runtime.h> +#include <linux/pm.h> +#include <linux/soundwire/sdw.h> +#include <linux/gpio.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio/consumer.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/hda_verbs.h> + +#include "rt715.h" + +static int rt715_index_write(struct regmap *regmap, unsigned int reg, + unsigned int value) +{ + int ret; + unsigned int addr = ((RT715_PRIV_INDEX_W_H) << 8) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) { + pr_err("Failed to set private value: %08x <= %04x %d\n", ret, + addr, value); + } + + return ret; +} + +static void rt715_get_gain(struct rt715_priv *rt715, unsigned int addr_h, + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) +{ + int ret; + /* R Channel */ + *r_val = (val_h << 8); + ret = regmap_read(rt715->regmap, addr_l, r_val); + if (ret < 0) + pr_err("Failed to get R channel gain.\n"); + + /* L Channel */ + val_h |= 0x20; + *l_val = (val_h << 8); + ret = regmap_read(rt715->regmap, addr_h, l_val); + if (ret < 0) + pr_err("Failed to get L channel gain.\n"); +} + +/* For Verb-Set Amplifier Gain (Verb ID = 3h) */ +static int rt715_set_amp_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + 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 rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + unsigned int addr_h, addr_l, val_h, val_ll, val_lr; + unsigned int read_ll, read_rl; + int i; + + /* Can't use update bit function, so read the original value first */ + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT715_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll); + + /* L Channel */ + if (mc->invert) { + /* for mute */ + val_ll = (mc->max - ucontrol->value.integer.value[0]) << 7; + /* keep gain */ + read_ll = read_ll & 0x7f; + val_ll |= read_ll; + } else { + /* for gain */ + val_ll = ((ucontrol->value.integer.value[0]) & 0x7f); + if (val_ll > mc->max) + val_ll = mc->max; + /* keep mute status */ + read_ll = read_ll & 0x80; + val_ll |= read_ll; + } + + /* R Channel */ + if (mc->invert) { + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + /* for mute */ + val_lr = (mc->max - ucontrol->value.integer.value[1]) << 7; + /* keep gain */ + read_rl = read_rl & 0x7f; + val_lr |= read_rl; + } else { + /* for gain */ + val_lr = ((ucontrol->value.integer.value[1]) & 0x7f); + if (val_lr > mc->max) + val_lr = mc->max; + /* keep mute status */ + read_rl = read_rl & 0x80; + val_lr |= read_rl; + } + + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + + if (val_ll == val_lr) { + /* Set both L/R channels at the same time */ + val_h = (1 << mc->shift) | (3 << 4); + regmap_write(rt715->regmap, addr_h, + (val_h << 8 | val_ll)); + regmap_write(rt715->regmap, addr_l, + (val_h << 8 | val_ll)); + } else { + /* Lch*/ + val_h = (1 << mc->shift) | (1 << 5); + regmap_write(rt715->regmap, addr_h, + (val_h << 8 | val_ll)); + /* Rch */ + val_h = (1 << mc->shift) | (1 << 4); + regmap_write(rt715->regmap, addr_l, + (val_h << 8 | val_lr)); + } + /* check result */ + if (mc->shift == RT715_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt715_get_gain(rt715, addr_h, addr_l, val_h, + &read_rl, &read_ll); + if (read_rl == val_lr && read_ll == val_ll) + break; + } + /* D0:power on state, D3: power saving mode */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + return 0; +} + +static int rt715_set_amp_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int addr_h, addr_l, val_h; + unsigned int read_ll, read_rl; + + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT715_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll); + + if (mc->invert) { + /* for mute status */ + read_ll = !((read_ll & 0x80) >> RT715_MUTE_SFT); + read_rl = !((read_rl & 0x80) >> RT715_MUTE_SFT); + } else { + /* for gain */ + read_ll = read_ll & 0x7f; + read_rl = read_rl & 0x7f; + } + ucontrol->value.integer.value[0] = read_ll; + ucontrol->value.integer.value[1] = read_rl; + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ + xmax, xinvert) } + +static const struct snd_kcontrol_new rt715_snd_controls[] = { + /* Capture switch */ + SOC_DOUBLE_R_EXT("ADC 07 Capture Switch", RT715_SET_GAIN_MIC_ADC_H, + RT715_SET_GAIN_MIC_ADC_L, RT715_DIR_IN_SFT, 1, 1, + rt715_set_amp_gain_get, rt715_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", RT715_SET_GAIN_LINE_ADC_H, + RT715_SET_GAIN_LINE_ADC_L, RT715_DIR_IN_SFT, 1, 1, + rt715_set_amp_gain_get, rt715_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", RT715_SET_GAIN_MIX_ADC_H, + RT715_SET_GAIN_MIX_ADC_L, RT715_DIR_IN_SFT, 1, 1, + rt715_set_amp_gain_get, rt715_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 27 Capture Switch", RT715_SET_GAIN_MIX_ADC2_H, + RT715_SET_GAIN_MIX_ADC2_L, RT715_DIR_IN_SFT, 1, 1, + rt715_set_amp_gain_get, rt715_set_amp_gain_put), + /* Volume Control */ + SOC_DOUBLE_R_EXT_TLV("ADC 07 Capture Volume", RT715_SET_GAIN_MIC_ADC_H, + RT715_SET_GAIN_MIC_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", RT715_SET_GAIN_LINE_ADC_H, + RT715_SET_GAIN_LINE_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", RT715_SET_GAIN_MIX_ADC_H, + RT715_SET_GAIN_MIX_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 27 Capture Volume", RT715_SET_GAIN_MIX_ADC2_H, + RT715_SET_GAIN_MIX_ADC2_L, RT715_DIR_IN_SFT, 0x3f, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + in_vol_tlv), + /* MIC Boost Control */ + SOC_DOUBLE_R_EXT_TLV("DMIC1 Boost", RT715_SET_GAIN_DMIC1_H, + RT715_SET_GAIN_DMIC1_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC2 Boost", RT715_SET_GAIN_DMIC2_H, + RT715_SET_GAIN_DMIC2_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC3 Boost", RT715_SET_GAIN_DMIC3_H, + RT715_SET_GAIN_DMIC3_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC4 Boost", RT715_SET_GAIN_DMIC4_H, + RT715_SET_GAIN_DMIC4_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("MIC1 Boost", RT715_SET_GAIN_MIC1_H, + RT715_SET_GAIN_MIC1_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("MIC2 Boost", RT715_SET_GAIN_MIC2_H, + RT715_SET_GAIN_MIC2_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("LINE1 Boost", RT715_SET_GAIN_LINE1_H, + RT715_SET_GAIN_LINE1_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("LINE2 Boost", RT715_SET_GAIN_LINE2_H, + RT715_SET_GAIN_LINE2_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), +}; + +static int rt715_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int reg, val; + int ret; + + /* nid = e->reg, vid = 0xf01 */ + reg = RT715_VERB_SET_CONNECT_SEL | e->reg; + ret = regmap_read(rt715->regmap, reg, &val); + if (ret < 0) { + dev_err(component->dev, "%s: sdw read failed: %d\n", + __func__, ret); + return ret; + } + + /* + * The first two indices of ADC Mux 24/25 are routed to the same + * hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2. + * To have a unique set of inputs, we skip the index1 of the muxes. + */ + if ((e->reg == RT715_MUX_IN3 || e->reg == RT715_MUX_IN4) && (val > 0)) + val -= 1; + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt715_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, reg; + int ret; + + if (item[0] >= e->items) + return -EINVAL; + + /* Verb ID = 0x701h, nid = e->reg */ + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + reg = RT715_VERB_SET_CONNECT_SEL | e->reg; + ret = regmap_read(rt715->regmap, reg, &val2); + if (ret < 0) { + dev_err(component->dev, "%s: sdw read failed: %d\n", + __func__, ret); + return ret; + } + + if (val == val2) + change = 0; + else + change = 1; + + if (change) { + reg = RT715_VERB_SET_CONNECT_SEL | e->reg; + regmap_write(rt715->regmap, reg, val); + } + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc_22_23_mux_text[] = { + "MIC1", + "MIC2", + "LINE1", + "LINE2", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +/* + * Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and + * 1 will be connected to the same dmic source, therefore we skip index 1 to + * avoid misunderstanding on usage of dapm routing. + */ +static const unsigned int rt715_adc_24_25_values[] = { + 0, + 2, + 3, + 4, + 5, +}; + +static const char * const adc_24_mux_text[] = { + "MIC2", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +static const char * const adc_25_mux_text[] = { + "MIC1", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +static SOC_ENUM_SINGLE_DECL( + rt715_adc22_enum, RT715_MUX_IN1, 0, adc_22_23_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt715_adc23_enum, RT715_MUX_IN2, 0, adc_22_23_mux_text); + +static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum, + RT715_MUX_IN3, 0, 0xf, + adc_24_mux_text, rt715_adc_24_25_values); + +static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum, + RT715_MUX_IN4, 0, 0xf, + adc_25_mux_text, rt715_adc_24_25_values); + +static const struct snd_kcontrol_new rt715_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_kcontrol_new rt715_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_kcontrol_new rt715_adc24_mux = + SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_kcontrol_new rt715_adc25_mux = + SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_soc_dapm_widget rt715_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + SND_SOC_DAPM_ADC("ADC 07", NULL, RT715_SET_STREAMID_MIC_ADC, 4, 0), + SND_SOC_DAPM_ADC("ADC 08", NULL, RT715_SET_STREAMID_LINE_ADC, 4, 0), + SND_SOC_DAPM_ADC("ADC 09", NULL, RT715_SET_STREAMID_MIX_ADC, 4, 0), + SND_SOC_DAPM_ADC("ADC 27", NULL, RT715_SET_STREAMID_MIX_ADC2, 4, 0), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc23_mux), + SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc24_mux), + SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc25_mux), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt715_audio_map[] = { + {"DP6TX", NULL, "ADC 09"}, + {"DP6TX", NULL, "ADC 08"}, + {"DP4TX", NULL, "ADC 07"}, + {"DP4TX", NULL, "ADC 27"}, + {"ADC 09", NULL, "ADC 22 Mux"}, + {"ADC 08", NULL, "ADC 23 Mux"}, + {"ADC 07", NULL, "ADC 24 Mux"}, + {"ADC 27", NULL, "ADC 25 Mux"}, + {"ADC 22 Mux", "MIC1", "MIC1"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 22 Mux", "DMIC1", "DMIC1"}, + {"ADC 22 Mux", "DMIC2", "DMIC2"}, + {"ADC 22 Mux", "DMIC3", "DMIC3"}, + {"ADC 22 Mux", "DMIC4", "DMIC4"}, + {"ADC 23 Mux", "MIC1", "MIC1"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1"}, + {"ADC 23 Mux", "LINE2", "LINE2"}, + {"ADC 23 Mux", "DMIC1", "DMIC1"}, + {"ADC 23 Mux", "DMIC2", "DMIC2"}, + {"ADC 23 Mux", "DMIC3", "DMIC3"}, + {"ADC 23 Mux", "DMIC4", "DMIC4"}, + {"ADC 24 Mux", "MIC2", "MIC2"}, + {"ADC 24 Mux", "DMIC1", "DMIC1"}, + {"ADC 24 Mux", "DMIC2", "DMIC2"}, + {"ADC 24 Mux", "DMIC3", "DMIC3"}, + {"ADC 24 Mux", "DMIC4", "DMIC4"}, + {"ADC 25 Mux", "MIC1", "MIC1"}, + {"ADC 25 Mux", "DMIC1", "DMIC1"}, + {"ADC 25 Mux", "DMIC2", "DMIC2"}, + {"ADC 25 Mux", "DMIC3", "DMIC3"}, + {"ADC 25 Mux", "DMIC4", "DMIC4"}, +}; + +static int rt715_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, + AC_PWRST_D0); + } + break; + + case SND_SOC_BIAS_STANDBY: + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, + AC_PWRST_D3); + break; + + default: + break; + } + dapm->bias_level = level; + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_rt715 = { + .set_bias_level = rt715_set_bias_level, + .controls = rt715_snd_controls, + .num_controls = ARRAY_SIZE(rt715_snd_controls), + .dapm_widgets = rt715_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt715_dapm_widgets), + .dapm_routes = rt715_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt715_audio_map), +}; + +static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + + struct sdw_stream_data *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + + /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = stream; + else + dai->capture_dma_data = stream; + + return 0; +} + +static void rt715_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + +{ + struct sdw_stream_data *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(stream); +} + +static int rt715_pcm_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 rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_data *stream; + int retval, port, num_channels; + unsigned int val = 0; + + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!stream) + return -EINVAL; + + if (!rt715->slave) + return -EINVAL; + + switch (dai->id) { + case RT715_AIF1: + direction = SDW_DATA_DIR_TX; + port = 6; + rt715_index_write(rt715->regmap, RT715_SDW_INPUT_SEL, 0xa500); + break; + case RT715_AIF2: + direction = SDW_DATA_DIR_TX; + port = 4; + rt715_index_write(rt715->regmap, RT715_SDW_INPUT_SEL, 0xa000); + break; + default: + dev_err(component->dev, "Invalid DAI id %d\n", dai->id); + return -EINVAL; + } + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = (1 << (num_channels)) - 1; + port_config.num = port; + + retval = sdw_stream_add_slave(rt715->slave, &stream_config, + &port_config, 1, stream->sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + switch (params_rate(params)) { + /* bit 14 0:48K 1:44.1K */ + /* bit 15 Stream Type 0:PCM 1:Non-PCM, should always be PCM */ + case 44100: + val |= 0x40 << 8; + break; + case 48000: + val |= 0x0 << 8; + break; + default: + dev_err(component->dev, "Unsupported sample rate %d\n", + params_rate(params)); + return -EINVAL; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 8: + break; + case 16: + val |= (0x1 << 4); + break; + case 20: + val |= (0x2 << 4); + break; + case 24: + val |= (0x3 << 4); + break; + case 32: + val |= (0x4 << 4); + break; + default: + return -EINVAL; + } + + regmap_write(rt715->regmap, RT715_MIC_ADC_FORMAT_H, val); + regmap_write(rt715->regmap, RT715_MIC_LINE_FORMAT_H, val); + regmap_write(rt715->regmap, RT715_MIX_ADC_FORMAT_H, val); + regmap_write(rt715->regmap, RT715_MIX_ADC2_FORMAT_H, val); + + return retval; +} + +static int rt715_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt715->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt715->slave, stream->sdw_stream); + return 0; +} + +#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT715_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static struct snd_soc_dai_ops rt715_ops = { + .hw_params = rt715_pcm_hw_params, + .hw_free = rt715_pcm_hw_free, + .set_sdw_stream = rt715_set_sdw_stream, + .shutdown = rt715_shutdown, +}; + +static struct snd_soc_dai_driver rt715_dai[] = { + { + .name = "rt715-aif1", + .id = RT715_AIF1, + .capture = { + .stream_name = "DP6 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT715_STEREO_RATES, + .formats = RT715_FORMATS, + }, + .ops = &rt715_ops, + }, + { + .name = "rt715-aif2", + .id = RT715_AIF2, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT715_STEREO_RATES, + .formats = RT715_FORMATS, + }, + .ops = &rt715_ops, + }, +}; + +/* Bus clock frequency */ +#define RT715_CLK_FREQ_9600000HZ 9600000 +#define RT715_CLK_FREQ_12000000HZ 12000000 +#define RT715_CLK_FREQ_6000000HZ 6000000 +#define RT715_CLK_FREQ_4800000HZ 4800000 +#define RT715_CLK_FREQ_2400000HZ 2400000 +#define RT715_CLK_FREQ_12288000HZ 12288000 + +int rt715_clock_config(struct device *dev) +{ + struct rt715_priv *rt715 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt715->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT715_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT715_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT715_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT715_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT715_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT715_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt715->regmap, 0xe0, value); + regmap_write(rt715->regmap, 0xf0, value); + + return 0; +} + +int rt715_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave) +{ + struct rt715_priv *rt715; + int ret; + + rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL); + if (!rt715) + return -ENOMEM; + + dev_set_drvdata(dev, rt715); + rt715->slave = slave; + rt715->regmap = regmap; + rt715->sdw_regmap = sdw_regmap; + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt715->hw_init = false; + rt715->first_hw_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_codec_dev_rt715, + rt715_dai, + ARRAY_SIZE(rt715_dai)); + + return ret; +} + +int rt715_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt715_priv *rt715 = dev_get_drvdata(dev); + + if (rt715->hw_init) + return 0; + + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + if (!rt715->first_hw_init) { + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + /* Mute nid=08h/09h */ + regmap_write(rt715->regmap, RT715_SET_GAIN_LINE_ADC_H, 0xb080); + regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC_H, 0xb080); + /* Mute nid=07h/27h */ + regmap_write(rt715->regmap, RT715_SET_GAIN_MIC_ADC_H, 0xb080); + regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC2_H, 0xb080); + + /* Set Pin Widget */ + regmap_write(rt715->regmap, RT715_SET_PIN_DMIC1, 0x20); + regmap_write(rt715->regmap, RT715_SET_PIN_DMIC2, 0x20); + regmap_write(rt715->regmap, RT715_SET_PIN_DMIC3, 0x20); + regmap_write(rt715->regmap, RT715_SET_PIN_DMIC4, 0x20); + /* Set Converter Stream */ + regmap_write(rt715->regmap, RT715_SET_STREAMID_LINE_ADC, 0x10); + regmap_write(rt715->regmap, RT715_SET_STREAMID_MIX_ADC, 0x10); + regmap_write(rt715->regmap, RT715_SET_STREAMID_MIC_ADC, 0x10); + regmap_write(rt715->regmap, RT715_SET_STREAMID_MIX_ADC2, 0x10); + /* Set Configuration Default */ + regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT1, 0xd0); + regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT2, 0x11); + regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT3, 0xa1); + regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT4, 0x81); + regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT1, 0xd1); + regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT2, 0x11); + regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT3, 0xa1); + regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT4, 0x81); + regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT1, 0xd0); + regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT2, 0x11); + regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT3, 0xa1); + regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT4, 0x81); + regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT1, 0xd1); + regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT2, 0x11); + regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT3, 0xa1); + regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT4, 0x81); + + /* Finish Initial Settings, set power to D3 */ + regmap_write(rt715->regmap, RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + if (rt715->first_hw_init) + regcache_mark_dirty(rt715->regmap); + else + rt715->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt715->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + return 0; +} + +MODULE_DESCRIPTION("ASoC rt715 driver"); +MODULE_DESCRIPTION("ASoC rt715 driver SDW"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h new file mode 100644 index 000000000000..df0f24f9bc0c --- /dev/null +++ b/sound/soc/codecs/rt715.h @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt715.h -- RT715 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT715_H__ +#define __RT715_H__ + +#include <linux/regulator/consumer.h> + +struct rt715_priv { + struct regmap *regmap; + struct regmap *sdw_regmap; + struct snd_soc_codec *codec; + struct sdw_slave *slave; + int dbg_nid; + int dbg_vid; + int dbg_payload; + enum sdw_slave_status status; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; +}; + +struct sdw_stream_data { + struct sdw_stream_runtime *sdw_stream; +}; + +/* NID */ +#define RT715_AUDIO_FUNCTION_GROUP 0x01 +#define RT715_MIC_ADC 0x07 +#define RT715_LINE_ADC 0x08 +#define RT715_MIX_ADC 0x09 +#define RT715_DMIC1 0x12 +#define RT715_DMIC2 0x13 +#define RT715_MIC1 0x18 +#define RT715_MIC2 0x19 +#define RT715_LINE1 0x1a +#define RT715_LINE2 0x1b +#define RT715_DMIC3 0x1d +#define RT715_DMIC4 0x29 +#define RT715_VENDOR_REGISTERS 0x20 +#define RT715_MUX_IN1 0x22 +#define RT715_MUX_IN2 0x23 +#define RT715_MUX_IN3 0x24 +#define RT715_MUX_IN4 0x25 +#define RT715_MIX_ADC2 0x27 +#define RT715_INLINE_CMD 0x55 + +/* Index (NID:20h) */ +#define RT715_SDW_INPUT_SEL 0x39 +#define RT715_EXT_DMIC_CLK_CTRL2 0x54 + +/* Verb */ +#define RT715_VERB_SET_CONNECT_SEL 0x3100 +#define RT715_VERB_GET_CONNECT_SEL 0xb100 +#define RT715_VERB_SET_EAPD_BTLENABLE 0x3c00 +#define RT715_VERB_SET_POWER_STATE 0x3500 +#define RT715_VERB_SET_CHANNEL_STREAMID 0x3600 +#define RT715_VERB_SET_PIN_WIDGET_CONTROL 0x3700 +#define RT715_VERB_SET_CONFIG_DEFAULT1 0x4c00 +#define RT715_VERB_SET_CONFIG_DEFAULT2 0x4d00 +#define RT715_VERB_SET_CONFIG_DEFAULT3 0x4e00 +#define RT715_VERB_SET_CONFIG_DEFAULT4 0x4f00 +#define RT715_VERB_SET_UNSOLICITED_ENABLE 0x3800 +#define RT715_SET_AMP_GAIN_MUTE_H 0x7300 +#define RT715_SET_AMP_GAIN_MUTE_L 0x8380 +#define RT715_READ_HDA_3 0x2012 +#define RT715_READ_HDA_2 0x2013 +#define RT715_READ_HDA_1 0x2014 +#define RT715_READ_HDA_0 0x2015 +#define RT715_PRIV_INDEX_W_H 0x7520 +#define RT715_PRIV_INDEX_W_L 0x85a0 +#define RT715_PRIV_DATA_W_H 0x7420 +#define RT715_PRIV_DATA_W_L 0x84a0 +#define RT715_PRIV_INDEX_R_H 0x9d20 +#define RT715_PRIV_INDEX_R_L 0xada0 +#define RT715_PRIV_DATA_R_H 0x9c20 +#define RT715_PRIV_DATA_R_L 0xaca0 +#define RT715_MIC_ADC_FORMAT_H 0x7207 +#define RT715_MIC_ADC_FORMAT_L 0x8287 +#define RT715_MIC_LINE_FORMAT_H 0x7208 +#define RT715_MIC_LINE_FORMAT_L 0x8288 +#define RT715_MIX_ADC_FORMAT_H 0x7209 +#define RT715_MIX_ADC_FORMAT_L 0x8289 +#define RT715_MIX_ADC2_FORMAT_H 0x7227 +#define RT715_MIX_ADC2_FORMAT_L 0x82a7 +#define RT715_FUNC_RESET 0xff01 + +#define RT715_SET_AUDIO_POWER_STATE\ + (RT715_VERB_SET_POWER_STATE | RT715_AUDIO_FUNCTION_GROUP) +#define RT715_SET_PIN_DMIC1\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC1) +#define RT715_SET_PIN_DMIC2\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC2) +#define RT715_SET_PIN_DMIC3\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC3) +#define RT715_SET_PIN_DMIC4\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC4) +#define RT715_SET_PIN_MIC1\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_MIC1) +#define RT715_SET_PIN_MIC2\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_MIC2) +#define RT715_SET_PIN_LINE1\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_LINE1) +#define RT715_SET_PIN_LINE2\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_LINE2) +#define RT715_SET_MIC1_UNSOLICITED_ENABLE\ + (RT715_VERB_SET_UNSOLICITED_ENABLE | RT715_MIC1) +#define RT715_SET_MIC2_UNSOLICITED_ENABLE\ + (RT715_VERB_SET_UNSOLICITED_ENABLE | RT715_MIC2) +#define RT715_SET_STREAMID_MIC_ADC\ + (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIC_ADC) +#define RT715_SET_STREAMID_LINE_ADC\ + (RT715_VERB_SET_CHANNEL_STREAMID | RT715_LINE_ADC) +#define RT715_SET_STREAMID_MIX_ADC\ + (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIX_ADC) +#define RT715_SET_STREAMID_MIX_ADC2\ + (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIX_ADC2) +#define RT715_SET_GAIN_MIC_ADC_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC_ADC) +#define RT715_SET_GAIN_MIC_ADC_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC_ADC) +#define RT715_SET_GAIN_LINE_ADC_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE_ADC) +#define RT715_SET_GAIN_LINE_ADC_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE_ADC) +#define RT715_SET_GAIN_MIX_ADC_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIX_ADC) +#define RT715_SET_GAIN_MIX_ADC_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIX_ADC) +#define RT715_SET_GAIN_MIX_ADC2_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIX_ADC2) +#define RT715_SET_GAIN_MIX_ADC2_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIX_ADC2) +#define RT715_SET_GAIN_DMIC1_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC1) +#define RT715_SET_GAIN_DMIC1_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC1) +#define RT715_SET_GAIN_DMIC2_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC2) +#define RT715_SET_GAIN_DMIC2_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC2) +#define RT715_SET_GAIN_DMIC3_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC3) +#define RT715_SET_GAIN_DMIC3_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC3) +#define RT715_SET_GAIN_DMIC4_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC4) +#define RT715_SET_GAIN_DMIC4_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC4) +#define RT715_SET_GAIN_MIC1_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC1) +#define RT715_SET_GAIN_MIC1_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC1) +#define RT715_SET_GAIN_MIC2_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC2) +#define RT715_SET_GAIN_MIC2_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC2) +#define RT715_SET_GAIN_LINE1_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE1) +#define RT715_SET_GAIN_LINE1_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE1) +#define RT715_SET_GAIN_LINE2_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE2) +#define RT715_SET_GAIN_LINE2_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE2) +#define RT715_SET_DMIC1_CONFIG_DEFAULT1\ + (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC1) +#define RT715_SET_DMIC2_CONFIG_DEFAULT1\ + (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC2) +#define RT715_SET_DMIC1_CONFIG_DEFAULT2\ + (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC1) +#define RT715_SET_DMIC2_CONFIG_DEFAULT2\ + (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC2) +#define RT715_SET_DMIC1_CONFIG_DEFAULT3\ + (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC1) +#define RT715_SET_DMIC2_CONFIG_DEFAULT3\ + (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC2) +#define RT715_SET_DMIC1_CONFIG_DEFAULT4\ + (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC1) +#define RT715_SET_DMIC2_CONFIG_DEFAULT4\ + (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC2) +#define RT715_SET_DMIC3_CONFIG_DEFAULT1\ + (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC3) +#define RT715_SET_DMIC4_CONFIG_DEFAULT1\ + (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC4) +#define RT715_SET_DMIC3_CONFIG_DEFAULT2\ + (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC3) +#define RT715_SET_DMIC4_CONFIG_DEFAULT2\ + (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC4) +#define RT715_SET_DMIC3_CONFIG_DEFAULT3\ + (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC3) +#define RT715_SET_DMIC4_CONFIG_DEFAULT3\ + (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC4) +#define RT715_SET_DMIC3_CONFIG_DEFAULT4\ + (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC3) +#define RT715_SET_DMIC4_CONFIG_DEFAULT4\ + (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC4) + +#define RT715_MUTE_SFT 7 +#define RT715_DIR_IN_SFT 6 +#define RT715_DIR_OUT_SFT 7 + +enum { + RT715_AIF1, + RT715_AIF2, + RT715_AIFS, +}; + +int rt715_io_init(struct device *dev, struct sdw_slave *slave); +int rt715_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave); + +int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, + unsigned int *sdw_addr_h, unsigned int *sdw_data_h, + unsigned int *sdw_addr_l, unsigned int *sdw_data_l); +int rt715_clock_config(struct device *dev); +#endif /* __RT715_H__ */ diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index aa1f9637d895..d5130193b4a2 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1344,7 +1344,8 @@ static int sgtl5000_set_power_regs(struct snd_soc_component *component) * if vddio == vdda the source of charge pump should be * assigned manually to VDDIO */ - if (vddio == vdda) { + if (regulator_is_equal(sgtl5000->supplies[VDDA].consumer, + sgtl5000->supplies[VDDIO].consumer)) { lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD; lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO << SGTL5000_VDDC_MAN_ASSN_SHIFT; @@ -1513,6 +1514,13 @@ err: return ret; } +static int sgtl5000_of_xlate_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) +{ + /* return dai id 0, whatever the endpoint index */ + return 0; +} + static const struct snd_soc_component_driver sgtl5000_driver = { .probe = sgtl5000_probe, .set_bias_level = sgtl5000_set_bias_level, @@ -1522,6 +1530,7 @@ static const struct snd_soc_component_driver sgtl5000_driver = { .num_dapm_widgets = ARRAY_SIZE(sgtl5000_dapm_widgets), .dapm_routes = sgtl5000_dapm_routes, .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes), + .of_xlate_dai_id = sgtl5000_of_xlate_dai_id, .suspend_bias_off = 1, .idle_bias_on = 1, .use_pmdown_time = 1, diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index f6f19fdc72f5..31daa60695bd 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -262,6 +262,25 @@ static SOC_ENUM_SINGLE_DECL(mic1lm_p_enum, AIC31XX_MICPGAPI, 2, static SOC_ENUM_SINGLE_DECL(mic1lm_m_enum, AIC31XX_MICPGAMI, 4, mic_select_text); +static const char * const hp_poweron_time_text[] = { + "0us", "15.3us", "153us", "1.53ms", "15.3ms", "76.2ms", + "153ms", "304ms", "610ms", "1.22s", "3.04s", "6.1s" }; + +static SOC_ENUM_SINGLE_DECL(hp_poweron_time_enum, AIC31XX_HPPOP, 3, + hp_poweron_time_text); + +static const char * const hp_rampup_step_text[] = { + "0ms", "0.98ms", "1.95ms", "3.9ms" }; + +static SOC_ENUM_SINGLE_DECL(hp_rampup_step_enum, AIC31XX_HPPOP, 1, + hp_rampup_step_text); + +static const char * const vol_soft_step_mode_text[] = { + "fast", "slow", "disabled" }; + +static SOC_ENUM_SINGLE_DECL(vol_soft_step_mode_enum, AIC31XX_DACSETUP, 0, + vol_soft_step_mode_text); + static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0); static const DECLARE_TLV_DB_SCALE(adc_fgain_tlv, 0, 10, 0); static const DECLARE_TLV_DB_SCALE(adc_cgain_tlv, -2000, 50, 0); @@ -285,6 +304,16 @@ static const struct snd_kcontrol_new common31xx_snd_controls[] = { SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL, AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv), + + /* HP de-pop control: apply power not immediately but via ramp + * function with these psarameters. Note that power up sequence + * has to wait for this to complete; this is implemented by + * polling HP driver status in aic31xx_dapm_power_event() + */ + SOC_ENUM("HP Output Driver Power-On time", hp_poweron_time_enum), + SOC_ENUM("HP Output Driver Ramp-up step", hp_rampup_step_enum), + + SOC_ENUM("Volume Soft Stepping", vol_soft_step_mode_enum), }; static const struct snd_kcontrol_new aic31xx_snd_controls[] = { @@ -357,6 +386,7 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w, struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); unsigned int reg = AIC31XX_DACFLAG1; unsigned int mask; + unsigned int timeout = 500 * USEC_PER_MSEC; switch (WIDGET_BIT(w->reg, w->shift)) { case WIDGET_BIT(AIC31XX_DACSETUP, 7): @@ -367,9 +397,13 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w, break; case WIDGET_BIT(AIC31XX_HPDRIVER, 7): mask = AIC31XX_HPLDRVPWRSTATUS_MASK; + if (event == SND_SOC_DAPM_POST_PMU) + timeout = 7 * USEC_PER_SEC; break; case WIDGET_BIT(AIC31XX_HPDRIVER, 6): mask = AIC31XX_HPRDRVPWRSTATUS_MASK; + if (event == SND_SOC_DAPM_POST_PMU) + timeout = 7 * USEC_PER_SEC; break; case WIDGET_BIT(AIC31XX_SPKAMP, 7): mask = AIC31XX_SPLDRVPWRSTATUS_MASK; @@ -389,9 +423,11 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: - return aic31xx_wait_bits(aic31xx, reg, mask, mask, 5000, 100); + return aic31xx_wait_bits(aic31xx, reg, mask, mask, + 5000, timeout / 5000); case SND_SOC_DAPM_POST_PMD: - return aic31xx_wait_bits(aic31xx, reg, mask, 0, 5000, 100); + return aic31xx_wait_bits(aic31xx, reg, mask, 0, + 5000, timeout / 5000); default: dev_dbg(component->dev, "Unhandled dapm widget event %d from %s\n", diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h index 83a8c7604cc3..0523884cee74 100644 --- a/sound/soc/codecs/tlv320aic31xx.h +++ b/sound/soc/codecs/tlv320aic31xx.h @@ -218,9 +218,6 @@ struct aic31xx_pdata { #define AIC31XX_GPIO1_ADC_MOD_CLK 0x10 #define AIC31XX_GPIO1_SDOUT 0x11 -/* AIC31XX_DACSETUP */ -#define AIC31XX_SOFTSTEP_MASK GENMASK(1, 0) - /* AIC31XX_DACMUTE */ #define AIC31XX_DACMUTE_MASK GENMASK(3, 2) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c new file mode 100644 index 000000000000..158e878abd6c --- /dev/null +++ b/sound/soc/codecs/wcd934x.c @@ -0,0 +1,5084 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019, Linaro Limited + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/wcd934x/registers.h> +#include <linux/mfd/wcd934x/wcd934x.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_clk.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/slimbus.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include "wcd-clsh-v2.h" + +#define WCD934X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +/* Fractional Rates */ +#define WCD934X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400) +#define WCD934X_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +/* slave port water mark level + * (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes) + */ +#define SLAVE_PORT_WATER_MARK_6BYTES 0 +#define SLAVE_PORT_WATER_MARK_9BYTES 1 +#define SLAVE_PORT_WATER_MARK_12BYTES 2 +#define SLAVE_PORT_WATER_MARK_15BYTES 3 +#define SLAVE_PORT_WATER_MARK_SHIFT 1 +#define SLAVE_PORT_ENABLE 1 +#define SLAVE_PORT_DISABLE 0 +#define WCD934X_SLIM_WATER_MARK_VAL \ + ((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \ + (SLAVE_PORT_ENABLE)) + +#define WCD934X_SLIM_NUM_PORT_REG 3 +#define WCD934X_SLIM_PGD_PORT_INT_TX_EN0 (WCD934X_SLIM_PGD_PORT_INT_EN0 + 2) +#define WCD934X_SLIM_IRQ_OVERFLOW BIT(0) +#define WCD934X_SLIM_IRQ_UNDERFLOW BIT(1) +#define WCD934X_SLIM_IRQ_PORT_CLOSED BIT(2) + +#define WCD934X_MCLK_CLK_12P288MHZ 12288000 +#define WCD934X_MCLK_CLK_9P6MHZ 9600000 + +/* Only valid for 9.6 MHz mclk */ +#define WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ 2400000 +#define WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ 4800000 + +/* Only valid for 12.288 MHz mclk */ +#define WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ 4096000 + +#define WCD934X_DMIC_CLK_DIV_2 0x0 +#define WCD934X_DMIC_CLK_DIV_3 0x1 +#define WCD934X_DMIC_CLK_DIV_4 0x2 +#define WCD934X_DMIC_CLK_DIV_6 0x3 +#define WCD934X_DMIC_CLK_DIV_8 0x4 +#define WCD934X_DMIC_CLK_DIV_16 0x5 +#define WCD934X_DMIC_CLK_DRIVE_DEFAULT 0x02 + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +#define WCD934X_RX_START 16 +#define WCD934X_NUM_INTERPOLATORS 9 +#define WCD934X_RX_PATH_CTL_OFFSET 20 +#define WCD934X_MAX_VALID_ADC_MUX 13 +#define WCD934X_INVALID_ADC_MUX 9 + +#define WCD934X_SLIM_RX_CH(p) \ + {.port = p + WCD934X_RX_START, .shift = p,} + +#define WCD934X_SLIM_TX_CH(p) \ + {.port = p, .shift = p,} + +/* Feature masks to distinguish codec version */ +#define DSD_DISABLED_MASK 0 +#define SLNQ_DISABLED_MASK 1 + +#define DSD_DISABLED BIT(DSD_DISABLED_MASK) +#define SLNQ_DISABLED BIT(SLNQ_DISABLED_MASK) + +/* As fine version info cannot be retrieved before wcd probe. + * Define three coarse versions for possible future use before wcd probe. + */ +#define WCD_VERSION_WCD9340_1_0 0x400 +#define WCD_VERSION_WCD9341_1_0 0x410 +#define WCD_VERSION_WCD9340_1_1 0x401 +#define WCD_VERSION_WCD9341_1_1 0x411 +#define WCD934X_AMIC_PWR_LEVEL_LP 0 +#define WCD934X_AMIC_PWR_LEVEL_DEFAULT 1 +#define WCD934X_AMIC_PWR_LEVEL_HP 2 +#define WCD934X_AMIC_PWR_LEVEL_HYBRID 3 +#define WCD934X_AMIC_PWR_LVL_MASK 0x60 +#define WCD934X_AMIC_PWR_LVL_SHIFT 0x5 + +#define WCD934X_DEC_PWR_LVL_MASK 0x06 +#define WCD934X_DEC_PWR_LVL_LP 0x02 +#define WCD934X_DEC_PWR_LVL_HP 0x04 +#define WCD934X_DEC_PWR_LVL_DF 0x00 +#define WCD934X_DEC_PWR_LVL_HYBRID WCD934X_DEC_PWR_LVL_DF + +#define WCD934X_DEF_MICBIAS_MV 1800 +#define WCD934X_MAX_MICBIAS_MV 2850 + +#define WCD_IIR_FILTER_SIZE (sizeof(u32) * BAND_MAX) + +#define WCD_IIR_FILTER_CTL(xname, iidx, bidx) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = wcd934x_iir_filter_info, \ + .get = wcd934x_get_iir_band_audio_mixer, \ + .put = wcd934x_put_iir_band_audio_mixer, \ + .private_value = (unsigned long)&(struct wcd_iir_filter_ctl) { \ + .iir_idx = iidx, \ + .band_idx = bidx, \ + .bytes_ext = {.max = WCD_IIR_FILTER_SIZE, }, \ + } \ +} + +#define WCD934X_INTERPOLATOR_PATH(id) \ + {"RX INT" #id "_1 MIX1 INP0", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1 INP0", "IIR0", "IIR0"}, \ + {"RX INT" #id "_1 MIX1 INP0", "IIR1", "IIR1"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1 INP1", "IIR0", "IIR0"}, \ + {"RX INT" #id "_1 MIX1 INP1", "IIR1", "IIR1"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1 INP2", "IIR0", "IIR0"}, \ + {"RX INT" #id "_1 MIX1 INP2", "IIR1", "IIR1"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP0"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP1"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP2"}, \ + {"RX INT" #id "_2 MUX", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_2 MUX", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_2 MUX", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_2 MUX", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_2 MUX", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_2 MUX", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_2 MUX", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_2 MUX", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_2 MUX", NULL, "INT" #id "_CLK"}, \ + {"RX INT" #id "_2 MUX", NULL, "DSMDEM" #id "_CLK"}, \ + {"RX INT" #id "_2 INTERP", NULL, "RX INT" #id "_2 MUX"}, \ + {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_2 INTERP"}, \ + {"RX INT" #id "_1 INTERP", NULL, "RX INT" #id "_1 MIX1"}, \ + {"RX INT" #id "_1 INTERP", NULL, "INT" #id "_CLK"}, \ + {"RX INT" #id "_1 INTERP", NULL, "DSMDEM" #id "_CLK"}, \ + {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_1 INTERP"} + +#define WCD934X_INTERPOLATOR_MIX2(id) \ + {"RX INT" #id " MIX2", NULL, "RX INT" #id " SEC MIX"}, \ + {"RX INT" #id " MIX2", NULL, "RX INT" #id " MIX2 INP"} + +#define WCD934X_SLIM_RX_AIF_PATH(id) \ + {"SLIM RX"#id" MUX", "AIF1_PB", "AIF1 PB"}, \ + {"SLIM RX"#id" MUX", "AIF2_PB", "AIF2 PB"}, \ + {"SLIM RX"#id" MUX", "AIF3_PB", "AIF3 PB"}, \ + {"SLIM RX"#id" MUX", "AIF4_PB", "AIF4 PB"}, \ + {"SLIM RX"#id, NULL, "SLIM RX"#id" MUX"} + +#define WCD934X_ADC_MUX(id) \ + {"ADC MUX" #id, "DMIC", "DMIC MUX" #id }, \ + {"ADC MUX" #id, "AMIC", "AMIC MUX" #id }, \ + {"DMIC MUX" #id, "DMIC0", "DMIC0"}, \ + {"DMIC MUX" #id, "DMIC1", "DMIC1"}, \ + {"DMIC MUX" #id, "DMIC2", "DMIC2"}, \ + {"DMIC MUX" #id, "DMIC3", "DMIC3"}, \ + {"DMIC MUX" #id, "DMIC4", "DMIC4"}, \ + {"DMIC MUX" #id, "DMIC5", "DMIC5"}, \ + {"AMIC MUX" #id, "ADC1", "ADC1"}, \ + {"AMIC MUX" #id, "ADC2", "ADC2"}, \ + {"AMIC MUX" #id, "ADC3", "ADC3"}, \ + {"AMIC MUX" #id, "ADC4", "ADC4"} + +#define WCD934X_IIR_INP_MUX(id) \ + {"IIR" #id, NULL, "IIR" #id " INP0 MUX"}, \ + {"IIR" #id " INP0 MUX", "DEC0", "ADC MUX0"}, \ + {"IIR" #id " INP0 MUX", "DEC1", "ADC MUX1"}, \ + {"IIR" #id " INP0 MUX", "DEC2", "ADC MUX2"}, \ + {"IIR" #id " INP0 MUX", "DEC3", "ADC MUX3"}, \ + {"IIR" #id " INP0 MUX", "DEC4", "ADC MUX4"}, \ + {"IIR" #id " INP0 MUX", "DEC5", "ADC MUX5"}, \ + {"IIR" #id " INP0 MUX", "DEC6", "ADC MUX6"}, \ + {"IIR" #id " INP0 MUX", "DEC7", "ADC MUX7"}, \ + {"IIR" #id " INP0 MUX", "DEC8", "ADC MUX8"}, \ + {"IIR" #id " INP0 MUX", "RX0", "SLIM RX0"}, \ + {"IIR" #id " INP0 MUX", "RX1", "SLIM RX1"}, \ + {"IIR" #id " INP0 MUX", "RX2", "SLIM RX2"}, \ + {"IIR" #id " INP0 MUX", "RX3", "SLIM RX3"}, \ + {"IIR" #id " INP0 MUX", "RX4", "SLIM RX4"}, \ + {"IIR" #id " INP0 MUX", "RX5", "SLIM RX5"}, \ + {"IIR" #id " INP0 MUX", "RX6", "SLIM RX6"}, \ + {"IIR" #id " INP0 MUX", "RX7", "SLIM RX7"}, \ + {"IIR" #id, NULL, "IIR" #id " INP1 MUX"}, \ + {"IIR" #id " INP1 MUX", "DEC0", "ADC MUX0"}, \ + {"IIR" #id " INP1 MUX", "DEC1", "ADC MUX1"}, \ + {"IIR" #id " INP1 MUX", "DEC2", "ADC MUX2"}, \ + {"IIR" #id " INP1 MUX", "DEC3", "ADC MUX3"}, \ + {"IIR" #id " INP1 MUX", "DEC4", "ADC MUX4"}, \ + {"IIR" #id " INP1 MUX", "DEC5", "ADC MUX5"}, \ + {"IIR" #id " INP1 MUX", "DEC6", "ADC MUX6"}, \ + {"IIR" #id " INP1 MUX", "DEC7", "ADC MUX7"}, \ + {"IIR" #id " INP1 MUX", "DEC8", "ADC MUX8"}, \ + {"IIR" #id " INP1 MUX", "RX0", "SLIM RX0"}, \ + {"IIR" #id " INP1 MUX", "RX1", "SLIM RX1"}, \ + {"IIR" #id " INP1 MUX", "RX2", "SLIM RX2"}, \ + {"IIR" #id " INP1 MUX", "RX3", "SLIM RX3"}, \ + {"IIR" #id " INP1 MUX", "RX4", "SLIM RX4"}, \ + {"IIR" #id " INP1 MUX", "RX5", "SLIM RX5"}, \ + {"IIR" #id " INP1 MUX", "RX6", "SLIM RX6"}, \ + {"IIR" #id " INP1 MUX", "RX7", "SLIM RX7"}, \ + {"IIR" #id, NULL, "IIR" #id " INP2 MUX"}, \ + {"IIR" #id " INP2 MUX", "DEC0", "ADC MUX0"}, \ + {"IIR" #id " INP2 MUX", "DEC1", "ADC MUX1"}, \ + {"IIR" #id " INP2 MUX", "DEC2", "ADC MUX2"}, \ + {"IIR" #id " INP2 MUX", "DEC3", "ADC MUX3"}, \ + {"IIR" #id " INP2 MUX", "DEC4", "ADC MUX4"}, \ + {"IIR" #id " INP2 MUX", "DEC5", "ADC MUX5"}, \ + {"IIR" #id " INP2 MUX", "DEC6", "ADC MUX6"}, \ + {"IIR" #id " INP2 MUX", "DEC7", "ADC MUX7"}, \ + {"IIR" #id " INP2 MUX", "DEC8", "ADC MUX8"}, \ + {"IIR" #id " INP2 MUX", "RX0", "SLIM RX0"}, \ + {"IIR" #id " INP2 MUX", "RX1", "SLIM RX1"}, \ + {"IIR" #id " INP2 MUX", "RX2", "SLIM RX2"}, \ + {"IIR" #id " INP2 MUX", "RX3", "SLIM RX3"}, \ + {"IIR" #id " INP2 MUX", "RX4", "SLIM RX4"}, \ + {"IIR" #id " INP2 MUX", "RX5", "SLIM RX5"}, \ + {"IIR" #id " INP2 MUX", "RX6", "SLIM RX6"}, \ + {"IIR" #id " INP2 MUX", "RX7", "SLIM RX7"}, \ + {"IIR" #id, NULL, "IIR" #id " INP3 MUX"}, \ + {"IIR" #id " INP3 MUX", "DEC0", "ADC MUX0"}, \ + {"IIR" #id " INP3 MUX", "DEC1", "ADC MUX1"}, \ + {"IIR" #id " INP3 MUX", "DEC2", "ADC MUX2"}, \ + {"IIR" #id " INP3 MUX", "DEC3", "ADC MUX3"}, \ + {"IIR" #id " INP3 MUX", "DEC4", "ADC MUX4"}, \ + {"IIR" #id " INP3 MUX", "DEC5", "ADC MUX5"}, \ + {"IIR" #id " INP3 MUX", "DEC6", "ADC MUX6"}, \ + {"IIR" #id " INP3 MUX", "DEC7", "ADC MUX7"}, \ + {"IIR" #id " INP3 MUX", "DEC8", "ADC MUX8"}, \ + {"IIR" #id " INP3 MUX", "RX0", "SLIM RX0"}, \ + {"IIR" #id " INP3 MUX", "RX1", "SLIM RX1"}, \ + {"IIR" #id " INP3 MUX", "RX2", "SLIM RX2"}, \ + {"IIR" #id " INP3 MUX", "RX3", "SLIM RX3"}, \ + {"IIR" #id " INP3 MUX", "RX4", "SLIM RX4"}, \ + {"IIR" #id " INP3 MUX", "RX5", "SLIM RX5"}, \ + {"IIR" #id " INP3 MUX", "RX6", "SLIM RX6"}, \ + {"IIR" #id " INP3 MUX", "RX7", "SLIM RX7"} + +#define WCD934X_SLIM_TX_AIF_PATH(id) \ + {"AIF1_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id }, \ + {"AIF2_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id }, \ + {"AIF3_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id }, \ + {"SLIM TX" #id, NULL, "CDC_IF TX" #id " MUX"} + +enum { + MIC_BIAS_1 = 1, + MIC_BIAS_2, + MIC_BIAS_3, + MIC_BIAS_4 +}; + +enum { + SIDO_SOURCE_INTERNAL, + SIDO_SOURCE_RCO_BG, +}; + +enum { + INTERP_EAR = 0, + INTERP_HPHL, + INTERP_HPHR, + INTERP_LO1, + INTERP_LO2, + INTERP_LO3_NA, /* LO3 not avalible in Tavil */ + INTERP_LO4_NA, + INTERP_SPKR1, /*INT7 WSA Speakers via soundwire */ + INTERP_SPKR2, /*INT8 WSA Speakers via soundwire */ + INTERP_MAX, +}; + +enum { + WCD934X_RX0 = 0, + WCD934X_RX1, + WCD934X_RX2, + WCD934X_RX3, + WCD934X_RX4, + WCD934X_RX5, + WCD934X_RX6, + WCD934X_RX7, + WCD934X_RX8, + WCD934X_RX9, + WCD934X_RX10, + WCD934X_RX11, + WCD934X_RX12, + WCD934X_RX_MAX, +}; + +enum { + WCD934X_TX0 = 0, + WCD934X_TX1, + WCD934X_TX2, + WCD934X_TX3, + WCD934X_TX4, + WCD934X_TX5, + WCD934X_TX6, + WCD934X_TX7, + WCD934X_TX8, + WCD934X_TX9, + WCD934X_TX10, + WCD934X_TX11, + WCD934X_TX12, + WCD934X_TX13, + WCD934X_TX14, + WCD934X_TX15, + WCD934X_TX_MAX, +}; + +struct wcd934x_slim_ch { + u32 ch_num; + u16 port; + u16 shift; + struct list_head list; +}; + +static const struct wcd934x_slim_ch wcd934x_tx_chs[WCD934X_TX_MAX] = { + WCD934X_SLIM_TX_CH(0), + WCD934X_SLIM_TX_CH(1), + WCD934X_SLIM_TX_CH(2), + WCD934X_SLIM_TX_CH(3), + WCD934X_SLIM_TX_CH(4), + WCD934X_SLIM_TX_CH(5), + WCD934X_SLIM_TX_CH(6), + WCD934X_SLIM_TX_CH(7), + WCD934X_SLIM_TX_CH(8), + WCD934X_SLIM_TX_CH(9), + WCD934X_SLIM_TX_CH(10), + WCD934X_SLIM_TX_CH(11), + WCD934X_SLIM_TX_CH(12), + WCD934X_SLIM_TX_CH(13), + WCD934X_SLIM_TX_CH(14), + WCD934X_SLIM_TX_CH(15), +}; + +static const struct wcd934x_slim_ch wcd934x_rx_chs[WCD934X_RX_MAX] = { + WCD934X_SLIM_RX_CH(0), /* 16 */ + WCD934X_SLIM_RX_CH(1), /* 17 */ + WCD934X_SLIM_RX_CH(2), + WCD934X_SLIM_RX_CH(3), + WCD934X_SLIM_RX_CH(4), + WCD934X_SLIM_RX_CH(5), + WCD934X_SLIM_RX_CH(6), + WCD934X_SLIM_RX_CH(7), + WCD934X_SLIM_RX_CH(8), + WCD934X_SLIM_RX_CH(9), + WCD934X_SLIM_RX_CH(10), + WCD934X_SLIM_RX_CH(11), + WCD934X_SLIM_RX_CH(12), +}; + +/* Codec supports 2 IIR filters */ +enum { + IIR0 = 0, + IIR1, + IIR_MAX, +}; + +/* Each IIR has 5 Filter Stages */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +enum { + COMPANDER_1, /* HPH_L */ + COMPANDER_2, /* HPH_R */ + COMPANDER_3, /* LO1_DIFF */ + COMPANDER_4, /* LO2_DIFF */ + COMPANDER_5, /* LO3_SE - not used in Tavil */ + COMPANDER_6, /* LO4_SE - not used in Tavil */ + COMPANDER_7, /* SWR SPK CH1 */ + COMPANDER_8, /* SWR SPK CH2 */ + COMPANDER_MAX, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_PB, + AIF2_CAP, + AIF3_PB, + AIF3_CAP, + AIF4_PB, + AIF4_VIFEED, + AIF4_MAD_TX, + NUM_CODEC_DAIS, +}; + +enum { + INTn_1_INP_SEL_ZERO = 0, + INTn_1_INP_SEL_DEC0, + INTn_1_INP_SEL_DEC1, + INTn_1_INP_SEL_IIR0, + INTn_1_INP_SEL_IIR1, + INTn_1_INP_SEL_RX0, + INTn_1_INP_SEL_RX1, + INTn_1_INP_SEL_RX2, + INTn_1_INP_SEL_RX3, + INTn_1_INP_SEL_RX4, + INTn_1_INP_SEL_RX5, + INTn_1_INP_SEL_RX6, + INTn_1_INP_SEL_RX7, +}; + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, + INTn_2_INP_SEL_RX4, + INTn_2_INP_SEL_RX5, + INTn_2_INP_SEL_RX6, + INTn_2_INP_SEL_RX7, + INTn_2_INP_SEL_PROXIMITY, +}; + +enum { + INTERP_MAIN_PATH, + INTERP_MIX_PATH, +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate sr_val_tbl[] = { + {8000, 0x0}, + {16000, 0x1}, + {32000, 0x3}, + {48000, 0x4}, + {96000, 0x5}, + {192000, 0x6}, + {384000, 0x7}, + {44100, 0x9}, + {88200, 0xA}, + {176400, 0xB}, + {352800, 0xC}, +}; + +struct wcd_slim_codec_dai_data { + struct list_head slim_ch_list; + struct slim_stream_config sconfig; + struct slim_stream_runtime *sruntime; +}; + +static const struct regmap_range_cfg wcd934x_ifc_ranges[] = { + { + .name = "WCD9335-IFC-DEV", + .range_min = 0x0, + .range_max = 0xffff, + .selector_reg = 0x800, + .selector_mask = 0xfff, + .selector_shift = 0, + .window_start = 0x800, + .window_len = 0x400, + }, +}; + +static struct regmap_config wcd934x_ifc_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xffff, + .ranges = wcd934x_ifc_ranges, + .num_ranges = ARRAY_SIZE(wcd934x_ifc_ranges), +}; + +struct wcd934x_codec { + struct device *dev; + struct clk_hw hw; + struct clk *extclk; + struct regmap *regmap; + struct regmap *if_regmap; + struct slim_device *sdev; + struct slim_device *sidev; + struct wcd_clsh_ctrl *clsh_ctrl; + struct snd_soc_component *component; + struct wcd934x_slim_ch rx_chs[WCD934X_RX_MAX]; + struct wcd934x_slim_ch tx_chs[WCD934X_TX_MAX]; + struct wcd_slim_codec_dai_data dai[NUM_CODEC_DAIS]; + int rate; + u32 version; + u32 hph_mode; + int num_rx_port; + int num_tx_port; + u32 tx_port_value[WCD934X_TX_MAX]; + u32 rx_port_value[WCD934X_RX_MAX]; + int sido_input_src; + int dmic_0_1_clk_cnt; + int dmic_2_3_clk_cnt; + int dmic_4_5_clk_cnt; + int dmic_sample_rate; + int comp_enabled[COMPANDER_MAX]; + int sysclk_users; + struct mutex sysclk_mutex; +}; + +#define to_wcd934x_codec(_hw) container_of(_hw, struct wcd934x_codec, hw) + +struct wcd_iir_filter_ctl { + unsigned int iir_idx; + unsigned int band_idx; + struct soc_bytes_ext bytes_ext; +}; + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); +static const DECLARE_TLV_DB_SCALE(ear_pa_gain, 0, 150, 0); + +/* Cutoff frequency for high pass filter */ +static const char * const cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" +}; + +static const char * const rx_cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ", + "CF_NEG_3DB_0P48HZ" +}; + +static const char * const rx_hph_mode_mux_text[] = { + "Class H Invalid", "Class-H Hi-Fi", "Class-H Low Power", "Class-AB", + "Class-H Hi-Fi Low Power" +}; + +static const char *const slim_rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB", +}; + +static const char * const rx_int0_7_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7", "PROXIMITY" +}; + +static const char * const rx_int_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7" +}; + +static const char * const rx_prim_mix_text[] = { + "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2", + "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_sidetone_mix_text[] = { + "ZERO", "SRC0", "SRC1", "SRC_SUM" +}; + +static const char * const iir_inp_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", + "DEC7", "DEC8", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_int_dem_inp_mux_text[] = { + "NORMAL_DSM_OUT", "CLSH_DSM_OUT", +}; + +static const char * const rx_int0_1_interp_mux_text[] = { + "ZERO", "RX INT0_1 MIX1", +}; + +static const char * const rx_int1_1_interp_mux_text[] = { + "ZERO", "RX INT1_1 MIX1", +}; + +static const char * const rx_int2_1_interp_mux_text[] = { + "ZERO", "RX INT2_1 MIX1", +}; + +static const char * const rx_int3_1_interp_mux_text[] = { + "ZERO", "RX INT3_1 MIX1", +}; + +static const char * const rx_int4_1_interp_mux_text[] = { + "ZERO", "RX INT4_1 MIX1", +}; + +static const char * const rx_int7_1_interp_mux_text[] = { + "ZERO", "RX INT7_1 MIX1", +}; + +static const char * const rx_int8_1_interp_mux_text[] = { + "ZERO", "RX INT8_1 MIX1", +}; + +static const char * const rx_int0_2_interp_mux_text[] = { + "ZERO", "RX INT0_2 MUX", +}; + +static const char * const rx_int1_2_interp_mux_text[] = { + "ZERO", "RX INT1_2 MUX", +}; + +static const char * const rx_int2_2_interp_mux_text[] = { + "ZERO", "RX INT2_2 MUX", +}; + +static const char * const rx_int3_2_interp_mux_text[] = { + "ZERO", "RX INT3_2 MUX", +}; + +static const char * const rx_int4_2_interp_mux_text[] = { + "ZERO", "RX INT4_2 MUX", +}; + +static const char * const rx_int7_2_interp_mux_text[] = { + "ZERO", "RX INT7_2 MUX", +}; + +static const char * const rx_int8_2_interp_mux_text[] = { + "ZERO", "RX INT8_2 MUX", +}; + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5" +}; + +static const char * const amic_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "ADC4" +}; + +static const char * const amic4_5_sel_text[] = { + "AMIC4", "AMIC5" +}; + +static const char * const adc_mux_text[] = { + "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2" +}; + +static const char * const cdc_if_tx0_mux_text[] = { + "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192" +}; + +static const char * const cdc_if_tx1_mux_text[] = { + "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192" +}; + +static const char * const cdc_if_tx2_mux_text[] = { + "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192" +}; + +static const char * const cdc_if_tx3_mux_text[] = { + "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192" +}; + +static const char * const cdc_if_tx4_mux_text[] = { + "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192" +}; + +static const char * const cdc_if_tx5_mux_text[] = { + "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192" +}; + +static const char * const cdc_if_tx6_mux_text[] = { + "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192" +}; + +static const char * const cdc_if_tx7_mux_text[] = { + "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192" +}; + +static const char * const cdc_if_tx8_mux_text[] = { + "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192" +}; + +static const char * const cdc_if_tx9_mux_text[] = { + "ZERO", "DEC7", "DEC7_192" +}; + +static const char * const cdc_if_tx10_mux_text[] = { + "ZERO", "DEC6", "DEC6_192" +}; + +static const char * const cdc_if_tx11_mux_text[] = { + "DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST" +}; + +static const char * const cdc_if_tx11_inp1_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", + "DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12" +}; + +static const char * const cdc_if_tx13_mux_text[] = { + "CDC_DEC_5", "MAD_BRDCST" +}; + +static const char * const cdc_if_tx13_inp1_mux_text[] = { + "ZERO", "DEC5", "DEC5_192" +}; + +static const struct soc_enum cf_dec0_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX0_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX1_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec2_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX2_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec3_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX3_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec4_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX4_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec5_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX5_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec6_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX6_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec7_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX7_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec8_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX8_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_int0_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX0_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD934X_CDC_RX0_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int1_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX1_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD934X_CDC_RX1_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int2_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX2_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD934X_CDC_RX2_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int3_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX3_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD934X_CDC_RX3_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int4_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX4_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD934X_CDC_RX4_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int7_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX7_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD934X_CDC_RX7_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int8_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX8_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const struct soc_enum slim_rx_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text); + +static const struct soc_enum rx_int0_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, 10, + rx_int0_7_mix_mux_text); + +static const struct soc_enum rx_int1_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int2_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int3_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int4_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int7_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, 10, + rx_int0_7_mix_mux_text); + +static const struct soc_enum rx_int8_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int0_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int1_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int2_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int3_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int4_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int7_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2, 4, + rx_sidetone_mix_text); + +static const struct soc_enum iir0_inp0_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir0_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir0_inp2_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir0_inp3_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir1_inp0_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir1_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir1_inp2_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir1_inp3_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum rx_int0_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX0_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int1_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX1_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int2_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX2_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum tx_adc_mux0_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux2_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux3_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux4_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 2, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux5_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 2, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux6_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 2, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux7_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 2, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux8_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 4, + ARRAY_SIZE(adc_mux_text), adc_mux_text); + +static const struct soc_enum rx_int0_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, + rx_int0_1_interp_mux_text); + +static const struct soc_enum rx_int1_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, + rx_int1_1_interp_mux_text); + +static const struct soc_enum rx_int2_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, + rx_int2_1_interp_mux_text); + +static const struct soc_enum rx_int3_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int3_1_interp_mux_text); + +static const struct soc_enum rx_int4_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int4_1_interp_mux_text); + +static const struct soc_enum rx_int7_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int7_1_interp_mux_text); + +static const struct soc_enum rx_int8_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int8_1_interp_mux_text); + +static const struct soc_enum rx_int0_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int0_2_interp_mux_text); + +static const struct soc_enum rx_int1_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int1_2_interp_mux_text); + +static const struct soc_enum rx_int2_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int2_2_interp_mux_text); + +static const struct soc_enum rx_int3_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int3_2_interp_mux_text); + +static const struct soc_enum rx_int4_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int4_2_interp_mux_text); + +static const struct soc_enum rx_int7_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int7_2_interp_mux_text); + +static const struct soc_enum rx_int8_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int8_2_interp_mux_text); + +static const struct soc_enum tx_dmic_mux0_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux2_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux3_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux4_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux5_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux6_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux7_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux8_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_amic_mux0_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux2_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux3_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux4_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux5_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux6_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux7_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux8_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, 5, + amic_mux_text); + +static const struct soc_enum tx_amic4_5_enum = + SOC_ENUM_SINGLE(WCD934X_TX_NEW_AMIC_4_5_SEL, 7, 2, amic4_5_sel_text); + +static const struct soc_enum cdc_if_tx0_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 0, + ARRAY_SIZE(cdc_if_tx0_mux_text), cdc_if_tx0_mux_text); +static const struct soc_enum cdc_if_tx1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 2, + ARRAY_SIZE(cdc_if_tx1_mux_text), cdc_if_tx1_mux_text); +static const struct soc_enum cdc_if_tx2_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 4, + ARRAY_SIZE(cdc_if_tx2_mux_text), cdc_if_tx2_mux_text); +static const struct soc_enum cdc_if_tx3_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 6, + ARRAY_SIZE(cdc_if_tx3_mux_text), cdc_if_tx3_mux_text); +static const struct soc_enum cdc_if_tx4_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 0, + ARRAY_SIZE(cdc_if_tx4_mux_text), cdc_if_tx4_mux_text); +static const struct soc_enum cdc_if_tx5_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 2, + ARRAY_SIZE(cdc_if_tx5_mux_text), cdc_if_tx5_mux_text); +static const struct soc_enum cdc_if_tx6_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 4, + ARRAY_SIZE(cdc_if_tx6_mux_text), cdc_if_tx6_mux_text); +static const struct soc_enum cdc_if_tx7_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 6, + ARRAY_SIZE(cdc_if_tx7_mux_text), cdc_if_tx7_mux_text); +static const struct soc_enum cdc_if_tx8_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 0, + ARRAY_SIZE(cdc_if_tx8_mux_text), cdc_if_tx8_mux_text); +static const struct soc_enum cdc_if_tx9_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 2, + ARRAY_SIZE(cdc_if_tx9_mux_text), cdc_if_tx9_mux_text); +static const struct soc_enum cdc_if_tx10_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 4, + ARRAY_SIZE(cdc_if_tx10_mux_text), cdc_if_tx10_mux_text); +static const struct soc_enum cdc_if_tx11_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 0, + ARRAY_SIZE(cdc_if_tx11_inp1_mux_text), + cdc_if_tx11_inp1_mux_text); +static const struct soc_enum cdc_if_tx11_mux_enum = + SOC_ENUM_SINGLE(WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0, + ARRAY_SIZE(cdc_if_tx11_mux_text), cdc_if_tx11_mux_text); +static const struct soc_enum cdc_if_tx13_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 4, + ARRAY_SIZE(cdc_if_tx13_inp1_mux_text), + cdc_if_tx13_inp1_mux_text); +static const struct soc_enum cdc_if_tx13_mux_enum = + SOC_ENUM_SINGLE(WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0, + ARRAY_SIZE(cdc_if_tx13_mux_text), cdc_if_tx13_mux_text); + +static int wcd934x_set_sido_input_src(struct wcd934x_codec *wcd, int sido_src) +{ + if (sido_src == wcd->sido_input_src) + return 0; + + if (sido_src == SIDO_SOURCE_INTERNAL) { + regmap_update_bits(wcd->regmap, WCD934X_ANA_BUCK_CTL, + WCD934X_ANA_BUCK_HI_ACCU_EN_MASK, 0); + usleep_range(100, 110); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BUCK_CTL, + WCD934X_ANA_BUCK_HI_ACCU_PRE_ENX_MASK, 0x0); + usleep_range(100, 110); + regmap_update_bits(wcd->regmap, WCD934X_ANA_RCO, + WCD934X_ANA_RCO_BG_EN_MASK, 0); + usleep_range(100, 110); + } else if (sido_src == SIDO_SOURCE_RCO_BG) { + regmap_update_bits(wcd->regmap, WCD934X_ANA_RCO, + WCD934X_ANA_RCO_BG_EN_MASK, + WCD934X_ANA_RCO_BG_ENABLE); + usleep_range(100, 110); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BUCK_CTL, + WCD934X_ANA_BUCK_PRE_EN1_MASK, + WCD934X_ANA_BUCK_PRE_EN1_ENABLE); + usleep_range(100, 110); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BUCK_CTL, + WCD934X_ANA_BUCK_PRE_EN2_MASK, + WCD934X_ANA_BUCK_PRE_EN2_ENABLE); + usleep_range(100, 110); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BUCK_CTL, + WCD934X_ANA_BUCK_HI_ACCU_EN_MASK, + WCD934X_ANA_BUCK_HI_ACCU_ENABLE); + usleep_range(100, 110); + } + wcd->sido_input_src = sido_src; + + return 0; +} + +static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) +{ + mutex_lock(&wcd->sysclk_mutex); + + if (++wcd->sysclk_users != 1) { + mutex_unlock(&wcd->sysclk_mutex); + return 0; + } + mutex_unlock(&wcd->sysclk_mutex); + + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_BIAS_EN_MASK, + WCD934X_ANA_BIAS_EN); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_PRECHRG_EN_MASK, + WCD934X_ANA_PRECHRG_EN); + /* + * 1ms delay is required after pre-charge is enabled + * as per HW requirement + */ + usleep_range(1000, 1100); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_PRECHRG_EN_MASK, 0); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_PRECHRG_MODE_MASK, 0); + + /* + * In data clock contrl register is changed + * to CLK_SYS_MCLK_PRG + */ + + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_EXT_CLK_BUF_EN_MASK, + WCD934X_EXT_CLK_BUF_EN); + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_EXT_CLK_DIV_RATIO_MASK, + WCD934X_EXT_CLK_DIV_BY_2); + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_MCLK_SRC_MASK, + WCD934X_MCLK_SRC_EXT_CLK); + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_MCLK_EN_MASK, WCD934X_MCLK_EN); + regmap_update_bits(wcd->regmap, + WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + WCD934X_CDC_FS_MCLK_CNT_EN_MASK, + WCD934X_CDC_FS_MCLK_CNT_ENABLE); + regmap_update_bits(wcd->regmap, + WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, + WCD934X_MCLK_EN_MASK, + WCD934X_MCLK_EN); + regmap_update_bits(wcd->regmap, WCD934X_CODEC_RPM_CLK_GATE, + WCD934X_CODEC_RPM_CLK_GATE_MASK, 0x0); + /* + * 10us sleep is required after clock is enabled + * as per HW requirement + */ + usleep_range(10, 15); + + wcd934x_set_sido_input_src(wcd, SIDO_SOURCE_RCO_BG); + + return 0; +} + +static int wcd934x_disable_ana_bias_and_syclk(struct wcd934x_codec *wcd) +{ + mutex_lock(&wcd->sysclk_mutex); + if (--wcd->sysclk_users != 0) { + mutex_unlock(&wcd->sysclk_mutex); + return 0; + } + mutex_unlock(&wcd->sysclk_mutex); + + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_EXT_CLK_BUF_EN_MASK | + WCD934X_MCLK_EN_MASK, 0x0); + wcd934x_set_sido_input_src(wcd, SIDO_SOURCE_INTERNAL); + + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_BIAS_EN_MASK, 0); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_PRECHRG_EN_MASK, 0); + + return 0; +} + +static int __wcd934x_cdc_mclk_enable(struct wcd934x_codec *wcd, bool enable) +{ + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(wcd->extclk); + + if (ret) { + dev_err(wcd->dev, "%s: ext clk enable failed\n", + __func__); + return ret; + } + ret = wcd934x_enable_ana_bias_and_sysclk(wcd); + } else { + int val; + + regmap_read(wcd->regmap, WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + &val); + + /* Don't disable clock if soundwire using it.*/ + if (val & WCD934X_CDC_SWR_CLK_EN_MASK) + return 0; + + wcd934x_disable_ana_bias_and_syclk(wcd); + clk_disable_unprepare(wcd->extclk); + } + + return ret; +} + +static int wcd934x_codec_enable_mclk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return __wcd934x_cdc_mclk_enable(wcd, true); + case SND_SOC_DAPM_POST_PMD: + return __wcd934x_cdc_mclk_enable(wcd, false); + } + + return 0; +} + +static int wcd934x_get_version(struct wcd934x_codec *wcd) +{ + int val1, val2, ver, ret; + struct regmap *regmap; + u16 id_minor; + u32 version_mask = 0; + + regmap = wcd->regmap; + ver = 0; + + ret = regmap_bulk_read(regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, + (u8 *)&id_minor, sizeof(u16)); + + if (ret) + return ret; + + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, &val1); + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, &val2); + + version_mask |= (!!((u8)val1 & 0x80)) << DSD_DISABLED_MASK; + version_mask |= (!!((u8)val2 & 0x01)) << SLNQ_DISABLED_MASK; + + switch (version_mask) { + case DSD_DISABLED | SLNQ_DISABLED: + if (id_minor == 0) + ver = WCD_VERSION_WCD9340_1_0; + else if (id_minor == 0x01) + ver = WCD_VERSION_WCD9340_1_1; + break; + case SLNQ_DISABLED: + if (id_minor == 0) + ver = WCD_VERSION_WCD9341_1_0; + else if (id_minor == 0x01) + ver = WCD_VERSION_WCD9341_1_1; + break; + } + + wcd->version = ver; + dev_info(wcd->dev, "WCD934X Minor:0x%x Version:0x%x\n", id_minor, ver); + + return 0; +} + +static void wcd934x_enable_efuse_sensing(struct wcd934x_codec *wcd) +{ + int rc, val; + + __wcd934x_cdc_mclk_enable(wcd, true); + + regmap_update_bits(wcd->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, + WCD934X_EFUSE_SENSE_STATE_MASK, + WCD934X_EFUSE_SENSE_STATE_DEF); + regmap_update_bits(wcd->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, + WCD934X_EFUSE_SENSE_EN_MASK, + WCD934X_EFUSE_SENSE_ENABLE); + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + wcd934x_set_sido_input_src(wcd, SIDO_SOURCE_RCO_BG); + + rc = regmap_read(wcd->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS, &val); + if (rc || (!(val & 0x01))) + WARN(1, "%s: Efuse sense is not complete val=%x, ret=%d\n", + __func__, val, rc); + + __wcd934x_cdc_mclk_enable(wcd, false); +} + +static int wcd934x_swrm_clock(struct wcd934x_codec *wcd, bool enable) +{ + if (enable) { + __wcd934x_cdc_mclk_enable(wcd, true); + regmap_update_bits(wcd->regmap, + WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + WCD934X_CDC_SWR_CLK_EN_MASK, + WCD934X_CDC_SWR_CLK_ENABLE); + } else { + regmap_update_bits(wcd->regmap, + WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + WCD934X_CDC_SWR_CLK_EN_MASK, 0); + __wcd934x_cdc_mclk_enable(wcd, false); + } + + return 0; +} + +static int wcd934x_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_val, u32 rate) +{ + struct snd_soc_component *comp = dai->component; + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + struct wcd934x_slim_ch *ch; + u8 cfg0, cfg1, inp0_sel, inp1_sel, inp2_sel; + int inp, j; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + inp = ch->shift + INTn_1_INP_SEL_RX0; + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the slim rx port + * is connected + */ + for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) { + /* Interpolators 5 and 6 are not aviliable in Tavil */ + if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) + continue; + + cfg0 = snd_soc_component_read32(comp, + WCD934X_CDC_RX_INP_MUX_RX_INT_CFG0(j)); + cfg1 = snd_soc_component_read32(comp, + WCD934X_CDC_RX_INP_MUX_RX_INT_CFG1(j)); + + inp0_sel = cfg0 & + WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + inp1_sel = (cfg0 >> 4) & + WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + inp2_sel = (cfg1 >> 4) & + WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + + if ((inp0_sel == inp) || (inp1_sel == inp) || + (inp2_sel == inp)) { + /* rate is in Hz */ + /* + * Ear and speaker primary path does not support + * native sample rates + */ + if ((j == INTERP_EAR || j == INTERP_SPKR1 || + j == INTERP_SPKR2) && rate == 44100) + dev_err(wcd->dev, + "Cannot set 44.1KHz on INT%d\n", + j); + else + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX_PATH_CTL(j), + WCD934X_CDC_MIX_PCM_RATE_MASK, + rate_val); + } + } + } + + return 0; +} + +static int wcd934x_set_mix_interpolator_rate(struct snd_soc_dai *dai, + int rate_val, u32 rate) +{ + struct snd_soc_component *component = dai->component; + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + struct wcd934x_slim_ch *ch; + int val, j; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) { + /* Interpolators 5 and 6 are not aviliable in Tavil */ + if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) + continue; + val = snd_soc_component_read32(component, + WCD934X_CDC_RX_INP_MUX_RX_INT_CFG1(j)) & + WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + + if (val == (ch->shift + INTn_2_INP_SEL_RX0)) { + /* + * Ear mix path supports only 48, 96, 192, + * 384KHz only + */ + if ((j == INTERP_EAR) && + (rate_val < 0x4 || + rate_val > 0x7)) { + dev_err(component->dev, + "Invalid rate for AIF_PB DAI(%d)\n", + dai->id); + return -EINVAL; + } + + snd_soc_component_update_bits(component, + WCD934X_CDC_RX_PATH_MIX_CTL(j), + WCD934X_CDC_MIX_PCM_RATE_MASK, + rate_val); + } + } + } + + return 0; +} + +static int wcd934x_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + int rate_val = 0; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++) { + if (sample_rate == sr_val_tbl[i].sample_rate) { + rate_val = sr_val_tbl[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(sr_val_tbl)) || (rate_val < 0)) { + dev_err(dai->dev, "Unsupported sample rate: %d\n", sample_rate); + return -EINVAL; + } + + ret = wcd934x_set_prim_interpolator_rate(dai, (u8)rate_val, + sample_rate); + if (ret) + return ret; + ret = wcd934x_set_mix_interpolator_rate(dai, (u8)rate_val, + sample_rate); + if (ret) + return ret; + + return ret; +} + +static int wcd934x_set_decimator_rate(struct snd_soc_dai *dai, + u8 rate_val, u32 rate) +{ + struct snd_soc_component *comp = dai->component; + struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp); + u8 shift = 0, shift_val = 0, tx_mux_sel; + struct wcd934x_slim_ch *ch; + int tx_port, tx_port_reg; + int decimator = -1; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + tx_port = ch->port; + /* Find the SB TX MUX input - which decimator is connected */ + switch (tx_port) { + case 0 ... 3: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0; + shift = (tx_port << 1); + shift_val = 0x03; + break; + case 4 ... 7: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1; + shift = ((tx_port - 4) << 1); + shift_val = 0x03; + break; + case 8 ... 10: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2; + shift = ((tx_port - 8) << 1); + shift_val = 0x03; + break; + case 11: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 0; + shift_val = 0x0F; + break; + case 13: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 4; + shift_val = 0x03; + break; + default: + dev_err(wcd->dev, "Invalid SLIM TX%u port DAI ID:%d\n", + tx_port, dai->id); + return -EINVAL; + } + + tx_mux_sel = snd_soc_component_read32(comp, tx_port_reg) & + (shift_val << shift); + + tx_mux_sel = tx_mux_sel >> shift; + switch (tx_port) { + case 0 ... 8: + if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3)) + decimator = tx_port; + break; + case 9 ... 10: + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = ((tx_port == 9) ? 7 : 6); + break; + case 11: + if ((tx_mux_sel >= 1) && (tx_mux_sel < 7)) + decimator = tx_mux_sel - 1; + break; + case 13: + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = 5; + break; + default: + dev_err(wcd->dev, "ERROR: Invalid tx_port: %d\n", + tx_port); + return -EINVAL; + } + + snd_soc_component_update_bits(comp, + WCD934X_CDC_TX_PATH_CTL(decimator), + WCD934X_CDC_TX_PATH_CTL_PCM_RATE_MASK, + rate_val); + } + + return 0; +} + +static int wcd934x_slim_set_hw_params(struct wcd934x_codec *wcd, + struct wcd_slim_codec_dai_data *dai_data, + int direction) +{ + struct list_head *slim_ch_list = &dai_data->slim_ch_list; + struct slim_stream_config *cfg = &dai_data->sconfig; + struct wcd934x_slim_ch *ch; + u16 payload = 0; + int ret, i; + + cfg->ch_count = 0; + cfg->direction = direction; + cfg->port_mask = 0; + + /* Configure slave interface device */ + list_for_each_entry(ch, slim_ch_list, list) { + cfg->ch_count++; + payload |= 1 << ch->shift; + cfg->port_mask |= BIT(ch->port); + } + + cfg->chs = kcalloc(cfg->ch_count, sizeof(unsigned int), GFP_KERNEL); + if (!cfg->chs) + return -ENOMEM; + + i = 0; + list_for_each_entry(ch, slim_ch_list, list) { + cfg->chs[i++] = ch->ch_num; + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + /* write to interface device */ + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_RX_PORT_MULTI_CHNL_0(ch->port), + payload); + + if (ret < 0) + goto err; + + /* configure the slave port for water mark and enable*/ + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_RX_PORT_CFG(ch->port), + WCD934X_SLIM_WATER_MARK_VAL); + if (ret < 0) + goto err; + } else { + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_TX_PORT_MULTI_CHNL_0(ch->port), + payload & 0x00FF); + if (ret < 0) + goto err; + + /* ports 8,9 */ + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_TX_PORT_MULTI_CHNL_1(ch->port), + (payload & 0xFF00) >> 8); + if (ret < 0) + goto err; + + /* configure the slave port for water mark and enable*/ + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_TX_PORT_CFG(ch->port), + WCD934X_SLIM_WATER_MARK_VAL); + + if (ret < 0) + goto err; + } + } + + dai_data->sruntime = slim_stream_allocate(wcd->sdev, "WCD934x-SLIM"); + + return 0; + +err: + dev_err(wcd->dev, "Error Setting slim hw params\n"); + kfree(cfg->chs); + cfg->chs = NULL; + + return ret; +} + +static int wcd934x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wcd934x_codec *wcd; + int ret, tx_fs_rate = 0; + + wcd = snd_soc_component_get_drvdata(dai->component); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = wcd934x_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(wcd->dev, "cannot set sample rate: %u\n", + params_rate(params)); + return ret; + } + switch (params_width(params)) { + case 16 ... 24: + wcd->dai[dai->id].sconfig.bps = params_width(params); + break; + default: + dev_err(wcd->dev, "Invalid format 0x%x\n", + params_width(params)); + return -EINVAL; + } + break; + + case SNDRV_PCM_STREAM_CAPTURE: + switch (params_rate(params)) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + case 384000: + tx_fs_rate = 7; + break; + default: + dev_err(wcd->dev, "Invalid TX sample rate: %d\n", + params_rate(params)); + return -EINVAL; + + }; + + ret = wcd934x_set_decimator_rate(dai, tx_fs_rate, + params_rate(params)); + if (ret < 0) { + dev_err(wcd->dev, "Cannot set TX Decimator rate\n"); + return ret; + } + switch (params_width(params)) { + case 16 ... 32: + wcd->dai[dai->id].sconfig.bps = params_width(params); + break; + default: + dev_err(wcd->dev, "Invalid format 0x%x\n", + params_width(params)); + return -EINVAL; + }; + break; + default: + dev_err(wcd->dev, "Invalid stream type %d\n", + substream->stream); + return -EINVAL; + }; + + wcd->dai[dai->id].sconfig.rate = params_rate(params); + wcd934x_slim_set_hw_params(wcd, &wcd->dai[dai->id], substream->stream); + + return 0; +} + +static int wcd934x_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wcd_slim_codec_dai_data *dai_data; + struct wcd934x_codec *wcd; + + wcd = snd_soc_component_get_drvdata(dai->component); + + dai_data = &wcd->dai[dai->id]; + + kfree(dai_data->sconfig.chs); + + return 0; +} + +static int wcd934x_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct wcd_slim_codec_dai_data *dai_data; + struct wcd934x_codec *wcd; + struct slim_stream_config *cfg; + + wcd = snd_soc_component_get_drvdata(dai->component); + + dai_data = &wcd->dai[dai->id]; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + cfg = &dai_data->sconfig; + slim_stream_prepare(dai_data->sruntime, cfg); + slim_stream_enable(dai_data->sruntime); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + slim_stream_unprepare(dai_data->sruntime); + slim_stream_disable(dai_data->sruntime); + break; + default: + break; + } + + return 0; +} + +static int wcd934x_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct wcd934x_codec *wcd; + int i; + + wcd = snd_soc_component_get_drvdata(dai->component); + + if (!tx_slot || !rx_slot) { + dev_err(wcd->dev, "Invalid tx_slot=%p, rx_slot=%p\n", + tx_slot, rx_slot); + return -EINVAL; + } + + if (wcd->rx_chs) { + wcd->num_rx_port = rx_num; + for (i = 0; i < rx_num; i++) { + wcd->rx_chs[i].ch_num = rx_slot[i]; + INIT_LIST_HEAD(&wcd->rx_chs[i].list); + } + } + + if (wcd->tx_chs) { + wcd->num_tx_port = tx_num; + for (i = 0; i < tx_num; i++) { + wcd->tx_chs[i].ch_num = tx_slot[i]; + INIT_LIST_HEAD(&wcd->tx_chs[i].list); + } + } + + return 0; +} + +static int wcd934x_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct wcd934x_slim_ch *ch; + struct wcd934x_codec *wcd; + int i = 0; + + wcd = snd_soc_component_get_drvdata(dai->component); + + switch (dai->id) { + case AIF1_PB: + case AIF2_PB: + case AIF3_PB: + case AIF4_PB: + if (!rx_slot || !rx_num) { + dev_err(wcd->dev, "Invalid rx_slot %p or rx_num %p\n", + rx_slot, rx_num); + return -EINVAL; + } + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) + rx_slot[i++] = ch->ch_num; + + *rx_num = i; + break; + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + if (!tx_slot || !tx_num) { + dev_err(wcd->dev, "Invalid tx_slot %p or tx_num %p\n", + tx_slot, tx_num); + return -EINVAL; + } + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) + tx_slot[i++] = ch->ch_num; + + *tx_num = i; + break; + default: + dev_err(wcd->dev, "Invalid DAI ID %x\n", dai->id); + break; + } + + return 0; +} + +static struct snd_soc_dai_ops wcd934x_dai_ops = { + .hw_params = wcd934x_hw_params, + .hw_free = wcd934x_hw_free, + .trigger = wcd934x_trigger, + .set_channel_map = wcd934x_set_channel_map, + .get_channel_map = wcd934x_get_channel_map, +}; + +static struct snd_soc_dai_driver wcd934x_slim_dais[] = { + [0] = { + .name = "wcd934x_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd934x_dai_ops, + }, + [1] = { + .name = "wcd934x_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD934X_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd934x_dai_ops, + }, + [2] = { + .name = "wcd934x_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd934x_dai_ops, + }, + [3] = { + .name = "wcd934x_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD934X_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd934x_dai_ops, + }, + [4] = { + .name = "wcd934x_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd934x_dai_ops, + }, + [5] = { + .name = "wcd934x_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD934X_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd934x_dai_ops, + }, + [6] = { + .name = "wcd934x_rx4", + .id = AIF4_PB, + .playback = { + .stream_name = "AIF4 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd934x_dai_ops, + }, +}; + +static int swclk_gate_enable(struct clk_hw *hw) +{ + return wcd934x_swrm_clock(to_wcd934x_codec(hw), true); +} + +static void swclk_gate_disable(struct clk_hw *hw) +{ + wcd934x_swrm_clock(to_wcd934x_codec(hw), false); +} + +static int swclk_gate_is_enabled(struct clk_hw *hw) +{ + struct wcd934x_codec *wcd = to_wcd934x_codec(hw); + int ret, val; + + regmap_read(wcd->regmap, WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, &val); + ret = val & WCD934X_CDC_SWR_CLK_EN_MASK; + + return ret; +} + +static unsigned long swclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return parent_rate / 2; +} + +static const struct clk_ops swclk_gate_ops = { + .prepare = swclk_gate_enable, + .unprepare = swclk_gate_disable, + .is_enabled = swclk_gate_is_enabled, + .recalc_rate = swclk_recalc_rate, + +}; + +static struct clk *wcd934x_register_mclk_output(struct wcd934x_codec *wcd) +{ + struct clk *parent = wcd->extclk; + struct device *dev = wcd->dev; + struct device_node *np = dev->parent->of_node; + const char *parent_clk_name = NULL; + const char *clk_name = "mclk"; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (of_property_read_u32(np, "clock-frequency", &wcd->rate)) + return NULL; + + parent_clk_name = __clk_get_name(parent); + + of_property_read_string(np, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = &swclk_gate_ops; + init.flags = 0; + init.parent_names = &parent_clk_name; + init.num_parents = 1; + wcd->hw.init = &init; + + hw = &wcd->hw; + ret = clk_hw_register(wcd->dev->parent, hw); + if (ret) + return ERR_PTR(ret); + + of_clk_add_provider(np, of_clk_src_simple_get, hw->clk); + + return NULL; +} + +static int wcd934x_get_micbias_val(struct device *dev, const char *micbias) +{ + int mv; + + if (of_property_read_u32(dev->parent->of_node, micbias, &mv)) { + dev_err(dev, "%s value not found, using default\n", micbias); + mv = WCD934X_DEF_MICBIAS_MV; + } else { + /* convert it to milli volts */ + mv = mv/1000; + } + + if (mv < 1000 || mv > 2850) { + dev_err(dev, "%s value not in valid range, using default\n", + micbias); + mv = WCD934X_DEF_MICBIAS_MV; + } + + return (mv - 1000) / 50; +} + +static int wcd934x_init_dmic(struct snd_soc_component *comp) +{ + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + u32 def_dmic_rate, dmic_clk_drv; + + vout_ctl_1 = wcd934x_get_micbias_val(comp->dev, + "qcom,micbias1-microvolt"); + vout_ctl_2 = wcd934x_get_micbias_val(comp->dev, + "qcom,micbias2-microvolt"); + vout_ctl_3 = wcd934x_get_micbias_val(comp->dev, + "qcom,micbias3-microvolt"); + vout_ctl_4 = wcd934x_get_micbias_val(comp->dev, + "qcom,micbias4-microvolt"); + + snd_soc_component_update_bits(comp, WCD934X_ANA_MICB1, + WCD934X_MICB_VAL_MASK, vout_ctl_1); + snd_soc_component_update_bits(comp, WCD934X_ANA_MICB2, + WCD934X_MICB_VAL_MASK, vout_ctl_2); + snd_soc_component_update_bits(comp, WCD934X_ANA_MICB3, + WCD934X_MICB_VAL_MASK, vout_ctl_3); + snd_soc_component_update_bits(comp, WCD934X_ANA_MICB4, + WCD934X_MICB_VAL_MASK, vout_ctl_4); + + if (wcd->rate == WCD934X_MCLK_CLK_9P6MHZ) + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + else + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ; + + wcd->dmic_sample_rate = def_dmic_rate; + + dmic_clk_drv = 0; + snd_soc_component_update_bits(comp, WCD934X_TEST_DEBUG_PAD_DRVCTL_0, + 0x0C, dmic_clk_drv << 2); + + return 0; +} + +static void wcd934x_hw_init(struct wcd934x_codec *wcd) +{ + struct regmap *rm = wcd->regmap; + + /* set SPKR rate to FS_2P4_3P072 */ + regmap_update_bits(rm, WCD934X_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08); + regmap_update_bits(rm, WCD934X_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08); + + /* Take DMICs out of reset */ + regmap_update_bits(rm, WCD934X_CPE_SS_DMIC_CFG, 0x80, 0x00); +} + +static int wcd934x_comp_init(struct snd_soc_component *component) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + + wcd934x_hw_init(wcd); + wcd934x_enable_efuse_sensing(wcd); + wcd934x_get_version(wcd); + + return 0; +} + +static irqreturn_t wcd934x_slim_irq_handler(int irq, void *data) +{ + struct wcd934x_codec *wcd = data; + unsigned long status = 0; + int i, j, port_id; + unsigned int val, int_val = 0; + irqreturn_t ret = IRQ_NONE; + bool tx; + unsigned short reg = 0; + + for (i = WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0; + i <= WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) { + regmap_read(wcd->if_regmap, i, &val); + status |= ((u32)val << (8 * j)); + } + + for_each_set_bit(j, &status, 32) { + tx = false; + port_id = j; + + if (j >= 16) { + tx = true; + port_id = j - 16; + } + + regmap_read(wcd->if_regmap, + WCD934X_SLIM_PGD_PORT_INT_RX_SOURCE0 + j, &val); + if (val) { + if (!tx) + reg = WCD934X_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + regmap_read(wcd->if_regmap, reg, &int_val); + } + + if (val & WCD934X_SLIM_IRQ_OVERFLOW) + dev_err_ratelimited(wcd->dev, + "overflow error on %s port %d, value %x\n", + (tx ? "TX" : "RX"), port_id, val); + + if (val & WCD934X_SLIM_IRQ_UNDERFLOW) + dev_err_ratelimited(wcd->dev, + "underflow error on %s port %d, value %x\n", + (tx ? "TX" : "RX"), port_id, val); + + if ((val & WCD934X_SLIM_IRQ_OVERFLOW) || + (val & WCD934X_SLIM_IRQ_UNDERFLOW)) { + if (!tx) + reg = WCD934X_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + regmap_read( + wcd->if_regmap, reg, &int_val); + if (int_val & (1 << (port_id % 8))) { + int_val = int_val ^ (1 << (port_id % 8)); + regmap_write(wcd->if_regmap, + reg, int_val); + } + } + + if (val & WCD934X_SLIM_IRQ_PORT_CLOSED) + dev_err_ratelimited(wcd->dev, + "Port Closed %s port %d, value %x\n", + (tx ? "TX" : "RX"), port_id, val); + + regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0 + (j / 8), + BIT(j % 8)); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int wcd934x_comp_probe(struct snd_soc_component *component) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + int i; + + snd_soc_component_init_regmap(component, wcd->regmap); + wcd->component = component; + + /* Class-H Init*/ + wcd->clsh_ctrl = wcd_clsh_ctrl_alloc(component, wcd->version); + if (IS_ERR(wcd->clsh_ctrl)) + return PTR_ERR(wcd->clsh_ctrl); + + /* Default HPH Mode to Class-H Low HiFi */ + wcd->hph_mode = CLS_H_LOHIFI; + + wcd934x_comp_init(component); + + for (i = 0; i < NUM_CODEC_DAIS; i++) + INIT_LIST_HEAD(&wcd->dai[i].slim_ch_list); + + wcd934x_init_dmic(component); + return 0; +} + +static void wcd934x_comp_remove(struct snd_soc_component *comp) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + + wcd_clsh_ctrl_free(wcd->clsh_ctrl); +} + +static int wcd934x_comp_set_sysclk(struct snd_soc_component *comp, + int clk_id, int source, + unsigned int freq, int dir) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int val = WCD934X_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ; + + wcd->rate = freq; + + if (wcd->rate == WCD934X_MCLK_CLK_12P288MHZ) + val = WCD934X_CODEC_RPM_CLK_MCLK_CFG_12P288MHZ; + + snd_soc_component_update_bits(comp, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + WCD934X_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK, + val); + + return clk_set_rate(wcd->extclk, freq); +} + +static uint32_t get_iir_band_coeff(struct snd_soc_component *component, + int iir_idx, int band_idx, int coeff_idx) +{ + u32 value = 0; + int reg, b2_reg; + + /* Address does not automatically update if reading */ + reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx; + b2_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx; + + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) * + sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_component_read32(component, b2_reg); + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_component_read32(component, b2_reg) << 8); + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_component_read32(component, b2_reg) << 16); + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= (snd_soc_component_read32(component, b2_reg) << 24); + return value; +} + +static void set_iir_band_coeff(struct snd_soc_component *component, + int iir_idx, int band_idx, uint32_t value) +{ + int reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx; + + snd_soc_component_write(component, reg, (value & 0xFF)); + snd_soc_component_write(component, reg, (value >> 8) & 0xFF); + snd_soc_component_write(component, reg, (value >> 16) & 0xFF); + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_component_write(component, reg, (value >> 24) & 0x3F); +} + +static int wcd934x_put_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + int iir_idx = ctl->iir_idx; + int band_idx = ctl->band_idx; + u32 coeff[BAND_MAX]; + int reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx; + + memcpy(&coeff[0], ucontrol->value.bytes.data, params->max); + + /* Mask top bit it is reserved */ + /* Updates addr automatically for each B2 write */ + snd_soc_component_write(component, reg, (band_idx * BAND_MAX * + sizeof(uint32_t)) & 0x7F); + + set_iir_band_coeff(component, iir_idx, band_idx, coeff[0]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[1]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[2]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[3]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[4]); + + return 0; +} + +static int wcd934x_get_iir_band_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + int iir_idx = ctl->iir_idx; + int band_idx = ctl->band_idx; + u32 coeff[BAND_MAX]; + + coeff[0] = get_iir_band_coeff(component, iir_idx, band_idx, 0); + coeff[1] = get_iir_band_coeff(component, iir_idx, band_idx, 1); + coeff[2] = get_iir_band_coeff(component, iir_idx, band_idx, 2); + coeff[3] = get_iir_band_coeff(component, iir_idx, band_idx, 3); + coeff[4] = get_iir_band_coeff(component, iir_idx, band_idx, 4); + + memcpy(ucontrol->value.bytes.data, &coeff[0], params->max); + + return 0; +} + +static int wcd934x_iir_filter_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; + ucontrol->count = params->max; + + return 0; +} + +static int wcd934x_compander_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + int comp = ((struct soc_mixer_control *)kc->private_value)->shift; + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + + ucontrol->value.integer.value[0] = wcd->comp_enabled[comp]; + + return 0; +} + +static int wcd934x_compander_set(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + int comp = ((struct soc_mixer_control *)kc->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + int sel; + + wcd->comp_enabled[comp] = value; + sel = value ? WCD934X_HPH_GAIN_SRC_SEL_COMPANDER : + WCD934X_HPH_GAIN_SRC_SEL_REGISTER; + + /* Any specific register configuration for compander */ + switch (comp) { + case COMPANDER_1: + /* Set Gain Source Select based on compander enable/disable */ + snd_soc_component_update_bits(component, WCD934X_HPH_L_EN, + WCD934X_HPH_GAIN_SRC_SEL_MASK, + sel); + break; + case COMPANDER_2: + snd_soc_component_update_bits(component, WCD934X_HPH_R_EN, + WCD934X_HPH_GAIN_SRC_SEL_MASK, + sel); + break; + case COMPANDER_3: + case COMPANDER_4: + case COMPANDER_7: + case COMPANDER_8: + break; + default: + break; + }; + + return 0; +} + +static int wcd934x_rx_hph_mode_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + + ucontrol->value.enumerated.item[0] = wcd->hph_mode; + + return 0; +} + +static int wcd934x_rx_hph_mode_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + if (mode_val == 0) { + dev_err(wcd->dev, "Invalid HPH Mode, default to ClSH HiFi\n"); + mode_val = CLS_H_LOHIFI; + } + wcd->hph_mode = mode_val; + + return 0; +} + +static int slim_rx_mux_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(dapm->dev); + + ucontrol->value.enumerated.item[0] = wcd->rx_port_value[w->shift]; + + return 0; +} + +static int slim_rx_mux_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(w->dapm->dev); + struct soc_enum *e = (struct soc_enum *)kc->private_value; + struct snd_soc_dapm_update *update = NULL; + u32 port_id = w->shift; + + if (wcd->rx_port_value[port_id] == ucontrol->value.enumerated.item[0]) + return 0; + + wcd->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + + switch (wcd->rx_port_value[port_id]) { + case 0: + list_del_init(&wcd->rx_chs[port_id].list); + break; + case 1: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF1_PB].slim_ch_list); + break; + case 2: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF2_PB].slim_ch_list); + break; + case 3: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF3_PB].slim_ch_list); + break; + case 4: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF4_PB].slim_ch_list); + break; + default: + dev_err(wcd->dev, "Unknown AIF %d\n", + wcd->rx_port_value[port_id]); + goto err; + } + + snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value[port_id], + e, update); + + return 0; +err: + return -EINVAL; +} + +static int wcd934x_int_dem_inp_mux_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kc->private_value; + struct snd_soc_component *component; + int reg, val, ret; + + component = snd_soc_dapm_kcontrol_component(kc); + val = ucontrol->value.enumerated.item[0]; + if (e->reg == WCD934X_CDC_RX0_RX_PATH_SEC0) + reg = WCD934X_CDC_RX0_RX_PATH_CFG0; + else if (e->reg == WCD934X_CDC_RX1_RX_PATH_SEC0) + reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + else if (e->reg == WCD934X_CDC_RX2_RX_PATH_SEC0) + reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + else + return -EINVAL; + + /* Set Look Ahead Delay */ + if (val) + snd_soc_component_update_bits(component, reg, + WCD934X_RX_DLY_ZN_EN_MASK, + WCD934X_RX_DLY_ZN_ENABLE); + else + snd_soc_component_update_bits(component, reg, + WCD934X_RX_DLY_ZN_EN_MASK, + WCD934X_RX_DLY_ZN_DISABLE); + + ret = snd_soc_dapm_put_enum_double(kc, ucontrol); + + return ret; +} + +static int wcd934x_dec_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + u16 mic_sel_reg = 0; + u8 mic_sel; + + comp = snd_soc_dapm_kcontrol_component(kcontrol); + + val = ucontrol->value.enumerated.item[0]; + if (val > e->items - 1) + return -EINVAL; + + switch (e->reg) { + case WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX0_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX4_TX_PATH_CFG0; + else if (e->shift_l == 4) + mic_sel_reg = WCD934X_CDC_TX8_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX1_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX5_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX2_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX6_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX3_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX7_TX_PATH_CFG0; + break; + default: + dev_err(comp->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + + /* ADC: 0, DMIC: 1 */ + mic_sel = val ? 0x0 : 0x1; + if (mic_sel_reg) + snd_soc_component_update_bits(comp, mic_sel_reg, BIT(7), + mic_sel << 7); + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static const struct snd_kcontrol_new rx_int0_2_mux = + SOC_DAPM_ENUM("RX INT0_2 MUX Mux", rx_int0_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int1_2_mux = + SOC_DAPM_ENUM("RX INT1_2 MUX Mux", rx_int1_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int2_2_mux = + SOC_DAPM_ENUM("RX INT2_2 MUX Mux", rx_int2_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int3_2_mux = + SOC_DAPM_ENUM("RX INT3_2 MUX Mux", rx_int3_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int4_2_mux = + SOC_DAPM_ENUM("RX INT4_2 MUX Mux", rx_int4_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int7_2_mux = + SOC_DAPM_ENUM("RX INT7_2 MUX Mux", rx_int7_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int8_2_mux = + SOC_DAPM_ENUM("RX INT8_2 MUX Mux", rx_int8_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP0 Mux", rx_int0_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP1 Mux", rx_int0_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP2 Mux", rx_int0_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP0 Mux", rx_int1_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP1 Mux", rx_int1_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP2 Mux", rx_int1_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP0 Mux", rx_int2_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP1 Mux", rx_int2_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP2 Mux", rx_int2_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP0 Mux", rx_int3_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP1 Mux", rx_int3_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP2 Mux", rx_int3_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP0 Mux", rx_int4_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP1 Mux", rx_int4_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP2 Mux", rx_int4_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP0 Mux", rx_int7_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP1 Mux", rx_int7_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP2 Mux", rx_int7_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP0 Mux", rx_int8_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP1 Mux", rx_int8_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP2 Mux", rx_int8_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int0_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT0 MIX2 INP Mux", rx_int0_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int1_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT1 MIX2 INP Mux", rx_int1_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int2_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT2 MIX2 INP Mux", rx_int2_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int3_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT3 MIX2 INP Mux", rx_int3_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int4_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT4 MIX2 INP Mux", rx_int4_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int7_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT7 MIX2 INP Mux", rx_int7_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new iir0_inp0_mux = + SOC_DAPM_ENUM("IIR0 INP0 Mux", iir0_inp0_mux_enum); +static const struct snd_kcontrol_new iir0_inp1_mux = + SOC_DAPM_ENUM("IIR0 INP1 Mux", iir0_inp1_mux_enum); +static const struct snd_kcontrol_new iir0_inp2_mux = + SOC_DAPM_ENUM("IIR0 INP2 Mux", iir0_inp2_mux_enum); +static const struct snd_kcontrol_new iir0_inp3_mux = + SOC_DAPM_ENUM("IIR0 INP3 Mux", iir0_inp3_mux_enum); + +static const struct snd_kcontrol_new iir1_inp0_mux = + SOC_DAPM_ENUM("IIR1 INP0 Mux", iir1_inp0_mux_enum); +static const struct snd_kcontrol_new iir1_inp1_mux = + SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum); +static const struct snd_kcontrol_new iir1_inp2_mux = + SOC_DAPM_ENUM("IIR1 INP2 Mux", iir1_inp2_mux_enum); +static const struct snd_kcontrol_new iir1_inp3_mux = + SOC_DAPM_ENUM("IIR1 INP3 Mux", iir1_inp3_mux_enum); + +static const struct snd_kcontrol_new slim_rx_mux[WCD934X_RX_MAX] = { + SOC_DAPM_ENUM_EXT("SLIM RX0 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), +}; + +static const struct snd_kcontrol_new rx_int1_asrc_switch[] = { + SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int2_asrc_switch[] = { + SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int3_asrc_switch[] = { + SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int4_asrc_switch[] = { + SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int0_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT0 DEM MUX Mux", rx_int0_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + wcd934x_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int1_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT1 DEM MUX Mux", rx_int1_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + wcd934x_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int2_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT2 DEM MUX Mux", rx_int2_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + wcd934x_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int0_1_interp_mux = + SOC_DAPM_ENUM("RX INT0_1 INTERP Mux", rx_int0_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int1_1_interp_mux = + SOC_DAPM_ENUM("RX INT1_1 INTERP Mux", rx_int1_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int2_1_interp_mux = + SOC_DAPM_ENUM("RX INT2_1 INTERP Mux", rx_int2_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int3_1_interp_mux = + SOC_DAPM_ENUM("RX INT3_1 INTERP Mux", rx_int3_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int4_1_interp_mux = + SOC_DAPM_ENUM("RX INT4_1 INTERP Mux", rx_int4_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int7_1_interp_mux = + SOC_DAPM_ENUM("RX INT7_1 INTERP Mux", rx_int7_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int8_1_interp_mux = + SOC_DAPM_ENUM("RX INT8_1 INTERP Mux", rx_int8_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int0_2_interp_mux = + SOC_DAPM_ENUM("RX INT0_2 INTERP Mux", rx_int0_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int1_2_interp_mux = + SOC_DAPM_ENUM("RX INT1_2 INTERP Mux", rx_int1_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int2_2_interp_mux = + SOC_DAPM_ENUM("RX INT2_2 INTERP Mux", rx_int2_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int3_2_interp_mux = + SOC_DAPM_ENUM("RX INT3_2 INTERP Mux", rx_int3_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int4_2_interp_mux = + SOC_DAPM_ENUM("RX INT4_2 INTERP Mux", rx_int4_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int7_2_interp_mux = + SOC_DAPM_ENUM("RX INT7_2 INTERP Mux", rx_int7_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int8_2_interp_mux = + SOC_DAPM_ENUM("RX INT8_2 INTERP Mux", rx_int8_2_interp_mux_enum); + +static const struct snd_kcontrol_new tx_dmic_mux0 = + SOC_DAPM_ENUM("DMIC MUX0 Mux", tx_dmic_mux0_enum); + +static const struct snd_kcontrol_new tx_dmic_mux1 = + SOC_DAPM_ENUM("DMIC MUX1 Mux", tx_dmic_mux1_enum); + +static const struct snd_kcontrol_new tx_dmic_mux2 = + SOC_DAPM_ENUM("DMIC MUX2 Mux", tx_dmic_mux2_enum); + +static const struct snd_kcontrol_new tx_dmic_mux3 = + SOC_DAPM_ENUM("DMIC MUX3 Mux", tx_dmic_mux3_enum); + +static const struct snd_kcontrol_new tx_dmic_mux4 = + SOC_DAPM_ENUM("DMIC MUX4 Mux", tx_dmic_mux4_enum); + +static const struct snd_kcontrol_new tx_dmic_mux5 = + SOC_DAPM_ENUM("DMIC MUX5 Mux", tx_dmic_mux5_enum); + +static const struct snd_kcontrol_new tx_dmic_mux6 = + SOC_DAPM_ENUM("DMIC MUX6 Mux", tx_dmic_mux6_enum); + +static const struct snd_kcontrol_new tx_dmic_mux7 = + SOC_DAPM_ENUM("DMIC MUX7 Mux", tx_dmic_mux7_enum); + +static const struct snd_kcontrol_new tx_dmic_mux8 = + SOC_DAPM_ENUM("DMIC MUX8 Mux", tx_dmic_mux8_enum); + +static const struct snd_kcontrol_new tx_amic_mux0 = + SOC_DAPM_ENUM("AMIC MUX0 Mux", tx_amic_mux0_enum); + +static const struct snd_kcontrol_new tx_amic_mux1 = + SOC_DAPM_ENUM("AMIC MUX1 Mux", tx_amic_mux1_enum); + +static const struct snd_kcontrol_new tx_amic_mux2 = + SOC_DAPM_ENUM("AMIC MUX2 Mux", tx_amic_mux2_enum); + +static const struct snd_kcontrol_new tx_amic_mux3 = + SOC_DAPM_ENUM("AMIC MUX3 Mux", tx_amic_mux3_enum); + +static const struct snd_kcontrol_new tx_amic_mux4 = + SOC_DAPM_ENUM("AMIC MUX4 Mux", tx_amic_mux4_enum); + +static const struct snd_kcontrol_new tx_amic_mux5 = + SOC_DAPM_ENUM("AMIC MUX5 Mux", tx_amic_mux5_enum); + +static const struct snd_kcontrol_new tx_amic_mux6 = + SOC_DAPM_ENUM("AMIC MUX6 Mux", tx_amic_mux6_enum); + +static const struct snd_kcontrol_new tx_amic_mux7 = + SOC_DAPM_ENUM("AMIC MUX7 Mux", tx_amic_mux7_enum); + +static const struct snd_kcontrol_new tx_amic_mux8 = + SOC_DAPM_ENUM("AMIC MUX8 Mux", tx_amic_mux8_enum); + +static const struct snd_kcontrol_new tx_amic4_5 = + SOC_DAPM_ENUM("AMIC4_5 SEL Mux", tx_amic4_5_enum); + +static const struct snd_kcontrol_new tx_adc_mux0_mux = + SOC_DAPM_ENUM_EXT("ADC MUX0 Mux", tx_adc_mux0_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux1_mux = + SOC_DAPM_ENUM_EXT("ADC MUX1 Mux", tx_adc_mux1_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux2_mux = + SOC_DAPM_ENUM_EXT("ADC MUX2 Mux", tx_adc_mux2_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux3_mux = + SOC_DAPM_ENUM_EXT("ADC MUX3 Mux", tx_adc_mux3_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux4_mux = + SOC_DAPM_ENUM_EXT("ADC MUX4 Mux", tx_adc_mux4_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux5_mux = + SOC_DAPM_ENUM_EXT("ADC MUX5 Mux", tx_adc_mux5_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux6_mux = + SOC_DAPM_ENUM_EXT("ADC MUX6 Mux", tx_adc_mux6_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux7_mux = + SOC_DAPM_ENUM_EXT("ADC MUX7 Mux", tx_adc_mux7_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux8_mux = + SOC_DAPM_ENUM_EXT("ADC MUX8 Mux", tx_adc_mux8_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); + +static const struct snd_kcontrol_new cdc_if_tx0_mux = + SOC_DAPM_ENUM("CDC_IF TX0 MUX Mux", cdc_if_tx0_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx1_mux = + SOC_DAPM_ENUM("CDC_IF TX1 MUX Mux", cdc_if_tx1_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx2_mux = + SOC_DAPM_ENUM("CDC_IF TX2 MUX Mux", cdc_if_tx2_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx3_mux = + SOC_DAPM_ENUM("CDC_IF TX3 MUX Mux", cdc_if_tx3_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx4_mux = + SOC_DAPM_ENUM("CDC_IF TX4 MUX Mux", cdc_if_tx4_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx5_mux = + SOC_DAPM_ENUM("CDC_IF TX5 MUX Mux", cdc_if_tx5_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx6_mux = + SOC_DAPM_ENUM("CDC_IF TX6 MUX Mux", cdc_if_tx6_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx7_mux = + SOC_DAPM_ENUM("CDC_IF TX7 MUX Mux", cdc_if_tx7_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx8_mux = + SOC_DAPM_ENUM("CDC_IF TX8 MUX Mux", cdc_if_tx8_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx9_mux = + SOC_DAPM_ENUM("CDC_IF TX9 MUX Mux", cdc_if_tx9_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx10_mux = + SOC_DAPM_ENUM("CDC_IF TX10 MUX Mux", cdc_if_tx10_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx11_mux = + SOC_DAPM_ENUM("CDC_IF TX11 MUX Mux", cdc_if_tx11_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx11_inp1_mux = + SOC_DAPM_ENUM("CDC_IF TX11 INP1 MUX Mux", cdc_if_tx11_inp1_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx13_mux = + SOC_DAPM_ENUM("CDC_IF TX13 MUX Mux", cdc_if_tx13_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx13_inp1_mux = + SOC_DAPM_ENUM("CDC_IF TX13 INP1 MUX Mux", cdc_if_tx13_inp1_mux_enum); + +static int slim_tx_mixer_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(dapm->dev); + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kc->private_value; + int port_id = mixer->shift; + + ucontrol->value.integer.value[0] = wcd->tx_port_value[port_id]; + + return 0; +} + +static int slim_tx_mixer_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(widget->dapm->dev); + struct snd_soc_dapm_update *update = NULL; + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kc->private_value; + int enable = ucontrol->value.integer.value[0]; + int dai_id = widget->shift; + int port_id = mixer->shift; + + /* only add to the list if value not set */ + if (enable == wcd->tx_port_value[port_id]) + return 0; + + wcd->tx_port_value[port_id] = enable; + + if (enable) + list_add_tail(&wcd->tx_chs[port_id].list, + &wcd->dai[dai_id].slim_ch_list); + else + list_del_init(&wcd->tx_chs[port_id].list); + + snd_soc_dapm_mixer_update_power(widget->dapm, kc, enable, update); + + return 0; +} + +static const struct snd_kcontrol_new aif1_slim_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_slim_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_slim_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new wcd934x_snd_controls[] = { + /* Gain Controls */ + SOC_SINGLE_TLV("EAR PA Volume", WCD934X_ANA_EAR, 4, 4, 1, ear_pa_gain), + SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 24, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 24, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT1 Volume", WCD934X_DIFF_LO_LO1_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT2 Volume", WCD934X_DIFF_LO_LO2_COMPANDER, + 3, 16, 1, line_gain), + + SOC_SINGLE_TLV("ADC1 Volume", WCD934X_ANA_AMIC1, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD934X_ANA_AMIC2, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD934X_ANA_AMIC3, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD934X_ANA_AMIC4, 0, 20, 0, analog_gain), + + SOC_SINGLE_S8_TLV("RX0 Digital Volume", WCD934X_CDC_RX0_RX_VOL_CTL, + -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_S8_TLV("RX1 Digital Volume", WCD934X_CDC_RX1_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX2 Digital Volume", WCD934X_CDC_RX2_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX3 Digital Volume", WCD934X_CDC_RX3_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX4 Digital Volume", WCD934X_CDC_RX4_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX7 Digital Volume", WCD934X_CDC_RX7_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX8 Digital Volume", WCD934X_CDC_RX8_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX0 Mix Digital Volume", + WCD934X_CDC_RX0_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX1 Mix Digital Volume", + WCD934X_CDC_RX1_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX2 Mix Digital Volume", + WCD934X_CDC_RX2_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX3 Mix Digital Volume", + WCD934X_CDC_RX3_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX4 Mix Digital Volume", + WCD934X_CDC_RX4_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX7 Mix Digital Volume", + WCD934X_CDC_RX7_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX8 Mix Digital Volume", + WCD934X_CDC_RX8_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + + SOC_SINGLE_S8_TLV("DEC0 Volume", WCD934X_CDC_TX0_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC1 Volume", WCD934X_CDC_TX1_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC2 Volume", WCD934X_CDC_TX2_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC3 Volume", WCD934X_CDC_TX3_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC4 Volume", WCD934X_CDC_TX4_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC5 Volume", WCD934X_CDC_TX5_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC6 Volume", WCD934X_CDC_TX6_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC7 Volume", WCD934X_CDC_TX7_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC8 Volume", WCD934X_CDC_TX8_TX_VOL_CTL, + -84, 40, digital_gain), + + SOC_SINGLE_S8_TLV("IIR0 INP0 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR0 INP1 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR0 INP2 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR0 INP3 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP0 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP1 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP2 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP3 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, -84, 40, + digital_gain), + + SOC_ENUM("TX0 HPF cut off", cf_dec0_enum), + SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), + SOC_ENUM("TX3 HPF cut off", cf_dec3_enum), + SOC_ENUM("TX4 HPF cut off", cf_dec4_enum), + SOC_ENUM("TX5 HPF cut off", cf_dec5_enum), + SOC_ENUM("TX6 HPF cut off", cf_dec6_enum), + SOC_ENUM("TX7 HPF cut off", cf_dec7_enum), + SOC_ENUM("TX8 HPF cut off", cf_dec8_enum), + + SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum), + SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum), + SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum), + SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum), + SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum), + SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum), + SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum), + SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum), + SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum), + SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum), + SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum), + SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum), + SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum), + SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum), + + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + wcd934x_rx_hph_mode_get, wcd934x_rx_hph_mode_put), + + SOC_SINGLE("IIR1 Band1 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 0, 1, 0), + SOC_SINGLE("IIR1 Band2 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 1, 1, 0), + SOC_SINGLE("IIR1 Band3 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 2, 1, 0), + SOC_SINGLE("IIR1 Band4 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 3, 1, 0), + SOC_SINGLE("IIR1 Band5 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 4, 1, 0), + SOC_SINGLE("IIR2 Band1 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 0, 1, 0), + SOC_SINGLE("IIR2 Band2 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 1, 1, 0), + SOC_SINGLE("IIR2 Band3 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 2, 1, 0), + SOC_SINGLE("IIR2 Band4 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 3, 1, 0), + SOC_SINGLE("IIR2 Band5 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 4, 1, 0), + WCD_IIR_FILTER_CTL("IIR0 Band1", IIR0, BAND1), + WCD_IIR_FILTER_CTL("IIR0 Band2", IIR0, BAND2), + WCD_IIR_FILTER_CTL("IIR0 Band3", IIR0, BAND3), + WCD_IIR_FILTER_CTL("IIR0 Band4", IIR0, BAND4), + WCD_IIR_FILTER_CTL("IIR0 Band5", IIR0, BAND5), + + WCD_IIR_FILTER_CTL("IIR1 Band1", IIR1, BAND1), + WCD_IIR_FILTER_CTL("IIR1 Band2", IIR1, BAND2), + WCD_IIR_FILTER_CTL("IIR1 Band3", IIR1, BAND3), + WCD_IIR_FILTER_CTL("IIR1 Band4", IIR1, BAND4), + WCD_IIR_FILTER_CTL("IIR1 Band5", IIR1, BAND5), + + SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), +}; + +static void wcd934x_codec_enable_int_port(struct wcd_slim_codec_dai_data *dai, + struct snd_soc_component *component) +{ + int port_num = 0; + unsigned short reg = 0; + unsigned int val = 0; + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + struct wcd934x_slim_ch *ch; + + list_for_each_entry(ch, &dai->slim_ch_list, list) { + if (ch->port >= WCD934X_RX_START) { + port_num = ch->port - WCD934X_RX_START; + reg = WCD934X_SLIM_PGD_PORT_INT_EN0 + (port_num / 8); + } else { + port_num = ch->port; + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8); + } + + regmap_read(wcd->if_regmap, reg, &val); + if (!(val & BIT(port_num % 8))) + regmap_write(wcd->if_regmap, reg, + val | BIT(port_num % 8)); + } +} + +static int wcd934x_codec_enable_slim(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp); + struct wcd_slim_codec_dai_data *dai = &wcd->dai[w->shift]; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wcd934x_codec_enable_int_port(dai, comp); + break; + } + + return 0; +} + +static void wcd934x_codec_hd2_control(struct snd_soc_component *component, + u16 interp_idx, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + + switch (interp_idx) { + case INTERP_HPHL: + hd2_scale_reg = WCD934X_CDC_RX1_RX_PATH_SEC3; + hd2_enable_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + break; + case INTERP_HPHR: + hd2_scale_reg = WCD934X_CDC_RX2_RX_PATH_SEC3; + hd2_enable_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + break; + default: + return; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(component, hd2_scale_reg, + WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_MASK, + WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_0P3125); + snd_soc_component_update_bits(component, hd2_enable_reg, + WCD934X_CDC_RX_PATH_CFG_HD2_EN_MASK, + WCD934X_CDC_RX_PATH_CFG_HD2_ENABLE); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, hd2_enable_reg, + WCD934X_CDC_RX_PATH_CFG_HD2_EN_MASK, + WCD934X_CDC_RX_PATH_CFG_HD2_DISABLE); + snd_soc_component_update_bits(component, hd2_scale_reg, + WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_MASK, + WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_0P0000); + } +} + +static void wcd934x_codec_hphdelay_lutbypass(struct snd_soc_component *comp, + u16 interp_idx, int event) +{ + u8 hph_dly_mask; + u16 hph_lut_bypass_reg = 0; + u16 hph_comp_ctrl7 = 0; + + switch (interp_idx) { + case INTERP_HPHL: + hph_dly_mask = 1; + hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHL_COMP_LUT; + hph_comp_ctrl7 = WCD934X_CDC_COMPANDER1_CTL7; + break; + case INTERP_HPHR: + hph_dly_mask = 2; + hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHR_COMP_LUT; + hph_comp_ctrl7 = WCD934X_CDC_COMPANDER2_CTL7; + break; + default: + return; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(comp, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, 0x0); + snd_soc_component_update_bits(comp, hph_lut_bypass_reg, + WCD934X_HPH_LUT_BYPASS_MASK, + WCD934X_HPH_LUT_BYPASS_ENABLE); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(comp, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, hph_dly_mask); + snd_soc_component_update_bits(comp, hph_lut_bypass_reg, + WCD934X_HPH_LUT_BYPASS_MASK, + WCD934X_HPH_LUT_BYPASS_DISABLE); + } +} + +static int wcd934x_config_compander(struct snd_soc_component *comp, + int interp_n, int event) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int compander; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + /* EAR does not have compander */ + if (!interp_n) + return 0; + + compander = interp_n - 1; + if (!wcd->comp_enabled[compander]) + return 0; + + comp_ctl0_reg = WCD934X_CDC_COMPANDER1_CTL0 + (compander * 8); + rx_path_cfg0_reg = WCD934X_CDC_RX1_RX_PATH_CFG0 + (compander * 20); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable Compander Clock */ + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_CLK_EN_MASK, + WCD934X_COMP_CLK_ENABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, + WCD934X_COMP_SOFT_RST_ENABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, + WCD934X_COMP_SOFT_RST_DISABLE); + snd_soc_component_update_bits(comp, rx_path_cfg0_reg, + WCD934X_HPH_CMP_EN_MASK, + WCD934X_HPH_CMP_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(comp, rx_path_cfg0_reg, + WCD934X_HPH_CMP_EN_MASK, + WCD934X_HPH_CMP_DISABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_HALT_MASK, + WCD934X_COMP_HALT); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, + WCD934X_COMP_SOFT_RST_ENABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, + WCD934X_COMP_SOFT_RST_DISABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_CLK_EN_MASK, 0x0); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, 0x0); + break; + } + + return 0; +} + +static int wcd934x_codec_enable_interp_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int interp_idx = w->shift; + u16 main_reg = WCD934X_CDC_RX0_RX_PATH_CTL + (interp_idx * 20); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Clk enable */ + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_CLK_EN_MASK, + WCD934X_RX_CLK_ENABLE); + wcd934x_codec_hd2_control(comp, interp_idx, event); + wcd934x_codec_hphdelay_lutbypass(comp, interp_idx, event); + wcd934x_config_compander(comp, interp_idx, event); + break; + case SND_SOC_DAPM_POST_PMD: + wcd934x_config_compander(comp, interp_idx, event); + wcd934x_codec_hphdelay_lutbypass(comp, interp_idx, event); + wcd934x_codec_hd2_control(comp, interp_idx, event); + /* Clk Disable */ + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_CLK_EN_MASK, 0); + /* Reset enable and disable */ + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_RESET_MASK, + WCD934X_RX_RESET_ENABLE); + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_RESET_MASK, + WCD934X_RX_RESET_DISABLE); + /* Reset rate to 48K*/ + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_PCM_RATE_MASK, + WCD934X_RX_PCM_RATE_F_48K); + break; + } + + return 0; +} + +static int wcd934x_codec_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int offset_val = 0; + u16 gain_reg, mix_reg; + int val = 0; + + gain_reg = WCD934X_CDC_RX0_RX_VOL_MIX_CTL + + (w->shift * WCD934X_RX_PATH_CTL_OFFSET); + mix_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL + + (w->shift * WCD934X_RX_PATH_CTL_OFFSET); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Clk enable */ + snd_soc_component_update_bits(comp, mix_reg, + WCD934X_CDC_RX_MIX_CLK_EN_MASK, + WCD934X_CDC_RX_MIX_CLK_ENABLE); + break; + + case SND_SOC_DAPM_POST_PMU: + val = snd_soc_component_read32(comp, gain_reg); + val += offset_val; + snd_soc_component_write(comp, gain_reg, val); + break; + }; + + return 0; +} + +static int wcd934x_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int reg = w->reg; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* B1 GAIN */ + snd_soc_component_write(comp, reg, + snd_soc_component_read32(comp, reg)); + /* B2 GAIN */ + reg++; + snd_soc_component_write(comp, reg, + snd_soc_component_read32(comp, reg)); + /* B3 GAIN */ + reg++; + snd_soc_component_write(comp, reg, + snd_soc_component_read32(comp, reg)); + /* B4 GAIN */ + reg++; + snd_soc_component_write(comp, reg, + snd_soc_component_read32(comp, reg)); + /* B5 GAIN */ + reg++; + snd_soc_component_write(comp, reg, + snd_soc_component_read32(comp, reg)); + break; + default: + break; + } + return 0; +} + +static int wcd934x_codec_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + u16 gain_reg; + + gain_reg = WCD934X_CDC_RX0_RX_VOL_CTL + (w->shift * + WCD934X_RX_PATH_CTL_OFFSET); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write(comp, gain_reg, + snd_soc_component_read32(comp, gain_reg)); + break; + }; + + return 0; +} + +static int wcd934x_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disable AutoChop timer during power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, 0x0); + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, CLS_H_NORMAL); + + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, CLS_H_NORMAL); + break; + }; + + return 0; +} + +static int wcd934x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + u8 dem_inp; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Read DEM INP Select */ + dem_inp = snd_soc_component_read32(comp, + WCD934X_CDC_RX1_RX_PATH_SEC0) & 0x03; + + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + return -EINVAL; + } + if (hph_mode != CLS_H_LP) + /* Ripple freq control enable */ + snd_soc_component_update_bits(comp, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, + WCD934X_SIDO_RIPPLE_FREQ_ENABLE); + /* Disable AutoChop timer during power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, 0x0); + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, hph_mode); + + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, hph_mode); + if (hph_mode != CLS_H_LP) + /* Ripple freq control disable */ + snd_soc_component_update_bits(comp, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, 0x0); + + break; + default: + break; + }; + + return 0; +} + +static int wcd934x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + u8 dem_inp; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dem_inp = snd_soc_component_read32(comp, + WCD934X_CDC_RX2_RX_PATH_SEC0) & 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + return -EINVAL; + } + if (hph_mode != CLS_H_LP) + /* Ripple freq control enable */ + snd_soc_component_update_bits(comp, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, + WCD934X_SIDO_RIPPLE_FREQ_ENABLE); + /* Disable AutoChop timer during power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, 0x0); + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + hph_mode); + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, hph_mode); + if (hph_mode != CLS_H_LP) + /* Ripple freq control disable */ + snd_soc_component_update_bits(comp, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, 0x0); + break; + default: + break; + }; + + return 0; +} + +static int wcd934x_codec_lineout_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_LO, CLS_AB); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_LO, CLS_AB); + break; + } + + return 0; +} + +static int wcd934x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is needed. + */ + usleep_range(20000, 20100); + + snd_soc_component_update_bits(comp, WCD934X_HPH_L_TEST, + WCD934X_HPH_OCP_DET_MASK, + WCD934X_HPH_OCP_DET_ENABLE); + /* Remove Mute on primary path */ + snd_soc_component_update_bits(comp, WCD934X_CDC_RX1_RX_PATH_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + 0); + /* Enable GM3 boost */ + snd_soc_component_update_bits(comp, WCD934X_HPH_CNP_WG_CTL, + WCD934X_HPH_GM3_BOOST_EN_MASK, + WCD934X_HPH_GM3_BOOST_ENABLE); + /* Enable AutoChop timer at the end of power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, + WCD934X_HPH_AUTOCHOP_TIMER_ENABLE); + /* Remove mix path mute */ + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX1_RX_PATH_MIX_CTL, + WCD934X_CDC_RX_PGA_MUTE_EN_MASK, 0x00); + break; + case SND_SOC_DAPM_PRE_PMD: + /* Enable DSD Mute before PA disable */ + snd_soc_component_update_bits(comp, WCD934X_HPH_L_TEST, + WCD934X_HPH_OCP_DET_MASK, + WCD934X_HPH_OCP_DET_DISABLE); + snd_soc_component_update_bits(comp, WCD934X_CDC_RX1_RX_PATH_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + WCD934X_RX_PATH_PGA_MUTE_ENABLE); + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX1_RX_PATH_MIX_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + WCD934X_RX_PATH_PGA_MUTE_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA disable. If compander is + * disabled, then 20ms delay is needed after PA disable. + */ + usleep_range(20000, 20100); + break; + }; + + return 0; +} + +static int wcd934x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is needed. + */ + usleep_range(20000, 20100); + snd_soc_component_update_bits(comp, WCD934X_HPH_R_TEST, + WCD934X_HPH_OCP_DET_MASK, + WCD934X_HPH_OCP_DET_ENABLE); + /* Remove mute */ + snd_soc_component_update_bits(comp, WCD934X_CDC_RX2_RX_PATH_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + 0); + /* Enable GM3 boost */ + snd_soc_component_update_bits(comp, WCD934X_HPH_CNP_WG_CTL, + WCD934X_HPH_GM3_BOOST_EN_MASK, + WCD934X_HPH_GM3_BOOST_ENABLE); + /* Enable AutoChop timer at the end of power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, + WCD934X_HPH_AUTOCHOP_TIMER_ENABLE); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_component_read32(comp, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL)) & 0x10) + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL, + WCD934X_CDC_RX_PGA_MUTE_EN_MASK, + WCD934X_CDC_RX_PGA_MUTE_DISABLE); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(comp, WCD934X_HPH_R_TEST, + WCD934X_HPH_OCP_DET_MASK, + WCD934X_HPH_OCP_DET_DISABLE); + snd_soc_component_update_bits(comp, WCD934X_CDC_RX2_RX_PATH_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + WCD934X_RX_PATH_PGA_MUTE_ENABLE); + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL, + WCD934X_CDC_RX_PGA_MUTE_EN_MASK, + WCD934X_CDC_RX_PGA_MUTE_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA disable. If compander is + * disabled, then 20ms delay is needed after PA disable. + */ + usleep_range(20000, 20100); + break; + }; + + return 0; +} + +static u32 wcd934x_get_dmic_sample_rate(struct snd_soc_component *comp, + unsigned int dmic, + struct wcd934x_codec *wcd) +{ + u8 tx_stream_fs; + u8 adc_mux_index = 0, adc_mux_sel = 0; + bool dec_found = false; + u16 adc_mux_ctl_reg, tx_fs_reg; + u32 dmic_fs; + + while (dec_found == 0 && adc_mux_index < WCD934X_MAX_VALID_ADC_MUX) { + if (adc_mux_index < 4) { + adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + (adc_mux_index * 2); + } else if (adc_mux_index < WCD934X_INVALID_ADC_MUX) { + adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_index - 4; + } else if (adc_mux_index == WCD934X_INVALID_ADC_MUX) { + ++adc_mux_index; + continue; + } + adc_mux_sel = ((snd_soc_component_read32(comp, adc_mux_ctl_reg) + & 0xF8) >> 3) - 1; + + if (adc_mux_sel == dmic) { + dec_found = true; + break; + } + + ++adc_mux_index; + } + + if (dec_found && adc_mux_index <= 8) { + tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index); + tx_stream_fs = snd_soc_component_read32(comp, tx_fs_reg) & 0x0F; + if (tx_stream_fs <= 4) { + if (wcd->dmic_sample_rate <= + WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ) + dmic_fs = wcd->dmic_sample_rate; + else + dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ; + } else + dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + } else { + dmic_fs = wcd->dmic_sample_rate; + } + + return dmic_fs; +} + +static u8 wcd934x_get_dmic_clk_val(struct snd_soc_component *comp, + u32 mclk_rate, u32 dmic_clk_rate) +{ + u32 div_factor; + u8 dmic_ctl_val; + + /* Default value to return in case of error */ + if (mclk_rate == WCD934X_MCLK_CLK_9P6MHZ) + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2; + else + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3; + + if (dmic_clk_rate == 0) { + dev_err(comp->dev, + "%s: dmic_sample_rate cannot be 0\n", + __func__); + goto done; + } + + div_factor = mclk_rate / dmic_clk_rate; + switch (div_factor) { + case 2: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2; + break; + case 3: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3; + break; + case 4: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_4; + break; + case 6: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_6; + break; + case 8: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_8; + break; + case 16: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_16; + break; + default: + dev_err(comp->dev, + "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n", + __func__, div_factor, mclk_rate, dmic_clk_rate); + break; + } + +done: + return dmic_ctl_val; +} + +static int wcd934x_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + u8 dmic_clk_en = 0x01; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + u8 dmic_rate_val, dmic_rate_shift = 1; + unsigned int dmic; + u32 dmic_sample_rate; + int ret; + char *wname; + + wname = strpbrk(w->name, "012345"); + if (!wname) { + dev_err(comp->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(comp->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &wcd->dmic_0_1_clk_cnt; + dmic_clk_reg = WCD934X_CPE_SS_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &wcd->dmic_2_3_clk_cnt; + dmic_clk_reg = WCD934X_CPE_SS_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &wcd->dmic_4_5_clk_cnt; + dmic_clk_reg = WCD934X_CPE_SS_DMIC2_CTL; + break; + default: + dev_err(comp->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + }; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dmic_sample_rate = wcd934x_get_dmic_sample_rate(comp, dmic, + wcd); + dmic_rate_val = wcd934x_get_dmic_clk_val(comp, wcd->rate, + dmic_sample_rate); + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + dmic_rate_val = dmic_rate_val << dmic_rate_shift; + snd_soc_component_update_bits(comp, dmic_clk_reg, + WCD934X_DMIC_RATE_MASK, + dmic_rate_val); + snd_soc_component_update_bits(comp, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + + break; + case SND_SOC_DAPM_POST_PMD: + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) + snd_soc_component_update_bits(comp, dmic_clk_reg, + dmic_clk_en, 0); + break; + }; + + return 0; +} + +static int wcd934x_codec_find_amic_input(struct snd_soc_component *comp, + int adc_mux_n) +{ + u16 mask, shift, adc_mux_in_reg; + u16 amic_mux_sel_reg; + bool is_amic; + + if (adc_mux_n < 0 || adc_mux_n > WCD934X_MAX_VALID_ADC_MUX || + adc_mux_n == WCD934X_INVALID_ADC_MUX) + return 0; + + if (adc_mux_n < 3) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + adc_mux_n; + mask = 0x03; + shift = 0; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + } else if (adc_mux_n < 4) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x03; + shift = 0; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + } else if (adc_mux_n < 7) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + (adc_mux_n - 4); + mask = 0x0C; + shift = 2; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 8) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x0C; + shift = 2; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 12) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + ((adc_mux_n == 8) ? (adc_mux_n - 8) : + (adc_mux_n - 9)); + mask = 0x30; + shift = 4; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 13) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x30; + shift = 4; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1; + mask = 0xC0; + shift = 6; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } + + is_amic = (((snd_soc_component_read32(comp, adc_mux_in_reg) + & mask) >> shift) == 1); + if (!is_amic) + return 0; + + return snd_soc_component_read32(comp, amic_mux_sel_reg) & 0x07; +} + +static u16 wcd934x_codec_get_amic_pwlvl_reg(struct snd_soc_component *comp, + int amic) +{ + u16 pwr_level_reg = 0; + + switch (amic) { + case 1: + case 2: + pwr_level_reg = WCD934X_ANA_AMIC1; + break; + + case 3: + case 4: + pwr_level_reg = WCD934X_ANA_AMIC3; + break; + default: + break; + } + + return pwr_level_reg; +} + +static int wcd934x_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + unsigned int decimator; + char *dec_adc_mux_name = NULL; + char *widget_name = NULL; + char *wname; + int ret = 0, amic_n; + u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + char *dec; + u8 hpf_coff_freq; + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + + wname = widget_name; + dec_adc_mux_name = strsep(&widget_name, " "); + if (!dec_adc_mux_name) { + dev_err(comp->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + dec_adc_mux_name = widget_name; + + dec = strpbrk(dec_adc_mux_name, "012345678"); + if (!dec) { + dev_err(comp->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + dev_err(comp->dev, "%s: Invalid decimator = %s\n", + __func__, wname); + ret = -EINVAL; + goto out; + } + + tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + 16 * decimator; + hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 + 16 * decimator; + dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * decimator; + tx_gain_ctl_reg = WCD934X_CDC_TX0_TX_VOL_CTL + 16 * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + amic_n = wcd934x_codec_find_amic_input(comp, decimator); + if (amic_n) + pwr_level_reg = wcd934x_codec_get_amic_pwlvl_reg(comp, + amic_n); + + if (!pwr_level_reg) + break; + + switch ((snd_soc_component_read32(comp, pwr_level_reg) & + WCD934X_AMIC_PWR_LVL_MASK) >> + WCD934X_AMIC_PWR_LVL_SHIFT) { + case WCD934X_AMIC_PWR_LEVEL_LP: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_LP); + break; + case WCD934X_AMIC_PWR_LEVEL_HP: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_HP); + break; + case WCD934X_AMIC_PWR_LEVEL_DEFAULT: + case WCD934X_AMIC_PWR_LEVEL_HYBRID: + default: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_DF); + break; + } + break; + case SND_SOC_DAPM_POST_PMU: + hpf_coff_freq = (snd_soc_component_read32(comp, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + if (hpf_coff_freq != CF_MIN_3DB_150HZ) { + snd_soc_component_update_bits(comp, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + snd_soc_component_update_bits(comp, hpf_gate_reg, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_component_update_bits(comp, hpf_gate_reg, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK, + 0); + } + /* apply gain after decimator is enabled */ + snd_soc_component_write(comp, tx_gain_ctl_reg, + snd_soc_component_read32(comp, + tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_coff_freq = (snd_soc_component_read32(comp, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + + if (hpf_coff_freq != CF_MIN_3DB_150HZ) { + snd_soc_component_update_bits(comp, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_coff_freq << 5); + snd_soc_component_update_bits(comp, hpf_gate_reg, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_component_update_bits(comp, hpf_gate_reg, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK, + 0); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, + 0x10, 0x00); + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_DF); + break; + }; +out: + kfree(wname); + return ret; +} + +static void wcd934x_codec_set_tx_hold(struct snd_soc_component *comp, + u16 amic_reg, bool set) +{ + u8 mask = 0x20; + u8 val; + + if (amic_reg == WCD934X_ANA_AMIC1 || + amic_reg == WCD934X_ANA_AMIC3) + mask = 0x40; + + val = set ? mask : 0x00; + + switch (amic_reg) { + case WCD934X_ANA_AMIC1: + case WCD934X_ANA_AMIC2: + snd_soc_component_update_bits(comp, WCD934X_ANA_AMIC2, + mask, val); + break; + case WCD934X_ANA_AMIC3: + case WCD934X_ANA_AMIC4: + snd_soc_component_update_bits(comp, WCD934X_ANA_AMIC4, + mask, val); + break; + default: + break; + } +} + +static int wcd934x_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd934x_codec_set_tx_hold(comp, w->reg, true); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget wcd934x_dapm_widgets[] = { + /* Analog Outputs */ + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), + SND_SOC_DAPM_OUTPUT("LINEOUT1"), + SND_SOC_DAPM_OUTPUT("LINEOUT2"), + SND_SOC_DAPM_OUTPUT("SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("SPK2 OUT"), + SND_SOC_DAPM_OUTPUT("ANC EAR"), + SND_SOC_DAPM_OUTPUT("ANC HPHL"), + SND_SOC_DAPM_OUTPUT("ANC HPHR"), + SND_SOC_DAPM_OUTPUT("WDMA3_OUT"), + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT1"), + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT2"), + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, + AIF2_PB, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, + AIF3_PB, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM, + AIF4_PB, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("SLIM RX0 MUX", SND_SOC_NOPM, WCD934X_RX0, 0, + &slim_rx_mux[WCD934X_RX0]), + SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, WCD934X_RX1, 0, + &slim_rx_mux[WCD934X_RX1]), + SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, WCD934X_RX2, 0, + &slim_rx_mux[WCD934X_RX2]), + SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, WCD934X_RX3, 0, + &slim_rx_mux[WCD934X_RX3]), + SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, WCD934X_RX4, 0, + &slim_rx_mux[WCD934X_RX4]), + SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, WCD934X_RX5, 0, + &slim_rx_mux[WCD934X_RX5]), + SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, WCD934X_RX6, 0, + &slim_rx_mux[WCD934X_RX6]), + SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, WCD934X_RX7, 0, + &slim_rx_mux[WCD934X_RX7]), + + SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", SND_SOC_NOPM, INTERP_LO1, 0, + &rx_int3_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", SND_SOC_NOPM, INTERP_LO2, 0, + &rx_int4_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp2_mux), + SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int1_asrc_switch, + ARRAY_SIZE(rx_int1_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int2_asrc_switch, + ARRAY_SIZE(rx_int2_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int3_asrc_switch, + ARRAY_SIZE(rx_int3_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int4_asrc_switch, + ARRAY_SIZE(rx_int4_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, NULL, 0), + SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", WCD934X_CDC_RX0_RX_PATH_CFG0, 4, + 0, &rx_int0_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", WCD934X_CDC_RX1_RX_PATH_CFG0, 4, + 0, &rx_int1_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", WCD934X_CDC_RX2_RX_PATH_CFG0, 4, + 0, &rx_int2_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3 MIX2 INP", WCD934X_CDC_RX3_RX_PATH_CFG0, 4, + 0, &rx_int3_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4 MIX2 INP", WCD934X_CDC_RX4_RX_PATH_CFG0, 4, + 0, &rx_int4_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7 MIX2 INP", WCD934X_CDC_RX7_RX_PATH_CFG0, 4, + 0, &rx_int7_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("IIR0 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp0_mux), + SND_SOC_DAPM_MUX("IIR0 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp1_mux), + SND_SOC_DAPM_MUX("IIR0 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp2_mux), + SND_SOC_DAPM_MUX("IIR0 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp3_mux), + SND_SOC_DAPM_MUX("IIR1 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp0_mux), + SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux), + SND_SOC_DAPM_MUX("IIR1 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp2_mux), + SND_SOC_DAPM_MUX("IIR1 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp3_mux), + + SND_SOC_DAPM_PGA_E("IIR0", WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + 0, 0, NULL, 0, wcd934x_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("IIR1", WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, + 1, 0, NULL, 0, wcd934x_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MIXER("SRC0", WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SRC1", WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int0_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int1_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT2 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int2_dem_inp_mux), + + SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3_1 INTERP", SND_SOC_NOPM, INTERP_LO1, 0, + &rx_int3_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4_1 INTERP", SND_SOC_NOPM, INTERP_LO2, 0, + &rx_int4_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 INTERP", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 INTERP", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT0_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int0_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT1_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int1_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT2_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int2_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT3_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int3_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT4_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int4_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT7_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int7_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT8_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int8_2_interp_mux), + SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd934x_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, WCD934X_ANA_HPH, + 5, 0, wcd934x_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, WCD934X_ANA_HPH, + 4, 0, wcd934x_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT3 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd934x_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd934x_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("HPHL PA", WCD934X_ANA_HPH, 7, 0, NULL, 0, + wcd934x_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PA", WCD934X_ANA_HPH, 6, 0, NULL, 0, + wcd934x_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD934X_ANA_LO_1_2, 7, 0, NULL, 0, + NULL, 0), + SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD934X_ANA_LO_1_2, 6, 0, NULL, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("RX_BIAS", WCD934X_ANA_RX_SUPPLIES, 0, 0, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("SBOOST0", WCD934X_CDC_RX7_RX_PATH_CFG1, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SBOOST0_CLK", WCD934X_CDC_BOOST0_BOOST_PATH_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SBOOST1", WCD934X_CDC_RX8_RX_PATH_CFG1, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SBOOST1_CLK", WCD934X_CDC_BOOST1_BOOST_PATH_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("INT0_CLK", SND_SOC_NOPM, INTERP_EAR, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT1_CLK", SND_SOC_NOPM, INTERP_HPHL, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT2_CLK", SND_SOC_NOPM, INTERP_HPHR, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT3_CLK", SND_SOC_NOPM, INTERP_LO1, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT4_CLK", SND_SOC_NOPM, INTERP_LO2, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT7_CLK", SND_SOC_NOPM, INTERP_SPKR1, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT8_CLK", SND_SOC_NOPM, INTERP_SPKR2, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DSMDEM0_CLK", WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM1_CLK", WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM2_CLK", WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM3_CLK", WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM4_CLK", WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM7_CLK", WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM8_CLK", WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_mclk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* TX */ + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + SND_SOC_DAPM_INPUT("DMIC0 Pin"), + SND_SOC_DAPM_INPUT("DMIC1 Pin"), + SND_SOC_DAPM_INPUT("DMIC2 Pin"), + SND_SOC_DAPM_INPUT("DMIC3 Pin"), + SND_SOC_DAPM_INPUT("DMIC4 Pin"), + SND_SOC_DAPM_INPUT("DMIC5 Pin"), + + SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, + AIF1_CAP, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM, + AIF2_CAP, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM, + AIF3_CAP, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("SLIM TX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX7", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX8", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX9", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX10", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX11", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX13", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("DMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_dmic_mux0), + SND_SOC_DAPM_MUX("DMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_dmic_mux1), + SND_SOC_DAPM_MUX("DMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_dmic_mux2), + SND_SOC_DAPM_MUX("DMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_dmic_mux3), + SND_SOC_DAPM_MUX("DMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_dmic_mux4), + SND_SOC_DAPM_MUX("DMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_dmic_mux5), + SND_SOC_DAPM_MUX("DMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_dmic_mux6), + SND_SOC_DAPM_MUX("DMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_dmic_mux7), + SND_SOC_DAPM_MUX("DMIC MUX8", SND_SOC_NOPM, 0, 0, &tx_dmic_mux8), + SND_SOC_DAPM_MUX("AMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_amic_mux0), + SND_SOC_DAPM_MUX("AMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_amic_mux1), + SND_SOC_DAPM_MUX("AMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_amic_mux2), + SND_SOC_DAPM_MUX("AMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_amic_mux3), + SND_SOC_DAPM_MUX("AMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_amic_mux4), + SND_SOC_DAPM_MUX("AMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_amic_mux5), + SND_SOC_DAPM_MUX("AMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_amic_mux6), + SND_SOC_DAPM_MUX("AMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_amic_mux7), + SND_SOC_DAPM_MUX("AMIC MUX8", SND_SOC_NOPM, 0, 0, &tx_amic_mux8), + SND_SOC_DAPM_MUX_E("ADC MUX0", WCD934X_CDC_TX0_TX_PATH_CTL, 5, 0, + &tx_adc_mux0_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX1", WCD934X_CDC_TX1_TX_PATH_CTL, 5, 0, + &tx_adc_mux1_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX2", WCD934X_CDC_TX2_TX_PATH_CTL, 5, 0, + &tx_adc_mux2_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX3", WCD934X_CDC_TX3_TX_PATH_CTL, 5, 0, + &tx_adc_mux3_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX4", WCD934X_CDC_TX4_TX_PATH_CTL, 5, 0, + &tx_adc_mux4_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX5", WCD934X_CDC_TX5_TX_PATH_CTL, 5, 0, + &tx_adc_mux5_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX6", WCD934X_CDC_TX6_TX_PATH_CTL, 5, 0, + &tx_adc_mux6_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX7", WCD934X_CDC_TX7_TX_PATH_CTL, 5, 0, + &tx_adc_mux7_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX8", WCD934X_CDC_TX8_TX_PATH_CTL, 5, 0, + &tx_adc_mux8_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD934X_ANA_AMIC1, 7, 0, + wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD934X_ANA_AMIC2, 7, 0, + wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD934X_ANA_AMIC3, 7, 0, + wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD934X_ANA_AMIC4, 7, 0, + wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("MIC BIAS1", WCD934X_ANA_MICB1, 6, 0, NULL, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS2", WCD934X_ANA_MICB2, 6, 0, NULL, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS3", WCD934X_ANA_MICB3, 6, 0, NULL, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS4", WCD934X_ANA_MICB4, 6, 0, NULL, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("AMIC4_5 SEL", SND_SOC_NOPM, 0, 0, &tx_amic4_5), + SND_SOC_DAPM_MUX("CDC_IF TX0 MUX", SND_SOC_NOPM, WCD934X_TX0, 0, + &cdc_if_tx0_mux), + SND_SOC_DAPM_MUX("CDC_IF TX1 MUX", SND_SOC_NOPM, WCD934X_TX1, 0, + &cdc_if_tx1_mux), + SND_SOC_DAPM_MUX("CDC_IF TX2 MUX", SND_SOC_NOPM, WCD934X_TX2, 0, + &cdc_if_tx2_mux), + SND_SOC_DAPM_MUX("CDC_IF TX3 MUX", SND_SOC_NOPM, WCD934X_TX3, 0, + &cdc_if_tx3_mux), + SND_SOC_DAPM_MUX("CDC_IF TX4 MUX", SND_SOC_NOPM, WCD934X_TX4, 0, + &cdc_if_tx4_mux), + SND_SOC_DAPM_MUX("CDC_IF TX5 MUX", SND_SOC_NOPM, WCD934X_TX5, 0, + &cdc_if_tx5_mux), + SND_SOC_DAPM_MUX("CDC_IF TX6 MUX", SND_SOC_NOPM, WCD934X_TX6, 0, + &cdc_if_tx6_mux), + SND_SOC_DAPM_MUX("CDC_IF TX7 MUX", SND_SOC_NOPM, WCD934X_TX7, 0, + &cdc_if_tx7_mux), + SND_SOC_DAPM_MUX("CDC_IF TX8 MUX", SND_SOC_NOPM, WCD934X_TX8, 0, + &cdc_if_tx8_mux), + SND_SOC_DAPM_MUX("CDC_IF TX9 MUX", SND_SOC_NOPM, WCD934X_TX9, 0, + &cdc_if_tx9_mux), + SND_SOC_DAPM_MUX("CDC_IF TX10 MUX", SND_SOC_NOPM, WCD934X_TX10, 0, + &cdc_if_tx10_mux), + SND_SOC_DAPM_MUX("CDC_IF TX11 MUX", SND_SOC_NOPM, WCD934X_TX11, 0, + &cdc_if_tx11_mux), + SND_SOC_DAPM_MUX("CDC_IF TX11 INP1 MUX", SND_SOC_NOPM, WCD934X_TX11, 0, + &cdc_if_tx11_inp1_mux), + SND_SOC_DAPM_MUX("CDC_IF TX13 MUX", SND_SOC_NOPM, WCD934X_TX13, 0, + &cdc_if_tx13_mux), + SND_SOC_DAPM_MUX("CDC_IF TX13 INP1 MUX", SND_SOC_NOPM, WCD934X_TX13, 0, + &cdc_if_tx13_inp1_mux), + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_slim_cap_mixer, + ARRAY_SIZE(aif1_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_slim_cap_mixer, + ARRAY_SIZE(aif2_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_slim_cap_mixer, + ARRAY_SIZE(aif3_slim_cap_mixer)), +}; + +static const struct snd_soc_dapm_route wcd934x_audio_map[] = { + /* RX0-RX7 */ + WCD934X_SLIM_RX_AIF_PATH(0), + WCD934X_SLIM_RX_AIF_PATH(1), + WCD934X_SLIM_RX_AIF_PATH(2), + WCD934X_SLIM_RX_AIF_PATH(3), + WCD934X_SLIM_RX_AIF_PATH(4), + WCD934X_SLIM_RX_AIF_PATH(5), + WCD934X_SLIM_RX_AIF_PATH(6), + WCD934X_SLIM_RX_AIF_PATH(7), + + /* RX0 Ear out */ + WCD934X_INTERPOLATOR_PATH(0), + WCD934X_INTERPOLATOR_MIX2(0), + {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"}, + {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"}, + {"RX INT0 DAC", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "RX INT0 DAC"}, + {"EAR", NULL, "EAR PA"}, + + /* RX1 Headphone left */ + WCD934X_INTERPOLATOR_PATH(1), + WCD934X_INTERPOLATOR_MIX2(1), + {"RX INT1 MIX3", NULL, "RX INT1 MIX2"}, + {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX3"}, + {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"}, + {"RX INT1 DAC", NULL, "RX_BIAS"}, + {"HPHL PA", NULL, "RX INT1 DAC"}, + {"HPHL", NULL, "HPHL PA"}, + + /* RX2 Headphone right */ + WCD934X_INTERPOLATOR_PATH(2), + WCD934X_INTERPOLATOR_MIX2(2), + {"RX INT2 MIX3", NULL, "RX INT2 MIX2"}, + {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 MIX3"}, + {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"}, + {"RX INT2 DAC", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "RX INT2 DAC"}, + {"HPHR", NULL, "HPHR PA"}, + + /* RX3 HIFi LineOut1 */ + WCD934X_INTERPOLATOR_PATH(3), + WCD934X_INTERPOLATOR_MIX2(3), + {"RX INT3 MIX3", NULL, "RX INT3 MIX2"}, + {"RX INT3 DAC", NULL, "RX INT3 MIX3"}, + {"RX INT3 DAC", NULL, "RX_BIAS"}, + {"LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"LINEOUT1", NULL, "LINEOUT1 PA"}, + + /* RX4 HIFi LineOut2 */ + WCD934X_INTERPOLATOR_PATH(4), + WCD934X_INTERPOLATOR_MIX2(4), + {"RX INT4 MIX3", NULL, "RX INT4 MIX2"}, + {"RX INT4 DAC", NULL, "RX INT4 MIX3"}, + {"RX INT4 DAC", NULL, "RX_BIAS"}, + {"LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"LINEOUT2", NULL, "LINEOUT2 PA"}, + + /* RX7 Speaker Left Out PA */ + WCD934X_INTERPOLATOR_PATH(7), + WCD934X_INTERPOLATOR_MIX2(7), + {"RX INT7 CHAIN", NULL, "RX INT7 MIX2"}, + {"RX INT7 CHAIN", NULL, "RX_BIAS"}, + {"RX INT7 CHAIN", NULL, "SBOOST0"}, + {"RX INT7 CHAIN", NULL, "SBOOST0_CLK"}, + {"SPK1 OUT", NULL, "RX INT7 CHAIN"}, + + /* RX8 Speaker Right Out PA */ + WCD934X_INTERPOLATOR_PATH(8), + {"RX INT8 CHAIN", NULL, "RX INT8 SEC MIX"}, + {"RX INT8 CHAIN", NULL, "RX_BIAS"}, + {"RX INT8 CHAIN", NULL, "SBOOST1"}, + {"RX INT8 CHAIN", NULL, "SBOOST1_CLK"}, + {"SPK2 OUT", NULL, "RX INT8 CHAIN"}, + + /* Tx */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + + WCD934X_SLIM_TX_AIF_PATH(0), + WCD934X_SLIM_TX_AIF_PATH(1), + WCD934X_SLIM_TX_AIF_PATH(2), + WCD934X_SLIM_TX_AIF_PATH(3), + WCD934X_SLIM_TX_AIF_PATH(4), + WCD934X_SLIM_TX_AIF_PATH(5), + WCD934X_SLIM_TX_AIF_PATH(6), + WCD934X_SLIM_TX_AIF_PATH(7), + WCD934X_SLIM_TX_AIF_PATH(8), + + WCD934X_ADC_MUX(0), + WCD934X_ADC_MUX(1), + WCD934X_ADC_MUX(2), + WCD934X_ADC_MUX(3), + WCD934X_ADC_MUX(4), + WCD934X_ADC_MUX(5), + WCD934X_ADC_MUX(6), + WCD934X_ADC_MUX(7), + WCD934X_ADC_MUX(8), + + {"CDC_IF TX0 MUX", "DEC0", "ADC MUX0"}, + {"CDC_IF TX1 MUX", "DEC1", "ADC MUX1"}, + {"CDC_IF TX2 MUX", "DEC2", "ADC MUX2"}, + {"CDC_IF TX3 MUX", "DEC3", "ADC MUX3"}, + {"CDC_IF TX4 MUX", "DEC4", "ADC MUX4"}, + {"CDC_IF TX5 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX6 MUX", "DEC6", "ADC MUX6"}, + {"CDC_IF TX7 MUX", "DEC7", "ADC MUX7"}, + {"CDC_IF TX8 MUX", "DEC8", "ADC MUX8"}, + + {"AMIC4_5 SEL", "AMIC4", "AMIC4"}, + {"AMIC4_5 SEL", "AMIC5", "AMIC5"}, + + { "DMIC0", NULL, "DMIC0 Pin" }, + { "DMIC1", NULL, "DMIC1 Pin" }, + { "DMIC2", NULL, "DMIC2 Pin" }, + { "DMIC3", NULL, "DMIC3 Pin" }, + { "DMIC4", NULL, "DMIC4 Pin" }, + { "DMIC5", NULL, "DMIC5 Pin" }, + + {"ADC1", NULL, "AMIC1"}, + {"ADC2", NULL, "AMIC2"}, + {"ADC3", NULL, "AMIC3"}, + {"ADC4", NULL, "AMIC4_5 SEL"}, + + WCD934X_IIR_INP_MUX(0), + WCD934X_IIR_INP_MUX(1), + + {"SRC0", NULL, "IIR0"}, + {"SRC1", NULL, "IIR1"}, +}; + +static const struct snd_soc_component_driver wcd934x_component_drv = { + .probe = wcd934x_comp_probe, + .remove = wcd934x_comp_remove, + .set_sysclk = wcd934x_comp_set_sysclk, + .controls = wcd934x_snd_controls, + .num_controls = ARRAY_SIZE(wcd934x_snd_controls), + .dapm_widgets = wcd934x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wcd934x_dapm_widgets), + .dapm_routes = wcd934x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wcd934x_audio_map), +}; + +static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd) +{ + struct device *dev = &wcd->sdev->dev; + struct device_node *ifc_dev_np; + + ifc_dev_np = of_parse_phandle(dev->of_node, "slim-ifc-dev", 0); + if (!ifc_dev_np) { + dev_err(dev, "No Interface device found\n"); + return -EINVAL; + } + + wcd->sidev = of_slim_get_device(wcd->sdev->ctrl, ifc_dev_np); + if (!wcd->sidev) { + dev_err(dev, "Unable to get SLIM Interface device\n"); + return -EINVAL; + } + + slim_get_logical_addr(wcd->sidev); + wcd->if_regmap = regmap_init_slimbus(wcd->sidev, + &wcd934x_ifc_regmap_config); + if (IS_ERR(wcd->if_regmap)) { + dev_err(dev, "Failed to allocate ifc register map\n"); + return PTR_ERR(wcd->if_regmap); + } + + of_property_read_u32(dev->parent->of_node, "qcom,dmic-sample-rate", + &wcd->dmic_sample_rate); + + return 0; +} + +static int wcd934x_codec_probe(struct platform_device *pdev) +{ + struct wcd934x_ddata *data = dev_get_drvdata(pdev->dev.parent); + struct wcd934x_codec *wcd; + struct device *dev = &pdev->dev; + int ret, irq; + + wcd = devm_kzalloc(&pdev->dev, sizeof(*wcd), GFP_KERNEL); + if (!wcd) + return -ENOMEM; + + wcd->dev = dev; + wcd->regmap = data->regmap; + wcd->extclk = data->extclk; + wcd->sdev = to_slim_device(data->dev); + mutex_init(&wcd->sysclk_mutex); + + ret = wcd934x_codec_parse_data(wcd); + if (ret) { + dev_err(wcd->dev, "Failed to get SLIM IRQ\n"); + return ret; + } + + /* set default rate 9P6MHz */ + regmap_update_bits(wcd->regmap, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + WCD934X_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK, + WCD934X_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ); + memcpy(wcd->rx_chs, wcd934x_rx_chs, sizeof(wcd934x_rx_chs)); + memcpy(wcd->tx_chs, wcd934x_tx_chs, sizeof(wcd934x_tx_chs)); + + irq = regmap_irq_get_virq(data->irq_data, WCD934X_IRQ_SLIMBUS); + if (irq < 0) { + dev_err(wcd->dev, "Failed to get SLIM IRQ\n"); + return irq; + } + + ret = devm_request_threaded_irq(dev, irq, NULL, + wcd934x_slim_irq_handler, + IRQF_TRIGGER_RISING, + "slim", wcd); + if (ret) { + dev_err(dev, "Failed to request slimbus irq\n"); + return ret; + } + + wcd934x_register_mclk_output(wcd); + platform_set_drvdata(pdev, wcd); + + return devm_snd_soc_register_component(dev, &wcd934x_component_drv, + wcd934x_slim_dais, + ARRAY_SIZE(wcd934x_slim_dais)); +} + +static const struct platform_device_id wcd934x_driver_id[] = { + { + .name = "wcd934x-codec", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, wcd934x_driver_id); + +static struct platform_driver wcd934x_codec_driver = { + .probe = &wcd934x_codec_probe, + .id_table = wcd934x_driver_id, + .driver = { + .name = "wcd934x-codec", + } +}; + +MODULE_ALIAS("platform:wcd934x-codec"); +module_platform_driver(wcd934x_codec_driver); +MODULE_DESCRIPTION("WCD934x codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 2a9b610f6d43..d3d32b501aca 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1030,8 +1030,8 @@ static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, return -ETIMEDOUT; } -static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, - const void *buf, size_t len) +static int wm_coeff_write_ctrl_raw(struct wm_coeff_ctl *ctl, + const void *buf, size_t len) { struct wm_adsp *dsp = ctl->dsp; void *scratch; @@ -1061,6 +1061,23 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, return 0; } +static int wm_coeff_write_ctrl(struct wm_coeff_ctl *ctl, + const void *buf, size_t len) +{ + int ret = 0; + + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) + ret = -EPERM; + else if (buf != ctl->cache) + memcpy(ctl->cache, buf, len); + + ctl->set = 1; + if (ctl->enabled && ctl->dsp->running) + ret = wm_coeff_write_ctrl_raw(ctl, buf, len); + + return ret; +} + static int wm_coeff_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { @@ -1071,16 +1088,7 @@ static int wm_coeff_put(struct snd_kcontrol *kctl, int ret = 0; mutex_lock(&ctl->dsp->pwr_lock); - - if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) - ret = -EPERM; - else - memcpy(ctl->cache, p, ctl->len); - - ctl->set = 1; - if (ctl->enabled && ctl->dsp->running) - ret = wm_coeff_write_control(ctl, p, ctl->len); - + ret = wm_coeff_write_ctrl(ctl, p, ctl->len); mutex_unlock(&ctl->dsp->pwr_lock); return ret; @@ -1096,15 +1104,10 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, mutex_lock(&ctl->dsp->pwr_lock); - if (copy_from_user(ctl->cache, bytes, size)) { + if (copy_from_user(ctl->cache, bytes, size)) ret = -EFAULT; - } else { - ctl->set = 1; - if (ctl->enabled && ctl->dsp->running) - ret = wm_coeff_write_control(ctl, ctl->cache, size); - else if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) - ret = -EPERM; - } + else + ret = wm_coeff_write_ctrl(ctl, ctl->cache, size); mutex_unlock(&ctl->dsp->pwr_lock); @@ -1135,8 +1138,8 @@ static int wm_coeff_put_acked(struct snd_kcontrol *kctl, return ret; } -static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, - void *buf, size_t len) +static int wm_coeff_read_ctrl_raw(struct wm_coeff_ctl *ctl, + void *buf, size_t len) { struct wm_adsp *dsp = ctl->dsp; void *scratch; @@ -1166,29 +1169,37 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, return 0; } -static int wm_coeff_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *ucontrol) +static int wm_coeff_read_ctrl(struct wm_coeff_ctl *ctl, void *buf, size_t len) { - struct soc_bytes_ext *bytes_ext = - (struct soc_bytes_ext *)kctl->private_value; - struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); - char *p = ucontrol->value.bytes.data; int ret = 0; - mutex_lock(&ctl->dsp->pwr_lock); - if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { if (ctl->enabled && ctl->dsp->running) - ret = wm_coeff_read_control(ctl, p, ctl->len); + return wm_coeff_read_ctrl_raw(ctl, buf, len); else - ret = -EPERM; + return -EPERM; } else { if (!ctl->flags && ctl->enabled && ctl->dsp->running) - ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); + ret = wm_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); - memcpy(p, ctl->cache, ctl->len); + if (buf != ctl->cache) + memcpy(buf, ctl->cache, len); } + return ret; +} + +static int wm_coeff_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + char *p = ucontrol->value.bytes.data; + int ret; + + mutex_lock(&ctl->dsp->pwr_lock); + ret = wm_coeff_read_ctrl(ctl, p, ctl->len); mutex_unlock(&ctl->dsp->pwr_lock); return ret; @@ -1204,15 +1215,7 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, mutex_lock(&ctl->dsp->pwr_lock); - if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { - if (ctl->enabled && ctl->dsp->running) - ret = wm_coeff_read_control(ctl, ctl->cache, size); - else - ret = -EPERM; - } else { - if (!ctl->flags && ctl->enabled && ctl->dsp->running) - ret = wm_coeff_read_control(ctl, ctl->cache, size); - } + ret = wm_coeff_read_ctrl_raw(ctl, ctl->cache, size); if (!ret && copy_to_user(bytes, ctl->cache, size)) ret = -EFAULT; @@ -1340,7 +1343,7 @@ static int wm_coeff_init_control_caches(struct wm_adsp *dsp) * created so we don't need to do anything. */ if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { - ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); + ret = wm_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); if (ret < 0) return ret; } @@ -1358,7 +1361,8 @@ static int wm_coeff_sync_controls(struct wm_adsp *dsp) if (!ctl->enabled) continue; if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { - ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len); + ret = wm_coeff_write_ctrl_raw(ctl, ctl->cache, + ctl->len); if (ret < 0) return ret; } @@ -2048,7 +2052,7 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, if (len > ctl->len) return -EINVAL; - ret = wm_coeff_write_control(ctl, buf, len); + ret = wm_coeff_write_ctrl(ctl, buf, len); kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl->name); snd_ctl_notify(dsp->component->card->snd_card, @@ -2070,7 +2074,7 @@ int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, if (len > ctl->len) return -EINVAL; - return wm_coeff_read_control(ctl, buf, len); + return wm_coeff_read_ctrl(ctl, buf, len); } EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c new file mode 100644 index 000000000000..b59f1d0e7f84 --- /dev/null +++ b/sound/soc/codecs/wsa881x.c @@ -0,0 +1,1185 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2017, The Linux Foundation. +// Copyright (c) 2019, Linaro Limited + +#include <linux/bitops.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define WSA881X_DIGITAL_BASE 0x3000 +#define WSA881X_ANALOG_BASE 0x3100 + +/* Digital register address space */ +#define WSA881X_CHIP_ID0 (WSA881X_DIGITAL_BASE + 0x0000) +#define WSA881X_CHIP_ID1 (WSA881X_DIGITAL_BASE + 0x0001) +#define WSA881X_CHIP_ID2 (WSA881X_DIGITAL_BASE + 0x0002) +#define WSA881X_CHIP_ID3 (WSA881X_DIGITAL_BASE + 0x0003) +#define WSA881X_BUS_ID (WSA881X_DIGITAL_BASE + 0x0004) +#define WSA881X_CDC_RST_CTL (WSA881X_DIGITAL_BASE + 0x0005) +#define WSA881X_CDC_TOP_CLK_CTL (WSA881X_DIGITAL_BASE + 0x0006) +#define WSA881X_CDC_ANA_CLK_CTL (WSA881X_DIGITAL_BASE + 0x0007) +#define WSA881X_CDC_DIG_CLK_CTL (WSA881X_DIGITAL_BASE + 0x0008) +#define WSA881X_CLOCK_CONFIG (WSA881X_DIGITAL_BASE + 0x0009) +#define WSA881X_ANA_CTL (WSA881X_DIGITAL_BASE + 0x000A) +#define WSA881X_SWR_RESET_EN (WSA881X_DIGITAL_BASE + 0x000B) +#define WSA881X_RESET_CTL (WSA881X_DIGITAL_BASE + 0x000C) +#define WSA881X_TADC_VALUE_CTL (WSA881X_DIGITAL_BASE + 0x000F) +#define WSA881X_TEMP_DETECT_CTL (WSA881X_DIGITAL_BASE + 0x0010) +#define WSA881X_TEMP_MSB (WSA881X_DIGITAL_BASE + 0x0011) +#define WSA881X_TEMP_LSB (WSA881X_DIGITAL_BASE + 0x0012) +#define WSA881X_TEMP_CONFIG0 (WSA881X_DIGITAL_BASE + 0x0013) +#define WSA881X_TEMP_CONFIG1 (WSA881X_DIGITAL_BASE + 0x0014) +#define WSA881X_CDC_CLIP_CTL (WSA881X_DIGITAL_BASE + 0x0015) +#define WSA881X_SDM_PDM9_LSB (WSA881X_DIGITAL_BASE + 0x0016) +#define WSA881X_SDM_PDM9_MSB (WSA881X_DIGITAL_BASE + 0x0017) +#define WSA881X_CDC_RX_CTL (WSA881X_DIGITAL_BASE + 0x0018) +#define WSA881X_DEM_BYPASS_DATA0 (WSA881X_DIGITAL_BASE + 0x0019) +#define WSA881X_DEM_BYPASS_DATA1 (WSA881X_DIGITAL_BASE + 0x001A) +#define WSA881X_DEM_BYPASS_DATA2 (WSA881X_DIGITAL_BASE + 0x001B) +#define WSA881X_DEM_BYPASS_DATA3 (WSA881X_DIGITAL_BASE + 0x001C) +#define WSA881X_OTP_CTRL0 (WSA881X_DIGITAL_BASE + 0x001D) +#define WSA881X_OTP_CTRL1 (WSA881X_DIGITAL_BASE + 0x001E) +#define WSA881X_HDRIVE_CTL_GROUP1 (WSA881X_DIGITAL_BASE + 0x001F) +#define WSA881X_INTR_MODE (WSA881X_DIGITAL_BASE + 0x0020) +#define WSA881X_INTR_MASK (WSA881X_DIGITAL_BASE + 0x0021) +#define WSA881X_INTR_STATUS (WSA881X_DIGITAL_BASE + 0x0022) +#define WSA881X_INTR_CLEAR (WSA881X_DIGITAL_BASE + 0x0023) +#define WSA881X_INTR_LEVEL (WSA881X_DIGITAL_BASE + 0x0024) +#define WSA881X_INTR_SET (WSA881X_DIGITAL_BASE + 0x0025) +#define WSA881X_INTR_TEST (WSA881X_DIGITAL_BASE + 0x0026) +#define WSA881X_PDM_TEST_MODE (WSA881X_DIGITAL_BASE + 0x0030) +#define WSA881X_ATE_TEST_MODE (WSA881X_DIGITAL_BASE + 0x0031) +#define WSA881X_PIN_CTL_MODE (WSA881X_DIGITAL_BASE + 0x0032) +#define WSA881X_PIN_CTL_OE (WSA881X_DIGITAL_BASE + 0x0033) +#define WSA881X_PIN_WDATA_IOPAD (WSA881X_DIGITAL_BASE + 0x0034) +#define WSA881X_PIN_STATUS (WSA881X_DIGITAL_BASE + 0x0035) +#define WSA881X_DIG_DEBUG_MODE (WSA881X_DIGITAL_BASE + 0x0037) +#define WSA881X_DIG_DEBUG_SEL (WSA881X_DIGITAL_BASE + 0x0038) +#define WSA881X_DIG_DEBUG_EN (WSA881X_DIGITAL_BASE + 0x0039) +#define WSA881X_SWR_HM_TEST1 (WSA881X_DIGITAL_BASE + 0x003B) +#define WSA881X_SWR_HM_TEST2 (WSA881X_DIGITAL_BASE + 0x003C) +#define WSA881X_TEMP_DETECT_DBG_CTL (WSA881X_DIGITAL_BASE + 0x003D) +#define WSA881X_TEMP_DEBUG_MSB (WSA881X_DIGITAL_BASE + 0x003E) +#define WSA881X_TEMP_DEBUG_LSB (WSA881X_DIGITAL_BASE + 0x003F) +#define WSA881X_SAMPLE_EDGE_SEL (WSA881X_DIGITAL_BASE + 0x0044) +#define WSA881X_IOPAD_CTL (WSA881X_DIGITAL_BASE + 0x0045) +#define WSA881X_SPARE_0 (WSA881X_DIGITAL_BASE + 0x0050) +#define WSA881X_SPARE_1 (WSA881X_DIGITAL_BASE + 0x0051) +#define WSA881X_SPARE_2 (WSA881X_DIGITAL_BASE + 0x0052) +#define WSA881X_OTP_REG_0 (WSA881X_DIGITAL_BASE + 0x0080) +#define WSA881X_OTP_REG_1 (WSA881X_DIGITAL_BASE + 0x0081) +#define WSA881X_OTP_REG_2 (WSA881X_DIGITAL_BASE + 0x0082) +#define WSA881X_OTP_REG_3 (WSA881X_DIGITAL_BASE + 0x0083) +#define WSA881X_OTP_REG_4 (WSA881X_DIGITAL_BASE + 0x0084) +#define WSA881X_OTP_REG_5 (WSA881X_DIGITAL_BASE + 0x0085) +#define WSA881X_OTP_REG_6 (WSA881X_DIGITAL_BASE + 0x0086) +#define WSA881X_OTP_REG_7 (WSA881X_DIGITAL_BASE + 0x0087) +#define WSA881X_OTP_REG_8 (WSA881X_DIGITAL_BASE + 0x0088) +#define WSA881X_OTP_REG_9 (WSA881X_DIGITAL_BASE + 0x0089) +#define WSA881X_OTP_REG_10 (WSA881X_DIGITAL_BASE + 0x008A) +#define WSA881X_OTP_REG_11 (WSA881X_DIGITAL_BASE + 0x008B) +#define WSA881X_OTP_REG_12 (WSA881X_DIGITAL_BASE + 0x008C) +#define WSA881X_OTP_REG_13 (WSA881X_DIGITAL_BASE + 0x008D) +#define WSA881X_OTP_REG_14 (WSA881X_DIGITAL_BASE + 0x008E) +#define WSA881X_OTP_REG_15 (WSA881X_DIGITAL_BASE + 0x008F) +#define WSA881X_OTP_REG_16 (WSA881X_DIGITAL_BASE + 0x0090) +#define WSA881X_OTP_REG_17 (WSA881X_DIGITAL_BASE + 0x0091) +#define WSA881X_OTP_REG_18 (WSA881X_DIGITAL_BASE + 0x0092) +#define WSA881X_OTP_REG_19 (WSA881X_DIGITAL_BASE + 0x0093) +#define WSA881X_OTP_REG_20 (WSA881X_DIGITAL_BASE + 0x0094) +#define WSA881X_OTP_REG_21 (WSA881X_DIGITAL_BASE + 0x0095) +#define WSA881X_OTP_REG_22 (WSA881X_DIGITAL_BASE + 0x0096) +#define WSA881X_OTP_REG_23 (WSA881X_DIGITAL_BASE + 0x0097) +#define WSA881X_OTP_REG_24 (WSA881X_DIGITAL_BASE + 0x0098) +#define WSA881X_OTP_REG_25 (WSA881X_DIGITAL_BASE + 0x0099) +#define WSA881X_OTP_REG_26 (WSA881X_DIGITAL_BASE + 0x009A) +#define WSA881X_OTP_REG_27 (WSA881X_DIGITAL_BASE + 0x009B) +#define WSA881X_OTP_REG_28 (WSA881X_DIGITAL_BASE + 0x009C) +#define WSA881X_OTP_REG_29 (WSA881X_DIGITAL_BASE + 0x009D) +#define WSA881X_OTP_REG_30 (WSA881X_DIGITAL_BASE + 0x009E) +#define WSA881X_OTP_REG_31 (WSA881X_DIGITAL_BASE + 0x009F) +#define WSA881X_OTP_REG_63 (WSA881X_DIGITAL_BASE + 0x00BF) + +/* Analog Register address space */ +#define WSA881X_BIAS_REF_CTRL (WSA881X_ANALOG_BASE + 0x0000) +#define WSA881X_BIAS_TEST (WSA881X_ANALOG_BASE + 0x0001) +#define WSA881X_BIAS_BIAS (WSA881X_ANALOG_BASE + 0x0002) +#define WSA881X_TEMP_OP (WSA881X_ANALOG_BASE + 0x0003) +#define WSA881X_TEMP_IREF_CTRL (WSA881X_ANALOG_BASE + 0x0004) +#define WSA881X_TEMP_ISENS_CTRL (WSA881X_ANALOG_BASE + 0x0005) +#define WSA881X_TEMP_CLK_CTRL (WSA881X_ANALOG_BASE + 0x0006) +#define WSA881X_TEMP_TEST (WSA881X_ANALOG_BASE + 0x0007) +#define WSA881X_TEMP_BIAS (WSA881X_ANALOG_BASE + 0x0008) +#define WSA881X_TEMP_ADC_CTRL (WSA881X_ANALOG_BASE + 0x0009) +#define WSA881X_TEMP_DOUT_MSB (WSA881X_ANALOG_BASE + 0x000A) +#define WSA881X_TEMP_DOUT_LSB (WSA881X_ANALOG_BASE + 0x000B) +#define WSA881X_ADC_EN_MODU_V (WSA881X_ANALOG_BASE + 0x0010) +#define WSA881X_ADC_EN_MODU_I (WSA881X_ANALOG_BASE + 0x0011) +#define WSA881X_ADC_EN_DET_TEST_V (WSA881X_ANALOG_BASE + 0x0012) +#define WSA881X_ADC_EN_DET_TEST_I (WSA881X_ANALOG_BASE + 0x0013) +#define WSA881X_ADC_SEL_IBIAS (WSA881X_ANALOG_BASE + 0x0014) +#define WSA881X_ADC_EN_SEL_IBAIS (WSA881X_ANALOG_BASE + 0x0015) +#define WSA881X_SPKR_DRV_EN (WSA881X_ANALOG_BASE + 0x001A) +#define WSA881X_SPKR_DRV_GAIN (WSA881X_ANALOG_BASE + 0x001B) +#define WSA881X_PA_GAIN_SEL_MASK BIT(3) +#define WSA881X_PA_GAIN_SEL_REG BIT(3) +#define WSA881X_PA_GAIN_SEL_DRE 0 +#define WSA881X_SPKR_PAG_GAIN_MASK GENMASK(7, 4) +#define WSA881X_SPKR_DAC_CTL (WSA881X_ANALOG_BASE + 0x001C) +#define WSA881X_SPKR_DRV_DBG (WSA881X_ANALOG_BASE + 0x001D) +#define WSA881X_SPKR_PWRSTG_DBG (WSA881X_ANALOG_BASE + 0x001E) +#define WSA881X_SPKR_OCP_CTL (WSA881X_ANALOG_BASE + 0x001F) +#define WSA881X_SPKR_OCP_MASK GENMASK(7, 6) +#define WSA881X_SPKR_OCP_EN BIT(7) +#define WSA881X_SPKR_OCP_HOLD BIT(6) +#define WSA881X_SPKR_CLIP_CTL (WSA881X_ANALOG_BASE + 0x0020) +#define WSA881X_SPKR_BBM_CTL (WSA881X_ANALOG_BASE + 0x0021) +#define WSA881X_SPKR_MISC_CTL1 (WSA881X_ANALOG_BASE + 0x0022) +#define WSA881X_SPKR_MISC_CTL2 (WSA881X_ANALOG_BASE + 0x0023) +#define WSA881X_SPKR_BIAS_INT (WSA881X_ANALOG_BASE + 0x0024) +#define WSA881X_SPKR_PA_INT (WSA881X_ANALOG_BASE + 0x0025) +#define WSA881X_SPKR_BIAS_CAL (WSA881X_ANALOG_BASE + 0x0026) +#define WSA881X_SPKR_BIAS_PSRR (WSA881X_ANALOG_BASE + 0x0027) +#define WSA881X_SPKR_STATUS1 (WSA881X_ANALOG_BASE + 0x0028) +#define WSA881X_SPKR_STATUS2 (WSA881X_ANALOG_BASE + 0x0029) +#define WSA881X_BOOST_EN_CTL (WSA881X_ANALOG_BASE + 0x002A) +#define WSA881X_BOOST_EN_MASK BIT(7) +#define WSA881X_BOOST_EN BIT(7) +#define WSA881X_BOOST_CURRENT_LIMIT (WSA881X_ANALOG_BASE + 0x002B) +#define WSA881X_BOOST_PS_CTL (WSA881X_ANALOG_BASE + 0x002C) +#define WSA881X_BOOST_PRESET_OUT1 (WSA881X_ANALOG_BASE + 0x002D) +#define WSA881X_BOOST_PRESET_OUT2 (WSA881X_ANALOG_BASE + 0x002E) +#define WSA881X_BOOST_FORCE_OUT (WSA881X_ANALOG_BASE + 0x002F) +#define WSA881X_BOOST_LDO_PROG (WSA881X_ANALOG_BASE + 0x0030) +#define WSA881X_BOOST_SLOPE_COMP_ISENSE_FB (WSA881X_ANALOG_BASE + 0x0031) +#define WSA881X_BOOST_RON_CTL (WSA881X_ANALOG_BASE + 0x0032) +#define WSA881X_BOOST_LOOP_STABILITY (WSA881X_ANALOG_BASE + 0x0033) +#define WSA881X_BOOST_ZX_CTL (WSA881X_ANALOG_BASE + 0x0034) +#define WSA881X_BOOST_START_CTL (WSA881X_ANALOG_BASE + 0x0035) +#define WSA881X_BOOST_MISC1_CTL (WSA881X_ANALOG_BASE + 0x0036) +#define WSA881X_BOOST_MISC2_CTL (WSA881X_ANALOG_BASE + 0x0037) +#define WSA881X_BOOST_MISC3_CTL (WSA881X_ANALOG_BASE + 0x0038) +#define WSA881X_BOOST_ATEST_CTL (WSA881X_ANALOG_BASE + 0x0039) +#define WSA881X_SPKR_PROT_FE_GAIN (WSA881X_ANALOG_BASE + 0x003A) +#define WSA881X_SPKR_PROT_FE_CM_LDO_SET (WSA881X_ANALOG_BASE + 0x003B) +#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 (WSA881X_ANALOG_BASE + 0x003C) +#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 (WSA881X_ANALOG_BASE + 0x003D) +#define WSA881X_SPKR_PROT_ATEST1 (WSA881X_ANALOG_BASE + 0x003E) +#define WSA881X_SPKR_PROT_ATEST2 (WSA881X_ANALOG_BASE + 0x003F) +#define WSA881X_SPKR_PROT_FE_VSENSE_VCM (WSA881X_ANALOG_BASE + 0x0040) +#define WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 (WSA881X_ANALOG_BASE + 0x0041) +#define WSA881X_BONGO_RESRV_REG1 (WSA881X_ANALOG_BASE + 0x0042) +#define WSA881X_BONGO_RESRV_REG2 (WSA881X_ANALOG_BASE + 0x0043) +#define WSA881X_SPKR_PROT_SAR (WSA881X_ANALOG_BASE + 0x0044) +#define WSA881X_SPKR_STATUS3 (WSA881X_ANALOG_BASE + 0x0045) + +#define SWRS_SCP_FRAME_CTRL_BANK(m) (0x60 + 0x10 * (m)) +#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m)) +#define SWR_SLV_MAX_REG_ADDR 0x390 +#define SWR_SLV_START_REG_ADDR 0x40 +#define SWR_SLV_MAX_BUF_LEN 20 +#define BYTES_PER_LINE 12 +#define SWR_SLV_RD_BUF_LEN 8 +#define SWR_SLV_WR_BUF_LEN 32 +#define SWR_SLV_MAX_DEVICES 2 +#define WSA881X_MAX_SWR_PORTS 4 +#define WSA881X_VERSION_ENTRY_SIZE 27 +#define WSA881X_OCP_CTL_TIMER_SEC 2 +#define WSA881X_OCP_CTL_TEMP_CELSIUS 25 +#define WSA881X_OCP_CTL_POLL_TIMER_SEC 60 + +#define WSA881X_PA_GAIN_TLV(xname, reg, shift, max, invert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ + .put = wsa881x_put_pa_gain, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } + +static struct reg_default wsa881x_defaults[] = { + { WSA881X_CHIP_ID0, 0x00 }, + { WSA881X_CHIP_ID1, 0x00 }, + { WSA881X_CHIP_ID2, 0x00 }, + { WSA881X_CHIP_ID3, 0x02 }, + { WSA881X_BUS_ID, 0x00 }, + { WSA881X_CDC_RST_CTL, 0x00 }, + { WSA881X_CDC_TOP_CLK_CTL, 0x03 }, + { WSA881X_CDC_ANA_CLK_CTL, 0x00 }, + { WSA881X_CDC_DIG_CLK_CTL, 0x00 }, + { WSA881X_CLOCK_CONFIG, 0x00 }, + { WSA881X_ANA_CTL, 0x08 }, + { WSA881X_SWR_RESET_EN, 0x00 }, + { WSA881X_TEMP_DETECT_CTL, 0x01 }, + { WSA881X_TEMP_MSB, 0x00 }, + { WSA881X_TEMP_LSB, 0x00 }, + { WSA881X_TEMP_CONFIG0, 0x00 }, + { WSA881X_TEMP_CONFIG1, 0x00 }, + { WSA881X_CDC_CLIP_CTL, 0x03 }, + { WSA881X_SDM_PDM9_LSB, 0x00 }, + { WSA881X_SDM_PDM9_MSB, 0x00 }, + { WSA881X_CDC_RX_CTL, 0x7E }, + { WSA881X_DEM_BYPASS_DATA0, 0x00 }, + { WSA881X_DEM_BYPASS_DATA1, 0x00 }, + { WSA881X_DEM_BYPASS_DATA2, 0x00 }, + { WSA881X_DEM_BYPASS_DATA3, 0x00 }, + { WSA881X_OTP_CTRL0, 0x00 }, + { WSA881X_OTP_CTRL1, 0x00 }, + { WSA881X_HDRIVE_CTL_GROUP1, 0x00 }, + { WSA881X_INTR_MODE, 0x00 }, + { WSA881X_INTR_STATUS, 0x00 }, + { WSA881X_INTR_CLEAR, 0x00 }, + { WSA881X_INTR_LEVEL, 0x00 }, + { WSA881X_INTR_SET, 0x00 }, + { WSA881X_INTR_TEST, 0x00 }, + { WSA881X_PDM_TEST_MODE, 0x00 }, + { WSA881X_ATE_TEST_MODE, 0x00 }, + { WSA881X_PIN_CTL_MODE, 0x00 }, + { WSA881X_PIN_CTL_OE, 0x00 }, + { WSA881X_PIN_WDATA_IOPAD, 0x00 }, + { WSA881X_PIN_STATUS, 0x00 }, + { WSA881X_DIG_DEBUG_MODE, 0x00 }, + { WSA881X_DIG_DEBUG_SEL, 0x00 }, + { WSA881X_DIG_DEBUG_EN, 0x00 }, + { WSA881X_SWR_HM_TEST1, 0x08 }, + { WSA881X_SWR_HM_TEST2, 0x00 }, + { WSA881X_TEMP_DETECT_DBG_CTL, 0x00 }, + { WSA881X_TEMP_DEBUG_MSB, 0x00 }, + { WSA881X_TEMP_DEBUG_LSB, 0x00 }, + { WSA881X_SAMPLE_EDGE_SEL, 0x0C }, + { WSA881X_SPARE_0, 0x00 }, + { WSA881X_SPARE_1, 0x00 }, + { WSA881X_SPARE_2, 0x00 }, + { WSA881X_OTP_REG_0, 0x01 }, + { WSA881X_OTP_REG_1, 0xFF }, + { WSA881X_OTP_REG_2, 0xC0 }, + { WSA881X_OTP_REG_3, 0xFF }, + { WSA881X_OTP_REG_4, 0xC0 }, + { WSA881X_OTP_REG_5, 0xFF }, + { WSA881X_OTP_REG_6, 0xFF }, + { WSA881X_OTP_REG_7, 0xFF }, + { WSA881X_OTP_REG_8, 0xFF }, + { WSA881X_OTP_REG_9, 0xFF }, + { WSA881X_OTP_REG_10, 0xFF }, + { WSA881X_OTP_REG_11, 0xFF }, + { WSA881X_OTP_REG_12, 0xFF }, + { WSA881X_OTP_REG_13, 0xFF }, + { WSA881X_OTP_REG_14, 0xFF }, + { WSA881X_OTP_REG_15, 0xFF }, + { WSA881X_OTP_REG_16, 0xFF }, + { WSA881X_OTP_REG_17, 0xFF }, + { WSA881X_OTP_REG_18, 0xFF }, + { WSA881X_OTP_REG_19, 0xFF }, + { WSA881X_OTP_REG_20, 0xFF }, + { WSA881X_OTP_REG_21, 0xFF }, + { WSA881X_OTP_REG_22, 0xFF }, + { WSA881X_OTP_REG_23, 0xFF }, + { WSA881X_OTP_REG_24, 0x03 }, + { WSA881X_OTP_REG_25, 0x01 }, + { WSA881X_OTP_REG_26, 0x03 }, + { WSA881X_OTP_REG_27, 0x11 }, + { WSA881X_OTP_REG_63, 0x40 }, + /* WSA881x Analog registers */ + { WSA881X_BIAS_REF_CTRL, 0x6C }, + { WSA881X_BIAS_TEST, 0x16 }, + { WSA881X_BIAS_BIAS, 0xF0 }, + { WSA881X_TEMP_OP, 0x00 }, + { WSA881X_TEMP_IREF_CTRL, 0x56 }, + { WSA881X_TEMP_ISENS_CTRL, 0x47 }, + { WSA881X_TEMP_CLK_CTRL, 0x87 }, + { WSA881X_TEMP_TEST, 0x00 }, + { WSA881X_TEMP_BIAS, 0x51 }, + { WSA881X_TEMP_DOUT_MSB, 0x00 }, + { WSA881X_TEMP_DOUT_LSB, 0x00 }, + { WSA881X_ADC_EN_MODU_V, 0x00 }, + { WSA881X_ADC_EN_MODU_I, 0x00 }, + { WSA881X_ADC_EN_DET_TEST_V, 0x00 }, + { WSA881X_ADC_EN_DET_TEST_I, 0x00 }, + { WSA881X_ADC_EN_SEL_IBAIS, 0x10 }, + { WSA881X_SPKR_DRV_EN, 0x74 }, + { WSA881X_SPKR_DRV_DBG, 0x15 }, + { WSA881X_SPKR_PWRSTG_DBG, 0x00 }, + { WSA881X_SPKR_OCP_CTL, 0xD4 }, + { WSA881X_SPKR_CLIP_CTL, 0x90 }, + { WSA881X_SPKR_PA_INT, 0x54 }, + { WSA881X_SPKR_BIAS_CAL, 0xAC }, + { WSA881X_SPKR_STATUS1, 0x00 }, + { WSA881X_SPKR_STATUS2, 0x00 }, + { WSA881X_BOOST_EN_CTL, 0x18 }, + { WSA881X_BOOST_CURRENT_LIMIT, 0x7A }, + { WSA881X_BOOST_PRESET_OUT2, 0x70 }, + { WSA881X_BOOST_FORCE_OUT, 0x0E }, + { WSA881X_BOOST_LDO_PROG, 0x16 }, + { WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x71 }, + { WSA881X_BOOST_RON_CTL, 0x0F }, + { WSA881X_BOOST_ZX_CTL, 0x34 }, + { WSA881X_BOOST_START_CTL, 0x23 }, + { WSA881X_BOOST_MISC1_CTL, 0x80 }, + { WSA881X_BOOST_MISC2_CTL, 0x00 }, + { WSA881X_BOOST_MISC3_CTL, 0x00 }, + { WSA881X_BOOST_ATEST_CTL, 0x00 }, + { WSA881X_SPKR_PROT_FE_GAIN, 0x46 }, + { WSA881X_SPKR_PROT_FE_CM_LDO_SET, 0x3B }, + { WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1, 0x8D }, + { WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2, 0x8D }, + { WSA881X_SPKR_PROT_ATEST1, 0x01 }, + { WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x8D }, + { WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1, 0x4D }, + { WSA881X_SPKR_PROT_SAR, 0x00 }, + { WSA881X_SPKR_STATUS3, 0x00 }, +}; + +static const struct reg_sequence wsa881x_pre_pmu_pa_2_0[] = { + { WSA881X_SPKR_DRV_GAIN, 0x41, 0 }, + { WSA881X_SPKR_MISC_CTL1, 0x87, 0 }, +}; + +static const struct reg_sequence wsa881x_vi_txfe_en_2_0[] = { + { WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x85, 0 }, + { WSA881X_SPKR_PROT_ATEST2, 0x0A, 0 }, + { WSA881X_SPKR_PROT_FE_GAIN, 0x47, 0 }, +}; + +/* Default register reset values for WSA881x rev 2.0 */ +static struct reg_sequence wsa881x_rev_2_0[] = { + { WSA881X_RESET_CTL, 0x00, 0x00 }, + { WSA881X_TADC_VALUE_CTL, 0x01, 0x00 }, + { WSA881X_INTR_MASK, 0x1B, 0x00 }, + { WSA881X_IOPAD_CTL, 0x00, 0x00 }, + { WSA881X_OTP_REG_28, 0x3F, 0x00 }, + { WSA881X_OTP_REG_29, 0x3F, 0x00 }, + { WSA881X_OTP_REG_30, 0x01, 0x00 }, + { WSA881X_OTP_REG_31, 0x01, 0x00 }, + { WSA881X_TEMP_ADC_CTRL, 0x03, 0x00 }, + { WSA881X_ADC_SEL_IBIAS, 0x45, 0x00 }, + { WSA881X_SPKR_DRV_GAIN, 0xC1, 0x00 }, + { WSA881X_SPKR_DAC_CTL, 0x42, 0x00 }, + { WSA881X_SPKR_BBM_CTL, 0x02, 0x00 }, + { WSA881X_SPKR_MISC_CTL1, 0x40, 0x00 }, + { WSA881X_SPKR_MISC_CTL2, 0x07, 0x00 }, + { WSA881X_SPKR_BIAS_INT, 0x5F, 0x00 }, + { WSA881X_SPKR_BIAS_PSRR, 0x44, 0x00 }, + { WSA881X_BOOST_PS_CTL, 0xA0, 0x00 }, + { WSA881X_BOOST_PRESET_OUT1, 0xB7, 0x00 }, + { WSA881X_BOOST_LOOP_STABILITY, 0x8D, 0x00 }, + { WSA881X_SPKR_PROT_ATEST2, 0x02, 0x00 }, + { WSA881X_BONGO_RESRV_REG1, 0x5E, 0x00 }, + { WSA881X_BONGO_RESRV_REG2, 0x07, 0x00 }, +}; + +enum wsa_port_ids { + WSA881X_PORT_DAC, + WSA881X_PORT_COMP, + WSA881X_PORT_BOOST, + WSA881X_PORT_VISENSE, +}; + +/* 4 ports */ +static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = { + { + /* DAC */ + .num = 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + }, { + /* COMP */ + .num = 2, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + }, { + /* BOOST */ + .num = 3, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + }, { + /* VISENSE */ + .num = 4, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + } +}; + +static struct sdw_port_config wsa881x_pconfig[WSA881X_MAX_SWR_PORTS] = { + { + .num = 1, + .ch_mask = 0x1, + }, { + .num = 2, + .ch_mask = 0xf, + }, { + .num = 3, + .ch_mask = 0x3, + }, { /* IV feedback */ + .num = 4, + .ch_mask = 0x3, + }, +}; + +static bool wsa881x_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA881X_CHIP_ID0: + case WSA881X_CHIP_ID1: + case WSA881X_CHIP_ID2: + case WSA881X_CHIP_ID3: + case WSA881X_BUS_ID: + case WSA881X_CDC_RST_CTL: + case WSA881X_CDC_TOP_CLK_CTL: + case WSA881X_CDC_ANA_CLK_CTL: + case WSA881X_CDC_DIG_CLK_CTL: + case WSA881X_CLOCK_CONFIG: + case WSA881X_ANA_CTL: + case WSA881X_SWR_RESET_EN: + case WSA881X_RESET_CTL: + case WSA881X_TADC_VALUE_CTL: + case WSA881X_TEMP_DETECT_CTL: + case WSA881X_TEMP_MSB: + case WSA881X_TEMP_LSB: + case WSA881X_TEMP_CONFIG0: + case WSA881X_TEMP_CONFIG1: + case WSA881X_CDC_CLIP_CTL: + case WSA881X_SDM_PDM9_LSB: + case WSA881X_SDM_PDM9_MSB: + case WSA881X_CDC_RX_CTL: + case WSA881X_DEM_BYPASS_DATA0: + case WSA881X_DEM_BYPASS_DATA1: + case WSA881X_DEM_BYPASS_DATA2: + case WSA881X_DEM_BYPASS_DATA3: + case WSA881X_OTP_CTRL0: + case WSA881X_OTP_CTRL1: + case WSA881X_HDRIVE_CTL_GROUP1: + case WSA881X_INTR_MODE: + case WSA881X_INTR_MASK: + case WSA881X_INTR_STATUS: + case WSA881X_INTR_CLEAR: + case WSA881X_INTR_LEVEL: + case WSA881X_INTR_SET: + case WSA881X_INTR_TEST: + case WSA881X_PDM_TEST_MODE: + case WSA881X_ATE_TEST_MODE: + case WSA881X_PIN_CTL_MODE: + case WSA881X_PIN_CTL_OE: + case WSA881X_PIN_WDATA_IOPAD: + case WSA881X_PIN_STATUS: + case WSA881X_DIG_DEBUG_MODE: + case WSA881X_DIG_DEBUG_SEL: + case WSA881X_DIG_DEBUG_EN: + case WSA881X_SWR_HM_TEST1: + case WSA881X_SWR_HM_TEST2: + case WSA881X_TEMP_DETECT_DBG_CTL: + case WSA881X_TEMP_DEBUG_MSB: + case WSA881X_TEMP_DEBUG_LSB: + case WSA881X_SAMPLE_EDGE_SEL: + case WSA881X_IOPAD_CTL: + case WSA881X_SPARE_0: + case WSA881X_SPARE_1: + case WSA881X_SPARE_2: + case WSA881X_OTP_REG_0: + case WSA881X_OTP_REG_1: + case WSA881X_OTP_REG_2: + case WSA881X_OTP_REG_3: + case WSA881X_OTP_REG_4: + case WSA881X_OTP_REG_5: + case WSA881X_OTP_REG_6: + case WSA881X_OTP_REG_7: + case WSA881X_OTP_REG_8: + case WSA881X_OTP_REG_9: + case WSA881X_OTP_REG_10: + case WSA881X_OTP_REG_11: + case WSA881X_OTP_REG_12: + case WSA881X_OTP_REG_13: + case WSA881X_OTP_REG_14: + case WSA881X_OTP_REG_15: + case WSA881X_OTP_REG_16: + case WSA881X_OTP_REG_17: + case WSA881X_OTP_REG_18: + case WSA881X_OTP_REG_19: + case WSA881X_OTP_REG_20: + case WSA881X_OTP_REG_21: + case WSA881X_OTP_REG_22: + case WSA881X_OTP_REG_23: + case WSA881X_OTP_REG_24: + case WSA881X_OTP_REG_25: + case WSA881X_OTP_REG_26: + case WSA881X_OTP_REG_27: + case WSA881X_OTP_REG_28: + case WSA881X_OTP_REG_29: + case WSA881X_OTP_REG_30: + case WSA881X_OTP_REG_31: + case WSA881X_OTP_REG_63: + case WSA881X_BIAS_REF_CTRL: + case WSA881X_BIAS_TEST: + case WSA881X_BIAS_BIAS: + case WSA881X_TEMP_OP: + case WSA881X_TEMP_IREF_CTRL: + case WSA881X_TEMP_ISENS_CTRL: + case WSA881X_TEMP_CLK_CTRL: + case WSA881X_TEMP_TEST: + case WSA881X_TEMP_BIAS: + case WSA881X_TEMP_ADC_CTRL: + case WSA881X_TEMP_DOUT_MSB: + case WSA881X_TEMP_DOUT_LSB: + case WSA881X_ADC_EN_MODU_V: + case WSA881X_ADC_EN_MODU_I: + case WSA881X_ADC_EN_DET_TEST_V: + case WSA881X_ADC_EN_DET_TEST_I: + case WSA881X_ADC_SEL_IBIAS: + case WSA881X_ADC_EN_SEL_IBAIS: + case WSA881X_SPKR_DRV_EN: + case WSA881X_SPKR_DRV_GAIN: + case WSA881X_SPKR_DAC_CTL: + case WSA881X_SPKR_DRV_DBG: + case WSA881X_SPKR_PWRSTG_DBG: + case WSA881X_SPKR_OCP_CTL: + case WSA881X_SPKR_CLIP_CTL: + case WSA881X_SPKR_BBM_CTL: + case WSA881X_SPKR_MISC_CTL1: + case WSA881X_SPKR_MISC_CTL2: + case WSA881X_SPKR_BIAS_INT: + case WSA881X_SPKR_PA_INT: + case WSA881X_SPKR_BIAS_CAL: + case WSA881X_SPKR_BIAS_PSRR: + case WSA881X_SPKR_STATUS1: + case WSA881X_SPKR_STATUS2: + case WSA881X_BOOST_EN_CTL: + case WSA881X_BOOST_CURRENT_LIMIT: + case WSA881X_BOOST_PS_CTL: + case WSA881X_BOOST_PRESET_OUT1: + case WSA881X_BOOST_PRESET_OUT2: + case WSA881X_BOOST_FORCE_OUT: + case WSA881X_BOOST_LDO_PROG: + case WSA881X_BOOST_SLOPE_COMP_ISENSE_FB: + case WSA881X_BOOST_RON_CTL: + case WSA881X_BOOST_LOOP_STABILITY: + case WSA881X_BOOST_ZX_CTL: + case WSA881X_BOOST_START_CTL: + case WSA881X_BOOST_MISC1_CTL: + case WSA881X_BOOST_MISC2_CTL: + case WSA881X_BOOST_MISC3_CTL: + case WSA881X_BOOST_ATEST_CTL: + case WSA881X_SPKR_PROT_FE_GAIN: + case WSA881X_SPKR_PROT_FE_CM_LDO_SET: + case WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1: + case WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2: + case WSA881X_SPKR_PROT_ATEST1: + case WSA881X_SPKR_PROT_ATEST2: + case WSA881X_SPKR_PROT_FE_VSENSE_VCM: + case WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1: + case WSA881X_BONGO_RESRV_REG1: + case WSA881X_BONGO_RESRV_REG2: + case WSA881X_SPKR_PROT_SAR: + case WSA881X_SPKR_STATUS3: + return true; + default: + return false; + } +} + +static bool wsa881x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA881X_CHIP_ID0: + case WSA881X_CHIP_ID1: + case WSA881X_CHIP_ID2: + case WSA881X_CHIP_ID3: + case WSA881X_BUS_ID: + case WSA881X_TEMP_MSB: + case WSA881X_TEMP_LSB: + case WSA881X_SDM_PDM9_LSB: + case WSA881X_SDM_PDM9_MSB: + case WSA881X_OTP_CTRL1: + case WSA881X_INTR_STATUS: + case WSA881X_ATE_TEST_MODE: + case WSA881X_PIN_STATUS: + case WSA881X_SWR_HM_TEST2: + case WSA881X_SPKR_STATUS1: + case WSA881X_SPKR_STATUS2: + case WSA881X_SPKR_STATUS3: + case WSA881X_OTP_REG_0: + case WSA881X_OTP_REG_1: + case WSA881X_OTP_REG_2: + case WSA881X_OTP_REG_3: + case WSA881X_OTP_REG_4: + case WSA881X_OTP_REG_5: + case WSA881X_OTP_REG_31: + case WSA881X_TEMP_DOUT_MSB: + case WSA881X_TEMP_DOUT_LSB: + case WSA881X_TEMP_OP: + case WSA881X_SPKR_PROT_SAR: + return true; + default: + return false; + } +} + +static struct regmap_config wsa881x_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wsa881x_defaults, + .num_reg_defaults = ARRAY_SIZE(wsa881x_defaults), + .volatile_reg = wsa881x_volatile_register, + .readable_reg = wsa881x_readable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .can_multi_write = true, +}; + +enum { + G_18DB = 0, + G_16P5DB, + G_15DB, + G_13P5DB, + G_12DB, + G_10P5DB, + G_9DB, + G_7P5DB, + G_6DB, + G_4P5DB, + G_3DB, + G_1P5DB, + G_0DB, +}; + +/* + * Private data Structure for wsa881x. All parameters related to + * WSA881X codec needs to be defined here. + */ +struct wsa881x_priv { + struct regmap *regmap; + struct device *dev; + struct sdw_slave *slave; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WSA881X_MAX_SWR_PORTS]; + struct gpio_desc *sd_n; + int version; + int active_ports; + bool port_prepared[WSA881X_MAX_SWR_PORTS]; + bool port_enable[WSA881X_MAX_SWR_PORTS]; + bool stream_prepared; +}; + +static void wsa881x_init(struct wsa881x_priv *wsa881x) +{ + struct regmap *rm = wsa881x->regmap; + unsigned int val = 0; + + regmap_read(rm, WSA881X_CHIP_ID1, &wsa881x->version); + regmap_register_patch(wsa881x->regmap, wsa881x_rev_2_0, + ARRAY_SIZE(wsa881x_rev_2_0)); + + /* Enable software reset output from soundwire slave */ + regmap_update_bits(rm, WSA881X_SWR_RESET_EN, 0x07, 0x07); + + /* Bring out of analog reset */ + regmap_update_bits(rm, WSA881X_CDC_RST_CTL, 0x02, 0x02); + + /* Bring out of digital reset */ + regmap_update_bits(rm, WSA881X_CDC_RST_CTL, 0x01, 0x01); + regmap_update_bits(rm, WSA881X_CLOCK_CONFIG, 0x10, 0x10); + regmap_update_bits(rm, WSA881X_SPKR_OCP_CTL, 0x02, 0x02); + regmap_update_bits(rm, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80); + regmap_update_bits(rm, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06); + regmap_update_bits(rm, WSA881X_SPKR_BIAS_INT, 0xFF, 0x00); + regmap_update_bits(rm, WSA881X_SPKR_PA_INT, 0xF0, 0x40); + regmap_update_bits(rm, WSA881X_SPKR_PA_INT, 0x0E, 0x0E); + regmap_update_bits(rm, WSA881X_BOOST_LOOP_STABILITY, 0x03, 0x03); + regmap_update_bits(rm, WSA881X_BOOST_MISC2_CTL, 0xFF, 0x14); + regmap_update_bits(rm, WSA881X_BOOST_START_CTL, 0x80, 0x80); + regmap_update_bits(rm, WSA881X_BOOST_START_CTL, 0x03, 0x00); + regmap_update_bits(rm, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x0C, 0x04); + regmap_update_bits(rm, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x03, 0x00); + + regmap_read(rm, WSA881X_OTP_REG_0, &val); + if (val) + regmap_update_bits(rm, WSA881X_BOOST_PRESET_OUT1, 0xF0, 0x70); + + regmap_update_bits(rm, WSA881X_BOOST_PRESET_OUT2, 0xF0, 0x30); + regmap_update_bits(rm, WSA881X_SPKR_DRV_EN, 0x08, 0x08); + regmap_update_bits(rm, WSA881X_BOOST_CURRENT_LIMIT, 0x0F, 0x08); + regmap_update_bits(rm, WSA881X_SPKR_OCP_CTL, 0x30, 0x30); + regmap_update_bits(rm, WSA881X_SPKR_OCP_CTL, 0x0C, 0x00); + regmap_update_bits(rm, WSA881X_OTP_REG_28, 0x3F, 0x3A); + regmap_update_bits(rm, WSA881X_BONGO_RESRV_REG1, 0xFF, 0xB2); + regmap_update_bits(rm, WSA881X_BONGO_RESRV_REG2, 0xFF, 0x05); +} + +static int wsa881x_component_probe(struct snd_soc_component *comp) +{ + struct wsa881x_priv *wsa881x = snd_soc_component_get_drvdata(comp); + + snd_soc_component_init_regmap(comp, wsa881x->regmap); + + return 0; +} + +static int wsa881x_put_pa_gain(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kc); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kc->private_value; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + int val, ret, min_gain, max_gain; + + max_gain = (max - ucontrol->value.integer.value[0]) & mask; + /* + * Gain has to set incrementally in 4 steps + * as per HW sequence + */ + if (max_gain > G_4P5DB) + min_gain = G_0DB; + else + min_gain = max_gain + 3; + /* + * 1ms delay is needed before change in gain + * as per HW requirement. + */ + usleep_range(1000, 1010); + + for (val = min_gain; max_gain <= val; val--) { + ret = snd_soc_component_update_bits(comp, + WSA881X_SPKR_DRV_GAIN, + WSA881X_SPKR_PAG_GAIN_MASK, + val << 4); + if (ret < 0) + dev_err(comp->dev, "Failed to change PA gain"); + + usleep_range(1000, 1010); + } + return 0; +} + +static int wsa881x_get_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa881x_priv *data = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + ucontrol->value.integer.value[0] = data->port_enable[portidx]; + + + return 0; +} + +static int wsa881x_boost_ctrl(struct snd_soc_component *comp, bool enable) +{ + if (enable) + snd_soc_component_update_bits(comp, WSA881X_BOOST_EN_CTL, + WSA881X_BOOST_EN_MASK, + WSA881X_BOOST_EN); + else + snd_soc_component_update_bits(comp, WSA881X_BOOST_EN_CTL, + WSA881X_BOOST_EN_MASK, 0); + /* + * 1.5ms sleep is needed after boost enable/disable as per + * HW requirement + */ + usleep_range(1500, 1510); + return 0; +} + +static int wsa881x_set_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa881x_priv *data = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + if (ucontrol->value.integer.value[0]) + data->port_enable[portidx] = true; + else + data->port_enable[portidx] = false; + + if (portidx == WSA881X_PORT_BOOST) /* Boost Switch */ + wsa881x_boost_ctrl(comp, data->port_enable[portidx]); + + return 0; +} + +static const char * const smart_boost_lvl_text[] = { + "6.625 V", "6.750 V", "6.875 V", "7.000 V", + "7.125 V", "7.250 V", "7.375 V", "7.500 V", + "7.625 V", "7.750 V", "7.875 V", "8.000 V", + "8.125 V", "8.250 V", "8.375 V", "8.500 V" +}; + +static const struct soc_enum smart_boost_lvl_enum = + SOC_ENUM_SINGLE(WSA881X_BOOST_PRESET_OUT1, 0, + ARRAY_SIZE(smart_boost_lvl_text), + smart_boost_lvl_text); + +static const DECLARE_TLV_DB_SCALE(pa_gain, 0, 150, 0); + +static const struct snd_kcontrol_new wsa881x_snd_controls[] = { + SOC_ENUM("Smart Boost Level", smart_boost_lvl_enum), + WSA881X_PA_GAIN_TLV("PA Volume", WSA881X_SPKR_DRV_GAIN, + 4, 0xC, 1, pa_gain), + SOC_SINGLE_EXT("DAC Switch", WSA881X_PORT_DAC, 0, 1, 0, + wsa881x_get_port, wsa881x_set_port), + SOC_SINGLE_EXT("COMP Switch", WSA881X_PORT_COMP, 0, 1, 0, + wsa881x_get_port, wsa881x_set_port), + SOC_SINGLE_EXT("BOOST Switch", WSA881X_PORT_BOOST, 0, 1, 0, + wsa881x_get_port, wsa881x_set_port), + SOC_SINGLE_EXT("VISENSE Switch", WSA881X_PORT_VISENSE, 0, 1, 0, + wsa881x_get_port, wsa881x_set_port), +}; + +static const struct snd_soc_dapm_route wsa881x_audio_map[] = { + { "RDAC", NULL, "IN" }, + { "RDAC", NULL, "DCLK" }, + { "RDAC", NULL, "ACLK" }, + { "RDAC", NULL, "Bandgap" }, + { "SPKR PGA", NULL, "RDAC" }, + { "SPKR", NULL, "SPKR PGA" }, +}; + +static int wsa881x_visense_txfe_ctrl(struct snd_soc_component *comp, + bool enable) +{ + struct wsa881x_priv *wsa881x = snd_soc_component_get_drvdata(comp); + + if (enable) { + regmap_multi_reg_write(wsa881x->regmap, wsa881x_vi_txfe_en_2_0, + ARRAY_SIZE(wsa881x_vi_txfe_en_2_0)); + } else { + snd_soc_component_update_bits(comp, + WSA881X_SPKR_PROT_FE_VSENSE_VCM, + 0x08, 0x08); + /* + * 200us sleep is needed after visense txfe disable as per + * HW requirement. + */ + usleep_range(200, 210); + snd_soc_component_update_bits(comp, WSA881X_SPKR_PROT_FE_GAIN, + 0x01, 0x00); + } + return 0; +} + +static int wsa881x_visense_adc_ctrl(struct snd_soc_component *comp, + bool enable) +{ + snd_soc_component_update_bits(comp, WSA881X_ADC_EN_MODU_V, BIT(7), + (enable << 7)); + snd_soc_component_update_bits(comp, WSA881X_ADC_EN_MODU_I, BIT(7), + (enable << 7)); + return 0; +} + +static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wsa881x_priv *wsa881x = snd_soc_component_get_drvdata(comp); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(comp, WSA881X_SPKR_OCP_CTL, + WSA881X_SPKR_OCP_MASK, + WSA881X_SPKR_OCP_EN); + regmap_multi_reg_write(wsa881x->regmap, wsa881x_pre_pmu_pa_2_0, + ARRAY_SIZE(wsa881x_pre_pmu_pa_2_0)); + + snd_soc_component_update_bits(comp, WSA881X_SPKR_DRV_GAIN, + WSA881X_PA_GAIN_SEL_MASK, + WSA881X_PA_GAIN_SEL_REG); + break; + case SND_SOC_DAPM_POST_PMU: + if (wsa881x->port_prepared[WSA881X_PORT_VISENSE]) { + wsa881x_visense_txfe_ctrl(comp, true); + snd_soc_component_update_bits(comp, + WSA881X_ADC_EN_SEL_IBAIS, + 0x07, 0x01); + wsa881x_visense_adc_ctrl(comp, true); + } + + break; + case SND_SOC_DAPM_POST_PMD: + if (wsa881x->port_prepared[WSA881X_PORT_VISENSE]) { + wsa881x_visense_adc_ctrl(comp, false); + wsa881x_visense_txfe_ctrl(comp, false); + } + + snd_soc_component_update_bits(comp, WSA881X_SPKR_OCP_CTL, + WSA881X_SPKR_OCP_MASK, + WSA881X_SPKR_OCP_EN | + WSA881X_SPKR_OCP_HOLD); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_DAC_E("RDAC", NULL, WSA881X_SPKR_DAC_CTL, 7, 0, + NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("SPKR PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + wsa881x_spkr_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DCLK", WSA881X_CDC_DIG_CLK_CTL, 0, 0, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("ACLK", WSA881X_CDC_ANA_CLK_CTL, 0, 0, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("Bandgap", WSA881X_TEMP_OP, 3, 0, + NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("SPKR"), +}; + +static int wsa881x_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); + int ret; + + if (wsa881x->stream_prepared) { + sdw_disable_stream(wsa881x->sruntime); + sdw_deprepare_stream(wsa881x->sruntime); + wsa881x->stream_prepared = false; + } + + + ret = sdw_prepare_stream(wsa881x->sruntime); + if (ret) + return ret; + + /** + * NOTE: there is a strict hw requirement about the ordering of port + * enables and actual PA enable. PA enable should only happen after + * soundwire ports are enabled if not DC on the line is accumulated + * resulting in Click/Pop Noise + * PA enable/mute are handled as part of DAPM and digital mute. + */ + + ret = sdw_enable_stream(wsa881x->sruntime); + if (ret) { + sdw_deprepare_stream(wsa881x->sruntime); + return ret; + } + wsa881x->stream_prepared = true; + + return ret; +} + +static int wsa881x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); + int i; + + wsa881x->active_ports = 0; + for (i = 0; i < WSA881X_MAX_SWR_PORTS; i++) { + if (!wsa881x->port_enable[i]) + continue; + + wsa881x->port_config[wsa881x->active_ports] = + wsa881x_pconfig[i]; + wsa881x->active_ports++; + } + + return sdw_stream_add_slave(wsa881x->slave, &wsa881x->sconfig, + wsa881x->port_config, wsa881x->active_ports, + wsa881x->sruntime); +} + +static int wsa881x_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); + + if (wsa881x->stream_prepared) { + sdw_disable_stream(wsa881x->sruntime); + sdw_deprepare_stream(wsa881x->sruntime); + sdw_stream_remove_slave(wsa881x->slave, wsa881x->sruntime); + wsa881x->stream_prepared = false; + } + + return 0; +} + +static int wsa881x_set_sdw_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); + + wsa881x->sruntime = stream; + + return 0; +} + +static int wsa881x_digital_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); + + if (mute) + regmap_update_bits(wsa881x->regmap, WSA881X_SPKR_DRV_EN, 0x80, + 0x00); + else + regmap_update_bits(wsa881x->regmap, WSA881X_SPKR_DRV_EN, 0x80, + 0x80); + + return 0; +} + +static struct snd_soc_dai_ops wsa881x_dai_ops = { + .hw_params = wsa881x_hw_params, + .prepare = wsa881x_prepare, + .hw_free = wsa881x_hw_free, + .mute_stream = wsa881x_digital_mute, + .set_sdw_stream = wsa881x_set_sdw_stream, +}; + +static struct snd_soc_dai_driver wsa881x_dais[] = { + { + .name = "SPKR", + .id = 0, + .playback = { + .stream_name = "SPKR Playback", + .rate_max = 48000, + .rate_min = 48000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &wsa881x_dai_ops, + }, +}; + +static const struct snd_soc_component_driver wsa881x_component_drv = { + .name = "WSA881x", + .probe = wsa881x_component_probe, + .controls = wsa881x_snd_controls, + .num_controls = ARRAY_SIZE(wsa881x_snd_controls), + .dapm_widgets = wsa881x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets), + .dapm_routes = wsa881x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map), +}; + +static int wsa881x_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_ATTACHED && slave->dev_num > 0) + wsa881x_init(wsa881x); + + return 0; +} + +static int wsa881x_port_prep(struct sdw_slave *slave, + struct sdw_prepare_ch *prepare_ch, + enum sdw_port_prep_ops state) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(&slave->dev); + + if (state == SDW_OPS_PORT_POST_PREP) + wsa881x->port_prepared[prepare_ch->num - 1] = true; + else + wsa881x->port_prepared[prepare_ch->num - 1] = false; + + return 0; +} + +static int wsa881x_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank), + 0x01); + + return 0; +} + +static struct sdw_slave_ops wsa881x_slave_ops = { + .update_status = wsa881x_update_status, + .bus_config = wsa881x_bus_config, + .port_prep = wsa881x_port_prep, +}; + +static int wsa881x_probe(struct sdw_slave *pdev, + const struct sdw_device_id *id) +{ + struct wsa881x_priv *wsa881x; + + wsa881x = devm_kzalloc(&pdev->dev, sizeof(*wsa881x), GFP_KERNEL); + if (!wsa881x) + return -ENOMEM; + + wsa881x->sd_n = devm_gpiod_get_optional(&pdev->dev, "powerdown", + GPIOD_FLAGS_BIT_NONEXCLUSIVE); + if (IS_ERR(wsa881x->sd_n)) { + dev_err(&pdev->dev, "Shutdown Control GPIO not found\n"); + return PTR_ERR(wsa881x->sd_n); + } + + dev_set_drvdata(&pdev->dev, wsa881x); + wsa881x->slave = pdev; + wsa881x->dev = &pdev->dev; + wsa881x->sconfig.ch_count = 1; + wsa881x->sconfig.bps = 1; + wsa881x->sconfig.frame_rate = 48000; + wsa881x->sconfig.direction = SDW_DATA_DIR_RX; + wsa881x->sconfig.type = SDW_STREAM_PDM; + pdev->prop.sink_ports = GENMASK(WSA881X_MAX_SWR_PORTS, 0); + pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop; + gpiod_set_value(wsa881x->sd_n, 1); + + wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config); + if (IS_ERR(wsa881x->regmap)) { + dev_err(&pdev->dev, "regmap_init failed\n"); + return PTR_ERR(wsa881x->regmap); + } + + return devm_snd_soc_register_component(&pdev->dev, + &wsa881x_component_drv, + wsa881x_dais, + ARRAY_SIZE(wsa881x_dais)); +} + +static const struct sdw_device_id wsa881x_slave_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x2010, 0), + SDW_SLAVE_ENTRY(0x0217, 0x2110, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, wsa881x_slave_id); + +static struct sdw_driver wsa881x_codec_driver = { + .probe = wsa881x_probe, + .ops = &wsa881x_slave_ops, + .id_table = wsa881x_slave_id, + .driver = { + .name = "wsa881x-codec", + } +}; +module_sdw_driver(wsa881x_codec_driver); + +MODULE_DESCRIPTION("WSA881x codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 65112b9d8588..7eeca2150b2d 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -390,10 +390,6 @@ static const struct snd_soc_dai_ops dw_i2s_dai_ops = { .set_fmt = dw_i2s_set_fmt, }; -static const struct snd_soc_component_driver dw_i2s_component = { - .name = "dw-i2s", -}; - #ifdef CONFIG_PM static int dw_i2s_runtime_suspend(struct device *dev) { @@ -413,26 +409,30 @@ static int dw_i2s_runtime_resume(struct device *dev) return 0; } -static int dw_i2s_suspend(struct snd_soc_dai *dai) +static int dw_i2s_suspend(struct snd_soc_component *component) { - struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component); if (dev->capability & DW_I2S_MASTER) clk_disable(dev->clk); return 0; } -static int dw_i2s_resume(struct snd_soc_dai *dai) +static int dw_i2s_resume(struct snd_soc_component *component) { - struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component); + struct snd_soc_dai *dai; if (dev->capability & DW_I2S_MASTER) clk_enable(dev->clk); - if (dai->playback_active) - dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK); - if (dai->capture_active) - dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE); + for_each_component_dais(component, dai) { + if (dai->playback_active) + dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK); + if (dai->capture_active) + dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE); + } + return 0; } @@ -441,6 +441,12 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) #define dw_i2s_resume NULL #endif +static const struct snd_soc_component_driver dw_i2s_component = { + .name = "dw-i2s", + .suspend = dw_i2s_suspend, + .resume = dw_i2s_resume, +}; + /* * The following tables allow a direct lookup of various parameters * defined in the I2S block's configuration in terms of sound system @@ -629,8 +635,6 @@ static int dw_i2s_probe(struct platform_device *pdev) return -ENOMEM; dw_i2s_dai->ops = &dw_i2s_dai_ops; - dw_i2s_dai->suspend = dw_i2s_suspend; - dw_i2s_dai->resume = dw_i2s_resume; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); dev->i2s_base = devm_ioremap_resource(&pdev->dev, res); diff --git a/sound/soc/dwc/dwc-pcm.c b/sound/soc/dwc/dwc-pcm.c index 4771eb5fbe2a..4b25aca3804f 100644 --- a/sound/soc/dwc/dwc-pcm.c +++ b/sound/soc/dwc/dwc-pcm.c @@ -162,7 +162,6 @@ static int dw_pcm_hw_params(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; struct dw_i2s_dev *dev = runtime->private_data; - int ret; switch (params_channels(hw_params)) { case 2: @@ -187,18 +186,7 @@ static int dw_pcm_hw_params(struct snd_soc_component *component, return -EINVAL; } - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) - return ret; - else - return 0; -} - -static int dw_pcm_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); + return 0; } static int dw_pcm_trigger(struct snd_soc_component *component, @@ -256,28 +244,19 @@ static int dw_pcm_new(struct snd_soc_component *component, { size_t size = dw_pcm_hardware.buffer_bytes_max; - snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_CONTINUOUS, NULL, size, size); return 0; } -static void dw_pcm_free(struct snd_soc_component *component, - struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static const struct snd_soc_component_driver dw_pcm_component = { .open = dw_pcm_open, .close = dw_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = dw_pcm_hw_params, - .hw_free = dw_pcm_hw_free, .trigger = dw_pcm_trigger, .pointer = dw_pcm_pointer, .pcm_construct = dw_pcm_new, - .pcm_destruct = dw_pcm_free, }; int dw_pcm_register(struct platform_device *pdev) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 39ea9bda1394..9ce55feaac22 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -256,7 +256,7 @@ static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card, unsigned int pll_out; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); codec_dai = rtd->codec_dai; if (dapm->dev != codec_dai->dev) return 0; diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index a3cfceea7d2f..0dcebc24c312 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -41,26 +41,65 @@ static struct snd_pcm_hw_constraint_list fsl_asrc_rate_constraints = { * The following tables map the relationship between asrc_inclk/asrc_outclk in * fsl_asrc.h and the registers of ASRCSR */ -static unsigned char input_clk_map_imx35[] = { +static unsigned char input_clk_map_imx35[ASRC_CLK_MAP_LEN] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, }; -static unsigned char output_clk_map_imx35[] = { +static unsigned char output_clk_map_imx35[ASRC_CLK_MAP_LEN] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, }; /* i.MX53 uses the same map for input and output */ -static unsigned char input_clk_map_imx53[] = { +static unsigned char input_clk_map_imx53[ASRC_CLK_MAP_LEN] = { /* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */ 0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd, + 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, + 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, }; -static unsigned char output_clk_map_imx53[] = { +static unsigned char output_clk_map_imx53[ASRC_CLK_MAP_LEN] = { /* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */ 0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd, + 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, + 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, }; -static unsigned char *clk_map[2]; +/** + * i.MX8QM/i.MX8QXP uses the same map for input and output. + * clk_map_imx8qm[0] is for i.MX8QM asrc0 + * clk_map_imx8qm[1] is for i.MX8QM asrc1 + * clk_map_imx8qxp[0] is for i.MX8QXP asrc0 + * clk_map_imx8qxp[1] is for i.MX8QXP asrc1 + */ +static unsigned char clk_map_imx8qm[2][ASRC_CLK_MAP_LEN] = { + { + 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, + }, + { + 0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0, + 0x0, 0x1, 0x2, 0x3, 0xb, 0xc, 0xf, 0xf, 0xd, 0xe, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, + 0x4, 0x5, 0x6, 0xf, 0x8, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, + }, +}; + +static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = { + { + 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xf, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xf, + 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, + }, + { + 0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0, + 0x0, 0x1, 0x2, 0x3, 0x7, 0x8, 0xf, 0xf, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, + 0xf, 0xf, 0x6, 0xf, 0xf, 0xf, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, + }, +}; /** * Select the pre-processing and post-processing options @@ -353,8 +392,8 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) } /* Validate input and output clock sources */ - clk_index[IN] = clk_map[IN][config->inclk]; - clk_index[OUT] = clk_map[OUT][config->outclk]; + clk_index[IN] = asrc_priv->clk_map[IN][config->inclk]; + clk_index[OUT] = asrc_priv->clk_map[OUT][config->outclk]; /* We only have output clock for ideal ratio mode */ clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]]; @@ -398,13 +437,13 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) /* Set the channel number */ channels = config->channel_num; - if (asrc_priv->channel_bits < 4) + if (asrc_priv->soc->channel_bits < 4) channels /= 2; /* Update channels for current pair */ regmap_update_bits(asrc_priv->regmap, REG_ASRCNCR, - ASRCNCR_ANCi_MASK(index, asrc_priv->channel_bits), - ASRCNCR_ANCi(index, channels, asrc_priv->channel_bits)); + ASRCNCR_ANCi_MASK(index, asrc_priv->soc->channel_bits), + ASRCNCR_ANCi(index, channels, asrc_priv->soc->channel_bits)); /* Default setting: Automatic selection for processing mode */ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, @@ -531,7 +570,7 @@ static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream, struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai); /* Odd channel number is not valid for older ASRC (channel_bits==3) */ - if (asrc_priv->channel_bits == 3) + if (asrc_priv->soc->channel_bits == 3) snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2); @@ -905,6 +944,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) struct resource *res; void __iomem *regs; int irq, ret, i; + u32 map_idx; char tmp[16]; asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL); @@ -964,14 +1004,37 @@ static int fsl_asrc_probe(struct platform_device *pdev) } } + asrc_priv->soc = of_device_get_match_data(&pdev->dev); + if (!asrc_priv->soc) { + dev_err(&pdev->dev, "failed to get soc data\n"); + return -ENODEV; + } + if (of_device_is_compatible(np, "fsl,imx35-asrc")) { - asrc_priv->channel_bits = 3; - clk_map[IN] = input_clk_map_imx35; - clk_map[OUT] = output_clk_map_imx35; - } else { - asrc_priv->channel_bits = 4; - clk_map[IN] = input_clk_map_imx53; - clk_map[OUT] = output_clk_map_imx53; + asrc_priv->clk_map[IN] = input_clk_map_imx35; + asrc_priv->clk_map[OUT] = output_clk_map_imx35; + } else if (of_device_is_compatible(np, "fsl,imx53-asrc")) { + asrc_priv->clk_map[IN] = input_clk_map_imx53; + asrc_priv->clk_map[OUT] = output_clk_map_imx53; + } else if (of_device_is_compatible(np, "fsl,imx8qm-asrc") || + of_device_is_compatible(np, "fsl,imx8qxp-asrc")) { + ret = of_property_read_u32(np, "fsl,asrc-clk-map", &map_idx); + if (ret) { + dev_err(&pdev->dev, "failed to get clk map index\n"); + return ret; + } + + if (map_idx > 1) { + dev_err(&pdev->dev, "unsupported clk map index\n"); + return -EINVAL; + } + if (of_device_is_compatible(np, "fsl,imx8qm-asrc")) { + asrc_priv->clk_map[IN] = clk_map_imx8qm[map_idx]; + asrc_priv->clk_map[OUT] = clk_map_imx8qm[map_idx]; + } else { + asrc_priv->clk_map[IN] = clk_map_imx8qxp[map_idx]; + asrc_priv->clk_map[OUT] = clk_map_imx8qxp[map_idx]; + } } ret = fsl_asrc_init(asrc_priv); @@ -1113,9 +1176,31 @@ static const struct dev_pm_ops fsl_asrc_pm = { SET_SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume) }; +static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = { + .use_edma = false, + .channel_bits = 3, +}; + +static const struct fsl_asrc_soc_data fsl_asrc_imx53_data = { + .use_edma = false, + .channel_bits = 4, +}; + +static const struct fsl_asrc_soc_data fsl_asrc_imx8qm_data = { + .use_edma = true, + .channel_bits = 4, +}; + +static const struct fsl_asrc_soc_data fsl_asrc_imx8qxp_data = { + .use_edma = true, + .channel_bits = 4, +}; + static const struct of_device_id fsl_asrc_ids[] = { - { .compatible = "fsl,imx35-asrc", }, - { .compatible = "fsl,imx53-asrc", }, + { .compatible = "fsl,imx35-asrc", .data = &fsl_asrc_imx35_data }, + { .compatible = "fsl,imx53-asrc", .data = &fsl_asrc_imx53_data }, + { .compatible = "fsl,imx8qm-asrc", .data = &fsl_asrc_imx8qm_data }, + { .compatible = "fsl,imx8qxp-asrc", .data = &fsl_asrc_imx8qxp_data }, {} }; MODULE_DEVICE_TABLE(of, fsl_asrc_ids); diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index 2b57e8c53728..8a821132d9d0 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -308,6 +308,29 @@ enum asrc_inclk { INCLK_SSI3_TX = 0x0b, INCLK_SPDIF_TX = 0x0c, INCLK_ASRCK1_CLK = 0x0f, + + /* clocks for imx8 */ + INCLK_AUD_PLL_DIV_CLK0 = 0x10, + INCLK_AUD_PLL_DIV_CLK1 = 0x11, + INCLK_AUD_CLK0 = 0x12, + INCLK_AUD_CLK1 = 0x13, + INCLK_ESAI0_RX_CLK = 0x14, + INCLK_ESAI0_TX_CLK = 0x15, + INCLK_SPDIF0_RX = 0x16, + INCLK_SPDIF1_RX = 0x17, + INCLK_SAI0_RX_BCLK = 0x18, + INCLK_SAI0_TX_BCLK = 0x19, + INCLK_SAI1_RX_BCLK = 0x1a, + INCLK_SAI1_TX_BCLK = 0x1b, + INCLK_SAI2_RX_BCLK = 0x1c, + INCLK_SAI3_RX_BCLK = 0x1d, + INCLK_ASRC0_MUX_CLK = 0x1e, + + INCLK_ESAI1_RX_CLK = 0x20, + INCLK_ESAI1_TX_CLK = 0x21, + INCLK_SAI6_TX_BCLK = 0x22, + INCLK_HDMI_RX_SAI0_RX_BCLK = 0x24, + INCLK_HDMI_TX_SAI0_TX_BCLK = 0x25, }; enum asrc_outclk { @@ -325,9 +348,33 @@ enum asrc_outclk { OUTCLK_SSI3_RX = 0x0b, OUTCLK_SPDIF_RX = 0x0c, OUTCLK_ASRCK1_CLK = 0x0f, + + /* clocks for imx8 */ + OUTCLK_AUD_PLL_DIV_CLK0 = 0x10, + OUTCLK_AUD_PLL_DIV_CLK1 = 0x11, + OUTCLK_AUD_CLK0 = 0x12, + OUTCLK_AUD_CLK1 = 0x13, + OUTCLK_ESAI0_RX_CLK = 0x14, + OUTCLK_ESAI0_TX_CLK = 0x15, + OUTCLK_SPDIF0_RX = 0x16, + OUTCLK_SPDIF1_RX = 0x17, + OUTCLK_SAI0_RX_BCLK = 0x18, + OUTCLK_SAI0_TX_BCLK = 0x19, + OUTCLK_SAI1_RX_BCLK = 0x1a, + OUTCLK_SAI1_TX_BCLK = 0x1b, + OUTCLK_SAI2_RX_BCLK = 0x1c, + OUTCLK_SAI3_RX_BCLK = 0x1d, + OUTCLK_ASRCO_MUX_CLK = 0x1e, + + OUTCLK_ESAI1_RX_CLK = 0x20, + OUTCLK_ESAI1_TX_CLK = 0x21, + OUTCLK_SAI6_TX_BCLK = 0x22, + OUTCLK_HDMI_RX_SAI0_RX_BCLK = 0x24, + OUTCLK_HDMI_TX_SAI0_TX_BCLK = 0x25, }; #define ASRC_CLK_MAX_NUM 16 +#define ASRC_CLK_MAP_LEN 0x30 enum asrc_word_width { ASRC_WIDTH_24_BIT = 0, @@ -388,6 +435,17 @@ struct dma_block { }; /** + * fsl_asrc_soc_data: soc specific data + * + * @use_edma: using edma as dma device or not + * @channel_bits: width of ASRCNCR register for each pair + */ +struct fsl_asrc_soc_data { + bool use_edma; + unsigned int channel_bits; +}; + +/** * fsl_asrc_pair: ASRC Pair private data * * @asrc_priv: pointer to its parent module @@ -431,8 +489,9 @@ struct fsl_asrc_pair { * @asrck_clk: clock sources to driver ASRC internal logic * @lock: spin lock for resource protection * @pair: pair pointers - * @channel_bits: width of ASRCNCR register for each pair + * @soc: soc specific data * @channel_avail: non-occupied channel numbers + * @clk_map: clock map for input/output clock * @asrc_rate: default sample rate for ASoC Back-Ends * @asrc_width: default sample width for ASoC Back-Ends * @regcache_cfg: store register value of REG_ASRCFG @@ -450,8 +509,9 @@ struct fsl_asrc { spinlock_t lock; struct fsl_asrc_pair *pair[ASRC_PAIR_MAX_NUM]; - unsigned int channel_bits; + const struct fsl_asrc_soc_data *soc; unsigned int channel_avail; + unsigned char *clk_map[2]; int asrc_rate; int asrc_width; diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index d6146de9acd2..ece130f59d15 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -197,21 +197,34 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component, dma_cap_set(DMA_SLAVE, mask); dma_cap_set(DMA_CYCLIC, mask); - /* Get DMA request of Back-End */ - tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx"); - tmp_data = tmp_chan->private; - pair->dma_data.dma_request = tmp_data->dma_request; - dma_release_channel(tmp_chan); - - /* Get DMA request of Front-End */ - tmp_chan = fsl_asrc_get_dma_channel(pair, dir); - tmp_data = tmp_chan->private; - pair->dma_data.dma_request2 = tmp_data->dma_request; - pair->dma_data.peripheral_type = tmp_data->peripheral_type; - pair->dma_data.priority = tmp_data->priority; - dma_release_channel(tmp_chan); + /* + * An EDMA DEV_TO_DEV channel is fixed and bound with DMA event of each + * peripheral, unlike SDMA channel that is allocated dynamically. So no + * need to configure dma_request and dma_request2, but get dma_chan via + * dma_request_slave_channel directly with dma name of Front-End device + */ + if (!asrc_priv->soc->use_edma) { + /* Get DMA request of Back-End */ + tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx"); + tmp_data = tmp_chan->private; + pair->dma_data.dma_request = tmp_data->dma_request; + dma_release_channel(tmp_chan); + + /* Get DMA request of Front-End */ + tmp_chan = fsl_asrc_get_dma_channel(pair, dir); + tmp_data = tmp_chan->private; + pair->dma_data.dma_request2 = tmp_data->dma_request; + pair->dma_data.peripheral_type = tmp_data->peripheral_type; + pair->dma_data.priority = tmp_data->priority; + dma_release_channel(tmp_chan); + + pair->dma_chan[dir] = + dma_request_channel(mask, filter, &pair->dma_data); + } else { + pair->dma_chan[dir] = + fsl_asrc_get_dma_channel(pair, dir); + } - pair->dma_chan[dir] = dma_request_channel(mask, filter, &pair->dma_data); if (!pair->dma_chan[dir]) { dev_err(dev, "failed to request DMA channel for Back-End\n"); return -EINVAL; @@ -428,7 +441,6 @@ static void fsl_asrc_dma_pcm_free(struct snd_soc_component *component, struct snd_soc_component_driver fsl_asrc_component = { .name = DRV_NAME, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = fsl_asrc_dma_hw_params, .hw_free = fsl_asrc_dma_hw_free, .trigger = fsl_asrc_dma_trigger, diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 2868c4f97cb2..13ae089c1911 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -903,7 +903,6 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) dma->dai.name = DRV_NAME; dma->dai.open = fsl_dma_open; dma->dai.close = fsl_dma_close; - dma->dai.ioctl = snd_soc_pcm_lib_ioctl; dma->dai.hw_params = fsl_dma_hw_params; dma->dai.hw_free = fsl_dma_hw_free; dma->dai.pointer = fsl_dma_pointer; diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index b517e4bc1b87..8c3ea7300972 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -958,7 +958,8 @@ static int fsl_sai_probe(struct platform_device *pdev) if (irq < 0) return irq; - ret = devm_request_irq(&pdev->dev, irq, fsl_sai_isr, 0, np->name, sai); + ret = devm_request_irq(&pdev->dev, irq, fsl_sai_isr, IRQF_SHARED, + np->name, sai); if (ret) { dev_err(&pdev->dev, "failed to claim irq %u\n", irq); return ret; diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 537dc69256f0..5c97269be346 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1136,7 +1136,6 @@ static const struct snd_soc_component_driver fsl_ssi_component = { }; static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { - .bus_control = true, .symmetric_channels = 1, .probe = fsl_ssi_dai_probe, .playback = { diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c index 71590ca6394b..5ef6881395e0 100644 --- a/sound/soc/fsl/imx-audmix.c +++ b/sound/soc/fsl/imx-audmix.c @@ -289,7 +289,7 @@ static int imx_audmix_probe(struct platform_device *pdev) priv->dai[num_dai + i].ignore_pmdown_time = 1; priv->dai[num_dai + i].ops = &imx_audmix_be_ops; - priv->dai_conf[i].of_node = args.np; + priv->dai_conf[i].dlc.of_node = args.np; priv->dai_conf[i].name_prefix = dai_name; priv->dapm_routes[i].source = diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 08131d147983..f20d5b1c3848 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -338,7 +338,6 @@ static void snd_imx_pcm_free(struct snd_soc_component *component, static const struct snd_soc_component_driver imx_soc_component_fiq = { .open = snd_imx_open, .close = snd_imx_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = snd_imx_pcm_hw_params, .prepare = snd_imx_pcm_prepare, .trigger = snd_imx_pcm_trigger, diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index 42031ba7da31..f8488e8f5f5b 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -373,7 +373,6 @@ static struct snd_soc_dai_driver imx_ssi_dai = { static struct snd_soc_dai_driver imx_ac97_dai = { .probe = imx_ssi_dai_probe, - .bus_control = true, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 5237ac96b756..ed7211d744b3 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -360,7 +360,6 @@ static const struct snd_soc_component_driver mpc5200_audio_dma_component = { .open = psc_dma_open, .close = psc_dma_close, .hw_free = psc_dma_hw_free, - .ioctl = snd_soc_pcm_lib_ioctl, .pointer = psc_dma_pointer, .trigger = psc_dma_trigger, .hw_params = psc_dma_hw_params, diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index e5b9c04d1565..a082ae636a4f 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -233,7 +233,6 @@ static const struct snd_soc_dai_ops psc_ac97_digital_ops = { static struct snd_soc_dai_driver psc_ac97_dai[] = { { .name = "mpc5200-psc-ac97.0", - .bus_control = true, .probe = psc_ac97_probe, .playback = { .stream_name = "AC97 Playback", @@ -253,7 +252,6 @@ static struct snd_soc_dai_driver psc_ac97_dai[] = { }, { .name = "mpc5200-psc-ac97.1", - .bus_control = true, .playback = { .stream_name = "AC97 SPDIF", .channels_min = 1, diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 47e3d1943d7e..340bd2be39a7 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -387,27 +387,6 @@ static int sst_media_prepare(struct snd_pcm_substream *substream, return ret_val; } -static int sst_media_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - int ret; - - ret = - snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(params)); - if (ret) - return ret; - memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); - return 0; -} - -static int sst_media_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - return snd_pcm_lib_free_pages(substream); -} - static int sst_enable_ssp(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -473,8 +452,6 @@ static const struct snd_soc_dai_ops sst_media_dai_ops = { .startup = sst_media_open, .shutdown = sst_media_close, .prepare = sst_media_prepare, - .hw_params = sst_media_hw_params, - .hw_free = sst_media_hw_free, .mute_stream = sst_media_digital_mute, }; @@ -677,7 +654,7 @@ static int sst_soc_pcm_new(struct snd_soc_component *component, if (dai->driver->playback.channels_min || dai->driver->capture.channels_min) { - snd_pcm_lib_preallocate_pages_for_all(pcm, + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_DMA), SST_MIN_BUFFER, SST_MAX_BUFFER); @@ -705,7 +682,6 @@ static const struct snd_soc_component_driver sst_soc_platform_drv = { .probe = sst_soc_probe, .remove = sst_soc_remove, .open = sst_soc_open, - .ioctl = snd_soc_pcm_lib_ioctl, .trigger = sst_soc_trigger, .pointer = sst_soc_pointer, .compr_ops = &sst_platform_compr_ops, diff --git a/sound/soc/intel/baytrail/sst-baytrail-pcm.c b/sound/soc/intel/baytrail/sst-baytrail-pcm.c index 1d780fcc448c..53383055c8dc 100644 --- a/sound/soc/intel/baytrail/sst-baytrail-pcm.c +++ b/sound/soc/intel/baytrail/sst-baytrail-pcm.c @@ -102,8 +102,6 @@ static int sst_byt_pcm_hw_params(struct snd_soc_component *component, return ret; } - snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); - ret = sst_byt_stream_buffer(byt, pcm_data->stream, substream->dma_buffer.addr, params_buffer_bytes(params)); @@ -121,17 +119,6 @@ static int sst_byt_pcm_hw_params(struct snd_soc_component *component, return 0; } -static int sst_byt_pcm_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - - dev_dbg(rtd->dev, "PCM: hw_free\n"); - snd_pcm_lib_free_pages(substream); - - return 0; -} - static int sst_byt_pcm_restore_stream_context(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -315,9 +302,8 @@ static int sst_byt_pcm_new(struct snd_soc_component *component, if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { size = sst_byt_pcm_hardware.buffer_bytes_max; - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - pdata->dma_dev, - size, size); + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, + pdata->dma_dev, size, size); } return 0; @@ -373,9 +359,7 @@ static const struct snd_soc_component_driver byt_dai_component = { .probe = sst_byt_pcm_probe, .open = sst_byt_pcm_open, .close = sst_byt_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = sst_byt_pcm_hw_params, - .hw_free = sst_byt_pcm_hw_free, .trigger = sst_byt_pcm_trigger, .pointer = sst_byt_pcm_pointer, .mmap = sst_byt_pcm_mmap, diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index ef20316e83d1..9ca2567d0059 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -13,6 +13,19 @@ menuconfig SND_SOC_INTEL_MACH if SND_SOC_INTEL_MACH +config SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES + bool "Use more user friendly long card names" + help + Some drivers report the I/O configuration to userspace through the + soundcard's long card name in the control user space AP. An unfortunate + side effect is that this long name may also be used by the GUI, + confusing users with information they don't need. + This option prevents the long name from being modified, and the I/O + configuration will be provided through a different component interface. + Select Y if userspace like UCM (Use Case Manager) uses the component + interface. + If unsure select N. + if SND_SOC_INTEL_HASWELL config SND_SOC_INTEL_HASWELL_MACH @@ -31,12 +44,27 @@ endif ## SND_SOC_INTEL_HASWELL if SND_SOC_INTEL_HASWELL || SND_SOC_SOF_BROADWELL +config SND_SOC_INTEL_BDW_RT5650_MACH + tristate "Broadwell with RT5650 codec" + depends on I2C + depends on I2C_DESIGNWARE_PLATFORM || COMPILE_TEST + depends on X86_INTEL_LPSS || COMPILE_TEST + select SND_SOC_RT5645 + help + This adds the ASoC machine driver for Intel Broadwell platforms with + the RT5650 codec. + Say Y if you have such a device. + If unsure select "N". + config SND_SOC_INTEL_BDW_RT5677_MACH tristate "Broadwell with RT5677 codec" depends on I2C depends on I2C_DESIGNWARE_PLATFORM || COMPILE_TEST depends on GPIOLIB || COMPILE_TEST depends on X86_INTEL_LPSS || COMPILE_TEST + depends on SPI_MASTER + select SPI_PXA2XX + select SND_SOC_RT5677_SPI select SND_SOC_RT5677 help This adds support for Intel Broadwell platform based boards with @@ -261,6 +289,7 @@ config SND_SOC_INTEL_DA7219_MAX98357A_GENERIC select SND_SOC_DA7219 select SND_SOC_MAX98357A select SND_SOC_DMIC + select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_HDAC_HDMI config SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON @@ -387,6 +416,7 @@ config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH select SND_SOC_RT5682 select SND_SOC_MAX98357A select SND_SOC_DMIC + select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_HDAC_HDMI help This adds support for ASoC machine driver for Geminilake platforms @@ -400,6 +430,7 @@ if SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC || SND_SOC_SOF_HDA_AUDIO_CODEC config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH tristate "SKL/KBL/BXT/APL with HDA Codecs" + select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_HDAC_HDMI select SND_SOC_DMIC # SND_SOC_HDAC_HDA is already selected @@ -419,6 +450,7 @@ config SND_SOC_INTEL_SOF_RT5682_MACH (SND_SOC_SOF_BAYTRAIL && (X86_INTEL_LPSS || COMPILE_TEST)) select SND_SOC_RT5682 select SND_SOC_DMIC + select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_HDAC_HDMI help This adds support for ASoC machine driver for SOF platforms @@ -448,6 +480,7 @@ config SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH select SND_SOC_RT5682 select SND_SOC_DMIC select SND_SOC_HDAC_HDMI + select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC help This adds support for ASoC machine driver for SOF platform with RT1011 + RT5682 I2S codec. @@ -456,4 +489,22 @@ config SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH endif ## SND_SOC_SOF_COMETLAKE_LP && SND_SOC_SOF_HDA_LINK +if SND_SOC_SOF_JASPERLAKE + +config SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH + tristate "SOF with DA7219 and MAX98373 in I2S Mode" + depends on I2C && ACPI + depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_DA7219 + select SND_SOC_MAX98373 + select SND_SOC_DMIC + select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC + help + This adds support for ASoC machine driver for SOF platforms + with DA7219 + MAX98373 I2S audio codec. + Say Y if you have such a device. + If unsure select "N". + +endif ## SND_SOC_SOF_JASPERLAKE + endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index ba1aa89db09d..b74ddd49bd39 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -2,6 +2,7 @@ snd-soc-sst-haswell-objs := haswell.o snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o +snd-soc-sst-bdw-rt5650-mach-objs := bdw-rt5650.o 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 hda_dsp_common.o @@ -28,6 +29,7 @@ snd-soc-skl_rt286-objs := skl_rt286.o snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o hda_dsp_common.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o +snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o hda_dsp_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o @@ -37,6 +39,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON) += snd-soc-sst-bxt-da721 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_RT5650_MACH) += snd-soc-sst-bdw-rt5650-mach.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 obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o @@ -58,3 +61,5 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o +obj-$(CONFIG_SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH) += snd-soc-sof_da7219_max98373.o + diff --git a/sound/soc/intel/boards/bdw-rt5650.c b/sound/soc/intel/boards/bdw-rt5650.c new file mode 100644 index 000000000000..1a302436d450 --- /dev/null +++ b/sound/soc/intel/boards/bdw-rt5650.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ASoC machine driver for Intel Broadwell platforms with RT5650 codec + * + * Copyright 2019, The Chromium OS Authors. All rights reserved. + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.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 <sound/soc-acpi.h> + +#include "../common/sst-dsp.h" +#include "../haswell/sst-haswell-ipc.h" + +#include "../../codecs/rt5645.h" + +struct bdw_rt5650_priv { + struct gpio_desc *gpio_hp_en; + struct snd_soc_component *component; +}; + +static const struct snd_soc_dapm_widget bdw_rt5650_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("DMIC Pair1", NULL), + SND_SOC_DAPM_MIC("DMIC Pair2", NULL), +}; + +static const struct snd_soc_dapm_route bdw_rt5650_map[] = { + /* Speakers */ + {"Speaker", NULL, "SPOL"}, + {"Speaker", NULL, "SPOR"}, + + /* Headset jack connectors */ + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, + + /* Digital MICs + * DMIC Pair1 are the two DMICs connected on the DMICN1 connector. + * DMIC Pair2 are the two DMICs connected on the DMICN2 connector. + * Facing the camera, DMIC Pair1 are on the left side, DMIC Pair2 + * are on the right side. + */ + {"DMIC L1", NULL, "DMIC Pair1"}, + {"DMIC R1", NULL, "DMIC Pair1"}, + {"DMIC L2", NULL, "DMIC Pair2"}, + {"DMIC R2", NULL, "DMIC Pair2"}, + + /* CODEC BE connections */ + {"SSP0 CODEC IN", NULL, "AIF1 Capture"}, + {"AIF1 Playback", NULL, "SSP0 CODEC OUT"}, +}; + +static const struct snd_kcontrol_new bdw_rt5650_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("DMIC Pair1"), + SOC_DAPM_PIN_SWITCH("DMIC Pair2"), +}; + + +static struct snd_soc_jack headphone_jack; +static struct snd_soc_jack mic_jack; + +static struct snd_soc_jack_pin headphone_jack_pin = { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, +}; + +static struct snd_soc_jack_pin mic_jack_pin = { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, +}; + +static int broadwell_ssp0_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); + + /* The ADSP will covert the FE rate to 48k, max 4-channels */ + rate->min = rate->max = 48000; + channels->min = 2; + channels->max = 4; + + /* set SSP0 to 24 bit */ + snd_mask_set_format(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int bdw_rt5650_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; + + /* Workaround: set codec PLL to 19.2MHz that PLL source is + * from MCLK(24MHz) to conform 2.4MHz DMIC clock. + */ + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK, + 24000000, 19200000); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec pll: %d\n", ret); + return ret; + } + + /* The actual MCLK freq is 24MHz. The codec is told that MCLK is + * 24.576MHz to satisfy the requirement of rl6231_get_clk_info. + * ASRC is enabled on AD and DA filters to ensure good audio quality. + */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1, 24576000, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec sysclk configuration\n"); + return ret; + } + + return ret; +} + +static struct snd_soc_ops bdw_rt5650_ops = { + .hw_params = bdw_rt5650_hw_params, +}; + +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) +static int bdw_rt5650_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct sst_pdata *pdata = dev_get_platdata(component->dev); + struct sst_hsw *broadwell = pdata->dsp; + int ret; + + /* Set ADSP SSP port settings + * clock_divider = 4 means BCLK = MCLK/5 = 24MHz/5 = 4.8MHz + */ + ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0, + SST_HSW_DEVICE_MCLK_FREQ_24_MHZ, + SST_HSW_DEVICE_TDM_CLOCK_MASTER, 4); + if (ret < 0) { + dev_err(rtd->dev, "error: failed to set device config\n"); + return ret; + } + + return 0; +} +#endif + +static int bdw_rt5650_init(struct snd_soc_pcm_runtime *rtd) +{ + struct bdw_rt5650_priv *bdw_rt5650 = + 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; + int ret; + + /* Enable codec ASRC function for Stereo DAC/Stereo1 ADC/DMIC/I2S1. + * The ASRC clock source is clk_i2s1_asrc. + */ + rt5645_sel_asrc_clk_src(component, + RT5645_DA_STEREO_FILTER | + RT5645_DA_MONO_L_FILTER | + RT5645_DA_MONO_R_FILTER | + RT5645_AD_STEREO_FILTER | + RT5645_AD_MONO_L_FILTER | + RT5645_AD_MONO_R_FILTER, + RT5645_CLK_SEL_I2S1_ASRC); + + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); + + if (ret < 0) { + dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret); + return ret; + } + + /* Create and initialize headphone jack */ + if (snd_soc_card_jack_new(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &headphone_jack, + &headphone_jack_pin, 1)) { + dev_err(component->dev, "Can't create headphone jack\n"); + } + + /* Create and initialize mic jack */ + if (snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE, + &mic_jack, &mic_jack_pin, 1)) { + dev_err(component->dev, "Can't create mic jack\n"); + } + + rt5645_set_jack_detect(component, &headphone_jack, &mic_jack, NULL); + + bdw_rt5650->component = component; + + return 0; +} + +/* broadwell digital audio interface glue - connects codec <--> CPU */ +SND_SOC_DAILINK_DEF(dummy, + DAILINK_COMP_ARRAY(COMP_DUMMY())); + +SND_SOC_DAILINK_DEF(fe, + DAILINK_COMP_ARRAY(COMP_CPU("System Pin"))); + +SND_SOC_DAILINK_DEF(platform, + DAILINK_COMP_ARRAY(COMP_PLATFORM("haswell-pcm-audio"))); + +SND_SOC_DAILINK_DEF(be, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5650:00", "rt5645-aif1"))); + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) +SND_SOC_DAILINK_DEF(ssp0_port, + DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port"))); +#else +SND_SOC_DAILINK_DEF(ssp0_port, + DAILINK_COMP_ARRAY(COMP_DUMMY())); +#endif + +static struct snd_soc_dai_link bdw_rt5650_dais[] = { + /* Front End DAI links */ + { + .name = "System PCM", + .stream_name = "System Playback", + .dynamic = 1, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) + .init = bdw_rt5650_rtd_init, +#endif + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(fe, dummy, platform), + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "Codec", + .id = 0, + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = broadwell_ssp0_fixup, + .ops = &bdw_rt5650_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = bdw_rt5650_init, + SND_SOC_DAILINK_REG(ssp0_port, be, platform), + }, +}; + +/* ASoC machine driver for Broadwell DSP + RT5650 */ +static struct snd_soc_card bdw_rt5650_card = { + .name = "bdw-rt5650", + .owner = THIS_MODULE, + .dai_link = bdw_rt5650_dais, + .num_links = ARRAY_SIZE(bdw_rt5650_dais), + .dapm_widgets = bdw_rt5650_widgets, + .num_dapm_widgets = ARRAY_SIZE(bdw_rt5650_widgets), + .dapm_routes = bdw_rt5650_map, + .num_dapm_routes = ARRAY_SIZE(bdw_rt5650_map), + .controls = bdw_rt5650_controls, + .num_controls = ARRAY_SIZE(bdw_rt5650_controls), + .fully_routed = true, +}; + +static int bdw_rt5650_probe(struct platform_device *pdev) +{ + struct bdw_rt5650_priv *bdw_rt5650; + struct snd_soc_acpi_mach *mach; + int ret; + + bdw_rt5650_card.dev = &pdev->dev; + + /* Allocate driver private struct */ + bdw_rt5650 = devm_kzalloc(&pdev->dev, sizeof(struct bdw_rt5650_priv), + GFP_KERNEL); + if (!bdw_rt5650) + return -ENOMEM; + + /* override plaform name, if required */ + mach = (&pdev->dev)->platform_data; + ret = snd_soc_fixup_dai_links_platform_name(&bdw_rt5650_card, + mach->mach_params.platform); + + if (ret) + return ret; + + snd_soc_card_set_drvdata(&bdw_rt5650_card, bdw_rt5650); + + return devm_snd_soc_register_card(&pdev->dev, &bdw_rt5650_card); +} + +static struct platform_driver bdw_rt5650_audio = { + .probe = bdw_rt5650_probe, + .driver = { + .name = "bdw-rt5650", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(bdw_rt5650_audio) + +/* Module information */ +MODULE_AUTHOR("Ben Zhang <benzh@chromium.org>"); +MODULE_DESCRIPTION("Intel Broadwell RT5650 machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bdw-rt5650"); diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index 2af8e5a62da8..bb643c99069d 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -295,6 +295,14 @@ SND_SOC_DAILINK_DEF(platform, SND_SOC_DAILINK_DEF(be, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RT5677CE:00", "rt5677-aif1"))); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) +SND_SOC_DAILINK_DEF(ssp0_port, + DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port"))); +#else +SND_SOC_DAILINK_DEF(ssp0_port, + DAILINK_COMP_ARRAY(COMP_DUMMY())); +#endif + /* Wake on voice interface */ SND_SOC_DAILINK_DEFS(dsp, DAILINK_COMP_ARRAY(COMP_CPU("spi-RT5677AA:00")), @@ -342,7 +350,7 @@ static struct snd_soc_dai_link bdw_rt5677_dais[] = { .dpcm_playback = 1, .dpcm_capture = 1, .init = bdw_rt5677_init, - SND_SOC_DAILINK_REG(dummy, be, dummy), + SND_SOC_DAILINK_REG(ssp0_port, be, platform), }, }; diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index db7e1e87156d..b9c12e24c70b 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -164,6 +164,14 @@ SND_SOC_DAILINK_DEF(platform, SND_SOC_DAILINK_DEF(codec, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-INT343A:00", "rt286-aif1"))); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) +SND_SOC_DAILINK_DEF(ssp0_port, + DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port"))); +#else +SND_SOC_DAILINK_DEF(ssp0_port, + DAILINK_COMP_ARRAY(COMP_DUMMY())); +#endif + /* broadwell digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broadwell_rt286_dais[] = { /* Front End DAI links */ @@ -218,7 +226,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { .ops = &broadwell_rt286_ops, .dpcm_playback = 1, .dpcm_capture = 1, - SND_SOC_DAILINK_REG(dummy, codec, dummy), + SND_SOC_DAILINK_REG(ssp0_port, codec, platform), }, }; diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 5873abb46441..33b13f3ca152 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -161,13 +161,13 @@ static int broxton_ssp_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = 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; + chan->min = chan->max = DUAL_CHANNEL; /* set SSP to 24 bit */ snd_mask_none(fmt); @@ -313,12 +313,12 @@ static const struct snd_soc_ops broxton_da7219_fe_ops = { static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); if (params_channels(params) == 2) - channels->min = channels->max = 2; + chan->min = chan->max = 2; else - channels->min = channels->max = 4; + chan->min = chan->max = 4; return 0; } diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index eabf9d8468ae..067a97e7e6a8 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -206,13 +206,13 @@ static int broxton_ssp5_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = 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 covert the FE rate to 48k, stereo */ rate->min = rate->max = 48000; - channels->min = channels->max = 2; + chan->min = chan->max = 2; /* set SSP5 to 24 bit */ snd_mask_none(fmt); @@ -255,9 +255,9 @@ static const struct snd_pcm_hw_constraint_list constraints_rates = { static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - channels->min = channels->max = 4; + chan->min = chan->max = 4; return 0; } diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index 54e97455d7f6..0adc5a5e134a 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -234,14 +234,6 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime) return 0; } -static const struct snd_soc_pcm_stream byt_cht_es8316_dai_params = { - .formats = SNDRV_PCM_FMTBIT_S24_LE, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, -}; - static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -360,7 +352,10 @@ static struct snd_soc_dai_link byt_cht_es8316_dais[] = { /* SoC card */ static char codec_name[SND_ACPI_I2C_ID_LEN]; +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES) static char long_name[50]; /* = "bytcht-es8316-*-spk-*-mic" */ +#endif +static char components_string[32]; /* = "cfg-spk:* cfg-mic:* */ static int byt_cht_es8316_suspend(struct snd_soc_card *card) { @@ -573,11 +568,19 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) } } - /* register the soc card */ + snprintf(components_string, sizeof(components_string), + "cfg-spk:%s cfg-mic:%s", + (quirk & BYT_CHT_ES8316_MONO_SPEAKER) ? "1" : "2", + mic_name[BYT_CHT_ES8316_MAP(quirk)]); + byt_cht_es8316_card.components = components_string; +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES) snprintf(long_name, sizeof(long_name), "bytcht-es8316-%s-spk-%s-mic", (quirk & BYT_CHT_ES8316_MONO_SPEAKER) ? "mono" : "stereo", mic_name[BYT_CHT_ES8316_MAP(quirk)]); byt_cht_es8316_card.long_name = long_name; +#endif + + /* register the soc card */ snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv); ret = devm_snd_soc_register_card(dev, &byt_cht_es8316_card); diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 243f683bc02a..6bd9ae813be2 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -933,14 +933,6 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) return 0; } -static const struct snd_soc_pcm_stream byt_rt5640_dai_params = { - .formats = SNDRV_PCM_FMTBIT_S24_LE, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, -}; - static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -948,65 +940,43 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - int ret; + int ret, bits; /* The DSP will covert the FE rate to 48k, stereo */ rate->min = rate->max = 48000; channels->min = channels->max = 2; if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || - (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { - + (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { /* set SSP0 to 16-bit */ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); - - /* - * Default mode for SSP configuration is TDM 4 slot, override config - * with explicit setting to I2S 2ch 16-bit. The word length is set with - * dai_set_tdm_slot() since there is no other API exposed - */ - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS - ); - if (ret < 0) { - dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); - return ret; - } - - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16); - if (ret < 0) { - dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); - return ret; - } - + bits = 16; } else { - /* set SSP2 to 24-bit */ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + bits = 24; + } - /* - * Default mode for SSP configuration is TDM 4 slot, override config - * with explicit setting to I2S 2ch 24-bit. The word length is set with - * dai_set_tdm_slot() since there is no other API exposed - */ - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS - ); - if (ret < 0) { - dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); - return ret; - } + /* + * Default mode for SSP configuration is TDM 4 slot, override config + * with explicit setting to I2S 2ch. The word length is set with + * dai_set_tdm_slot() since there is no other API exposed + */ + ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) { + dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); + return ret; + } - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); - if (ret < 0) { - dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); - return ret; - } + ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, bits); + if (ret < 0) { + dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); + return ret; } + return 0; } @@ -1084,9 +1054,10 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { /* SoC card */ static char byt_rt5640_codec_name[SND_ACPI_I2C_ID_LEN]; -static char byt_rt5640_codec_aif_name[12]; /* = "rt5640-aif[1|2]" */ -static char byt_rt5640_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES) static char byt_rt5640_long_name[40]; /* = "bytcr-rt5640-*-spk-*-mic" */ +#endif +static char byt_rt5640_components[32]; /* = "cfg-spk:* cfg-mic:*" */ static int byt_rt5640_suspend(struct snd_soc_card *card) { @@ -1266,28 +1237,12 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) log_quirks(&pdev->dev); if ((byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) || - (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { - - /* fixup codec aif name */ - snprintf(byt_rt5640_codec_aif_name, - sizeof(byt_rt5640_codec_aif_name), - "%s", "rt5640-aif2"); - - byt_rt5640_dais[dai_index].codecs->dai_name = - byt_rt5640_codec_aif_name; - } + (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) + byt_rt5640_dais[dai_index].codecs->dai_name = "rt5640-aif2"; if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || - (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { - - /* fixup cpu dai name name */ - snprintf(byt_rt5640_cpu_dai_name, - sizeof(byt_rt5640_cpu_dai_name), - "%s", "ssp0-port"); - - byt_rt5640_dais[dai_index].cpus->dai_name = - byt_rt5640_cpu_dai_name; - } + (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) + byt_rt5640_dais[dai_index].cpus->dai_name = "ssp0-port"; if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) { priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); @@ -1309,12 +1264,19 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) } } + snprintf(byt_rt5640_components, sizeof(byt_rt5640_components), + "cfg-spk:%s cfg-mic:%s", + (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) ? "1" : "2", + map_name[BYT_RT5640_MAP(byt_rt5640_quirk)]); + byt_rt5640_card.components = byt_rt5640_components; +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES) snprintf(byt_rt5640_long_name, sizeof(byt_rt5640_long_name), "bytcr-rt5640-%s-spk-%s-mic", (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) ? "mono" : "stereo", map_name[BYT_RT5640_MAP(byt_rt5640_quirk)]); byt_rt5640_card.long_name = byt_rt5640_long_name; +#endif /* override plaform name, if required */ platform_name = mach->mach_params.platform; diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 4606f6f582d6..5074bb53f98e 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -656,14 +656,6 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) return 0; } -static const struct snd_soc_pcm_stream byt_rt5651_dai_params = { - .formats = SNDRV_PCM_FMTBIT_S24_LE, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, -}; - static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -795,9 +787,10 @@ 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" */ +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES) static char byt_rt5651_long_name[50]; /* = "bytcr-rt5651-*-spk-*-mic[-swapped-hp]" */ +#endif +static char byt_rt5651_components[50]; /* = "cfg-spk:* cfg-mic:*" */ static int byt_rt5651_suspend(struct snd_soc_card *card) { @@ -876,7 +869,6 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) const char *platform_name; struct acpi_device *adev; struct device *codec_dev; - const char *hp_swapped; bool is_bytcr = false; int ret_val = 0; int dai_index = 0; @@ -996,10 +988,11 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) if (byt_rt5651_gpios) { devm_acpi_dev_add_driver_gpios(codec_dev, byt_rt5651_gpios); - 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"); + priv->ext_amp_gpio = devm_fwnode_gpiod_get(&pdev->dev, + codec_dev->fwnode, + "ext-amp-enable", + GPIOD_OUT_LOW, + "speaker-amp"); if (IS_ERR(priv->ext_amp_gpio)) { ret_val = PTR_ERR(priv->ext_amp_gpio); switch (ret_val) { @@ -1015,10 +1008,11 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) return ret_val; } } - priv->hp_detect = devm_fwnode_get_index_gpiod_from_child( - &pdev->dev, "hp-detect", 0, - codec_dev->fwnode, - GPIOD_IN, "hp-detect"); + priv->hp_detect = devm_fwnode_gpiod_get(&pdev->dev, + codec_dev->fwnode, + "hp-detect", + GPIOD_IN, + "hp-detect"); if (IS_ERR(priv->hp_detect)) { ret_val = PTR_ERR(priv->hp_detect); switch (ret_val) { @@ -1041,26 +1035,12 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) log_quirks(&pdev->dev); if ((byt_rt5651_quirk & BYT_RT5651_SSP2_AIF2) || - (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2)) { - /* fixup codec aif name */ - snprintf(byt_rt5651_codec_aif_name, - sizeof(byt_rt5651_codec_aif_name), - "%s", "rt5651-aif2"); - - byt_rt5651_dais[dai_index].codecs->dai_name = - byt_rt5651_codec_aif_name; - } + (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2)) + byt_rt5651_dais[dai_index].codecs->dai_name = "rt5651-aif2"; if ((byt_rt5651_quirk & BYT_RT5651_SSP0_AIF1) || - (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2)) { - /* fixup cpu dai name name */ - snprintf(byt_rt5651_cpu_dai_name, - sizeof(byt_rt5651_cpu_dai_name), - "%s", "ssp0-port"); - - byt_rt5651_dais[dai_index].cpus->dai_name = - byt_rt5651_cpu_dai_name; - } + (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2)) + byt_rt5651_dais[dai_index].cpus->dai_name = "ssp0-port"; if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) { priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); @@ -1080,17 +1060,23 @@ 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_components, sizeof(byt_rt5651_components), + "cfg-spk:%s cfg-mic:%s%s", + (byt_rt5651_quirk & BYT_RT5651_MONO_SPEAKER) ? "1" : "2", + mic_name[BYT_RT5651_MAP(byt_rt5651_quirk)], + (byt_rt5651_quirk & BYT_RT5651_HP_LR_SWAPPED) ? + " cfg-hp:lrswap" : ""); + byt_rt5651_card.components = byt_rt5651_components; +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES) snprintf(byt_rt5651_long_name, sizeof(byt_rt5651_long_name), "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); + mic_name[BYT_RT5651_MAP(byt_rt5651_quirk)], + (byt_rt5651_quirk & BYT_RT5651_HP_LR_SWAPPED) ? + "-hp-swapped" : ""); byt_rt5651_card.long_name = byt_rt5651_long_name; +#endif /* override plaform name, if required */ platform_name = mach->mach_params.platform; diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index c68a5b85a4a0..b5b016d493f1 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -515,8 +515,6 @@ static struct cht_acpi_card snd_soc_cards[] = { }; static char cht_rt5645_codec_name[SND_ACPI_I2C_ID_LEN]; -static char cht_rt5645_codec_aif_name[12]; /* = "rt5645-aif[1|2]" */ -static char cht_rt5645_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ u64 aif_value; /* 1: AIF1, 2: AIF2 */ @@ -641,28 +639,12 @@ static int snd_cht_mc_probe(struct platform_device *pdev) log_quirks(&pdev->dev); if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) || - (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) { - - /* fixup codec aif name */ - snprintf(cht_rt5645_codec_aif_name, - sizeof(cht_rt5645_codec_aif_name), - "%s", "rt5645-aif2"); - - cht_dailink[dai_index].codecs->dai_name = - cht_rt5645_codec_aif_name; - } + (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) + cht_dailink[dai_index].codecs->dai_name = "rt5645-aif2"; if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) || - (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) { - - /* fixup cpu dai name name */ - snprintf(cht_rt5645_cpu_dai_name, - sizeof(cht_rt5645_cpu_dai_name), - "%s", "ssp0-port"); - - cht_dailink[dai_index].cpus->dai_name = - cht_rt5645_cpu_dai_name; - } + (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) + cht_dailink[dai_index].cpus->dai_name = "ssp0-port"; /* override plaform name, if required */ platform_name = mach->mach_params.platform; diff --git a/sound/soc/intel/boards/cml_rt1011_rt5682.c b/sound/soc/intel/boards/cml_rt1011_rt5682.c index 5f1bf6d3800c..d6efc554898c 100644 --- a/sound/soc/intel/boards/cml_rt1011_rt5682.c +++ b/sound/soc/intel/boards/cml_rt1011_rt5682.c @@ -405,19 +405,19 @@ static struct snd_soc_dai_link cml_rt1011_rt5682_dailink[] = { static struct snd_soc_codec_conf rt1011_conf[] = { { - .dev_name = "i2c-10EC1011:00", + .dlc = COMP_CODEC_CONF("i2c-10EC1011:00"), .name_prefix = "WL", }, { - .dev_name = "i2c-10EC1011:01", + .dlc = COMP_CODEC_CONF("i2c-10EC1011:01"), .name_prefix = "WR", }, { - .dev_name = "i2c-10EC1011:02", + .dlc = COMP_CODEC_CONF("i2c-10EC1011:02"), .name_prefix = "TL", }, { - .dev_name = "i2c-10EC1011:03", + .dlc = COMP_CODEC_CONF("i2c-10EC1011:03"), .name_prefix = "TR", }, }; diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c index b36264d1d1cd..4a6d117ea7af 100644 --- a/sound/soc/intel/boards/glk_rt5682_max98357a.c +++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c @@ -118,13 +118,13 @@ static int geminilake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = 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; + chan->min = chan->max = DUAL_CHANNEL; /* set SSP to 24 bit */ snd_mask_none(fmt); @@ -249,16 +249,6 @@ static const struct snd_pcm_hw_constraint_list constraints_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, }; @@ -272,13 +262,13 @@ static struct snd_pcm_hw_constraint_list constraints_channels_quad = { 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, + struct snd_interval *chan = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); /* * set BE channel constraint as user FE channels */ - channels->min = channels->max = 4; + chan->min = chan->max = 4; return 0; } diff --git a/sound/soc/intel/boards/hda_dsp_common.c b/sound/soc/intel/boards/hda_dsp_common.c index ed36b68d6705..9179f07f9ee4 100644 --- a/sound/soc/intel/boards/hda_dsp_common.c +++ b/sound/soc/intel/boards/hda_dsp_common.c @@ -63,7 +63,7 @@ int hda_dsp_hdmi_build_controls(struct snd_soc_card *card, "%s: mapping HDMI converter %d to PCM %d (%p)\n", __func__, i, hpcm->device, spcm); } else { - hpcm->pcm = 0; + hpcm->pcm = NULL; hpcm->device = SNDRV_PCM_INVALID_DEVICE; dev_warn(card->dev, "%s: no PCM in topology for HDMI converter %d\n\n", diff --git a/sound/soc/intel/boards/kbl_da7219_max98357a.c b/sound/soc/intel/boards/kbl_da7219_max98357a.c index 537a88932bb6..bc7f9a9ce9af 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98357a.c +++ b/sound/soc/intel/boards/kbl_da7219_max98357a.c @@ -141,13 +141,13 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = 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; + chan->min = chan->max = DUAL_CHANNEL; /* set SSP to 24 bit */ snd_mask_none(fmt); @@ -305,7 +305,7 @@ static const struct snd_soc_ops kabylake_da7219_fe_ops = { static int kabylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); /* @@ -313,9 +313,9 @@ static int kabylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, */ if (params_channels(params) == 2) - channels->min = channels->max = 2; + chan->min = chan->max = 2; else - channels->min = channels->max = 4; + chan->min = chan->max = 4; return 0; } @@ -336,19 +336,6 @@ static struct snd_soc_ops kabylake_dmic_ops = { .startup = kabylake_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 const unsigned int ch_mono[] = { - 1, -}; - SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY())); diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c index 829f95fc4179..7a13e9b35187 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98927.c +++ b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -176,7 +176,7 @@ static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *runtime = substream->private_data; - int ret = 0, j; + int ret, j; for (j = 0; j < runtime->num_codecs; j++) { struct snd_soc_dai *codec_dai = runtime->codec_dais[j]; @@ -279,7 +279,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_soc_dpcm *dpcm = container_of( @@ -298,7 +298,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, !strcmp(rtd->card->name, "kblmax98373")) { /* The ADSP will convert the FE rate to 48k, stereo */ rate->min = rate->max = 48000; - channels->min = channels->max = DUAL_CHANNEL; + chan->min = chan->max = DUAL_CHANNEL; /* set SSP to 24 bit */ snd_mask_none(fmt); @@ -313,7 +313,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, !strcmp(fe_dai_link->name, "Kbl Audio Headset Playback") || !strcmp(fe_dai_link->name, "Kbl Audio Capture Port")) { rate->min = rate->max = 48000; - channels->min = channels->max = 2; + chan->min = chan->max = 2; snd_mask_none(fmt); snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); } @@ -341,6 +341,9 @@ static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) kabylake_ssp1_map, ARRAY_SIZE(kabylake_ssp1_map)); + if (ret) + return ret; + /* * Headset buttons map to the google Reference headset. * These can be configured by userspace. @@ -491,7 +494,7 @@ static const struct snd_soc_ops kabylake_da7219_fe_ops = { static int kabylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); /* @@ -499,9 +502,9 @@ static int kabylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, */ if (params_channels(params) == 2) - channels->min = channels->max = 2; + chan->min = chan->max = 2; else - channels->min = channels->max = 4; + chan->min = chan->max = 4; return 0; } @@ -571,12 +574,12 @@ static struct snd_soc_ops skylake_refcap_ops = { static struct snd_soc_codec_conf max98927_codec_conf[] = { { - .dev_name = MAX98927_DEV0_NAME, + .dlc = COMP_CODEC_CONF(MAX98927_DEV0_NAME), .name_prefix = "Right", }, { - .dev_name = MAX98927_DEV1_NAME, + .dlc = COMP_CODEC_CONF(MAX98927_DEV1_NAME), .name_prefix = "Left", }, }; @@ -584,12 +587,12 @@ static struct snd_soc_codec_conf max98927_codec_conf[] = { static struct snd_soc_codec_conf max98373_codec_conf[] = { { - .dev_name = MAX98373_DEV0_NAME, + .dlc = COMP_CODEC_CONF(MAX98373_DEV0_NAME), .name_prefix = "Right", }, { - .dev_name = MAX98373_DEV1_NAME, + .dlc = COMP_CODEC_CONF(MAX98373_DEV1_NAME), .name_prefix = "Left", }, }; @@ -1092,7 +1095,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) struct kbl_codec_private *ctx; struct snd_soc_dai_link *kbl_dai_link; struct snd_soc_dai_link_component **codecs; - int i = 0; + int i; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) diff --git a/sound/soc/intel/boards/kbl_rt5660.c b/sound/soc/intel/boards/kbl_rt5660.c index 74fe1f3a5479..e23dea9ab79a 100644 --- a/sound/soc/intel/boards/kbl_rt5660.c +++ b/sound/soc/intel/boards/kbl_rt5660.c @@ -138,13 +138,13 @@ static int kabylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = 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; + chan->min = chan->max = DUAL_CHANNEL; /* set SSP0 to 24 bit */ snd_mask_none(fmt); diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index 7cefda341fbf..d8f2ff7139a9 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -229,11 +229,11 @@ static const struct snd_soc_dapm_route kabylake_5663_map[] = { static struct snd_soc_codec_conf max98927_codec_conf[] = { { - .dev_name = MAXIM_DEV0_NAME, + .dlc = COMP_CODEC_CONF(MAXIM_DEV0_NAME), .name_prefix = "Right", }, { - .dev_name = MAXIM_DEV1_NAME, + .dlc = COMP_CODEC_CONF(MAXIM_DEV1_NAME), .name_prefix = "Left", }, }; @@ -398,7 +398,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_soc_dpcm *dpcm = container_of( @@ -413,7 +413,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, !strcmp(fe_dai_link->name, "Kbl Audio Headset Playback") || !strcmp(fe_dai_link->name, "Kbl Audio Capture Port")) { rate->min = rate->max = 48000; - channels->min = channels->max = 2; + chan->min = chan->max = 2; snd_mask_none(fmt); snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); } @@ -454,13 +454,13 @@ static struct snd_soc_ops kabylake_rt5663_ops = { static int kabylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2) - channels->min = channels->max = 2; + chan->min = chan->max = 2; else - channels->min = channels->max = 4; + chan->min = chan->max = 4; 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 3e5f6bead229..96c814f36458 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -193,11 +193,11 @@ static const struct snd_soc_dapm_route kabylake_map[] = { static struct snd_soc_codec_conf max98927_codec_conf[] = { { - .dev_name = MAXIM_DEV0_NAME, + .dlc = COMP_CODEC_CONF(MAXIM_DEV0_NAME), .name_prefix = "Right", }, { - .dev_name = MAXIM_DEV1_NAME, + .dlc = COMP_CODEC_CONF(MAXIM_DEV1_NAME), .name_prefix = "Left", }, }; @@ -333,7 +333,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_soc_dpcm *dpcm = container_of( @@ -348,15 +348,15 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, !strcmp(fe_dai_link->name, "Kbl Audio Headset Playback") || !strcmp(fe_dai_link->name, "Kbl Audio Capture Port")) { rate->min = rate->max = 48000; - channels->min = channels->max = 2; + chan->min = chan->max = 2; snd_mask_none(fmt); 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) - channels->min = channels->max = 2; + chan->min = chan->max = 2; else - channels->min = channels->max = 4; + chan->min = chan->max = 4; } /* * The speaker on the SSP0 supports S16_LE and not S24_LE. @@ -761,7 +761,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) { struct kbl_codec_private *ctx; struct snd_soc_acpi_mach *mach; - int ret = 0; + int ret; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.c b/sound/soc/intel/boards/skl_hda_dsp_common.c index eb419e1ec42b..78ff5f24c40e 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_common.c +++ b/sound/soc/intel/boards/skl_hda_dsp_common.c @@ -41,16 +41,19 @@ int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device) return 0; } -SND_SOC_DAILINK_DEFS(idisp1, - DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin")), +SND_SOC_DAILINK_DEF(idisp1_cpu, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin"))); +SND_SOC_DAILINK_DEF(idisp1_codec, DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1"))); -SND_SOC_DAILINK_DEFS(idisp2, - DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin")), +SND_SOC_DAILINK_DEF(idisp2_cpu, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin"))); +SND_SOC_DAILINK_DEF(idisp2_codec, DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2"))); -SND_SOC_DAILINK_DEFS(idisp3, - DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin")), +SND_SOC_DAILINK_DEF(idisp3_cpu, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin"))); +SND_SOC_DAILINK_DEF(idisp3_codec, DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3"))); SND_SOC_DAILINK_DEF(analog_cpu, @@ -83,21 +86,21 @@ struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS] = { .id = 1, .dpcm_playback = 1, .no_pcm = 1, - SND_SOC_DAILINK_REG(idisp1), + SND_SOC_DAILINK_REG(idisp1_cpu, idisp1_codec, platform), }, { .name = "iDisp2", .id = 2, .dpcm_playback = 1, .no_pcm = 1, - SND_SOC_DAILINK_REG(idisp2), + SND_SOC_DAILINK_REG(idisp2_cpu, idisp2_codec, platform), }, { .name = "iDisp3", .id = 3, .dpcm_playback = 1, .no_pcm = 1, - SND_SOC_DAILINK_REG(idisp3), + SND_SOC_DAILINK_REG(idisp3_cpu, idisp3_codec, platform), }, { .name = "Analog Playback and Capture", diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 4e45901e3a2f..11eaee9ae41f 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -100,6 +100,8 @@ static struct snd_soc_card hda_soc_card = { .late_probe = skl_hda_card_late_probe, }; +static char hda_soc_components[30]; + #define IDISP_DAI_COUNT 3 #define HDAC_DAI_COUNT 2 #define DMIC_DAI_COUNT 2 @@ -183,6 +185,12 @@ static int skl_hda_audio_probe(struct platform_device *pdev) hda_soc_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&hda_soc_card, ctx); + if (mach->mach_params.dmic_num > 0) { + snprintf(hda_soc_components, sizeof(hda_soc_components), + "cfg-dmics:%d", mach->mach_params.dmic_num); + hda_soc_card.components = hda_soc_components; + } + return devm_snd_soc_register_card(&pdev->dev, &hda_soc_card); } diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 3ce8efbeed12..e6de3b28d840 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -139,13 +139,13 @@ static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = 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 covert the FE rate to 48k, stereo */ rate->min = rate->max = 48000; - channels->min = channels->max = 2; + chan->min = chan->max = 2; /* set SSP0 to 24 bit */ snd_mask_none(fmt); @@ -315,13 +315,13 @@ static const struct snd_soc_ops skylake_nau8825_ops = { static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2) - channels->min = channels->max = 2; + chan->min = chan->max = 2; else - channels->min = channels->max = 4; + chan->min = chan->max = 4; return 0; } diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index 1a7ac8bdf543..c99c8b23e509 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -147,11 +147,11 @@ static const struct snd_soc_dapm_route skylake_map[] = { static struct snd_soc_codec_conf ssm4567_codec_conf[] = { { - .dev_name = "i2c-INT343B:00", + .dlc = COMP_CODEC_CONF("i2c-INT343B:00"), .name_prefix = "Left", }, { - .dev_name = "i2c-INT343B:01", + .dlc = COMP_CODEC_CONF("i2c-INT343B:01"), .name_prefix = "Right", }, }; @@ -317,13 +317,13 @@ static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = 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 covert the FE rate to 48k, stereo */ rate->min = rate->max = 48000; - channels->min = channels->max = 2; + chan->min = chan->max = 2; /* set SSP0 to 24 bit */ snd_mask_none(fmt); @@ -334,12 +334,12 @@ static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2) - channels->min = channels->max = 2; + chan->min = chan->max = 2; else - channels->min = channels->max = 4; + chan->min = chan->max = 4; return 0; } diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 231349a47cc9..a9aec66a2351 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -211,13 +211,13 @@ static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); /* The output is 48KHz, stereo, 16bits */ rate->min = rate->max = 48000; - channels->min = channels->max = 2; + chan->min = chan->max = 2; /* set SSP0 to 24 bit */ snd_mask_none(fmt); @@ -247,12 +247,12 @@ static const struct snd_soc_ops skylake_rt286_ops = { static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - struct snd_interval *channels = hw_param_interval(params, + struct snd_interval *chan = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); if (params_channels(params) == 2) - channels->min = channels->max = 2; + chan->min = chan->max = 2; else - channels->min = channels->max = 4; + chan->min = chan->max = 4; return 0; } diff --git a/sound/soc/intel/boards/sof_da7219_max98373.c b/sound/soc/intel/boards/sof_da7219_max98373.c new file mode 100644 index 000000000000..8f44f13d2848 --- /dev/null +++ b/sound/soc/intel/boards/sof_da7219_max98373.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2019 Intel Corporation. + +/* + * Intel SOF Machine driver for DA7219 + MAX98373 codec + */ + +#include <linux/input.h> +#include <linux/module.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <linux/platform_device.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include "../../codecs/da7219.h" +#include "../../codecs/da7219-aad.h" +#include "hda_dsp_common.h" + +#define DIALOG_CODEC_DAI "da7219-hifi" +#define MAX98373_CODEC_DAI "max98373-aif1" +#define MAXIM_DEV0_NAME "i2c-MX98373:00" +#define MAXIM_DEV1_NAME "i2c-MX98373:01" + +struct hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct card_private { + struct snd_soc_jack headset; + struct list_head hdmi_pcm_list; + struct snd_soc_jack hdmi[3]; +}; + +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, DIALOG_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_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, + 0, 0); + if (ret) + dev_err(card->dev, "failed to stop PLL: %d\n", ret); + } else if (SND_SOC_DAPM_EVENT_ON(event)) { + ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL_SRM, + 0, DA7219_PLL_FREQ_OUT_98304); + if (ret) + dev_err(card->dev, "failed to start PLL: %d\n", ret); + } + + return ret; +} + +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), +}; + +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + { "Headphone Jack", NULL, "HPL" }, + { "Headphone Jack", NULL, "HPR" }, + + { "Left Spk", NULL, "Left BE_OUT" }, + { "Right Spk", NULL, "Right BE_OUT" }, + + { "MIC", NULL, "Headset Mic" }, + + { "Headphone Jack", NULL, "Platform Clock" }, + { "Headset Mic", NULL, "Platform Clock" }, +}; + +static struct snd_soc_jack headset; + +static int da7219_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + 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, DA7219_CLKSRC_MCLK, 24000000, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(rtd->dev, "can't set codec sysclk configuration\n"); + return 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, + &headset, NULL, 0); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); + return ret; + } + + jack = &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); + da7219_aad_jack_det(component, jack); + + return ret; +} + +static int ssp1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *runtime = substream->private_data; + int ret, j; + + for (j = 0; j < runtime->num_codecs; j++) { + struct snd_soc_dai *codec_dai = runtime->codec_dais[j]; + + if (!strcmp(codec_dai->component->name, MAXIM_DEV0_NAME)) { + /* vmon_slot_no = 0 imon_slot_no = 1 for TX slots */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 3, 4, 16); + if (ret < 0) { + dev_err(runtime->dev, "DEV0 TDM slot err:%d\n", ret); + return ret; + } + } + if (!strcmp(codec_dai->component->name, MAXIM_DEV1_NAME)) { + /* vmon_slot_no = 2 imon_slot_no = 3 for TX slots */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC, 3, 4, 16); + if (ret < 0) { + dev_err(runtime->dev, "DEV1 TDM slot err:%d\n", ret); + return ret; + } + } + } + + return 0; +} + +static struct snd_soc_ops ssp1_ops = { + .hw_params = ssp1_hw_params, +}; + +static struct snd_soc_codec_conf max98373_codec_conf[] = { + { + .dlc = COMP_CODEC_CONF(MAXIM_DEV0_NAME), + .name_prefix = "Right", + }, + { + .dlc = COMP_CODEC_CONF(MAXIM_DEV1_NAME), + .name_prefix = "Left", + }, +}; + +static int hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = dai->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static int card_late_probe(struct snd_soc_card *card) +{ + struct card_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_acpi_mach *mach = (card->dev)->platform_data; + struct hdmi_pcm *pcm; + + if (mach->mach_params.common_hdmi_codec_drv) { + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct hdmi_pcm, + head); + return hda_dsp_hdmi_build_controls(card, + pcm->codec_dai->component); + } + + return -EINVAL; +} + +SND_SOC_DAILINK_DEF(ssp0_pin, + DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin"))); +SND_SOC_DAILINK_DEF(ssp0_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7219:00", DIALOG_CODEC_DAI))); + +SND_SOC_DAILINK_DEF(ssp1_pin, + DAILINK_COMP_ARRAY(COMP_CPU("SSP1 Pin"))); +SND_SOC_DAILINK_DEF(ssp1_amps, + DAILINK_COMP_ARRAY( + /* Left */ COMP_CODEC(MAXIM_DEV0_NAME, MAX98373_CODEC_DAI), + /* Right */ COMP_CODEC(MAXIM_DEV1_NAME, MAX98373_CODEC_DAI))); + +SND_SOC_DAILINK_DEF(dmic_pin, + DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin"))); +SND_SOC_DAILINK_DEF(dmic_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi"))); + +SND_SOC_DAILINK_DEF(idisp1_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin"))); +SND_SOC_DAILINK_DEF(idisp1_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1"))); + +SND_SOC_DAILINK_DEF(idisp2_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin"))); +SND_SOC_DAILINK_DEF(idisp2_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2"))); + +SND_SOC_DAILINK_DEF(idisp3_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin"))); +SND_SOC_DAILINK_DEF(idisp3_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3"))); + +SND_SOC_DAILINK_DEF(platform, /* subject to be overridden during probe */ + DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3"))); + +static struct snd_soc_dai_link dais[] = { + /* Back End DAI links */ + { + .name = "SSP1-Codec", + .id = 0, + .ignore_pmdown_time = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, /* IV feedback */ + .ops = &ssp1_ops, + SND_SOC_DAILINK_REG(ssp1_pin, ssp1_amps, platform), + }, + { + .name = "SSP0-Codec", + .id = 1, + .no_pcm = 1, + .init = da7219_codec_init, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(ssp0_pin, ssp0_codec, platform), + }, + { + .name = "dmic01", + .id = 2, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform), + }, + { + .name = "iDisp1", + .id = 3, + .init = hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform), + }, + { + .name = "iDisp2", + .id = 4, + .init = hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform), + }, + { + .name = "iDisp3", + .id = 5, + .init = hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform), + }, +}; + +static struct snd_soc_card card_da7219_m98373 = { + .name = "da7219max", + .owner = THIS_MODULE, + .dai_link = dais, + .num_links = ARRAY_SIZE(dais), + .controls = controls, + .num_controls = ARRAY_SIZE(controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + .codec_conf = max98373_codec_conf, + .num_configs = ARRAY_SIZE(max98373_codec_conf), + .fully_routed = true, + .late_probe = card_late_probe, +}; + +static int audio_probe(struct platform_device *pdev) +{ + static struct snd_soc_card *card; + struct snd_soc_acpi_mach *mach; + struct card_private *ctx; + int ret; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + card = (struct snd_soc_card *)pdev->id_entry->driver_data; + card->dev = &pdev->dev; + + mach = (&pdev->dev)->platform_data; + ret = snd_soc_fixup_dai_links_platform_name(card, + mach->mach_params.platform); + if (ret) + return ret; + + snd_soc_card_set_drvdata(card, ctx); + + return devm_snd_soc_register_card(&pdev->dev, card); +} + +static const struct platform_device_id board_ids[] = { + { + .name = "sof_da7219_max98373", + .driver_data = (kernel_ulong_t)&card_da7219_m98373, + }, + { } +}; + +static struct platform_driver audio = { + .probe = audio_probe, + .driver = { + .name = "sof_da7219_max98373", + .pm = &snd_soc_pm_ops, + }, + .id_table = board_ids, +}; +module_platform_driver(audio) + +/* Module information */ +MODULE_DESCRIPTION("ASoC Intel(R) SOF Machine driver"); +MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sof_da7219_max98373"); diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 751b8ea6ae1f..8a13231dee15 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -35,6 +35,10 @@ #define SOF_RT5682_SSP_AMP(quirk) \ (((quirk) << SOF_RT5682_SSP_AMP_SHIFT) & SOF_RT5682_SSP_AMP_MASK) #define SOF_RT5682_MCLK_BYTCHT_EN BIT(9) +#define SOF_RT5682_NUM_HDMIDEV_SHIFT 10 +#define SOF_RT5682_NUM_HDMIDEV_MASK (GENMASK(12, 10)) +#define SOF_RT5682_NUM_HDMIDEV(quirk) \ + ((quirk << SOF_RT5682_NUM_HDMIDEV_SHIFT) & SOF_RT5682_NUM_HDMIDEV_MASK) /* Default: MCLK on, MCLK 19.2M, SSP0 */ static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN | @@ -594,6 +598,19 @@ static int sof_audio_probe(struct platform_device *pdev) if (!ctx) return -ENOMEM; + if (pdev->id_entry && pdev->id_entry->driver_data) + sof_rt5682_quirk = (unsigned long)pdev->id_entry->driver_data; + + dmi_check_system(sof_rt5682_quirk_table); + + mach = (&pdev->dev)->platform_data; + + /* A speaker amp might not be present when the quirk claims one is. + * Detect this via whether the machine driver match includes quirk_data. + */ + if ((sof_rt5682_quirk & SOF_SPEAKER_AMP_PRESENT) && !mach->quirk_data) + sof_rt5682_quirk &= ~SOF_SPEAKER_AMP_PRESENT; + if (soc_intel_is_byt() || soc_intel_is_cht()) { is_legacy_cpu = 1; dmic_be_num = 0; @@ -604,11 +621,13 @@ static int sof_audio_probe(struct platform_device *pdev) SOF_RT5682_SSP_CODEC(2); } else { dmic_be_num = 2; - hdmi_num = 3; + hdmi_num = (sof_rt5682_quirk & SOF_RT5682_NUM_HDMIDEV_MASK) >> + SOF_RT5682_NUM_HDMIDEV_SHIFT; + /* default number of HDMI DAI's */ + if (!hdmi_num) + hdmi_num = 3; } - dmi_check_system(sof_rt5682_quirk_table); - /* need to get main clock from pmc */ if (sof_rt5682_quirk & SOF_RT5682_MCLK_BYTCHT_EN) { ctx->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); @@ -652,7 +671,6 @@ static int sof_audio_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ctx->hdmi_pcm_list); sof_audio_card_rt5682.dev = &pdev->dev; - mach = (&pdev->dev)->platform_data; /* set platform name for each dailink */ ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_rt5682, @@ -683,6 +701,21 @@ static int sof_rt5682_remove(struct platform_device *pdev) return 0; } +static const struct platform_device_id board_ids[] = { + { + .name = "sof_rt5682", + }, + { + .name = "tgl_max98357a_rt5682", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(1) | + SOF_RT5682_NUM_HDMIDEV(4)), + }, + { } +}; + static struct platform_driver sof_audio = { .probe = sof_audio_probe, .remove = sof_rt5682_remove, @@ -690,6 +723,7 @@ static struct platform_driver sof_audio = { .name = "sof_rt5682", .pm = &snd_soc_pm_ops, }, + .id_table = board_ids, }; module_platform_driver(sof_audio) @@ -699,3 +733,4 @@ MODULE_AUTHOR("Bard Liao <bard.liao@intel.com>"); MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:sof_rt5682"); +MODULE_ALIAS("platform:tgl_max98357a_rt5682"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cfl-match.c b/sound/soc/intel/common/soc-acpi-intel-cfl-match.c index d6fd2026d0b8..ff9d6938b9f6 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cfl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cfl-match.c @@ -14,5 +14,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cfl_machines); +struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_sdw_machines[] = { + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cfl_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c index fb9ba8819706..f55634c4c2e8 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -59,5 +59,94 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_machines); +static const u64 rt711_0_adr[] = { + 0x000010025D071100 +}; + +static const u64 rt1308_1_adr[] = { + 0x000110025D130800 +}; + +static const u64 rt1308_2_adr[] = { + 0x000210025D130800 +}; + +static const u64 rt715_3_adr[] = { + 0x000310025D071500 +}; + +static const struct snd_soc_acpi_link_adr cml_3_in_1_default[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_0_adr), + .adr = rt711_0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1308_1_adr), + .adr = rt1308_1_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1308_2_adr), + .adr = rt1308_2_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt715_3_adr), + .adr = rt715_3_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr cml_3_in_1_mono_amp[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_0_adr), + .adr = rt711_0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1308_1_adr), + .adr = rt1308_1_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt715_3_adr), + .adr = rt715_3_adr, + }, + {} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[] = { + { + .link_mask = 0xF, /* 4 active links required */ + .links = cml_3_in_1_default, + .drv_name = "sdw_rt711_rt1308_rt715", + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-rt711-rt1308-rt715.tplg", + }, + { + /* + * link_mask should be 0xB, but all links are enabled by BIOS. + * This entry will be selected if there is no rt1308 exposed + * on link2 since it will fail to match the above entry. + */ + .link_mask = 0xF, + .links = cml_3_in_1_mono_amp, + .drv_name = "sdw_rt711_rt1308_rt715", + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-rt711-rt1308-mono-rt715.tplg", + }, + { + .link_mask = 0x2, /* RT700 connected on Link1 */ + .drv_name = "sdw_rt700", + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-rt700.tplg", + }, + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index 27588841c8b0..828980d5630d 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -27,5 +27,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); +struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[] = { + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_sdw_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 34eb0baaa951..35958553652e 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 @@ -30,6 +30,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { .sof_tplg_filename = "sof-bdw-rt286.tplg", }, { + .id = "10EC5650", + .drv_name = "bdw-rt5650", + .fw_filename = "intel/IntcSST2.bin", + .sof_fw_filename = "sof-bdw.ri", + .sof_tplg_filename = "sof-bdw-rt5650.tplg", + }, + { .id = "RT5677CE", .drv_name = "bdw-rt5677", .fw_filename = "intel/IntcSST2.bin", diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c index 38977669b576..67e9da4635f2 100644 --- a/sound/soc/intel/common/soc-acpi-intel-icl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -33,5 +33,103 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_machines); +static const u64 rt700_0_adr[] = { + 0x000010025D070000 +}; + +static const struct snd_soc_acpi_link_adr icl_rvp[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt700_0_adr), + .adr = rt700_0_adr, + }, + {} +}; + +static const u64 rt711_0_adr[] = { + 0x000010025D071100 +}; + +static const u64 rt1308_1_adr[] = { + 0x000110025D130800 +}; + +static const u64 rt1308_2_adr[] = { + 0x000210025D130800 +}; + +static const u64 rt715_3_adr[] = { + 0x000310025D715000 +}; + +static const struct snd_soc_acpi_link_adr icl_3_in_1_default[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_0_adr), + .adr = rt711_0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1308_1_adr), + .adr = rt1308_1_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1308_2_adr), + .adr = rt1308_2_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt715_3_adr), + .adr = rt715_3_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr icl_3_in_1_mono_amp[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_0_adr), + .adr = rt711_0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1308_1_adr), + .adr = rt1308_1_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt715_3_adr), + .adr = rt715_3_adr, + }, + {} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_sdw_machines[] = { + { + .link_mask = 0xF, /* 4 active links required */ + .links = icl_3_in_1_default, + .drv_name = "sdw_rt711_rt1308_rt715", + .sof_fw_filename = "sof-icl.ri", + .sof_tplg_filename = "sof-icl-rt711-rt1308-rt715.tplg", + }, + { + .link_mask = 0xB, /* 3 active links required */ + .links = icl_3_in_1_mono_amp, + .drv_name = "sdw_rt711_rt1308_rt715", + .sof_fw_filename = "sof-icl.ri", + .sof_tplg_filename = "sof-icl-rt711-rt1308-rt715-mono.tplg", + }, + { + .link_mask = 0x1, /* rt700 connected on link0 */ + .links = icl_rvp, + .drv_name = "sdw_rt700", + .sof_fw_filename = "sof-icl.ri", + .sof_tplg_filename = "sof-icl-rt700.tplg", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c index 1c68a04f0c6e..ed2b125f6a11 100644 --- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c @@ -10,6 +10,13 @@ #include <sound/soc-acpi-intel-match.h> struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { + { + .id = "DLGS7219", + .drv_name = "sof_da7219_max98373", + .machine_quirk = snd_soc_acpi_codec_list, + .sof_fw_filename = "sof-jsl.ri", + .sof_tplg_filename = "sof-jsl-da7219.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_jsl_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index 57a6298d6dca..5984dd151f3e 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -9,16 +9,76 @@ #include <sound/soc-acpi.h> #include <sound/soc-acpi-intel-match.h> +static struct snd_soc_acpi_codecs tgl_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +static const u64 rt711_0_adr[] = { + 0x000010025D071100 +}; + +static const u64 rt1308_1_adr[] = { + 0x000120025D130800, + 0x000122025D130800 +}; + +static const struct snd_soc_acpi_link_adr tgl_i2s_rt1308[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_0_adr), + .adr = rt711_0_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr tgl_rvp[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_0_adr), + .adr = rt711_0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1308_1_adr), + .adr = rt1308_1_adr, + }, + {} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { { .id = "10EC1308", - .drv_name = "tgl_rt1308", + .drv_name = "rt711_rt1308", + .link_mask = 0x1, /* RT711 on SoundWire link0 */ + .links = tgl_i2s_rt1308, + .sof_fw_filename = "sof-tgl.ri", + .sof_tplg_filename = "sof-tgl-rt711-rt1308.tplg", + }, + { + .id = "10EC5682", + .drv_name = "tgl_max98357a_rt5682", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &tgl_codecs, .sof_fw_filename = "sof-tgl.ri", - .sof_tplg_filename = "sof-tgl-rt1308.tplg", + .sof_tplg_filename = "sof-tgl-max98357a-rt5682.tplg", }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_machines); +/* this table is used when there is no I2S codec present */ +struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = { + { + .link_mask = 0x3, /* rt711 on link 0 and 2 rt1308s on link 1 */ + .links = tgl_rvp, + .drv_name = "sdw_rt711_rt1308_rt715", + .sof_fw_filename = "sof-tgl.ri", + .sof_tplg_filename = "sof-tgl-rt711-rt1308.tplg", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c index a3a5bba2fbd9..033d7c05d7fb 100644 --- a/sound/soc/intel/haswell/sst-haswell-pcm.c +++ b/sound/soc/intel/haswell/sst-haswell-pcm.c @@ -592,13 +592,6 @@ static int hsw_pcm_hw_params(struct snd_soc_component *component, return ret; } - ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); - if (ret < 0) { - dev_err(rtd->dev, "error: could not allocate %d bytes for PCM %d\n", - params_buffer_bytes(params), ret); - return ret; - } - dmab = snd_pcm_get_dma_buf(substream); ret = create_adsp_page_table(substream, pdata, rtd, runtime->dma_area, @@ -656,13 +649,6 @@ static int hsw_pcm_hw_params(struct snd_soc_component *component, return 0; } -static int hsw_pcm_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - snd_pcm_lib_free_pages(substream); - return 0; -} - static int hsw_pcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { @@ -930,7 +916,7 @@ static int hsw_pcm_new(struct snd_soc_component *component, if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - snd_pcm_lib_preallocate_pages_for_all(pcm, + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG, dev, hsw_pcm_hardware.buffer_bytes_max, @@ -1114,10 +1100,8 @@ static const struct snd_soc_component_driver hsw_dai_component = { .open = hsw_pcm_open, .close = hsw_pcm_close, .hw_params = hsw_pcm_hw_params, - .hw_free = hsw_pcm_hw_free, .trigger = hsw_pcm_trigger, .pointer = hsw_pcm_pointer, - .ioctl = snd_soc_pcm_lib_ioctl, .pcm_construct = hsw_pcm_new, .controls = hsw_volume_controls, .num_controls = ARRAY_SIZE(hsw_volume_controls), diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 900746c30bff..b99509675d29 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -77,13 +77,7 @@ static int skl_substream_alloc_pages(struct hdac_bus *bus, hdac_stream(stream)->period_bytes = 0; hdac_stream(stream)->format_val = 0; - return snd_pcm_lib_malloc_pages(substream, size); -} - -static int skl_substream_free_pages(struct hdac_bus *bus, - struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); + return 0; } static void skl_set_pcm_constrains(struct hdac_bus *bus, @@ -385,7 +379,6 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, static int skl_pcm_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct skl_dev *skl = get_skl_ctx(dai->dev); struct skl_module_cfg *mconfig; @@ -405,7 +398,7 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, snd_hdac_stream_cleanup(hdac_stream(stream)); hdac_stream(stream)->prepared = 0; - return skl_substream_free_pages(bus, substream); + return 0; } static int skl_be_hw_params(struct snd_pcm_substream *substream, @@ -1289,12 +1282,6 @@ static int skl_platform_soc_get_time_info( return 0; } -static void skl_platform_soc_free(struct snd_soc_component *component, - struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - #define MAX_PREALLOC_SIZE (32 * 1024 * 1024) static int skl_platform_soc_new(struct snd_soc_component *component, @@ -1312,10 +1299,10 @@ static int skl_platform_soc_new(struct snd_soc_component *component, size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024; if (size > MAX_PREALLOC_SIZE) size = MAX_PREALLOC_SIZE; - snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_DEV_SG, - &skl->pci->dev, - size, MAX_PREALLOC_SIZE); + snd_pcm_set_managed_buffer_all(pcm, + SNDRV_DMA_TYPE_DEV_SG, + &skl->pci->dev, + size, MAX_PREALLOC_SIZE); } return 0; @@ -1477,13 +1464,11 @@ static const struct snd_soc_component_driver skl_component = { .probe = skl_platform_soc_probe, .remove = skl_platform_soc_remove, .open = skl_platform_soc_open, - .ioctl = snd_soc_pcm_lib_ioctl, .trigger = skl_platform_soc_trigger, .pointer = skl_platform_soc_pointer, .get_time_info = skl_platform_soc_get_time_info, .mmap = skl_platform_soc_mmap, .pcm_construct = skl_platform_soc_new, - .pcm_destruct = skl_platform_soc_free, .module_get_upon_open = 1, /* increment refcount when a pcm is opened */ }; diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 58ba3e9469ba..f755ca2484cf 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -773,11 +773,6 @@ static void skl_codec_create(struct hdac_bus *bus) } } -static const struct hdac_bus_ops bus_core_ops = { - .command = snd_hdac_bus_send_cmd, - .get_response = snd_hdac_bus_get_response, -}; - static int skl_i915_init(struct hdac_bus *bus) { int err; @@ -888,7 +883,7 @@ static int skl_create(struct pci_dev *pci, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC) ext_ops = snd_soc_hdac_hda_get_ops(); #endif - snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, ext_ops); + snd_hdac_ext_bus_init(bus, &pci->dev, NULL, ext_ops); bus->use_posbuf = 1; skl->pci = pci; INIT_WORK(&skl->probe_work, skl_probe_work); diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index 38d48d101783..9d5405881209 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -324,12 +324,12 @@ static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, return ret; } -static int jz4740_i2s_suspend(struct snd_soc_dai *dai) +static int jz4740_i2s_suspend(struct snd_soc_component *component) { - struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); + struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); uint32_t conf; - if (dai->active) { + if (component->active) { conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); conf &= ~JZ_AIC_CONF_ENABLE; jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); @@ -342,9 +342,9 @@ static int jz4740_i2s_suspend(struct snd_soc_dai *dai) return 0; } -static int jz4740_i2s_resume(struct snd_soc_dai *dai) +static int jz4740_i2s_resume(struct snd_soc_component *component) { - struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); + struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); uint32_t conf; int ret; @@ -352,7 +352,7 @@ static int jz4740_i2s_resume(struct snd_soc_dai *dai) if (ret) return ret; - if (dai->active) { + if (component->active) { ret = clk_prepare_enable(i2s->clk_i2s); if (ret) { clk_disable_unprepare(i2s->clk_aic); @@ -455,8 +455,6 @@ static struct snd_soc_dai_driver jz4740_i2s_dai = { }, .symmetric_rates = 1, .ops = &jz4740_i2s_dai_ops, - .suspend = jz4740_i2s_suspend, - .resume = jz4740_i2s_resume, }; static struct snd_soc_dai_driver jz4780_i2s_dai = { @@ -475,12 +473,12 @@ static struct snd_soc_dai_driver jz4780_i2s_dai = { .formats = JZ4740_I2S_FMTS, }, .ops = &jz4740_i2s_dai_ops, - .suspend = jz4740_i2s_suspend, - .resume = jz4740_i2s_resume, }; static const struct snd_soc_component_driver jz4740_i2s_component = { .name = "jz4740-i2s", + .suspend = jz4740_i2s_suspend, + .resume = jz4740_i2s_resume, }; #ifdef CONFIG_OF diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index e28fb3449f1d..f882b4003edf 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -316,7 +316,6 @@ const struct snd_soc_component_driver kirkwood_soc_component = { .name = DRV_NAME, .open = kirkwood_dma_open, .close = kirkwood_dma_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = kirkwood_dma_hw_params, .hw_free = kirkwood_dma_hw_free, .prepare = kirkwood_dma_prepare, diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c index 10ea4fdbeb1e..4254f3a954dd 100644 --- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c +++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c @@ -6,11 +6,13 @@ * Author: Garlic Tseng <garlic.tseng@mediatek.com> */ +#include <linux/io.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <sound/soc.h> #include "mtk-afe-platform-driver.h" +#include <sound/pcm_params.h> #include "mtk-afe-fe-dai.h" #include "mtk-base-afe.h" @@ -120,50 +122,60 @@ int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; - int msb_at_bit33 = 0; - int ret, fs = 0; - - ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); - if (ret < 0) + int id = rtd->cpu_dai->id; + struct mtk_base_afe_memif *memif = &afe->memif[id]; + int ret; + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + snd_pcm_format_t format = params_format(params); + + if (afe->request_dram_resource) + afe->request_dram_resource(afe->dev); + + dev_dbg(afe->dev, "%s(), %s, ch %d, rate %d, fmt %d, dma_addr %pad, dma_area %p, dma_bytes 0x%zx\n", + __func__, memif->data->name, + channels, rate, format, + &substream->runtime->dma_addr, + substream->runtime->dma_area, + substream->runtime->dma_bytes); + + memset_io(substream->runtime->dma_area, 0, + substream->runtime->dma_bytes); + + /* set addr */ + ret = mtk_memif_set_addr(afe, id, + substream->runtime->dma_area, + substream->runtime->dma_addr, + substream->runtime->dma_bytes); + if (ret) { + dev_err(afe->dev, "%s(), error, id %d, set addr, ret %d\n", + __func__, id, ret); return ret; - - msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0; - memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr); - memif->buffer_size = substream->runtime->dma_bytes; - - /* start */ - mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base, - memif->phys_buf_addr); - /* end */ - mtk_regmap_write(afe->regmap, - memif->data->reg_ofs_base + AFE_BASE_END_OFFSET, - memif->phys_buf_addr + memif->buffer_size - 1); - - /* set MSB to 33-bit */ - mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg, - 1, msb_at_bit33, memif->data->msb_shift); + } /* set channel */ - if (memif->data->mono_shift >= 0) { - unsigned int mono = (params_channels(params) == 1) ? 1 : 0; - - mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg, - 1, mono, memif->data->mono_shift); + ret = mtk_memif_set_channel(afe, id, channels); + if (ret) { + dev_err(afe->dev, "%s(), error, id %d, set channel %d, ret %d\n", + __func__, id, channels, ret); + return ret; } /* set rate */ - if (memif->data->fs_shift < 0) - return 0; - - fs = afe->memif_fs(substream, params_rate(params)); - - if (fs < 0) - return -EINVAL; + ret = mtk_memif_set_rate_substream(substream, id, rate); + if (ret) { + dev_err(afe->dev, "%s(), error, id %d, set rate %d, ret %d\n", + __func__, id, rate, ret); + return ret; + } - mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg, - memif->data->fs_maskbit, fs, - memif->data->fs_shift); + /* set format */ + ret = mtk_memif_set_format(afe, id, format); + if (ret) { + dev_err(afe->dev, "%s(), error, id %d, set format %d, ret %d\n", + __func__, id, format, ret); + return ret; + } return 0; } @@ -172,7 +184,12 @@ EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_params); int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - return snd_pcm_lib_free_pages(substream); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + + if (afe->release_dram_resource) + afe->release_dram_resource(afe->dev); + + return 0; } EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_free); @@ -182,20 +199,25 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime * const runtime = substream->runtime; struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + int id = rtd->cpu_dai->id; + struct mtk_base_afe_memif *memif = &afe->memif[id]; struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage]; const struct mtk_base_irq_data *irq_data = irqs->irq_data; unsigned int counter = runtime->period_size; int fs; + int ret; dev_dbg(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: - mtk_regmap_update_bits(afe->regmap, - memif->data->enable_reg, - 1, 1, memif->data->enable_shift); + ret = mtk_memif_set_enable(afe, id); + if (ret) { + dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n", + __func__, id, ret); + return ret; + } /* set irq counter */ mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg, @@ -219,15 +241,19 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd, return 0; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: - mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg, - 1, 0, memif->data->enable_shift); + ret = mtk_memif_set_disable(afe, id); + if (ret) { + dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n", + __func__, id, ret); + } + /* disable interrupt */ mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg, 1, 0, irq_data->irq_en_shift); /* and clear pending IRQ */ mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg, 1 << irq_data->irq_clr_shift); - return 0; + return ret; default: return -EINVAL; } @@ -239,34 +265,15 @@ int mtk_afe_fe_prepare(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); - struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; - int hd_audio = 0; - int hd_align = 0; + int id = rtd->cpu_dai->id; + int pbuf_size; - /* set hd mode */ - switch (substream->runtime->format) { - case SNDRV_PCM_FORMAT_S16_LE: - hd_audio = 0; - break; - case SNDRV_PCM_FORMAT_S32_LE: - hd_audio = 1; - hd_align = 1; - break; - case SNDRV_PCM_FORMAT_S24_LE: - hd_audio = 1; - break; - default: - dev_err(afe->dev, "%s() error: unsupported format %d\n", - __func__, substream->runtime->format); - break; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (afe->get_memif_pbuf_size) { + pbuf_size = afe->get_memif_pbuf_size(substream); + mtk_memif_set_pbuf_size(afe, id, pbuf_size); + } } - - mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg, - 1, hd_audio, memif->data->hd_shift); - - mtk_regmap_update_bits(afe->regmap, memif->data->hd_align_reg, - 1, hd_align, memif->data->hd_align_mshift); - return 0; } EXPORT_SYMBOL_GPL(mtk_afe_fe_prepare); @@ -312,9 +319,9 @@ int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id) } EXPORT_SYMBOL_GPL(mtk_dynamic_irq_release); -int mtk_afe_dai_suspend(struct snd_soc_dai *dai) +int mtk_afe_suspend(struct snd_soc_component *component) { - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); struct device *dev = afe->dev; struct regmap *regmap = afe->regmap; int i; @@ -335,11 +342,11 @@ int mtk_afe_dai_suspend(struct snd_soc_dai *dai) afe->runtime_suspend(dev); return 0; } -EXPORT_SYMBOL_GPL(mtk_afe_dai_suspend); +EXPORT_SYMBOL_GPL(mtk_afe_suspend); -int mtk_afe_dai_resume(struct snd_soc_dai *dai) +int mtk_afe_resume(struct snd_soc_component *component) { - struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); struct device *dev = afe->dev; struct regmap *regmap = afe->regmap; int i = 0; @@ -359,7 +366,223 @@ int mtk_afe_dai_resume(struct snd_soc_dai *dai) afe->suspended = false; return 0; } -EXPORT_SYMBOL_GPL(mtk_afe_dai_resume); +EXPORT_SYMBOL_GPL(mtk_afe_resume); + +int mtk_memif_set_enable(struct mtk_base_afe *afe, int id) +{ + struct mtk_base_afe_memif *memif = &afe->memif[id]; + + if (memif->data->enable_shift < 0) { + dev_warn(afe->dev, "%s(), error, id %d, enable_shift < 0\n", + __func__, id); + return 0; + } + return mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg, + 1, 1, memif->data->enable_shift); +} +EXPORT_SYMBOL_GPL(mtk_memif_set_enable); + +int mtk_memif_set_disable(struct mtk_base_afe *afe, int id) +{ + struct mtk_base_afe_memif *memif = &afe->memif[id]; + + if (memif->data->enable_shift < 0) { + dev_warn(afe->dev, "%s(), error, id %d, enable_shift < 0\n", + __func__, id); + return 0; + } + return mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg, + 1, 0, memif->data->enable_shift); +} +EXPORT_SYMBOL_GPL(mtk_memif_set_disable); + +int mtk_memif_set_addr(struct mtk_base_afe *afe, int id, + unsigned char *dma_area, + dma_addr_t dma_addr, + size_t dma_bytes) +{ + struct mtk_base_afe_memif *memif = &afe->memif[id]; + int msb_at_bit33 = upper_32_bits(dma_addr) ? 1 : 0; + unsigned int phys_buf_addr = lower_32_bits(dma_addr); + unsigned int phys_buf_addr_upper_32 = upper_32_bits(dma_addr); + + memif->dma_area = dma_area; + memif->dma_addr = dma_addr; + memif->dma_bytes = dma_bytes; + + /* start */ + mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base, + phys_buf_addr); + /* end */ + if (memif->data->reg_ofs_end) + mtk_regmap_write(afe->regmap, + memif->data->reg_ofs_end, + phys_buf_addr + dma_bytes - 1); + else + mtk_regmap_write(afe->regmap, + memif->data->reg_ofs_base + + AFE_BASE_END_OFFSET, + phys_buf_addr + dma_bytes - 1); + + /* set start, end, upper 32 bits */ + if (memif->data->reg_ofs_base_msb) { + mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base_msb, + phys_buf_addr_upper_32); + mtk_regmap_write(afe->regmap, + memif->data->reg_ofs_end_msb, + phys_buf_addr_upper_32); + } + + /* set MSB to 33-bit */ + if (memif->data->msb_reg >= 0) + mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg, + 1, msb_at_bit33, memif->data->msb_shift); + + return 0; +} +EXPORT_SYMBOL_GPL(mtk_memif_set_addr); + +int mtk_memif_set_channel(struct mtk_base_afe *afe, + int id, unsigned int channel) +{ + struct mtk_base_afe_memif *memif = &afe->memif[id]; + unsigned int mono; + + if (memif->data->mono_shift < 0) + return 0; + + if (memif->data->quad_ch_mask) { + unsigned int quad_ch = (channel == 4) ? 1 : 0; + + mtk_regmap_update_bits(afe->regmap, memif->data->quad_ch_reg, + memif->data->quad_ch_mask, + quad_ch, memif->data->quad_ch_shift); + } + + if (memif->data->mono_invert) + mono = (channel == 1) ? 0 : 1; + else + mono = (channel == 1) ? 1 : 0; + + return mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg, + 1, mono, memif->data->mono_shift); +} +EXPORT_SYMBOL_GPL(mtk_memif_set_channel); + +static int mtk_memif_set_rate_fs(struct mtk_base_afe *afe, + int id, int fs) +{ + struct mtk_base_afe_memif *memif = &afe->memif[id]; + + if (memif->data->fs_shift >= 0) + mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg, + memif->data->fs_maskbit, + fs, memif->data->fs_shift); + + return 0; +} + +int mtk_memif_set_rate(struct mtk_base_afe *afe, + int id, unsigned int rate) +{ + int fs = 0; + + if (!afe->get_dai_fs) { + dev_err(afe->dev, "%s(), error, afe->get_dai_fs == NULL\n", + __func__); + return -EINVAL; + } + + fs = afe->get_dai_fs(afe, id, rate); + + if (fs < 0) + return -EINVAL; + + return mtk_memif_set_rate_fs(afe, id, fs); +} +EXPORT_SYMBOL_GPL(mtk_memif_set_rate); + +int mtk_memif_set_rate_substream(struct snd_pcm_substream *substream, + int id, unsigned int rate) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + + int fs = 0; + + if (!afe->memif_fs) { + dev_err(afe->dev, "%s(), error, afe->memif_fs == NULL\n", + __func__); + return -EINVAL; + } + + fs = afe->memif_fs(substream, rate); + + if (fs < 0) + return -EINVAL; + + return mtk_memif_set_rate_fs(afe, id, fs); +} +EXPORT_SYMBOL_GPL(mtk_memif_set_rate_substream); + +int mtk_memif_set_format(struct mtk_base_afe *afe, + int id, snd_pcm_format_t format) +{ + struct mtk_base_afe_memif *memif = &afe->memif[id]; + int hd_audio = 0; + int hd_align = 0; + + /* set hd mode */ + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_U16_LE: + hd_audio = 0; + break; + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_U32_LE: + hd_audio = 1; + hd_align = 1; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_U24_LE: + hd_audio = 1; + break; + default: + dev_err(afe->dev, "%s() error: unsupported format %d\n", + __func__, format); + break; + } + + mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg, + 1, hd_audio, memif->data->hd_shift); + + mtk_regmap_update_bits(afe->regmap, memif->data->hd_align_reg, + 1, hd_align, memif->data->hd_align_mshift); + + return 0; +} +EXPORT_SYMBOL_GPL(mtk_memif_set_format); + +int mtk_memif_set_pbuf_size(struct mtk_base_afe *afe, + int id, int pbuf_size) +{ + const struct mtk_base_memif_data *memif_data = afe->memif[id].data; + + if (memif_data->pbuf_mask == 0 || memif_data->minlen_mask == 0) + return 0; + + mtk_regmap_update_bits(afe->regmap, memif_data->pbuf_reg, + memif_data->pbuf_mask, + pbuf_size, memif_data->pbuf_shift); + + mtk_regmap_update_bits(afe->regmap, memif_data->minlen_reg, + memif_data->minlen_mask, + pbuf_size, memif_data->minlen_shift); + return 0; +} +EXPORT_SYMBOL_GPL(mtk_memif_set_pbuf_size); MODULE_DESCRIPTION("Mediatek simple fe dai operator"); MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>"); diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.h b/sound/soc/mediatek/common/mtk-afe-fe-dai.h index 55074fb9861a..8cec90671827 100644 --- a/sound/soc/mediatek/common/mtk-afe-fe-dai.h +++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.h @@ -31,7 +31,23 @@ extern const struct snd_soc_dai_ops mtk_afe_fe_ops; int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe); int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id); -int mtk_afe_dai_suspend(struct snd_soc_dai *dai); -int mtk_afe_dai_resume(struct snd_soc_dai *dai); +int mtk_afe_suspend(struct snd_soc_component *component); +int mtk_afe_resume(struct snd_soc_component *component); +int mtk_memif_set_enable(struct mtk_base_afe *afe, int id); +int mtk_memif_set_disable(struct mtk_base_afe *afe, int id); +int mtk_memif_set_addr(struct mtk_base_afe *afe, int id, + unsigned char *dma_area, + dma_addr_t dma_addr, + size_t dma_bytes); +int mtk_memif_set_channel(struct mtk_base_afe *afe, + int id, unsigned int channel); +int mtk_memif_set_rate(struct mtk_base_afe *afe, + int id, unsigned int rate); +int mtk_memif_set_rate_substream(struct snd_pcm_substream *substream, + int id, unsigned int rate); +int mtk_memif_set_format(struct mtk_base_afe *afe, + int id, snd_pcm_format_t format); +int mtk_memif_set_pbuf_size(struct mtk_base_afe *afe, + int id, int pbuf_size); #endif diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c index b6624d8d084b..44dfef713905 100644 --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c @@ -120,25 +120,16 @@ int mtk_afe_pcm_new(struct snd_soc_component *component, struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); size = afe->mtk_afe_hardware->buffer_bytes_max; - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - afe->dev, size, size); + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, + afe->dev, size, size); return 0; } EXPORT_SYMBOL_GPL(mtk_afe_pcm_new); -void mtk_afe_pcm_free(struct snd_soc_component *component, - struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} -EXPORT_SYMBOL_GPL(mtk_afe_pcm_free); - const struct snd_soc_component_driver mtk_afe_pcm_platform = { .name = AFE_PCM_NAME, - .ioctl = snd_soc_pcm_lib_ioctl, .pointer = mtk_afe_pcm_pointer, .pcm_construct = mtk_afe_pcm_new, - .pcm_destruct = mtk_afe_pcm_free, }; EXPORT_SYMBOL_GPL(mtk_afe_pcm_platform); diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.h b/sound/soc/mediatek/common/mtk-afe-platform-driver.h index e550d11568c3..fcc923b88f12 100644 --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.h +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.h @@ -21,8 +21,6 @@ snd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream); int mtk_afe_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd); -void mtk_afe_pcm_free(struct snd_soc_component *component, - struct snd_pcm *pcm); int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe); int mtk_afe_add_sub_dai_control(struct snd_soc_component *component); diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h index 60cb609a9790..a8cf44d98244 100644 --- a/sound/soc/mediatek/common/mtk-base-afe.h +++ b/sound/soc/mediatek/common/mtk-base-afe.h @@ -16,21 +16,38 @@ struct mtk_base_memif_data { const char *name; int reg_ofs_base; int reg_ofs_cur; + int reg_ofs_end; + int reg_ofs_base_msb; + int reg_ofs_cur_msb; + int reg_ofs_end_msb; int fs_reg; int fs_shift; int fs_maskbit; int mono_reg; int mono_shift; + int mono_invert; + int quad_ch_reg; + int quad_ch_mask; + int quad_ch_shift; int enable_reg; int enable_shift; int hd_reg; - int hd_align_reg; int hd_shift; + int hd_align_reg; int hd_align_mshift; int msb_reg; int msb_shift; + int msb2_reg; + int msb2_shift; int agent_disable_reg; int agent_disable_shift; + /* playback memif only */ + int pbuf_reg; + int pbuf_mask; + int pbuf_shift; + int minlen_reg; + int minlen_mask; + int minlen_shift; }; struct mtk_base_irq_data { @@ -84,6 +101,12 @@ struct mtk_base_afe { unsigned int rate); int (*irq_fs)(struct snd_pcm_substream *substream, unsigned int rate); + int (*get_dai_fs)(struct mtk_base_afe *afe, + int dai_id, unsigned int rate); + int (*get_memif_pbuf_size)(struct snd_pcm_substream *substream); + + int (*request_dram_resource)(struct device *dev); + int (*release_dram_resource)(struct device *dev); void *platform_priv; }; @@ -95,6 +118,9 @@ struct mtk_base_afe_memif { const struct mtk_base_memif_data *data; int irq_usage; int const_irq; + unsigned char *dma_area; + dma_addr_t dma_addr; + size_t dma_bytes; }; struct mtk_base_afe_irq { diff --git a/sound/soc/mediatek/common/mtk-btcvsd.c b/sound/soc/mediatek/common/mtk-btcvsd.c index 2b490ae2e642..668fef3e319a 100644 --- a/sound/soc/mediatek/common/mtk-btcvsd.c +++ b/sound/soc/mediatek/common/mtk-btcvsd.c @@ -1271,7 +1271,6 @@ static const struct snd_soc_component_driver mtk_btcvsd_snd_platform = { .probe = mtk_btcvsd_snd_component_probe, .open = mtk_pcm_btcvsd_open, .close = mtk_pcm_btcvsd_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = mtk_pcm_btcvsd_hw_params, .hw_free = mtk_pcm_btcvsd_hw_free, .prepare = mtk_pcm_btcvsd_prepare, diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c index 76502ba261c8..488603a0c4b1 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c +++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c @@ -549,8 +549,6 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = { { .name = "PCMO0", .id = MT2701_MEMIF_DL1, - .suspend = mtk_afe_dai_suspend, - .resume = mtk_afe_dai_resume, .playback = { .stream_name = "DL1", .channels_min = 1, @@ -565,8 +563,6 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = { { .name = "PCM_multi", .id = MT2701_MEMIF_DLM, - .suspend = mtk_afe_dai_suspend, - .resume = mtk_afe_dai_resume, .playback = { .stream_name = "DLM", .channels_min = 1, @@ -582,8 +578,6 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = { { .name = "PCM0", .id = MT2701_MEMIF_UL1, - .suspend = mtk_afe_dai_suspend, - .resume = mtk_afe_dai_resume, .capture = { .stream_name = "UL1", .channels_min = 1, @@ -598,8 +592,6 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = { { .name = "PCM1", .id = MT2701_MEMIF_UL2, - .suspend = mtk_afe_dai_suspend, - .resume = mtk_afe_dai_resume, .capture = { .stream_name = "UL2", .channels_min = 1, @@ -615,8 +607,6 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = { { .name = "PCM_BT_DL", .id = MT2701_MEMIF_DLBT, - .suspend = mtk_afe_dai_suspend, - .resume = mtk_afe_dai_resume, .playback = { .stream_name = "DLBT", .channels_min = 1, @@ -630,8 +620,6 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = { { .name = "PCM_BT_UL", .id = MT2701_MEMIF_ULBT, - .suspend = mtk_afe_dai_suspend, - .resume = mtk_afe_dai_resume, .capture = { .stream_name = "ULBT", .channels_min = 1, @@ -982,6 +970,8 @@ static const struct snd_soc_component_driver mt2701_afe_pcm_dai_component = { .num_dapm_widgets = ARRAY_SIZE(mt2701_afe_pcm_widgets), .dapm_routes = mt2701_afe_pcm_routes, .num_dapm_routes = ARRAY_SIZE(mt2701_afe_pcm_routes), + .suspend = mtk_afe_suspend, + .resume = mtk_afe_resume, }; static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = { diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c index 033c07fb599c..378bfc16ef52 100644 --- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c @@ -712,10 +712,8 @@ static int mt6797_afe_component_probe(struct snd_soc_component *component) static const struct snd_soc_component_driver mt6797_afe_component = { .name = AFE_PCM_NAME, .probe = mt6797_afe_component_probe, - .ioctl = snd_soc_pcm_lib_ioctl, .pointer = mtk_afe_pcm_pointer, .pcm_construct = mtk_afe_pcm_new, - .pcm_destruct = mtk_afe_pcm_free, }; static int mt6797_dai_memif_register(struct mtk_base_afe *afe) diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index 0ee29255e731..461e4de8c918 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -533,8 +533,6 @@ static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = { { .name = "DL1", /* downlink 1 */ .id = MT8173_AFE_MEMIF_DL1, - .suspend = mtk_afe_dai_suspend, - .resume = mtk_afe_dai_resume, .playback = { .stream_name = "DL1", .channels_min = 1, @@ -546,8 +544,6 @@ static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = { }, { .name = "VUL", /* voice uplink */ .id = MT8173_AFE_MEMIF_VUL, - .suspend = mtk_afe_dai_suspend, - .resume = mtk_afe_dai_resume, .capture = { .stream_name = "VUL", .channels_min = 1, @@ -584,8 +580,6 @@ static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = { { .name = "HDMI", .id = MT8173_AFE_MEMIF_HDMI, - .suspend = mtk_afe_dai_suspend, - .resume = mtk_afe_dai_resume, .playback = { .stream_name = "HDMI", .channels_min = 2, @@ -681,12 +675,16 @@ static const struct snd_soc_component_driver mt8173_afe_pcm_dai_component = { .num_dapm_widgets = ARRAY_SIZE(mt8173_afe_pcm_widgets), .dapm_routes = mt8173_afe_pcm_routes, .num_dapm_routes = ARRAY_SIZE(mt8173_afe_pcm_routes), + .suspend = mtk_afe_suspend, + .resume = mtk_afe_resume, }; static const struct snd_soc_component_driver mt8173_afe_hdmi_dai_component = { .name = "mt8173-afe-hdmi-dai", .dapm_routes = mt8173_afe_hdmi_routes, .num_dapm_routes = ARRAY_SIZE(mt8173_afe_hdmi_routes), + .suspend = mtk_afe_suspend, + .resume = mtk_afe_resume, }; static const char *aud_clks[MT8173_CLK_NUM] = { diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index 8717e87bfe26..2e1e61d8f127 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -209,7 +209,7 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev) "Property 'audio-codec' missing or invalid\n"); return -EINVAL; } - mt8173_rt5650_rt5514_codec_conf[0].of_node = + mt8173_rt5650_rt5514_codec_conf[0].dlc.of_node = mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node; card->dev = &pdev->dev; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index 9d4dd9721154..ebcc0b86286b 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -265,7 +265,7 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) "Property 'audio-codec' missing or invalid\n"); return -EINVAL; } - mt8173_rt5650_rt5676_codec_conf[0].of_node = + mt8173_rt5650_rt5676_codec_conf[0].dlc.of_node = mt8173_rt5650_rt5676_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node; mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codecs->of_node = diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index 76af09d8f1af..6e2270bbb10e 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -1050,10 +1050,8 @@ static int mt8183_afe_component_probe(struct snd_soc_component *component) static const struct snd_soc_component_driver mt8183_afe_component = { .name = AFE_PCM_NAME, .probe = mt8183_afe_component_probe, - .ioctl = snd_soc_pcm_lib_ioctl, .pointer = mtk_afe_pcm_pointer, .pcm_construct = mtk_afe_pcm_new, - .pcm_destruct = mtk_afe_pcm_free, }; static int mt8183_dai_memif_register(struct mtk_base_afe *afe) diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index 43f99e59a078..c65493721e90 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -367,7 +367,7 @@ static struct snd_soc_aux_dev mt8183_da7219_max98357_headset_dev = { static struct snd_soc_codec_conf mt6358_codec_conf[] = { { - .dev_name = "mt6358-sound", + .dlc = COMP_CODEC_CONF("mt6358-sound"), .name_prefix = "Mt6358", }, }; diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c index d6f3eefb8f09..c12b0d5e8ebf 100644 --- a/sound/soc/meson/axg-fifo.c +++ b/sound/soc/meson/axg-fifo.c @@ -34,7 +34,7 @@ static struct snd_pcm_hardware axg_fifo_hw = { .rate_max = 192000, .channels_min = 1, .channels_max = AXG_FIFO_CH_MAX, - .period_bytes_min = AXG_FIFO_MIN_DEPTH, + .period_bytes_min = AXG_FIFO_BURST, .period_bytes_max = UINT_MAX, .periods_min = 2, .periods_max = UINT_MAX, @@ -113,13 +113,10 @@ int axg_fifo_pcm_hw_params(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = ss->runtime; struct axg_fifo *fifo = axg_fifo_data(ss); + unsigned int burst_num, period, threshold; 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; + period = params_period_bytes(params); /* Setup dma memory pointers */ end_ptr = runtime->dma_addr + runtime->dma_bytes - AXG_FIFO_BURST; @@ -127,9 +124,24 @@ int axg_fifo_pcm_hw_params(struct snd_soc_component *component, regmap_write(fifo->map, FIFO_FINISH_ADDR, end_ptr); /* Setup interrupt periodicity */ - burst_num = params_period_bytes(params) / AXG_FIFO_BURST; + burst_num = period / AXG_FIFO_BURST; regmap_write(fifo->map, FIFO_INT_ADDR, burst_num); + /* + * Start the fifo request on the smallest of the following: + * - Half the fifo size + * - Half the period size + */ + threshold = min(period / 2, fifo->depth / 2); + + /* + * With the threshold in bytes, register value is: + * V = (threshold / burst) - 1 + */ + threshold /= AXG_FIFO_BURST; + regmap_field_write(fifo->field_threshold, + threshold ? threshold - 1 : 0); + /* Enable block count irq */ regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT), @@ -167,7 +179,7 @@ int axg_fifo_pcm_hw_free(struct snd_soc_component *component, regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT), 0); - return snd_pcm_lib_free_pages(ss); + return 0; } EXPORT_SYMBOL_GPL(axg_fifo_pcm_hw_free); @@ -215,17 +227,17 @@ int axg_fifo_pcm_open(struct snd_soc_component *component, /* * Make sure the buffer and period size are multiple of the FIFO - * minimum depth size + * burst */ ret = snd_pcm_hw_constraint_step(ss->runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - AXG_FIFO_MIN_DEPTH); + AXG_FIFO_BURST); if (ret) return ret; ret = snd_pcm_hw_constraint_step(ss->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - AXG_FIFO_MIN_DEPTH); + AXG_FIFO_BURST); if (ret) return ret; @@ -287,9 +299,9 @@ 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; - snd_pcm_lib_preallocate_pages(rtd->pcm->streams[type].substream, - SNDRV_DMA_TYPE_DEV, card->dev, - size, size); + snd_pcm_set_managed_buffer(rtd->pcm->streams[type].substream, + SNDRV_DMA_TYPE_DEV, card->dev, + size, size); return 0; } EXPORT_SYMBOL_GPL(axg_fifo_pcm_new); @@ -307,6 +319,7 @@ int axg_fifo_probe(struct platform_device *pdev) const struct axg_fifo_match_data *data; struct axg_fifo *fifo; void __iomem *regs; + int ret; data = of_device_get_match_data(dev); if (!data) { @@ -352,6 +365,26 @@ int axg_fifo_probe(struct platform_device *pdev) return fifo->irq; } + fifo->field_threshold = + devm_regmap_field_alloc(dev, fifo->map, data->field_threshold); + if (IS_ERR(fifo->field_threshold)) + return PTR_ERR(fifo->field_threshold); + + ret = of_property_read_u32(dev->of_node, "amlogic,fifo-depth", + &fifo->depth); + if (ret) { + /* Error out for anything but a missing property */ + if (ret != -EINVAL) + return ret; + /* + * If the property is missing, it might be because of an old + * DT. In such case, assume the smallest known fifo depth + */ + fifo->depth = 256; + dev_warn(dev, "fifo depth not found, assume %u bytes\n", + fifo->depth); + } + return devm_snd_soc_register_component(dev, data->component_drv, data->dai_drv, 1); } diff --git a/sound/soc/meson/axg-fifo.h b/sound/soc/meson/axg-fifo.h index cf928d43b558..b63acd723c87 100644 --- a/sound/soc/meson/axg-fifo.h +++ b/sound/soc/meson/axg-fifo.h @@ -9,7 +9,9 @@ struct clk; struct platform_device; +struct reg_field; struct regmap; +struct regmap_field; struct reset_control; struct snd_soc_component_driver; @@ -29,8 +31,6 @@ struct snd_soc_pcm_runtime; SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_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) @@ -50,8 +50,6 @@ struct snd_soc_pcm_runtime; #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 @@ -67,12 +65,15 @@ struct axg_fifo { struct regmap *map; struct clk *pclk; struct reset_control *arb; + struct regmap_field *field_threshold; + unsigned int depth; int irq; }; struct axg_fifo_match_data { const struct snd_soc_component_driver *component_drv; struct snd_soc_dai_driver *dai_drv; + struct reg_field field_threshold; }; int axg_fifo_pcm_open(struct snd_soc_component *component, diff --git a/sound/soc/meson/axg-frddr.c b/sound/soc/meson/axg-frddr.c index 665d75d49d7b..c3ae8ac30745 100644 --- a/sound/soc/meson/axg-frddr.c +++ b/sound/soc/meson/axg-frddr.c @@ -50,7 +50,7 @@ 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; + unsigned int val; int ret; /* Enable pclk to access registers and clock the fifo ip */ @@ -61,18 +61,10 @@ static int axg_frddr_dai_startup(struct snd_pcm_substream *substream, /* 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)); + /* Use all fifo depth */ + val = (fifo->depth / AXG_FIFO_BURST) - 1; + regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_FRDDR_DEPTH_MASK, + CTRL1_FRDDR_DEPTH(val)); return 0; } @@ -151,7 +143,6 @@ static const struct snd_soc_component_driver axg_frddr_component_drv = { .num_dapm_routes = ARRAY_SIZE(axg_frddr_dapm_routes), .open = axg_fifo_pcm_open, .close = axg_fifo_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = axg_fifo_pcm_hw_params, .hw_free = axg_fifo_pcm_hw_free, .pointer = axg_fifo_pcm_pointer, @@ -159,8 +150,9 @@ static const struct snd_soc_component_driver axg_frddr_component_drv = { }; static const struct axg_fifo_match_data axg_frddr_match_data = { - .component_drv = &axg_frddr_component_drv, - .dai_drv = &axg_frddr_dai_drv + .field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23), + .component_drv = &axg_frddr_component_drv, + .dai_drv = &axg_frddr_dai_drv }; static const struct snd_soc_dai_ops g12a_frddr_ops = { @@ -275,7 +267,6 @@ static const struct snd_soc_component_driver g12a_frddr_component_drv = { .num_dapm_routes = ARRAY_SIZE(g12a_frddr_dapm_routes), .open = axg_fifo_pcm_open, .close = axg_fifo_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = g12a_fifo_pcm_hw_params, .hw_free = axg_fifo_pcm_hw_free, .pointer = axg_fifo_pcm_pointer, @@ -283,8 +274,9 @@ static const struct snd_soc_component_driver g12a_frddr_component_drv = { }; static const struct axg_fifo_match_data g12a_frddr_match_data = { - .component_drv = &g12a_frddr_component_drv, - .dai_drv = &g12a_frddr_dai_drv + .field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23), + .component_drv = &g12a_frddr_component_drv, + .dai_drv = &g12a_frddr_dai_drv }; /* On SM1, the output selection in on CTRL2 */ @@ -345,7 +337,6 @@ static const struct snd_soc_component_driver sm1_frddr_component_drv = { .num_dapm_routes = ARRAY_SIZE(g12a_frddr_dapm_routes), .open = axg_fifo_pcm_open, .close = axg_fifo_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = g12a_fifo_pcm_hw_params, .hw_free = axg_fifo_pcm_hw_free, .pointer = axg_fifo_pcm_pointer, @@ -353,8 +344,9 @@ static const struct snd_soc_component_driver sm1_frddr_component_drv = { }; static const struct axg_fifo_match_data sm1_frddr_match_data = { - .component_drv = &sm1_frddr_component_drv, - .dai_drv = &g12a_frddr_dai_drv + .field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23), + .component_drv = &sm1_frddr_component_drv, + .dai_drv = &g12a_frddr_dai_drv }; static const struct of_device_id axg_frddr_of_match[] = { diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c index 7fef0b961496..e711abcf8c12 100644 --- a/sound/soc/meson/axg-toddr.c +++ b/sound/soc/meson/axg-toddr.c @@ -89,7 +89,6 @@ 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 */ @@ -107,11 +106,6 @@ static int axg_toddr_dai_startup(struct snd_pcm_substream *substream, /* 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; } @@ -183,7 +177,6 @@ static const struct snd_soc_component_driver axg_toddr_component_drv = { .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes), .open = axg_fifo_pcm_open, .close = axg_fifo_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = axg_fifo_pcm_hw_params, .hw_free = axg_fifo_pcm_hw_free, .pointer = axg_fifo_pcm_pointer, @@ -191,8 +184,9 @@ static const struct snd_soc_component_driver axg_toddr_component_drv = { }; static const struct axg_fifo_match_data axg_toddr_match_data = { - .component_drv = &axg_toddr_component_drv, - .dai_drv = &axg_toddr_dai_drv + .field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23), + .component_drv = &axg_toddr_component_drv, + .dai_drv = &axg_toddr_dai_drv }; static const struct snd_soc_dai_ops g12a_toddr_ops = { @@ -222,7 +216,6 @@ static const struct snd_soc_component_driver g12a_toddr_component_drv = { .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes), .open = axg_fifo_pcm_open, .close = axg_fifo_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = g12a_fifo_pcm_hw_params, .hw_free = axg_fifo_pcm_hw_free, .pointer = axg_fifo_pcm_pointer, @@ -230,8 +223,9 @@ static const struct snd_soc_component_driver g12a_toddr_component_drv = { }; static const struct axg_fifo_match_data g12a_toddr_match_data = { - .component_drv = &g12a_toddr_component_drv, - .dai_drv = &g12a_toddr_dai_drv + .field_threshold = REG_FIELD(FIFO_CTRL1, 16, 23), + .component_drv = &g12a_toddr_component_drv, + .dai_drv = &g12a_toddr_dai_drv }; static const char * const sm1_toddr_sel_texts[] = { @@ -292,7 +286,6 @@ static const struct snd_soc_component_driver sm1_toddr_component_drv = { .num_dapm_routes = ARRAY_SIZE(sm1_toddr_dapm_routes), .open = axg_fifo_pcm_open, .close = axg_fifo_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = g12a_fifo_pcm_hw_params, .hw_free = axg_fifo_pcm_hw_free, .pointer = axg_fifo_pcm_pointer, @@ -300,8 +293,9 @@ static const struct snd_soc_component_driver sm1_toddr_component_drv = { }; static const struct axg_fifo_match_data sm1_toddr_match_data = { - .component_drv = &sm1_toddr_component_drv, - .dai_drv = &g12a_toddr_dai_drv + .field_threshold = REG_FIELD(FIFO_CTRL1, 12, 23), + .component_drv = &sm1_toddr_component_drv, + .dai_drv = &g12a_toddr_dai_drv }; static const struct of_device_id axg_toddr_of_match[] = { diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index 129eb5251a5f..76e054d514a8 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -72,7 +72,7 @@ static int rear_amp_event(struct snd_soc_dapm_widget *widget, struct snd_soc_pcm_runtime *rtd; struct snd_soc_component *component; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); component = rtd->codec_dai->component; return rear_amp_power(component, SND_SOC_DAPM_EVENT_ON(event)); } diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c index 54a4c9213e83..287b5da739e5 100644 --- a/sound/soc/pxa/mmp-pcm.c +++ b/sound/soc/pxa/mmp-pcm.c @@ -225,7 +225,6 @@ static const struct snd_soc_component_driver mmp_soc_component = { .name = DRV_NAME, .open = mmp_pcm_open, .close = mmp_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = mmp_pcm_hw_params, .trigger = mmp_pcm_trigger, .pointer = mmp_pcm_pointer, diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 76fdce54f007..e615acaa0199 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -133,12 +133,12 @@ static void pxa_ssp_shutdown(struct snd_pcm_substream *substream, #ifdef CONFIG_PM -static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai) +static int pxa_ssp_suspend(struct snd_soc_component *component) { - struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + struct ssp_priv *priv = snd_soc_component_get_drvdata(component); struct ssp_device *ssp = priv->ssp; - if (!cpu_dai->active) + if (!component->active) clk_prepare_enable(ssp->clk); priv->cr0 = __raw_readl(ssp->mmio_base + SSCR0); @@ -151,9 +151,9 @@ static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai) return 0; } -static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai) +static int pxa_ssp_resume(struct snd_soc_component *component) { - struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + struct ssp_priv *priv = snd_soc_component_get_drvdata(component); struct ssp_device *ssp = priv->ssp; uint32_t sssr = SSSR_ROR | SSSR_TUR | SSSR_BCE; @@ -165,7 +165,7 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai) __raw_writel(priv->to, ssp->mmio_base + SSTO); __raw_writel(priv->psp, ssp->mmio_base + SSPSP); - if (cpu_dai->active) + if (component->active) pxa_ssp_enable(ssp); else clk_disable_unprepare(ssp->clk); @@ -850,8 +850,6 @@ static const struct snd_soc_dai_ops pxa_ssp_dai_ops = { static struct snd_soc_dai_driver pxa_ssp_dai = { .probe = pxa_ssp_probe, .remove = pxa_ssp_remove, - .suspend = pxa_ssp_suspend, - .resume = pxa_ssp_resume, .playback = { .channels_min = 1, .channels_max = 8, @@ -873,13 +871,14 @@ static const struct snd_soc_component_driver pxa_ssp_component = { .pcm_destruct = pxa2xx_soc_pcm_free, .open = pxa2xx_soc_pcm_open, .close = pxa2xx_soc_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = pxa2xx_soc_pcm_hw_params, .hw_free = pxa2xx_soc_pcm_hw_free, .prepare = pxa2xx_soc_pcm_prepare, .trigger = pxa2xx_soc_pcm_trigger, .pointer = pxa2xx_soc_pcm_pointer, .mmap = pxa2xx_soc_pcm_mmap, + .suspend = pxa_ssp_suspend, + .resume = pxa_ssp_resume, }; #ifdef CONFIG_OF diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 31e81a6f616f..4240fde6aae8 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -157,7 +157,6 @@ static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { { .name = "pxa2xx-ac97", - .bus_control = true, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, @@ -174,7 +173,6 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { }, { .name = "pxa2xx-ac97-aux", - .bus_control = true, .playback = { .stream_name = "AC97 Aux Playback", .channels_min = 1, @@ -191,7 +189,6 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { }, { .name = "pxa2xx-ac97-mic", - .bus_control = true, .capture = { .stream_name = "AC97 Mic Capture", .channels_min = 1, @@ -208,7 +205,6 @@ static const struct snd_soc_component_driver pxa_ac97_component = { .pcm_destruct = pxa2xx_soc_pcm_free, .open = pxa2xx_soc_pcm_open, .close = pxa2xx_soc_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = pxa2xx_soc_pcm_hw_params, .hw_free = pxa2xx_soc_pcm_hw_free, .prepare = pxa2xx_soc_pcm_prepare, diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index e77d707efde7..5f1c477b5833 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -261,7 +261,7 @@ static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream, } #ifdef CONFIG_PM -static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai) +static int pxa2xx_soc_pcm_suspend(struct snd_soc_component *component) { /* store registers */ pxa_i2s.sacr0 = SACR0; @@ -275,7 +275,7 @@ static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai) return 0; } -static int pxa2xx_i2s_resume(struct snd_soc_dai *dai) +static int pxa2xx_soc_pcm_resume(struct snd_soc_component *component) { pxa_i2s_wait(); @@ -290,8 +290,8 @@ static int pxa2xx_i2s_resume(struct snd_soc_dai *dai) } #else -#define pxa2xx_i2s_suspend NULL -#define pxa2xx_i2s_resume NULL +#define pxa2xx_soc_pcm_suspend NULL +#define pxa2xx_soc_pcm_resume NULL #endif static int pxa2xx_i2s_probe(struct snd_soc_dai *dai) @@ -342,8 +342,6 @@ static const struct snd_soc_dai_ops pxa_i2s_dai_ops = { static struct snd_soc_dai_driver pxa_i2s_dai = { .probe = pxa2xx_i2s_probe, .remove = pxa2xx_i2s_remove, - .suspend = pxa2xx_i2s_suspend, - .resume = pxa2xx_i2s_resume, .playback = { .channels_min = 2, .channels_max = 2, @@ -364,13 +362,14 @@ static const struct snd_soc_component_driver pxa_i2s_component = { .pcm_destruct = pxa2xx_soc_pcm_free, .open = pxa2xx_soc_pcm_open, .close = pxa2xx_soc_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = pxa2xx_soc_pcm_hw_params, .hw_free = pxa2xx_soc_pcm_hw_free, .prepare = pxa2xx_soc_pcm_prepare, .trigger = pxa2xx_soc_pcm_trigger, .pointer = pxa2xx_soc_pcm_pointer, .mmap = pxa2xx_soc_pcm_mmap, + .suspend = pxa2xx_soc_pcm_suspend, + .resume = pxa2xx_soc_pcm_resume, }; 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 07b3455a6f23..2b7839715dd5 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -22,7 +22,6 @@ static const struct snd_soc_component_driver pxa2xx_soc_platform = { .pcm_destruct = pxa2xx_soc_pcm_free, .open = pxa2xx_soc_pcm_open, .close = pxa2xx_soc_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = pxa2xx_soc_pcm_hw_params, .hw_free = pxa2xx_soc_pcm_hw_free, .prepare = pxa2xx_soc_pcm_prepare, diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 2e8892316423..b05091c283b7 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -543,7 +543,6 @@ static const struct snd_soc_component_driver lpass_component_driver = { .name = DRV_NAME, .open = lpass_platform_pcmops_open, .close = lpass_platform_pcmops_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = lpass_platform_pcmops_hw_params, .hw_free = lpass_platform_pcmops_hw_free, .prepare = lpass_platform_pcmops_prepare, diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 8150c10f081e..c0d422d0ab94 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -72,7 +72,7 @@ struct q6asm_dai_data { long long int sid; }; -static struct snd_pcm_hardware q6asm_dai_hardware_capture = { +static const struct snd_pcm_hardware q6asm_dai_hardware_capture = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | @@ -880,7 +880,6 @@ static const struct snd_soc_component_driver q6asm_fe_dai_component = { .open = q6asm_dai_open, .hw_params = q6asm_dai_hw_params, .close = q6asm_dai_close, - .ioctl = snd_soc_pcm_lib_ioctl, .prepare = q6asm_dai_prepare, .trigger = q6asm_dai_trigger, .pointer = q6asm_dai_pointer, diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 28f3cef696e6..3b5547a27aad 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -24,6 +24,9 @@ #define RIGHT_SPK_TDM_TX_MASK 0xC0 #define SPK_TDM_RX_MASK 0x03 #define NUM_TDM_SLOTS 8 +#define SLIM_MAX_TX_PORTS 16 +#define SLIM_MAX_RX_PORTS 16 +#define WCD934X_DEFAULT_MCLK_RATE 9600000 struct sdm845_snd_data { struct snd_soc_jack jack; @@ -36,6 +39,39 @@ struct sdm845_snd_data { static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; +static int sdm845_slim_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_link *dai_link = rtd->dai_link; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret = 0, i; + + for (i = 0 ; i < dai_link->num_codecs; i++) { + ret = snd_soc_dai_get_channel_map(rtd->codec_dais[i], + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + + if (ret != 0 && ret != -ENOTSUPP) { + pr_err("failed to get codec chan map, err:%d\n", ret); + return ret; + } else if (ret == -ENOTSUPP) { + /* Ignore unsupported */ + continue; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + rx_ch_cnt, rx_ch); + else + ret = snd_soc_dai_set_channel_map(cpu_dai, tx_ch_cnt, + tx_ch, 0, NULL); + } + + return 0; +} + static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -151,6 +187,11 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream, case QUATERNARY_TDM_TX_0: ret = sdm845_tdm_snd_hw_params(substream, params); break; + case SLIMBUS_0_RX...SLIMBUS_6_TX: + ret = sdm845_slim_snd_hw_params(substream, params); + break; + case QUATERNARY_MI2S_RX: + break; default: pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); break; @@ -173,7 +214,20 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card); struct snd_jack *jack; - int rval; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + /* + * Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[SLIM_MAX_RX_PORTS] = {144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156}; + unsigned int tx_ch[SLIM_MAX_TX_PORTS] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + int rval, i; + if (!pdata->jack_setup) { rval = snd_soc_card_jack_new(card, "Headset Jack", @@ -211,6 +265,21 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) return rval; } break; + case SLIMBUS_0_RX...SLIMBUS_6_TX: + for (i = 0 ; i < dai_link->num_codecs; i++) { + rval = snd_soc_dai_set_channel_map(rtd->codec_dais[i], + ARRAY_SIZE(tx_ch), + tx_ch, + ARRAY_SIZE(rx_ch), + rx_ch); + if (rval != 0 && rval != -ENOTSUPP) + return rval; + + snd_soc_dai_set_sysclk(rtd->codec_dais[i], 0, + WCD934X_DEFAULT_MCLK_RATE, + SNDRV_PCM_STREAM_PLAYBACK); + } + break; default: break; } @@ -257,6 +326,14 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) snd_soc_dai_set_fmt(cpu_dai, fmt); snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); break; + case QUATERNARY_MI2S_RX: + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, + MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + + + break; case QUATERNARY_TDM_RX_0: case QUATERNARY_TDM_TX_0: @@ -294,6 +371,8 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) } } break; + case SLIMBUS_0_RX...SLIMBUS_6_TX: + break; default: pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); @@ -338,6 +417,9 @@ static void sdm845_snd_shutdown(struct snd_pcm_substream *substream) 0, SNDRV_PCM_STREAM_PLAYBACK); } break; + case SLIMBUS_0_RX...SLIMBUS_6_TX: + case QUATERNARY_MI2S_RX: + break; default: pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); @@ -451,6 +533,8 @@ static int sdm845_snd_platform_remove(struct platform_device *pdev) static const struct of_device_id sdm845_snd_device_id[] = { { .compatible = "qcom,sdm845-sndcard" }, + { .compatible = "qcom,db845c-sndcard" }, + { .compatible = "lenovo,yoga-c630-sndcard" }, {}, }; MODULE_DEVICE_TABLE(of, sdm845_snd_device_id); diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c index b60b2268b608..5de633497f83 100644 --- a/sound/soc/samsung/bells.c +++ b/sound/soc/samsung/bells.c @@ -59,7 +59,7 @@ static int bells_set_bias_level(struct snd_soc_card *card, struct bells_drvdata *bells = card->drvdata; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]); codec_dai = rtd->codec_dai; component = codec_dai->component; @@ -105,7 +105,7 @@ static int bells_set_bias_level_post(struct snd_soc_card *card, struct bells_drvdata *bells = card->drvdata; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]); codec_dai = rtd->codec_dai; component = codec_dai->component; @@ -151,10 +151,10 @@ static int bells_late_probe(struct snd_soc_card *card) struct snd_soc_dai *wm9081_dai; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_AP_DSP].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_AP_DSP]); wm0010 = rtd->codec_dai->component; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]); component = rtd->codec_dai->component; aif1_dai = rtd->codec_dai; @@ -194,7 +194,7 @@ static int bells_late_probe(struct snd_soc_card *card) return ret; } - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_CP].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_CP]); aif2_dai = rtd->cpu_dai; ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0); @@ -206,7 +206,7 @@ static int bells_late_probe(struct snd_soc_card *card) if (card->num_rtd == DAI_CODEC_SUB) return 0; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_SUB].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_SUB]); aif3_dai = rtd->cpu_dai; wm9081_dai = rtd->codec_dai; @@ -381,7 +381,7 @@ static struct snd_soc_dai_link bells_dai_wm5110[] = { static struct snd_soc_codec_conf bells_codec_conf[] = { { - .dev_name = "wm9081.1-006c", + .dlc = COMP_CODEC_CONF("wm9081.1-006c"), .name_prefix = "Sub", }, }; diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 9722940da6a4..a57bb989a0ef 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1024,14 +1024,14 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) } #ifdef CONFIG_PM -static int i2s_suspend(struct snd_soc_dai *dai) +static int i2s_suspend(struct snd_soc_component *component) { - return pm_runtime_force_suspend(dai->dev); + return pm_runtime_force_suspend(component->dev); } -static int i2s_resume(struct snd_soc_dai *dai) +static int i2s_resume(struct snd_soc_component *component) { - return pm_runtime_force_resume(dai->dev); + return pm_runtime_force_resume(component->dev); } #else #define i2s_suspend NULL @@ -1140,6 +1140,9 @@ static const struct snd_soc_component_driver samsung_i2s_component = { .dapm_routes = samsung_i2s_dapm_routes, .num_dapm_routes = ARRAY_SIZE(samsung_i2s_dapm_routes), + + .suspend = i2s_suspend, + .resume = i2s_resume, }; #define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ @@ -1171,8 +1174,6 @@ static int i2s_alloc_dais(struct samsung_i2s_priv *priv, dai_drv->probe = samsung_i2s_dai_probe; dai_drv->remove = samsung_i2s_dai_remove; - dai_drv->suspend = i2s_suspend; - dai_drv->resume = i2s_resume; dai_drv->symmetric_rates = 1; dai_drv->ops = &samsung_i2s_dai_ops; diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c index 294dce111b05..66bcc2f97544 100644 --- a/sound/soc/samsung/idma.c +++ b/sound/soc/samsung/idma.c @@ -394,7 +394,6 @@ EXPORT_SYMBOL_GPL(idma_reg_addr_init); static const struct snd_soc_component_driver asoc_idma_platform = { .open = idma_open, .close = idma_close, - .ioctl = snd_soc_pcm_lib_ioctl, .trigger = idma_trigger, .pointer = idma_pointer, .mmap = idma_mmap, diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index 6132cee8550b..59904f44118b 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -22,7 +22,7 @@ static int littlemill_set_bias_level(struct snd_soc_card *card, struct snd_soc_dai *aif1_dai; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); aif1_dai = rtd->codec_dai; if (dapm->dev != aif1_dai->dev) @@ -69,7 +69,7 @@ static int littlemill_set_bias_level_post(struct snd_soc_card *card, struct snd_soc_dai *aif1_dai; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); aif1_dai = rtd->codec_dai; if (dapm->dev != aif1_dai->dev) @@ -180,7 +180,7 @@ static int bbclk_ev(struct snd_soc_dapm_widget *w, struct snd_soc_dai *aif2_dai; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]); aif2_dai = rtd->cpu_dai; switch (event) { @@ -263,11 +263,11 @@ static int littlemill_late_probe(struct snd_soc_card *card) struct snd_soc_dai *aif2_dai; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); component = rtd->codec_dai->component; aif1_dai = rtd->codec_dai; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]); aif2_dai = rtd->cpu_dai; ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c index 973f22bcc747..098eefc764db 100644 --- a/sound/soc/samsung/lowland.c +++ b/sound/soc/samsung/lowland.c @@ -126,7 +126,7 @@ static struct snd_soc_dai_link lowland_dai[] = { static struct snd_soc_codec_conf lowland_codec_conf[] = { { - .dev_name = "wm9081.1-006c", + .dlc = COMP_CODEC_CONF("wm9081.1-006c"), .name_prefix = "Sub", }, }; diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c index 38f536bafa09..1339e41e9860 100644 --- a/sound/soc/samsung/neo1973_wm8753.c +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -303,7 +303,7 @@ static struct snd_soc_aux_dev neo1973_aux_devs[] = { static struct snd_soc_codec_conf neo1973_codec_conf[] = { { - .dev_name = "lm4857.0-007c", + .dlc = COMP_CODEC_CONF("lm4857.0-007c"), .name_prefix = "Amp", }, }; diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index 92bdaf0878f8..60bfaed5f7a6 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -361,7 +361,7 @@ static int s3c24xx_i2s_probe(struct snd_soc_dai *dai) } #ifdef CONFIG_PM -static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai) +static int s3c24xx_i2s_suspend(struct snd_soc_component *component) { s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); @@ -373,7 +373,7 @@ static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai) return 0; } -static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai) +static int s3c24xx_i2s_resume(struct snd_soc_component *component) { int ret; @@ -408,8 +408,6 @@ static const struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = { static struct snd_soc_dai_driver s3c24xx_i2s_dai = { .probe = s3c24xx_i2s_probe, - .suspend = s3c24xx_i2s_suspend, - .resume = s3c24xx_i2s_resume, .playback = { .channels_min = 2, .channels_max = 2, @@ -425,6 +423,8 @@ static struct snd_soc_dai_driver s3c24xx_i2s_dai = { static const struct snd_soc_component_driver s3c24xx_i2s_component = { .name = "s3c24xx-i2s", + .suspend = s3c24xx_i2s_suspend, + .resume = s3c24xx_i2s_resume, }; static int s3c24xx_iis_dev_probe(struct platform_device *pdev) diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c index 8ea7799df028..f075aae9561a 100644 --- a/sound/soc/samsung/snow.c +++ b/sound/soc/samsung/snow.c @@ -106,7 +106,7 @@ static int snow_late_probe(struct snd_soc_card *card) struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai *codec_dai; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); /* In the multi-codec case codec_dais 0 is MAX98095 and 1 is HDMI. */ if (rtd->num_codecs > 1) diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 805c57986e0b..1a9f08a50394 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -91,6 +91,12 @@ struct samsung_spdif_info { static struct snd_dmaengine_dai_dma_data spdif_stereo_out; static struct samsung_spdif_info spdif_info; +static inline struct samsung_spdif_info +*component_to_info(struct snd_soc_component *component) +{ + return snd_soc_component_get_drvdata(component); +} + static inline struct samsung_spdif_info *to_info(struct snd_soc_dai *cpu_dai) { return snd_soc_dai_get_drvdata(cpu_dai); @@ -290,9 +296,9 @@ static void spdif_shutdown(struct snd_pcm_substream *substream, } #ifdef CONFIG_PM -static int spdif_suspend(struct snd_soc_dai *cpu_dai) +static int spdif_suspend(struct snd_soc_component *component) { - struct samsung_spdif_info *spdif = to_info(cpu_dai); + struct samsung_spdif_info *spdif = component_to_info(component); u32 con = spdif->saved_con; dev_dbg(spdif->dev, "Entered %s\n", __func__); @@ -307,9 +313,9 @@ static int spdif_suspend(struct snd_soc_dai *cpu_dai) return 0; } -static int spdif_resume(struct snd_soc_dai *cpu_dai) +static int spdif_resume(struct snd_soc_component *component) { - struct samsung_spdif_info *spdif = to_info(cpu_dai); + struct samsung_spdif_info *spdif = component_to_info(component); dev_dbg(spdif->dev, "Entered %s\n", __func__); @@ -343,12 +349,12 @@ static struct snd_soc_dai_driver samsung_spdif_dai = { SNDRV_PCM_RATE_96000), .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &spdif_dai_ops, - .suspend = spdif_suspend, - .resume = spdif_resume, }; static const struct snd_soc_component_driver samsung_spdif_component = { .name = "samsung-spdif", + .suspend = spdif_suspend, + .resume = spdif_resume, }; static int spdif_probe(struct platform_device *pdev) diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index 9e58cbed942a..ea0d1ec67f01 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -24,7 +24,7 @@ static int speyside_set_bias_level(struct snd_soc_card *card, struct snd_soc_dai *codec_dai; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]); codec_dai = rtd->codec_dai; if (dapm->dev != codec_dai->dev) @@ -60,7 +60,7 @@ static int speyside_set_bias_level_post(struct snd_soc_card *card, struct snd_soc_dai *codec_dai; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]); codec_dai = rtd->codec_dai; if (dapm->dev != codec_dai->dev) @@ -247,7 +247,7 @@ static struct snd_soc_aux_dev speyside_aux_dev[] = { static struct snd_soc_codec_conf speyside_codec_conf[] = { { - .dev_name = "wm9081.1-006c", + .dlc = COMP_CODEC_CONF("wm9081.1-006c"), .name_prefix = "Sub", }, }; diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c index bb9910d4cbe2..10ff14b856f2 100644 --- a/sound/soc/samsung/tm2_wm5110.c +++ b/sound/soc/samsung/tm2_wm5110.c @@ -282,7 +282,7 @@ static int tm2_set_bias_level(struct snd_soc_card *card, { struct snd_soc_pcm_runtime *rtd; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); if (dapm->dev != rtd->codec_dai->dev) return 0; @@ -314,7 +314,7 @@ static int tm2_late_probe(struct snd_soc_card *card) struct snd_soc_dai *aif2_dai; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[TM2_DAI_AIF1].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[TM2_DAI_AIF1]); aif1_dai = rtd->codec_dai; priv->component = rtd->codec_dai->component; @@ -324,7 +324,7 @@ static int tm2_late_probe(struct snd_soc_card *card) return ret; } - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[TM2_DAI_AIF2].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[TM2_DAI_AIF2]); aif2_dai = rtd->codec_dai; ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0); diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c index ef51f289fbc7..fdce28cc26c4 100644 --- a/sound/soc/samsung/tobermory.c +++ b/sound/soc/samsung/tobermory.c @@ -22,7 +22,7 @@ static int tobermory_set_bias_level(struct snd_soc_card *card, struct snd_soc_dai *codec_dai; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); codec_dai = rtd->codec_dai; if (dapm->dev != codec_dai->dev) @@ -65,7 +65,7 @@ static int tobermory_set_bias_level_post(struct snd_soc_card *card, struct snd_soc_dai *codec_dai; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); codec_dai = rtd->codec_dai; if (dapm->dev != codec_dai->dev) @@ -180,7 +180,7 @@ static int tobermory_late_probe(struct snd_soc_card *card) struct snd_soc_dai *codec_dai; int ret; - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); component = rtd->codec_dai->component; codec_dai = rtd->codec_dai; diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index 2b0eca02a8b9..eee1a1e994cb 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c @@ -179,11 +179,6 @@ static int camelot_hw_params(struct snd_soc_component *component, int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; int ret; - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) - return ret; - if (recv) { cam->rx_period_size = params_period_bytes(hw_params); cam->rx_period = 0; @@ -194,12 +189,6 @@ static int camelot_hw_params(struct snd_soc_component *component, return 0; } -static int camelot_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); -} - static int camelot_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -307,7 +296,7 @@ static int camelot_pcm_new(struct snd_soc_component *component, /* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel * in MMAP mode (i.e. aplay -M) */ - snd_pcm_lib_preallocate_pages_for_all(pcm, + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, NULL, DMABRG_PREALLOC_BUFFER, DMABRG_PREALLOC_BUFFER_MAX); @@ -318,9 +307,7 @@ static int camelot_pcm_new(struct snd_soc_component *component, static const struct snd_soc_component_driver sh7760_soc_component = { .open = camelot_pcm_open, .close = camelot_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = camelot_hw_params, - .hw_free = camelot_hw_free, .prepare = camelot_prepare, .trigger = camelot_trigger, .pointer = camelot_pos, diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index e384fdc8d60e..89119acfa911 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1732,20 +1732,6 @@ static int fsi_pcm_open(struct snd_soc_component *component, return ret; } -static int fsi_hw_params(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - return snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); -} - -static int fsi_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); -} - static snd_pcm_uframes_t fsi_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -1765,7 +1751,7 @@ static snd_pcm_uframes_t fsi_pointer(struct snd_soc_component *component, static int fsi_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { - snd_pcm_lib_preallocate_pages_for_all( + snd_pcm_set_managed_buffer_all( rtd->pcm, SNDRV_DMA_TYPE_DEV, rtd->card->snd_card->dev, @@ -1815,9 +1801,6 @@ static struct snd_soc_dai_driver fsi_soc_dai[] = { static const struct snd_soc_component_driver fsi_soc_component = { .name = "fsi", .open = fsi_pcm_open, - .ioctl = snd_soc_pcm_lib_ioctl, - .hw_params = fsi_hw_params, - .hw_free = fsi_hw_free, .pointer = fsi_pointer, .pcm_construct = fsi_pcm_new, }; diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c index 17622ceb98c0..475fc984f8c5 100644 --- a/sound/soc/sh/hac.c +++ b/sound/soc/sh/hac.c @@ -270,7 +270,6 @@ static const struct snd_soc_dai_ops hac_dai_ops = { static struct snd_soc_dai_driver sh4_hac_dai[] = { { .name = "hac-dai.0", - .bus_control = true, .playback = { .rates = AC97_RATES, .formats = AC97_FMTS, diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 399dc6e9bde5..0bfcb77e5f65 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -376,20 +376,15 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) */ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { - static const u32 dalign_values[8][2] = { - {0x76543210, 0x67452301}, - {0x00000032, 0x00000023}, - {0x00007654, 0x00006745}, - {0x00000076, 0x00000067}, - {0xfedcba98, 0xefcdab89}, - {0x000000ba, 0x000000ab}, - {0x0000fedc, 0x0000efcd}, - {0x000000fe, 0x000000ef}, + static const u32 dalign_values[8] = { + 0x76543210, 0x00000032, 0x00007654, 0x00000076, + 0xfedcba98, 0x000000ba, 0x0000fedc, 0x000000fe, }; - int id = 0, inv; + int id = 0; struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io); struct rsnd_mod *target; struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 dalign; /* * *Hardware* L/R and *Software* L/R are inverted for 16bit data. @@ -425,15 +420,15 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) if (mod == ssiu) id = rsnd_mod_id_sub(mod); - /* Non target mod or non 16bit needs normal DALIGN */ - if ((snd_pcm_format_width(runtime->format) != 16) || - (mod != target)) - inv = 0; - /* Target mod needs inverted DALIGN when 16bit */ - else - inv = 1; + dalign = dalign_values[id]; + + if (mod == target && snd_pcm_format_width(runtime->format) == 16) { + /* Target mod needs inverted DALIGN when 16bit */ + dalign = (dalign & 0xf0f0f0f0) >> 4 | + (dalign & 0x0f0f0f0f) << 4; + } - return dalign_values[id][inv]; + return dalign; } u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod) @@ -1223,10 +1218,10 @@ static int rsnd_preallocate_pages(struct snd_soc_pcm_runtime *rtd, for (substream = rtd->pcm->streams[stream].substream; substream; substream = substream->next) { - snd_pcm_lib_preallocate_pages(substream, - SNDRV_DMA_TYPE_DEV, - dev, - PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); + snd_pcm_set_managed_buffer(substream, + SNDRV_DMA_TYPE_DEV, + dev, + PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); } return 0; @@ -1405,7 +1400,6 @@ static int rsnd_hw_params(struct snd_soc_component *component, struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); struct snd_soc_pcm_runtime *fe = substream->private_data; - int ret; /* * rsnd assumes that it might be used under DPCM if user want to use @@ -1438,12 +1432,7 @@ static int rsnd_hw_params(struct snd_soc_component *component, dev_dbg(dev, "convert rate = %d\n", io->converted_rate); } - ret = rsnd_dai_call(hw_params, io, substream, hw_params); - if (ret) - return ret; - - return snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); + return rsnd_dai_call(hw_params, io, substream, hw_params); } static int rsnd_hw_free(struct snd_soc_component *component, @@ -1452,13 +1441,8 @@ static int rsnd_hw_free(struct snd_soc_component *component, struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - int ret; - - ret = rsnd_dai_call(hw_free, io, substream); - if (ret) - return ret; - return snd_pcm_lib_free_pages(substream); + return rsnd_dai_call(hw_free, io, substream); } static snd_pcm_uframes_t rsnd_pointer(struct snd_soc_component *component, @@ -1668,7 +1652,6 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, */ static const struct snd_soc_component_driver rsnd_soc_component = { .name = "rsnd", - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = rsnd_hw_params, .hw_free = rsnd_hw_free, .pointer = rsnd_pointer, @@ -1824,8 +1807,6 @@ static int rsnd_remove(struct platform_device *pdev) }; int ret = 0, i; - snd_soc_disconnect_sync(&pdev->dev); - pm_runtime_disable(&pdev->dev); for_each_rsnd_dai(rdai, priv, i) { diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c index a5e21e554da2..6a6ffd6d3192 100644 --- a/sound/soc/sh/siu_pcm.c +++ b/sound/soc/sh/siu_pcm.c @@ -281,41 +281,6 @@ static int siu_pcm_stmread_stop(struct siu_port *port_info) return 0; } -static int siu_pcm_hw_params(struct snd_soc_component *component, - struct snd_pcm_substream *ss, - struct snd_pcm_hw_params *hw_params) -{ - struct siu_info *info = siu_i2s_data; - struct device *dev = ss->pcm->card->dev; - int ret; - - dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id); - - ret = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params)); - if (ret < 0) - dev_err(dev, "snd_pcm_lib_malloc_pages() failed\n"); - - return ret; -} - -static int siu_pcm_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *ss) -{ - struct siu_info *info = siu_i2s_data; - struct siu_port *port_info = siu_port_info(ss); - struct device *dev = ss->pcm->card->dev; - struct siu_stream *siu_stream; - - if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) - siu_stream = &port_info->playback; - else - siu_stream = &port_info->capture; - - dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id); - - return snd_pcm_lib_free_pages(ss); -} - static bool filter(struct dma_chan *chan, void *slave) { struct sh_dmae_slave *param = slave; @@ -548,7 +513,7 @@ static int siu_pcm_new(struct snd_soc_component *component, if (ret < 0) return ret; - snd_pcm_lib_preallocate_pages_for_all(pcm, + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev, SIU_BUFFER_BYTES_MAX, SIU_BUFFER_BYTES_MAX); @@ -583,9 +548,6 @@ struct const snd_soc_component_driver siu_component = { .name = DRV_NAME, .open = siu_pcm_open, .close = siu_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, - .hw_params = siu_pcm_hw_params, - .hw_free = siu_pcm_hw_free, .prepare = siu_pcm_prepare, .trigger = siu_pcm_trigger, .pointer = siu_pcm_pointer_dma, diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index b94680fb26fa..14e175cdeeb8 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -418,10 +418,10 @@ int snd_soc_pcm_component_pointer(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; + int i; /* FIXME: use 1st pointer */ - for_each_rtd_components(rtd, rtdcom, component) + for_each_rtd_components(rtd, i, component) if (component->driver->pointer) return component->driver->pointer(component, substream); @@ -433,10 +433,10 @@ int snd_soc_pcm_component_ioctl(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; + int i; /* FIXME: use 1st ioctl */ - for_each_rtd_components(rtd, rtdcom, component) + for_each_rtd_components(rtd, i, component) if (component->driver->ioctl) return component->driver->ioctl(component, substream, cmd, arg); @@ -448,10 +448,9 @@ int snd_soc_pcm_component_sync_stop(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; - int ret; + int i, ret; - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (component->driver->ioctl) { ret = component->driver->sync_stop(component, substream); @@ -468,11 +467,11 @@ int snd_soc_pcm_component_copy_user(struct snd_pcm_substream *substream, void __user *buf, unsigned long bytes) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; + int i; /* FIXME. it returns 1st copy now */ - for_each_rtd_components(rtd, rtdcom, component) + for_each_rtd_components(rtd, i, component) if (component->driver->copy_user) return component->driver->copy_user( component, substream, channel, pos, buf, bytes); @@ -484,12 +483,12 @@ struct page *snd_soc_pcm_component_page(struct snd_pcm_substream *substream, unsigned long offset) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; struct page *page; + int i; /* FIXME. it returns 1st page now */ - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (component->driver->page) { page = component->driver->page(component, substream, offset); @@ -505,11 +504,11 @@ int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; + int i; /* FIXME. it returns 1st mmap now */ - for_each_rtd_components(rtd, rtdcom, component) + for_each_rtd_components(rtd, i, component) if (component->driver->mmap) return component->driver->mmap(component, substream, vma); @@ -519,11 +518,11 @@ int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream, int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; int ret; + int i; - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (component->driver->pcm_construct) { ret = component->driver->pcm_construct(component, rtd); if (ret < 0) @@ -536,13 +535,13 @@ int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd) void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; + int i; if (!rtd->pcm) return; - for_each_rtd_components(rtd, rtdcom, component) + for_each_rtd_components(rtd, i, component) if (component->driver->pcm_destruct) component->driver->pcm_destruct(component, rtd->pcm); } diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 6615ef64c7f5..223cd045719e 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -19,16 +19,16 @@ #include <sound/soc.h> #include <sound/initval.h> #include <sound/soc-dpcm.h> +#include <linux/pm_runtime.h> static int soc_compr_components_open(struct snd_compr_stream *cstream, struct snd_soc_component **last) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; - int ret; + int i, ret; - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->open) continue; @@ -53,9 +53,9 @@ static int soc_compr_components_free(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; + int i; - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (component == last) break; @@ -72,9 +72,18 @@ static int soc_compr_components_free(struct snd_compr_stream *cstream, static int soc_compr_open(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_component *component; + struct snd_soc_component *component, *save = NULL; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret; + int ret, i; + + for_each_rtd_components(rtd, i, component) { + ret = pm_runtime_get_sync(component->dev); + if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(component->dev); + save = component; + goto pm_err; + } + } mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); @@ -115,6 +124,14 @@ machine_err: cpu_dai->driver->cops->shutdown(cstream, cpu_dai); out: mutex_unlock(&rtd->card->pcm_mutex); +pm_err: + for_each_rtd_components(rtd, i, component) { + if (component == save) + break; + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + } + return ret; } @@ -209,39 +226,13 @@ be_err: return ret; } -/* - * Power down the audio subsystem pmdown_time msecs after close is called. - * This is to ensure there are no pops or clicks in between any music tracks - * due to DAPM power cycling. - */ -static void close_delayed_work(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); - - dev_dbg(rtd->dev, - "Compress ASoC: pop wq checking: %s status: %s waiting: %s\n", - codec_dai->driver->playback.stream_name, - codec_dai->playback_active ? "active" : "inactive", - rtd->pop_wait ? "yes" : "no"); - - /* are we waiting on this codec DAI stream */ - if (rtd->pop_wait == 1) { - rtd->pop_wait = 0; - snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, - SND_SOC_DAPM_STREAM_STOP); - } - - mutex_unlock(&rtd->card->pcm_mutex); -} - static int soc_compr_free(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; - int stream; + int stream, i; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); @@ -268,25 +259,15 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) cpu_dai->driver->cops->shutdown(cstream, cpu_dai); - if (cstream->direction == SND_COMPRESS_PLAYBACK) { - if (snd_soc_runtime_ignore_pmdown_time(rtd)) { - snd_soc_dapm_stream_event(rtd, - SNDRV_PCM_STREAM_PLAYBACK, - SND_SOC_DAPM_STREAM_STOP); - } else { - rtd->pop_wait = 1; - queue_delayed_work(system_power_efficient_wq, - &rtd->delayed_work, - msecs_to_jiffies(rtd->pmdown_time)); - } - } else { - /* capture streams can be powered down now */ - snd_soc_dapm_stream_event(rtd, - SNDRV_PCM_STREAM_CAPTURE, - SND_SOC_DAPM_STREAM_STOP); - } + snd_soc_dapm_stream_stop(rtd, stream); mutex_unlock(&rtd->card->pcm_mutex); + + for_each_rtd_components(rtd, i, component) { + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + } + return 0; } @@ -318,7 +299,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) for_each_dpcm_be(fe, stream, dpcm) dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; - dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); + snd_soc_dapm_stream_stop(fe, stream); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; @@ -344,10 +325,9 @@ static int soc_compr_components_trigger(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; - int ret; + int i, ret; - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->trigger) continue; @@ -447,10 +427,9 @@ static int soc_compr_components_set_params(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; - int ret; + int i, ret; - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->set_params) continue; @@ -579,9 +558,8 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int i, ret = 0; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); @@ -591,7 +569,7 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, goto err; } - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->get_params) continue; @@ -610,12 +588,11 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; - int ret = 0; + int i, ret = 0; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->get_caps) continue; @@ -633,12 +610,11 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; - int ret = 0; + int i, ret = 0; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->get_codec_caps) continue; @@ -656,9 +632,8 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int i, ret = 0; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); @@ -668,7 +643,7 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) goto err; } - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->ack) continue; @@ -688,8 +663,7 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; - int ret = 0; + int i, ret = 0; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); @@ -697,7 +671,7 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, if (cpu_dai->driver->cops && cpu_dai->driver->cops->pointer) cpu_dai->driver->cops->pointer(cstream, tstamp, cpu_dai); - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->pointer) continue; @@ -715,12 +689,11 @@ static int soc_compr_copy(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; - int ret = 0; + int i, ret = 0; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->copy) continue; @@ -738,9 +711,8 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret; + int i, ret; if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_metadata) { ret = cpu_dai->driver->cops->set_metadata(cstream, metadata, cpu_dai); @@ -748,7 +720,7 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, return ret; } - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->set_metadata) continue; @@ -767,9 +739,8 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret; + int i, ret; if (cpu_dai->driver->cops && cpu_dai->driver->cops->get_metadata) { ret = cpu_dai->driver->cops->get_metadata(cstream, metadata, cpu_dai); @@ -777,7 +748,7 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, return ret; } - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->get_metadata) continue; @@ -830,7 +801,6 @@ static struct snd_compr_ops soc_compr_dyn_ops = { int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_compr *compr; @@ -838,6 +808,7 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) char new_name[64]; int ret = 0, direction = 0; int playback = 0, capture = 0; + int i; if (rtd->num_codecs > 1) { dev_err(rtd->card->dev, @@ -906,7 +877,7 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); } - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->copy) continue; @@ -927,7 +898,7 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) } /* DAPM dai link stream work */ - rtd->close_delayed_work_func = close_delayed_work; + rtd->close_delayed_work_func = snd_soc_close_delayed_work; rtd->compr = compr; compr->private_data = rtd; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 8ef0efeed0a7..068d809c349a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -45,11 +45,6 @@ #define NAME_SIZE 32 -#ifdef CONFIG_DEBUG_FS -struct dentry *snd_soc_debugfs_root; -EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); -#endif - static DEFINE_MUTEX(client_mutex); static LIST_HEAD(component_list); static LIST_HEAD(unbind_card_list); @@ -73,23 +68,6 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); -#ifdef CONFIG_DMI -/* - * If a DMI filed contain strings in this blacklist (e.g. - * "Type2 - Board Manufacturer" or "Type1 - TBD by OEM"), it will be taken - * as invalid and dropped when setting the card long name from DMI info. - */ -static const char * const dmi_blacklist[] = { - "To be filled by OEM", - "TBD by OEM", - "Default String", - "Board Manufacturer", - "Board Vendor Name", - "Board Product Name", - NULL, /* terminator */ -}; -#endif - static ssize_t pmdown_time_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -150,6 +128,9 @@ static const struct attribute_group *soc_dev_attr_groups[] = { }; #ifdef CONFIG_DEBUG_FS +struct dentry *snd_soc_debugfs_root; +EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); + static void soc_init_component_debugfs(struct snd_soc_component *component) { if (!component->card->debugfs_card_root) @@ -277,49 +258,21 @@ static inline void snd_soc_debugfs_exit(void) #endif -/* - * This is glue code between snd_pcm_lib_ioctl() and - * snd_soc_component_driver :: ioctl - */ -int snd_soc_pcm_lib_ioctl(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - unsigned int cmd, void *arg) -{ - return snd_pcm_lib_ioctl(substream, cmd, arg); -} -EXPORT_SYMBOL_GPL(snd_soc_pcm_lib_ioctl); - -static int snd_soc_rtdcom_add(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_component *component) +static int snd_soc_rtd_add_component(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_component *component) { - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *comp; + int i; - for_each_rtd_components(rtd, rtdcom, comp) { + for_each_rtd_components(rtd, i, comp) { /* already connected */ if (comp == component) return 0; } - /* - * created rtdcom here will be freed when rtd->dev was freed. - * see - * soc_free_pcm_runtime() :: device_unregister(rtd->dev) - */ - rtdcom = devm_kzalloc(rtd->dev, sizeof(*rtdcom), GFP_KERNEL); - if (!rtdcom) - return -ENOMEM; - - rtdcom->component = component; - INIT_LIST_HEAD(&rtdcom->list); - - /* - * When rtd was freed, created rtdcom here will be - * also freed. - * And we don't need to call list_del(&rtdcom->list) - * when freed, because rtd is also freed. - */ - list_add_tail(&rtdcom->list, &rtd->component_list); + /* see for_each_rtd_components */ + rtd->components[rtd->num_components] = component; + rtd->num_components++; return 0; } @@ -327,8 +280,8 @@ static int snd_soc_rtdcom_add(struct snd_soc_pcm_runtime *rtd, struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd, const char *driver_name) { - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; + int i; if (!driver_name) return NULL; @@ -341,7 +294,7 @@ struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd, * But, if many components which have same driver name are connected * to 1 rtd, this function will return 1st found component. */ - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { const char *component_name = component->driver->name; if (!component_name) @@ -349,7 +302,7 @@ struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd, if ((component_name == driver_name) || strcmp(component_name, driver_name) == 0) - return rtdcom->component; + return component; } return NULL; @@ -389,22 +342,48 @@ struct snd_soc_component *snd_soc_lookup_component(struct device *dev, } EXPORT_SYMBOL_GPL(snd_soc_lookup_component); -struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, - const char *dai_link, int stream) +struct snd_soc_pcm_runtime +*snd_soc_get_pcm_runtime(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) { struct snd_soc_pcm_runtime *rtd; for_each_card_rtds(card, rtd) { - if (rtd->dai_link->no_pcm && - !strcmp(rtd->dai_link->name, dai_link)) - return rtd->pcm->streams[stream].substream; + if (rtd->dai_link == dai_link) + return rtd; } - dev_dbg(card->dev, "ASoC: failed to find dai link %s\n", dai_link); + dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link->name); return NULL; } -EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream); +EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); -static const struct snd_soc_ops null_snd_soc_ops; +/* + * Power down the audio subsystem pmdown_time msecs after close is called. + * This is to ensure there are no pops or clicks in between any music tracks + * due to DAPM power cycling. + */ +void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + + dev_dbg(rtd->dev, + "ASoC: pop wq checking: %s status: %s waiting: %s\n", + codec_dai->driver->playback.stream_name, + codec_dai->playback_active ? "active" : "inactive", + rtd->pop_wait ? "yes" : "no"); + + /* are we waiting on this codec DAI stream */ + if (rtd->pop_wait == 1) { + rtd->pop_wait = 0; + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, + SND_SOC_DAPM_STREAM_STOP); + } + + mutex_unlock(&rtd->card->pcm_mutex); +} +EXPORT_SYMBOL_GPL(snd_soc_close_delayed_work); static void soc_release_rtd_dev(struct device *dev) { @@ -449,6 +428,7 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { struct snd_soc_pcm_runtime *rtd; + struct snd_soc_component *component; struct device *dev; int ret; @@ -474,13 +454,17 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( /* * for rtd */ - rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL); + rtd = devm_kzalloc(dev, + sizeof(*rtd) + + sizeof(*component) * (dai_link->num_cpus + + dai_link->num_codecs + + dai_link->num_platforms), + GFP_KERNEL); if (!rtd) goto free_rtd; rtd->dev = dev; INIT_LIST_HEAD(&rtd->list); - INIT_LIST_HEAD(&rtd->component_list); INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients); INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients); INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients); @@ -502,8 +486,6 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( */ rtd->card = card; rtd->dai_link = dai_link; - if (!rtd->dai_link->ops) - rtd->dai_link->ops = &null_snd_soc_ops; /* see for_each_card_rtds */ list_add_tail(&rtd->list, &card->rtd_list); @@ -517,20 +499,6 @@ free_rtd: return NULL; } -struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, - const char *dai_link) -{ - struct snd_soc_pcm_runtime *rtd; - - for_each_card_rtds(card, rtd) { - if (!strcmp(rtd->dai_link->name, dai_link)) - return rtd; - } - dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link); - return NULL; -} -EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); - static void snd_soc_flush_all_delayed_work(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; @@ -586,16 +554,6 @@ int snd_soc_suspend(struct device *dev) if (card->suspend_pre) card->suspend_pre(card); - for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - if (rtd->dai_link->ignore_suspend) - continue; - - if (!cpu_dai->driver->bus_control) - snd_soc_dai_suspend(cpu_dai); - } - /* close any waiting streams */ snd_soc_flush_all_delayed_work(card); @@ -618,15 +576,25 @@ int snd_soc_suspend(struct device *dev) snd_soc_dapm_sync(&card->dapm); /* suspend all COMPONENTs */ - for_each_card_components(card, component) { - struct snd_soc_dapm_context *dapm = + for_each_card_rtds(card, rtd) { + + if (rtd->dai_link->ignore_suspend) + continue; + + for_each_rtd_components(rtd, i, component) { + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); - /* - * If there are paths active then the COMPONENT will be held - * with bias _ON and should not be suspended. - */ - if (!snd_soc_component_is_suspended(component)) { + /* + * ignore if component was already suspended + */ + if (snd_soc_component_is_suspended(component)) + continue; + + /* + * If there are paths active then the COMPONENT will be + * held with bias _ON and should not be suspended. + */ switch (snd_soc_dapm_get_bias_level(dapm)) { case SND_SOC_BIAS_STANDBY: /* @@ -657,19 +625,6 @@ int snd_soc_suspend(struct device *dev) } } - for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - if (rtd->dai_link->ignore_suspend) - continue; - - if (cpu_dai->driver->bus_control) - snd_soc_dai_suspend(cpu_dai); - - /* deactivate pins to sleep state */ - pinctrl_pm_select_sleep_state(cpu_dai->dev); - } - if (card->suspend_post) card->suspend_post(card); @@ -703,17 +658,6 @@ static void soc_resume_deferred(struct work_struct *work) if (card->resume_pre) card->resume_pre(card); - /* resume control bus DAIs */ - for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - if (rtd->dai_link->ignore_suspend) - continue; - - if (cpu_dai->driver->bus_control) - snd_soc_dai_resume(cpu_dai); - } - for_each_card_components(card, component) { if (snd_soc_component_is_suspended(component)) snd_soc_component_resume(component); @@ -747,16 +691,6 @@ static void soc_resume_deferred(struct work_struct *work) } } - for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - if (rtd->dai_link->ignore_suspend) - continue; - - if (!cpu_dai->driver->bus_control) - snd_soc_dai_resume(cpu_dai); - } - if (card->resume_post) card->resume_post(card); @@ -774,47 +708,20 @@ static void soc_resume_deferred(struct work_struct *work) int snd_soc_resume(struct device *dev) { struct snd_soc_card *card = dev_get_drvdata(dev); - bool bus_control = false; - struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dai *codec_dai; - int i; + struct snd_soc_component *component; /* If the card is not initialized yet there is nothing to do */ if (!card->instantiated) return 0; /* activate pins from sleep state */ - for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - if (cpu_dai->active) - pinctrl_pm_select_default_state(cpu_dai->dev); + for_each_card_components(card, component) + if (component->active) + pinctrl_pm_select_default_state(component->dev); - for_each_rtd_codec_dai(rtd, i, codec_dai) { - if (codec_dai->active) - pinctrl_pm_select_default_state(codec_dai->dev); - } - } - - /* - * DAIs that also act as the control bus master might have other drivers - * hanging off them so need to resume immediately. Other drivers don't - * have that problem and may take a substantial amount of time to resume - * due to I/O costs and anti-pop so handle them out of line. - */ - for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - bus_control |= cpu_dai->driver->bus_control; - } - if (bus_control) { - dev_dbg(dev, "ASoC: Resuming control bus master immediately\n"); - soc_resume_deferred(&card->deferred_resume_work); - } else { - dev_dbg(dev, "ASoC: Scheduling resume work\n"); - if (!schedule_work(&card->deferred_resume_work)) - dev_err(dev, "ASoC: resume work item may be lost\n"); - } + dev_dbg(dev, "ASoC: Scheduling resume work\n"); + if (!schedule_work(&card->deferred_resume_work)) + dev_err(dev, "ASoC: resume work item may be lost\n"); return 0; } @@ -926,47 +833,6 @@ struct snd_soc_dai *snd_soc_find_dai( } EXPORT_SYMBOL_GPL(snd_soc_find_dai); -/** - * snd_soc_find_dai_link - Find a DAI link - * - * @card: soc card - * @id: DAI link ID to match - * @name: DAI link name to match, optional - * @stream_name: DAI link stream name to match, optional - * - * This function will search all existing DAI links of the soc card to - * find the link of the same ID. Since DAI links may not have their - * unique ID, so name and stream name should also match if being - * specified. - * - * Return: pointer of DAI link, or NULL if not found. - */ -struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card, - int id, const char *name, - const char *stream_name) -{ - struct snd_soc_dai_link *link; - - lockdep_assert_held(&client_mutex); - - for_each_card_links(card, link) { - if (link->id != id) - continue; - - if (name && (!link->name || strcmp(name, link->name))) - continue; - - if (stream_name && (!link->stream_name - || strcmp(stream_name, link->stream_name))) - continue; - - return link; - } - - return NULL; -} -EXPORT_SYMBOL_GPL(snd_soc_find_dai_link); - static int soc_dai_link_sanity_check(struct snd_soc_card *card, struct snd_soc_dai_link *link) { @@ -1064,49 +930,40 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, } /** - * snd_soc_remove_dai_link - Remove a DAI link from the list - * @card: The ASoC card that owns the link - * @dai_link: The DAI link to remove + * snd_soc_remove_pcm_runtime - Remove a pcm_runtime from card + * @card: The ASoC card to which the pcm_runtime has + * @rtd: The pcm_runtime to remove * - * This function removes a DAI link from the ASoC card's link list. - * - * For DAI links previously added by topology, topology should - * remove them by using the dobj embedded in the link. + * This function removes a pcm_runtime from the ASoC card. */ -void snd_soc_remove_dai_link(struct snd_soc_card *card, - struct snd_soc_dai_link *dai_link) +void snd_soc_remove_pcm_runtime(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_pcm_runtime *rtd; - lockdep_assert_held(&client_mutex); /* * Notify the machine driver for extra destruction */ if (card->remove_dai_link) - card->remove_dai_link(card, dai_link); - - list_del(&dai_link->list); + card->remove_dai_link(card, rtd->dai_link); - rtd = snd_soc_get_pcm_runtime(card, dai_link->name); - if (rtd) - soc_free_pcm_runtime(rtd); + soc_free_pcm_runtime(rtd); } -EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link); +EXPORT_SYMBOL_GPL(snd_soc_remove_pcm_runtime); /** - * snd_soc_add_dai_link - Add a DAI link dynamically - * @card: The ASoC card to which the DAI link is added - * @dai_link: The new DAI link to add + * snd_soc_add_pcm_runtime - Add a pcm_runtime dynamically via dai_link + * @card: The ASoC card to which the pcm_runtime is added + * @dai_link: The DAI link to find pcm_runtime * - * This function adds a DAI link to the ASoC card's link list. + * This function adds a pcm_runtime ASoC card by using dai_link. * - * Note: Topology can use this API to add DAI links when probing the + * Note: Topology can use this API to add pcm_runtime when probing the * topology component. And machine drivers can still define static * DAI links in dai_link array. */ -int snd_soc_add_dai_link(struct snd_soc_card *card, - struct snd_soc_dai_link *dai_link) +int snd_soc_add_pcm_runtime(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) { struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai_link_component *codec, *platform; @@ -1141,7 +998,7 @@ int snd_soc_add_dai_link(struct snd_soc_card *card, dai_link->cpus->dai_name); goto _err_defer; } - snd_soc_rtdcom_add(rtd, rtd->cpu_dai->component); + snd_soc_rtd_add_component(rtd, rtd->cpu_dai->component); /* Find CODEC from registered CODECs */ rtd->num_codecs = dai_link->num_codecs; @@ -1153,7 +1010,7 @@ int snd_soc_add_dai_link(struct snd_soc_card *card, goto _err_defer; } - snd_soc_rtdcom_add(rtd, rtd->codec_dais[i]->component); + snd_soc_rtd_add_component(rtd, rtd->codec_dais[i]->component); } /* Single codec links expect codec and codec_dai in runtime data */ @@ -1165,54 +1022,135 @@ int snd_soc_add_dai_link(struct snd_soc_card *card, if (!snd_soc_is_matching_component(platform, component)) continue; - snd_soc_rtdcom_add(rtd, component); + snd_soc_rtd_add_component(rtd, component); } } - /* see for_each_card_links */ - list_add_tail(&dai_link->list, &card->dai_link_list); - return 0; _err_defer: - soc_free_pcm_runtime(rtd); + snd_soc_remove_pcm_runtime(card, rtd); return -EPROBE_DEFER; } -EXPORT_SYMBOL_GPL(snd_soc_add_dai_link); +EXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtime); -static void soc_set_of_name_prefix(struct snd_soc_component *component) +static int soc_dai_pcm_new(struct snd_soc_dai **dais, int num_dais, + struct snd_soc_pcm_runtime *rtd) { - struct device_node *of_node = soc_component_to_node(component); - const char *str; - int ret; + int i, ret = 0; - ret = of_property_read_string(of_node, "sound-name-prefix", &str); - if (!ret) - component->name_prefix = str; + for (i = 0; i < num_dais; ++i) { + struct snd_soc_dai_driver *drv = dais[i]->driver; + + if (drv->pcm_new) + ret = drv->pcm_new(rtd, dais[i]); + if (ret < 0) { + dev_err(dais[i]->dev, + "ASoC: Failed to bind %s with pcm device\n", + dais[i]->name); + return ret; + } + } + + return 0; +} + +static int soc_init_pcm_runtime(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_component *component; + int ret, num, i; + + /* set default power off timeout */ + rtd->pmdown_time = pmdown_time; + + /* do machine specific initialization */ + if (dai_link->init) { + ret = dai_link->init(rtd); + if (ret < 0) { + dev_err(card->dev, "ASoC: failed to init %s: %d\n", + dai_link->name, ret); + return ret; + } + } + + if (dai_link->dai_fmt) { + ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt); + if (ret) + return ret; + } + + /* add DPCM sysfs entries */ + soc_dpcm_debugfs_add(rtd); + + 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_rtd_components(rtd, i, 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; + } + + /* create compress_device if possible */ + ret = snd_soc_dai_compress_new(cpu_dai, rtd, num); + if (ret != -ENOTSUPP) { + if (ret < 0) + dev_err(card->dev, "ASoC: can't create compress %s\n", + dai_link->stream_name); + return ret; + } + + /* create the pcm */ + 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); + return ret; + } + ret = soc_dai_pcm_new(&cpu_dai, 1, rtd); + if (ret < 0) + return ret; + ret = soc_dai_pcm_new(rtd->codec_dais, + rtd->num_codecs, rtd); + return ret; } static void soc_set_name_prefix(struct snd_soc_card *card, struct snd_soc_component *component) { - int i; + struct device_node *of_node = soc_component_to_node(component); + const char *str; + int ret, i; - for (i = 0; i < card->num_configs && card->codec_conf; i++) { + for (i = 0; i < card->num_configs; i++) { struct snd_soc_codec_conf *map = &card->codec_conf[i]; - struct device_node *of_node = soc_component_to_node(component); - if (map->of_node && of_node != map->of_node) - continue; - if (map->dev_name && strcmp(component->name, map->dev_name)) - continue; - component->name_prefix = map->name_prefix; - return; + if (snd_soc_is_matching_component(&map->dlc, component)) { + component->name_prefix = map->name_prefix; + 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); + ret = of_property_read_string(of_node, "sound-name-prefix", &str); + if (ret < 0) + return; + + component->name_prefix = str; } static void soc_remove_component(struct snd_soc_component *component, @@ -1419,12 +1357,11 @@ static void soc_remove_link_components(struct snd_soc_card *card) { struct snd_soc_component *component; struct snd_soc_pcm_runtime *rtd; - struct snd_soc_rtdcom_list *rtdcom; - int order; + int i, order; for_each_comp_order(order) { for_each_card_rtds(card, rtd) { - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (component->driver->remove_order != order) continue; @@ -1438,12 +1375,11 @@ static int soc_probe_link_components(struct snd_soc_card *card) { struct snd_soc_component *component; struct snd_soc_pcm_runtime *rtd; - struct snd_soc_rtdcom_list *rtdcom; - int ret, order; + int i, ret, order; for_each_comp_order(order) { for_each_card_rtds(card, rtd) { - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (component->driver->probe_order != order) continue; @@ -1457,111 +1393,6 @@ static int soc_probe_link_components(struct snd_soc_card *card) return 0; } -void snd_soc_disconnect_sync(struct device *dev) -{ - struct snd_soc_component *component = - snd_soc_lookup_component(dev, NULL); - - if (!component || !component->card) - return; - - snd_card_disconnect_sync(component->card->snd_card); -} -EXPORT_SYMBOL_GPL(snd_soc_disconnect_sync); - -static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais, - struct snd_soc_pcm_runtime *rtd) -{ - int i, ret = 0; - - for (i = 0; i < num_dais; ++i) { - struct snd_soc_dai_driver *drv = dais[i]->driver; - - if (drv->pcm_new) - ret = drv->pcm_new(rtd, dais[i]); - if (ret < 0) { - dev_err(dais[i]->dev, - "ASoC: Failed to bind %s with pcm device\n", - dais[i]->name); - return ret; - } - } - - return 0; -} - -static int soc_link_init(struct snd_soc_card *card, - struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_dai_link *dai_link = rtd->dai_link; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_rtdcom_list *rtdcom; - struct snd_soc_component *component; - int ret, num; - - /* set default power off timeout */ - rtd->pmdown_time = pmdown_time; - - /* do machine specific initialization */ - if (dai_link->init) { - ret = dai_link->init(rtd); - if (ret < 0) { - dev_err(card->dev, "ASoC: failed to init %s: %d\n", - dai_link->name, ret); - return ret; - } - } - - if (dai_link->dai_fmt) { - ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt); - if (ret) - return ret; - } - - /* add DPCM sysfs entries */ - soc_dpcm_debugfs_add(rtd); - - 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_rtd_components(rtd, 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; - } - - /* create compress_device if possible */ - ret = snd_soc_dai_compress_new(cpu_dai, rtd, num); - if (ret != -ENOTSUPP) { - if (ret < 0) - dev_err(card->dev, "ASoC: can't create compress %s\n", - dai_link->stream_name); - return ret; - } - - /* create the pcm */ - 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); - return ret; - } - ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd); - if (ret < 0) - return ret; - ret = soc_link_dai_pcm_new(rtd->codec_dais, - rtd->num_codecs, rtd); - return ret; -} - static void soc_unbind_aux_dev(struct snd_soc_card *card) { struct snd_soc_component *component, *_component; @@ -1693,6 +1524,21 @@ EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt); #ifdef CONFIG_DMI /* + * If a DMI filed contain strings in this blacklist (e.g. + * "Type2 - Board Manufacturer" or "Type1 - TBD by OEM"), it will be taken + * as invalid and dropped when setting the card long name from DMI info. + */ +static const char * const dmi_blacklist[] = { + "To be filled by OEM", + "TBD by OEM", + "Default String", + "Board Manufacturer", + "Board Vendor Name", + "Board Product Name", + NULL, /* terminator */ +}; + +/* * Trim special characters, and replace '-' with '_' since '-' is used to * separate different DMI fields in the card long name. Only number and * alphabet characters and a few separator characters are kept. @@ -1944,7 +1790,7 @@ static void __soc_setup_card_name(char *name, int len, static void soc_cleanup_card_resources(struct snd_soc_card *card, int card_probed) { - struct snd_soc_dai_link *link, *_link; + struct snd_soc_pcm_runtime *rtd, *n; if (card->snd_card) snd_card_disconnect_sync(card->snd_card); @@ -1955,8 +1801,8 @@ static void soc_cleanup_card_resources(struct snd_soc_card *card, soc_remove_link_dais(card); soc_remove_link_components(card); - for_each_card_links_safe(card, link, _link) - snd_soc_remove_dai_link(card, link); + for_each_card_rtds_safe(card, rtd, n) + snd_soc_remove_pcm_runtime(card, rtd); /* remove auxiliary devices */ soc_remove_aux_devices(card); @@ -1995,6 +1841,7 @@ static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister) static int snd_soc_bind_card(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; + struct snd_soc_component *component; struct snd_soc_dai_link *dai_link; int ret, i, card_probed = 0; @@ -2014,7 +1861,7 @@ static int snd_soc_bind_card(struct snd_soc_card *card) /* add predefined DAI links to the list */ card->num_rtd = 0; for_each_card_prelinks(card, i, dai_link) { - ret = snd_soc_add_dai_link(card, dai_link); + ret = snd_soc_add_pcm_runtime(card, dai_link); if (ret < 0) goto probe_end; } @@ -2075,8 +1922,11 @@ static int snd_soc_bind_card(struct snd_soc_card *card) goto probe_end; } - for_each_card_rtds(card, rtd) - soc_link_init(card, rtd); + for_each_card_rtds(card, rtd) { + ret = soc_init_pcm_runtime(card, rtd); + if (ret < 0) + goto probe_end; + } snd_soc_dapm_link_dai_widgets(card); snd_soc_dapm_connect_dai_link_widgets(card); @@ -2143,17 +1993,9 @@ static int snd_soc_bind_card(struct snd_soc_card *card) snd_soc_dapm_sync(&card->dapm); /* deactivate pins to sleep state */ - for_each_card_rtds(card, rtd) { - struct snd_soc_dai *dai; - - for_each_rtd_codec_dai(rtd, i, dai) { - if (!dai->active) - pinctrl_pm_select_sleep_state(dai->dev); - } - - if (!rtd->cpu_dai->active) - pinctrl_pm_select_sleep_state(rtd->cpu_dai->dev); - } + for_each_card_components(card, component) + if (!component->active) + pinctrl_pm_select_sleep_state(component->dev); probe_end: if (ret < 0) @@ -2199,7 +2041,7 @@ static int soc_remove(struct platform_device *pdev) int snd_soc_poweroff(struct device *dev) { struct snd_soc_card *card = dev_get_drvdata(dev); - struct snd_soc_pcm_runtime *rtd; + struct snd_soc_component *component; if (!card->instantiated) return 0; @@ -2213,16 +2055,8 @@ int snd_soc_poweroff(struct device *dev) snd_soc_dapm_shutdown(card); /* deactivate pins to sleep state */ - for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; - int i; - - pinctrl_pm_select_sleep_state(cpu_dai->dev); - for_each_rtd_codec_dai(rtd, i, codec_dai) { - pinctrl_pm_select_sleep_state(codec_dai->dev); - } - } + for_each_card_components(card, component) + pinctrl_pm_select_sleep_state(component->dev); return 0; } @@ -2406,7 +2240,6 @@ int snd_soc_register_card(struct snd_soc_card *card) INIT_LIST_HEAD(&card->aux_comp_list); INIT_LIST_HEAD(&card->component_dev_list); INIT_LIST_HEAD(&card->list); - INIT_LIST_HEAD(&card->dai_link_list); INIT_LIST_HEAD(&card->rtd_list); INIT_LIST_HEAD(&card->dapm_dirty); INIT_LIST_HEAD(&card->dobj_list); @@ -2511,6 +2344,8 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_dai); * * @component: The component the DAIs are registered for * @dai_drv: DAI driver to use for the DAI + * @legacy_dai_naming: if %true, use legacy single-name format; + * if %false, use multiple-name format; * * Topology can use this API to register DAIs when probing a component. * These DAIs's widgets will be freed in the card cleanup and the DAIs @@ -3014,7 +2849,7 @@ void snd_soc_of_parse_node_prefix(struct device_node *np, return; } - codec_conf->of_node = of_node; + codec_conf->dlc.of_node = of_node; codec_conf->name_prefix = str; } EXPORT_SYMBOL_GPL(snd_soc_of_parse_node_prefix); diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 1c7f63871c1d..51031e330179 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -354,18 +354,6 @@ snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai, return delay; } -void snd_soc_dai_suspend(struct snd_soc_dai *dai) -{ - if (dai->driver->suspend) - dai->driver->suspend(dai); -} - -void snd_soc_dai_resume(struct snd_soc_dai *dai) -{ - if (dai->driver->resume) - dai->driver->resume(dai); -} - int snd_soc_dai_probe(struct snd_soc_dai *dai) { if (dai->driver->probe) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b6378f025836..bc20ad9abf8b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3441,17 +3441,8 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); -/** - * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value of a dapm enumerated double mixer control. - * - * Returns 0 for success. - */ -int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int __snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, int locked) { struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct snd_soc_card *card = dapm->card; @@ -3474,7 +3465,9 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mask |= e->mask << e->shift_r; } - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + if (!locked) + mutex_lock_nested(&card->dapm_mutex, + SND_SOC_DAPM_CLASS_RUNTIME); change = dapm_kcontrol_set_value(kcontrol, val); @@ -3496,16 +3489,51 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, card->update = NULL; } - mutex_unlock(&card->dapm_mutex); + if (!locked) + mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); return change; } + +/** + * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to set the value of a dapm enumerated double mixer control. + * + * Returns 0 for success. + */ +int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return __snd_soc_dapm_put_enum_double(kcontrol, ucontrol, 0); +} EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); /** + * snd_soc_dapm_put_enum_double_locked - dapm enumerated double mixer set + * callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to set the value of a dapm enumerated double mixer control. + * Must acquire dapm_mutex before calling the function. + * + * Returns 0 for success. + */ +int snd_soc_dapm_put_enum_double_locked(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + dapm_assert_locked(snd_soc_dapm_kcontrol_dapm(kcontrol)); + return __snd_soc_dapm_put_enum_double(kcontrol, ucontrol, 1); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double_locked); + +/** * snd_soc_dapm_info_pin_switch - Info for a pin switch * * @kcontrol: mixer control @@ -4447,6 +4475,29 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, mutex_unlock(&card->dapm_mutex); } +void snd_soc_dapm_stream_stop(struct snd_soc_pcm_runtime *rtd, int stream) +{ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (snd_soc_runtime_ignore_pmdown_time(rtd)) { + /* powered down playback stream now */ + snd_soc_dapm_stream_event(rtd, + SNDRV_PCM_STREAM_PLAYBACK, + SND_SOC_DAPM_STREAM_STOP); + } else { + /* start delayed pop wq here for playback streams */ + rtd->pop_wait = 1; + queue_delayed_work(system_power_efficient_wq, + &rtd->delayed_work, + msecs_to_jiffies(rtd->pmdown_time)); + } + } else { + /* capture streams can be powered down now */ + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, + SND_SOC_DAPM_STREAM_STOP); + } +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_stop); + /** * snd_soc_dapm_enable_pin_unlocked - enable pin. * @dapm: DAPM context diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index a428ff393ea2..2cc25651661c 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -104,7 +104,7 @@ static int dmaengine_pcm_hw_params(struct snd_soc_component *component, return ret; } - return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + return 0; } static int @@ -117,7 +117,6 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, struct dma_chan *chan = pcm->chan[substream->stream]; struct snd_dmaengine_dai_dma_data *dma_data; struct snd_pcm_hardware hw; - int ret; if (pcm->config && pcm->config->pcm_hardware) return snd_soc_set_runtime_hwparams(substream, @@ -138,12 +137,15 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) hw.info |= SNDRV_PCM_INFO_BATCH; - ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream, - dma_data, - &hw, - chan); - if (ret) - return ret; + /** + * FIXME: Remove the return value check to align with the code + * before adding snd_dmaengine_pcm_refine_runtime_hwparams + * function. + */ + snd_dmaengine_pcm_refine_runtime_hwparams(substream, + dma_data, + &hw, + chan); return snd_soc_set_runtime_hwparams(substream, &hw); } @@ -168,12 +170,6 @@ static int dmaengine_pcm_close(struct snd_soc_component *component, return snd_dmaengine_pcm_close(substream); } -static int dmaengine_pcm_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); -} - static int dmaengine_pcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { @@ -261,7 +257,7 @@ static int dmaengine_pcm_new(struct snd_soc_component *component, return -EINVAL; } - snd_pcm_lib_preallocate_pages(substream, + snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV_IRAM, dmaengine_dma_dev(pcm, substream), prealloc_buffer_size, @@ -329,9 +325,7 @@ static const struct snd_soc_component_driver dmaengine_pcm_component = { .probe_order = SND_SOC_COMP_ORDER_LATE, .open = dmaengine_pcm_open, .close = dmaengine_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = dmaengine_pcm_hw_params, - .hw_free = dmaengine_pcm_hw_free, .trigger = dmaengine_pcm_trigger, .pointer = dmaengine_pcm_pointer, .pcm_construct = dmaengine_pcm_new, @@ -342,9 +336,7 @@ static const struct snd_soc_component_driver dmaengine_pcm_component_process = { .probe_order = SND_SOC_COMP_ORDER_LATE, .open = dmaengine_pcm_open, .close = dmaengine_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = dmaengine_pcm_hw_params, - .hw_free = dmaengine_pcm_hw_free, .trigger = dmaengine_pcm_trigger, .pointer = dmaengine_pcm_pointer, .copy_user = dmaengine_copy_user, diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 01e7bc03d92f..ff1b7c7078e5 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -28,6 +28,60 @@ #define DPCM_MAX_BE_USERS 8 +static int soc_rtd_startup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) +{ + if (rtd->dai_link->ops && + rtd->dai_link->ops->startup) + return rtd->dai_link->ops->startup(substream); + return 0; +} + +static void soc_rtd_shutdown(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) +{ + if (rtd->dai_link->ops && + rtd->dai_link->ops->shutdown) + rtd->dai_link->ops->shutdown(substream); +} + +static int soc_rtd_prepare(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) +{ + if (rtd->dai_link->ops && + rtd->dai_link->ops->prepare) + return rtd->dai_link->ops->prepare(substream); + return 0; +} + +static int soc_rtd_hw_params(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + if (rtd->dai_link->ops && + rtd->dai_link->ops->hw_params) + return rtd->dai_link->ops->hw_params(substream, params); + return 0; +} + +static void soc_rtd_hw_free(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) +{ + if (rtd->dai_link->ops && + rtd->dai_link->ops->hw_free) + rtd->dai_link->ops->hw_free(substream); +} + +static int soc_rtd_trigger(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream, + int cmd) +{ + if (rtd->dai_link->ops && + rtd->dai_link->ops->trigger) + return rtd->dai_link->ops->trigger(substream, cmd); + return 0; +} + /** * snd_soc_runtime_activate() - Increment active count for PCM runtime components * @rtd: ASoC PCM runtime that is activated @@ -111,14 +165,14 @@ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream) */ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; bool ignore = true; + int i; if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) return true; - for_each_rtd_components(rtd, rtdcom, component) + for_each_rtd_components(rtd, i, component) ignore &= !component->driver->use_pmdown_time; return ignore; @@ -428,11 +482,10 @@ static int soc_pcm_components_open(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; - int ret = 0; + int i, ret = 0; - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { *last = component; ret = snd_soc_component_module_get_when_open(component); @@ -459,11 +512,10 @@ 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; - int ret = 0; + int i, ret = 0; - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (component == last) break; @@ -484,19 +536,16 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; 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; const char *codec_dai_name = "multicodec"; int i, ret = 0; - pinctrl_pm_select_default_state(cpu_dai->dev); - for_each_rtd_codec_dai(rtd, i, codec_dai) - pinctrl_pm_select_default_state(codec_dai->dev); + for_each_rtd_components(rtd, i, component) + pinctrl_pm_select_default_state(component->dev); - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) pm_runtime_get_sync(component->dev); - } mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); @@ -527,13 +576,11 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) codec_dai->rx_mask = 0; } - if (rtd->dai_link->ops->startup) { - ret = rtd->dai_link->ops->startup(substream); - if (ret < 0) { - pr_err("ASoC: %s startup failed: %d\n", - rtd->dai_link->name, ret); - goto machine_err; - } + ret = soc_rtd_startup(rtd, substream); + if (ret < 0) { + pr_err("ASoC: %s startup failed: %d\n", + rtd->dai_link->name, ret); + goto machine_err; } /* Dynamic PCM DAI links compat checks use dynamic capabilities */ @@ -600,8 +647,7 @@ dynamic: return 0; config_err: - if (rtd->dai_link->ops->shutdown) - rtd->dai_link->ops->shutdown(substream); + soc_rtd_shutdown(rtd, substream); machine_err: i = rtd->num_codecs; @@ -617,47 +663,18 @@ component_err: out: mutex_unlock(&rtd->card->pcm_mutex); - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); } - for_each_rtd_codec_dai(rtd, i, codec_dai) { - if (!codec_dai->active) - pinctrl_pm_select_sleep_state(codec_dai->dev); - } - if (!cpu_dai->active) - pinctrl_pm_select_sleep_state(cpu_dai->dev); + for_each_rtd_components(rtd, i, component) + if (!component->active) + pinctrl_pm_select_sleep_state(component->dev); return ret; } -/* - * Power down the audio subsystem pmdown_time msecs after close is called. - * This is to ensure there are no pops or clicks in between any music tracks - * due to DAPM power cycling. - */ -static void close_delayed_work(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_dai *codec_dai = rtd->codec_dais[0]; - - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); - - dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", - codec_dai->driver->playback.stream_name, - codec_dai->playback_active ? "active" : "inactive", - rtd->pop_wait ? "yes" : "no"); - - /* are we waiting on this codec DAI stream */ - if (rtd->pop_wait == 1) { - rtd->pop_wait = 0; - snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, - SND_SOC_DAPM_STREAM_STOP); - } - - mutex_unlock(&rtd->card->pcm_mutex); -} - static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd) { /* @@ -677,7 +694,6 @@ static int soc_pcm_close(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; int i; @@ -702,43 +718,22 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) for_each_rtd_codec_dai(rtd, i, codec_dai) snd_soc_dai_shutdown(codec_dai, substream); - if (rtd->dai_link->ops->shutdown) - rtd->dai_link->ops->shutdown(substream); + soc_rtd_shutdown(rtd, substream); soc_pcm_components_close(substream, NULL); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (snd_soc_runtime_ignore_pmdown_time(rtd)) { - /* powered down playback stream now */ - snd_soc_dapm_stream_event(rtd, - SNDRV_PCM_STREAM_PLAYBACK, - SND_SOC_DAPM_STREAM_STOP); - } else { - /* start delayed pop wq here for playback streams */ - rtd->pop_wait = 1; - queue_delayed_work(system_power_efficient_wq, - &rtd->delayed_work, - msecs_to_jiffies(rtd->pmdown_time)); - } - } else { - /* capture streams can be powered down now */ - snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, - SND_SOC_DAPM_STREAM_STOP); - } + snd_soc_dapm_stream_stop(rtd, substream->stream); mutex_unlock(&rtd->card->pcm_mutex); - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); } - for_each_rtd_codec_dai(rtd, i, codec_dai) { - if (!codec_dai->active) - pinctrl_pm_select_sleep_state(codec_dai->dev); - } - if (!cpu_dai->active) - pinctrl_pm_select_sleep_state(cpu_dai->dev); + for_each_rtd_components(rtd, i, component) + if (!component->active) + pinctrl_pm_select_sleep_state(component->dev); return 0; } @@ -752,23 +747,20 @@ static int soc_pcm_prepare(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; int i, ret = 0; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); - if (rtd->dai_link->ops->prepare) { - ret = rtd->dai_link->ops->prepare(substream); - if (ret < 0) { - dev_err(rtd->card->dev, "ASoC: machine prepare error:" - " %d\n", ret); - goto out; - } + ret = soc_rtd_prepare(rtd, substream); + if (ret < 0) { + dev_err(rtd->card->dev, + "ASoC: machine prepare error: %d\n", ret); + goto out; } - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { ret = snd_soc_component_prepare(component, substream); if (ret < 0) { dev_err(component->dev, @@ -829,11 +821,10 @@ 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; - int ret = 0; + int i, ret = 0; - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { if (component == last) break; @@ -853,7 +844,6 @@ static int soc_pcm_hw_params(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; int i, ret = 0; @@ -864,13 +854,11 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, if (ret) goto out; - if (rtd->dai_link->ops->hw_params) { - ret = rtd->dai_link->ops->hw_params(substream, params); - if (ret < 0) { - dev_err(rtd->card->dev, "ASoC: machine hw_params" - " failed: %d\n", ret); - goto out; - } + ret = soc_rtd_hw_params(rtd, substream, params); + if (ret < 0) { + dev_err(rtd->card->dev, + "ASoC: machine hw_params failed: %d\n", ret); + goto out; } for_each_rtd_codec_dai(rtd, i, codec_dai) { @@ -932,7 +920,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, snd_soc_dapm_update_dai(substream, params, cpu_dai); - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { ret = snd_soc_component_hw_params(component, substream, params); if (ret < 0) { dev_err(component->dev, @@ -965,8 +953,7 @@ codec_err: codec_dai->rate = 0; } - if (rtd->dai_link->ops->hw_free) - rtd->dai_link->ops->hw_free(substream); + soc_rtd_hw_free(rtd, substream); mutex_unlock(&rtd->card->pcm_mutex); return ret; @@ -1009,8 +996,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) } /* free any machine hw params */ - if (rtd->dai_link->ops->hw_free) - rtd->dai_link->ops->hw_free(substream); + soc_rtd_hw_free(rtd, substream); /* free any component resources */ soc_pcm_components_hw_free(substream, NULL); @@ -1033,18 +1019,15 @@ static int soc_pcm_trigger_start(struct snd_pcm_substream *substream, int cmd) { 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; int i, ret; - if (rtd->dai_link->ops->trigger) { - ret = rtd->dai_link->ops->trigger(substream, cmd); - if (ret < 0) - return ret; - } + ret = soc_rtd_trigger(rtd, substream, cmd); + if (ret < 0) + return ret; - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { ret = snd_soc_component_trigger(component, substream, cmd); if (ret < 0) return ret; @@ -1067,7 +1050,6 @@ static int soc_pcm_trigger_stop(struct snd_pcm_substream *substream, int cmd) { 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; int i, ret; @@ -1082,17 +1064,15 @@ static int soc_pcm_trigger_stop(struct snd_pcm_substream *substream, int cmd) if (ret < 0) return ret; - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { ret = snd_soc_component_trigger(component, substream, cmd); if (ret < 0) return ret; } - if (rtd->dai_link->ops->trigger) { - ret = rtd->dai_link->ops->trigger(substream, cmd); - if (ret < 0) - return ret; - } + ret = soc_rtd_trigger(rtd, substream, cmd); + if (ret < 0) + return ret; return 0; } @@ -2026,7 +2006,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) soc_pcm_close(substream); /* run the stream event for each BE */ - dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); + snd_soc_dapm_stream_stop(fe, stream); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); @@ -2897,7 +2877,6 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; struct snd_pcm *pcm; char new_name[64]; @@ -2916,10 +2895,10 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) for_each_rtd_codec_dai(rtd, i, codec_dai) { if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && - snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK)) + snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE)) playback = 1; if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) && - snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE)) + snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK)) capture = 1; } @@ -2974,7 +2953,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) if (rtd->dai_link->params) rtd->close_delayed_work_func = codec2codec_close_delayed_work; else - rtd->close_delayed_work_func = close_delayed_work; + rtd->close_delayed_work_func = snd_soc_close_delayed_work; pcm->nonatomic = rtd->dai_link->nonatomic; rtd->pcm = pcm; @@ -3007,7 +2986,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->ops.pointer = soc_pcm_pointer; } - for_each_rtd_components(rtd, rtdcom, component) { + for_each_rtd_components(rtd, i, component) { const struct snd_soc_component_driver *drv = component->driver; if (drv->ioctl) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 92e4f4d08bfa..d2ee6ad20e83 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -549,7 +549,8 @@ static void remove_link(struct snd_soc_component *comp, dobj->ops->link_unload(comp, dobj); list_del(&dobj->list); - snd_soc_remove_dai_link(comp->card, link); + snd_soc_remove_pcm_runtime(comp->card, + snd_soc_get_pcm_runtime(comp->card, link)); kfree(link->name); kfree(link->stream_name); @@ -604,9 +605,11 @@ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, ext_ops = tplg->bytes_ext_ops; num_ops = tplg->bytes_ext_ops_count; for (i = 0; i < num_ops; i++) { - if (!sbe->put && ext_ops[i].id == be->ext_ops.put) + if (!sbe->put && + ext_ops[i].id == le32_to_cpu(be->ext_ops.put)) sbe->put = ext_ops[i].put; - if (!sbe->get && ext_ops[i].id == be->ext_ops.get) + if (!sbe->get && + ext_ops[i].id == le32_to_cpu(be->ext_ops.get)) sbe->get = ext_ops[i].get; } @@ -621,11 +624,11 @@ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, num_ops = tplg->io_ops_count; for (i = 0; i < num_ops; i++) { - if (k->put == NULL && ops[i].id == hdr->ops.put) + if (k->put == NULL && ops[i].id == le32_to_cpu(hdr->ops.put)) k->put = ops[i].put; - if (k->get == NULL && ops[i].id == hdr->ops.get) + if (k->get == NULL && ops[i].id == le32_to_cpu(hdr->ops.get)) k->get = ops[i].get; - if (k->info == NULL && ops[i].id == hdr->ops.info) + if (k->info == NULL && ops[i].id == le32_to_cpu(hdr->ops.info)) k->info = ops[i].info; } @@ -638,11 +641,11 @@ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, num_ops = ARRAY_SIZE(io_ops); for (i = 0; i < num_ops; i++) { - if (k->put == NULL && ops[i].id == hdr->ops.put) + if (k->put == NULL && ops[i].id == le32_to_cpu(hdr->ops.put)) k->put = ops[i].put; - if (k->get == NULL && ops[i].id == hdr->ops.get) + if (k->get == NULL && ops[i].id == le32_to_cpu(hdr->ops.get)) k->get = ops[i].get; - if (k->info == NULL && ops[i].id == hdr->ops.info) + if (k->info == NULL && ops[i].id == le32_to_cpu(hdr->ops.info)) k->info = ops[i].info; } @@ -931,7 +934,7 @@ static int soc_tplg_denum_create_texts(struct soc_enum *se, if (se->dobj.control.dtexts == NULL) return -ENOMEM; - for (i = 0; i < ec->items; i++) { + for (i = 0; i < le32_to_cpu(ec->items); i++) { if (strnlen(ec->texts[i], SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { @@ -1325,7 +1328,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create( if (kc[i].name == NULL) goto err_sm; kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc[i].access = mc->hdr.access; + kc[i].access = le32_to_cpu(mc->hdr.access); /* we only support FL/FR channel mapping atm */ sm->reg = tplc_chan_get_reg(tplg, mc->channel, @@ -1337,10 +1340,10 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create( sm->rshift = tplc_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR); - sm->max = mc->max; - sm->min = mc->min; - sm->invert = mc->invert; - sm->platform_max = mc->platform_max; + sm->max = le32_to_cpu(mc->max); + sm->min = le32_to_cpu(mc->min); + sm->invert = le32_to_cpu(mc->invert); + sm->platform_max = le32_to_cpu(mc->platform_max); sm->dobj.index = tplg->index; INIT_LIST_HEAD(&sm->dobj.list); @@ -1401,7 +1404,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create( goto err_se; tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + - ec->priv.size); + le32_to_cpu(ec->priv.size)); dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n", ec->hdr.name); @@ -1411,7 +1414,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create( if (kc[i].name == NULL) goto err_se; kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc[i].access = ec->hdr.access; + kc[i].access = le32_to_cpu(ec->hdr.access); /* we only support FL/FR channel mapping atm */ se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); @@ -1420,8 +1423,8 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create( se->shift_r = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR); - se->items = ec->items; - se->mask = ec->mask; + se->items = le32_to_cpu(ec->items); + se->mask = le32_to_cpu(ec->mask); se->dobj.index = tplg->index; switch (le32_to_cpu(ec->hdr.ops.info)) { @@ -1523,9 +1526,9 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create( if (kc[i].name == NULL) goto err_sbe; kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc[i].access = be->hdr.access; + kc[i].access = le32_to_cpu(be->hdr.access); - sbe->max = be->max; + sbe->max = le32_to_cpu(be->max); INIT_LIST_HEAD(&sbe->dobj.list); /* map standard io handlers and check for external handlers */ @@ -1906,6 +1909,10 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, link->num_codecs = 1; link->num_platforms = 1; + link->dobj.index = tplg->index; + link->dobj.ops = tplg->ops; + link->dobj.type = SND_SOC_DOBJ_DAI_LINK; + if (strlen(pcm->pcm_name)) { link->name = kstrdup(pcm->pcm_name, GFP_KERNEL); link->stream_name = kstrdup(pcm->pcm_name, GFP_KERNEL); @@ -1936,15 +1943,12 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, goto err; } - ret = snd_soc_add_dai_link(tplg->comp->card, link); + ret = snd_soc_add_pcm_runtime(tplg->comp->card, link); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: adding FE link failed\n"); goto err; } - link->dobj.index = tplg->index; - link->dobj.ops = tplg->ops; - link->dobj.type = SND_SOC_DOBJ_DAI_LINK; list_add(&link->dobj.list, &tplg->comp->dobj_list); return 0; @@ -2220,6 +2224,47 @@ static int link_new_ver(struct soc_tplg *tplg, return 0; } +/** + * snd_soc_find_dai_link - Find a DAI link + * + * @card: soc card + * @id: DAI link ID to match + * @name: DAI link name to match, optional + * @stream_name: DAI link stream name to match, optional + * + * This function will search all existing DAI links of the soc card to + * find the link of the same ID. Since DAI links may not have their + * unique ID, so name and stream name should also match if being + * specified. + * + * Return: pointer of DAI link, or NULL if not found. + */ +static struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card, + int id, const char *name, + const char *stream_name) +{ + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai_link *link; + + for_each_card_rtds(card, rtd) { + link = rtd->dai_link; + + if (link->id != id) + continue; + + if (name && (!link->name || strcmp(name, link->name))) + continue; + + if (stream_name && (!link->stream_name + || strcmp(stream_name, link->stream_name))) + continue; + + return link; + } + + return NULL; +} + /* Find and configure an existing physical DAI link */ static int soc_tplg_link_config(struct soc_tplg *tplg, struct snd_soc_tplg_link_config *cfg) diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 2fd4562f5e63..922eac930df9 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -77,7 +77,6 @@ static int dummy_dma_open(struct snd_soc_component *component, static const struct snd_soc_component_driver dummy_platform = { .open = dummy_dma_open, - .ioctl = snd_soc_pcm_lib_ioctl, }; static const struct snd_soc_component_driver dummy_codec = { diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 71a0fc075a63..827b0ec92522 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -28,7 +28,7 @@ config SND_SOC_SOF_ACPI select IOSF_MBI if X86 && PCI help This adds support for ACPI enumeration. This option is required - to enable Intel Haswell/Broadwell/Baytrail/Cherrytrail devices + to enable Intel Broadwell/Baytrail/Cherrytrail devices Say Y if you need this option If unsure select "N". diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index b0a6f01bdc44..0a8bc72c28a5 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ - control.o trace.o utils.o + control.o trace.o utils.o sof-audio.o snd-sof-pci-objs := sof-pci-dev.o snd-sof-acpi-objs := sof-acpi-dev.o diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 7baf7f1507c3..dfc412e2d956 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -13,6 +13,7 @@ #include <linux/pm_runtime.h> #include <linux/leds.h> #include "sof-priv.h" +#include "sof-audio.h" static void update_mute_led(struct snd_sof_control *scontrol, struct snd_kcontrol *kcontrol, @@ -88,7 +89,7 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; - struct snd_sof_dev *sdev = scontrol->sdev; + struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; unsigned int i, channels = scontrol->num_channels; bool change = false; @@ -104,8 +105,8 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, } /* notify DSP of mixer updates */ - if (pm_runtime_active(sdev->dev)) - snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol, + if (pm_runtime_active(scomp->dev)) + snd_sof_ipc_set_get_comp_data(scontrol, SOF_IPC_COMP_SET_VALUE, SOF_CTRL_TYPE_VALUE_CHAN_GET, SOF_CTRL_CMD_VOLUME, @@ -135,7 +136,7 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; - struct snd_sof_dev *sdev = scontrol->sdev; + struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; unsigned int i, channels = scontrol->num_channels; bool change = false; @@ -153,8 +154,8 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, update_mute_led(scontrol, kcontrol, ucontrol); /* notify DSP of mixer updates */ - if (pm_runtime_active(sdev->dev)) - snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol, + if (pm_runtime_active(scomp->dev)) + snd_sof_ipc_set_get_comp_data(scontrol, SOF_IPC_COMP_SET_VALUE, SOF_CTRL_TYPE_VALUE_CHAN_GET, SOF_CTRL_CMD_SWITCH, @@ -185,7 +186,7 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, struct soc_enum *se = (struct soc_enum *)kcontrol->private_value; struct snd_sof_control *scontrol = se->dobj.private; - struct snd_sof_dev *sdev = scontrol->sdev; + struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; unsigned int i, channels = scontrol->num_channels; bool change = false; @@ -200,8 +201,8 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, } /* notify DSP of enum updates */ - if (pm_runtime_active(sdev->dev)) - snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol, + if (pm_runtime_active(scomp->dev)) + snd_sof_ipc_set_get_comp_data(scontrol, SOF_IPC_COMP_SET_VALUE, SOF_CTRL_TYPE_VALUE_CHAN_GET, SOF_CTRL_CMD_ENUM, @@ -216,14 +217,14 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; - struct snd_sof_dev *sdev = scontrol->sdev; + struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct sof_abi_hdr *data = cdata->data; size_t size; int ret = 0; if (be->max > sizeof(ucontrol->value.bytes.data)) { - dev_err_ratelimited(sdev->dev, + dev_err_ratelimited(scomp->dev, "error: data max %d exceeds ucontrol data array size\n", be->max); return -EINVAL; @@ -231,7 +232,7 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, size = data->size + sizeof(*data); if (size > be->max) { - dev_err_ratelimited(sdev->dev, + dev_err_ratelimited(scomp->dev, "error: DSP sent %zu bytes max is %d\n", size, be->max); ret = -EINVAL; @@ -251,20 +252,20 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; - struct snd_sof_dev *sdev = scontrol->sdev; + struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct sof_abi_hdr *data = cdata->data; size_t size = data->size + sizeof(*data); if (be->max > sizeof(ucontrol->value.bytes.data)) { - dev_err_ratelimited(sdev->dev, + dev_err_ratelimited(scomp->dev, "error: data max %d exceeds ucontrol data array size\n", be->max); return -EINVAL; } if (size > be->max) { - dev_err_ratelimited(sdev->dev, + dev_err_ratelimited(scomp->dev, "error: size too big %zu bytes max is %d\n", size, be->max); return -EINVAL; @@ -274,8 +275,8 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, memcpy(data, ucontrol->value.bytes.data, size); /* notify DSP of byte control updates */ - if (pm_runtime_active(sdev->dev)) - snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol, + if (pm_runtime_active(scomp->dev)) + snd_sof_ipc_set_get_comp_data(scontrol, SOF_IPC_COMP_SET_DATA, SOF_CTRL_TYPE_DATA_SET, scontrol->cmd, @@ -291,7 +292,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; - struct snd_sof_dev *sdev = scontrol->sdev; + struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct snd_ctl_tlv header; const struct snd_ctl_tlv __user *tlvd = @@ -307,14 +308,14 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, /* be->max is coming from topology */ if (header.length > be->max) { - dev_err_ratelimited(sdev->dev, "error: Bytes data size %d exceeds max %d.\n", + dev_err_ratelimited(scomp->dev, "error: Bytes data size %d exceeds max %d.\n", header.length, be->max); return -EINVAL; } /* Check that header id matches the command */ if (header.numid != scontrol->cmd) { - dev_err_ratelimited(sdev->dev, + dev_err_ratelimited(scomp->dev, "error: incorrect numid %d\n", header.numid); return -EINVAL; @@ -324,26 +325,26 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, return -EFAULT; if (cdata->data->magic != SOF_ABI_MAGIC) { - dev_err_ratelimited(sdev->dev, + dev_err_ratelimited(scomp->dev, "error: Wrong ABI magic 0x%08x.\n", cdata->data->magic); return -EINVAL; } if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { - dev_err_ratelimited(sdev->dev, "error: Incompatible ABI version 0x%08x.\n", + dev_err_ratelimited(scomp->dev, "error: Incompatible ABI version 0x%08x.\n", cdata->data->abi); return -EINVAL; } if (cdata->data->size + sizeof(const struct sof_abi_hdr) > be->max) { - dev_err_ratelimited(sdev->dev, "error: Mismatch in ABI data size (truncated?).\n"); + dev_err_ratelimited(scomp->dev, "error: Mismatch in ABI data size (truncated?).\n"); return -EINVAL; } /* notify DSP of byte control updates */ - if (pm_runtime_active(sdev->dev)) - snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol, + if (pm_runtime_active(scomp->dev)) + snd_sof_ipc_set_get_comp_data(scontrol, SOF_IPC_COMP_SET_DATA, SOF_CTRL_TYPE_DATA_SET, scontrol->cmd, @@ -359,7 +360,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; - struct snd_sof_dev *sdev = scontrol->sdev; + struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct snd_ctl_tlv header; struct snd_ctl_tlv __user *tlvd = @@ -382,7 +383,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, /* check data size doesn't exceed max coming from topology */ if (data_size > be->max) { - dev_err_ratelimited(sdev->dev, "error: user data size %d exceeds max size %d.\n", + dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %d.\n", data_size, be->max); ret = -EINVAL; goto out; diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 805918d3bcc0..44f9c04d54aa 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -10,7 +10,6 @@ #include <linux/firmware.h> #include <linux/module.h> -#include <asm/unaligned.h> #include <sound/soc.h> #include <sound/sof.h> #include "sof-priv.h" @@ -26,126 +25,6 @@ MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)"); #define TIMEOUT_DEFAULT_BOOT_MS 2000 /* - * Generic object lookup APIs. - */ - -struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_sof_dev *sdev, - const char *name) -{ - struct snd_sof_pcm *spcm; - - list_for_each_entry(spcm, &sdev->pcm_list, list) { - /* match with PCM dai name */ - if (strcmp(spcm->pcm.dai_name, name) == 0) - return spcm; - - /* match with playback caps name if set */ - if (*spcm->pcm.caps[0].name && - !strcmp(spcm->pcm.caps[0].name, name)) - return spcm; - - /* match with capture caps name if set */ - if (*spcm->pcm.caps[1].name && - !strcmp(spcm->pcm.caps[1].name, name)) - return spcm; - } - - return NULL; -} - -struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, - unsigned int comp_id, - int *direction) -{ - struct snd_sof_pcm *spcm; - - list_for_each_entry(spcm, &sdev->pcm_list, list) { - if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id == comp_id) { - *direction = SNDRV_PCM_STREAM_PLAYBACK; - return spcm; - } - if (spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id == comp_id) { - *direction = SNDRV_PCM_STREAM_CAPTURE; - return spcm; - } - } - - return NULL; -} - -struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, - unsigned int pcm_id) -{ - struct snd_sof_pcm *spcm; - - list_for_each_entry(spcm, &sdev->pcm_list, list) { - if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id) - return spcm; - } - - return NULL; -} - -struct snd_sof_widget *snd_sof_find_swidget(struct snd_sof_dev *sdev, - const char *name) -{ - struct snd_sof_widget *swidget; - - list_for_each_entry(swidget, &sdev->widget_list, list) { - if (strcmp(name, swidget->widget->name) == 0) - return swidget; - } - - return NULL; -} - -/* find widget by stream name and direction */ -struct snd_sof_widget *snd_sof_find_swidget_sname(struct snd_sof_dev *sdev, - const char *pcm_name, int dir) -{ - struct snd_sof_widget *swidget; - enum snd_soc_dapm_type type; - - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - type = snd_soc_dapm_aif_in; - else - type = snd_soc_dapm_aif_out; - - list_for_each_entry(swidget, &sdev->widget_list, list) { - if (!strcmp(pcm_name, swidget->widget->sname) && swidget->id == type) - return swidget; - } - - return NULL; -} - -struct snd_sof_dai *snd_sof_find_dai(struct snd_sof_dev *sdev, - const char *name) -{ - struct snd_sof_dai *dai; - - list_for_each_entry(dai, &sdev->dai_list, list) { - if (dai->name && (strcmp(name, dai->name) == 0)) - return dai; - } - - return NULL; -} - -bool snd_sof_dsp_d0i3_on_suspend(struct snd_sof_dev *sdev) -{ - struct snd_sof_pcm *spcm; - - list_for_each_entry(spcm, &sdev->pcm_list, list) { - if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored || - spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored) - return true; - } - - return false; -} - -/* * FW Panic/fault handling. */ @@ -214,104 +93,48 @@ out: EXPORT_SYMBOL(snd_sof_get_status); /* - * Generic buffer page table creation. - * Take the each physical page address and drop the least significant unused - * bits from each (based on PAGE_SIZE). Then pack valid page address bits - * into compressed page table. - */ - -int snd_sof_create_page_table(struct snd_sof_dev *sdev, - struct snd_dma_buffer *dmab, - unsigned char *page_table, size_t size) -{ - int i, pages; - - pages = snd_sgbuf_aligned_pages(size); - - dev_dbg(sdev->dev, "generating page table for %p size 0x%zx pages %d\n", - dmab->area, size, pages); - - for (i = 0; i < pages; i++) { - /* - * The number of valid address bits for each page is 20. - * idx determines the byte position within page_table - * where the current page's address is stored - * in the compressed page_table. - * This can be calculated by multiplying the page number by 2.5. - */ - u32 idx = (5 * i) >> 1; - u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; - u8 *pg_table; - - dev_vdbg(sdev->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); - - pg_table = (u8 *)(page_table + idx); - - /* - * pagetable compression: - * byte 0 byte 1 byte 2 byte 3 byte 4 byte 5 - * ___________pfn 0__________ __________pfn 1___________ _pfn 2... - * .... .... .... .... .... .... .... .... .... .... .... - * It is created by: - * 1. set current location to 0, PFN index i to 0 - * 2. put pfn[i] at current location in Little Endian byte order - * 3. calculate an intermediate value as - * x = (pfn[i+1] << 4) | (pfn[i] & 0xf) - * 4. put x at offset (current location + 2) in LE byte order - * 5. increment current location by 5 bytes, increment i by 2 - * 6. continue to (2) - */ - if (i & 1) - put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4, - pg_table); - else - put_unaligned_le32(pfn, pg_table); - } - - return pages; -} - -/* - * SOF Driver enumeration. + * FW Boot State Transition Diagram + * + * +-----------------------------------------------------------------------+ + * | | + * ------------------ ------------------ | + * | | | | | + * | BOOT_FAILED | | READY_FAILED |-------------------------+ | + * | | | | | | + * ------------------ ------------------ | | + * ^ ^ | | + * | | | | + * (FW Boot Timeout) (FW_READY FAIL) | | + * | | | | + * | | | | + * ------------------ | ------------------ | | + * | | | | | | | + * | IN_PROGRESS |---------------+------------->| COMPLETE | | | + * | | (FW Boot OK) (FW_READY OK) | | | | + * ------------------ ------------------ | | + * ^ | | | + * | | | | + * (FW Loading OK) (System Suspend/Runtime Suspend) + * | | | | + * | | | | + * ------------------ ------------------ | | | + * | | | |<-----+ | | + * | PREPARE | | NOT_STARTED |<---------------------+ | + * | | | |<---------------------------+ + * ------------------ ------------------ + * | ^ | ^ + * | | | | + * | +-----------------------+ | + * | (DSP Probe OK) | + * | | + * | | + * +------------------------------------+ + * (System Suspend/Runtime Suspend) */ -static int sof_machine_check(struct snd_sof_dev *sdev) -{ - struct snd_sof_pdata *plat_data = sdev->pdata; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC) - struct snd_soc_acpi_mach *machine; - int ret; -#endif - - if (plat_data->machine) - return 0; - -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC) - dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n"); - return -ENODEV; -#else - /* fallback to nocodec mode */ - dev_warn(sdev->dev, "No ASoC machine driver found - using nocodec\n"); - machine = devm_kzalloc(sdev->dev, sizeof(*machine), GFP_KERNEL); - if (!machine) - return -ENOMEM; - - ret = sof_nocodec_setup(sdev->dev, plat_data, machine, - plat_data->desc, plat_data->desc->ops); - if (ret < 0) - return ret; - - plat_data->machine = machine; - - return 0; -#endif -} static int sof_probe_continue(struct snd_sof_dev *sdev) { struct snd_sof_pdata *plat_data = sdev->pdata; - const char *drv_name; - const void *mach; - int size; int ret; /* probe the DSP hardware */ @@ -321,6 +144,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) return ret; } + sdev->fw_state = SOF_FW_BOOT_PREPARE; + /* check machine info */ ret = sof_machine_check(sdev); if (ret < 0) { @@ -360,7 +185,12 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto fw_load_err; } - /* boot the firmware */ + sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; + + /* + * Boot the firmware. The FW boot status will be modified + * in snd_sof_run_firmware() depending on the outcome. + */ ret = snd_sof_run_firmware(sdev); if (ret < 0) { dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n", @@ -397,22 +227,17 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto fw_run_err; } - drv_name = plat_data->machine->drv_name; - mach = (const void *)plat_data->machine; - size = sizeof(*plat_data->machine); - - /* register machine driver, pass machine info as pdata */ - plat_data->pdev_mach = - platform_device_register_data(sdev->dev, drv_name, - PLATFORM_DEVID_NONE, mach, size); - - if (IS_ERR(plat_data->pdev_mach)) { - ret = PTR_ERR(plat_data->pdev_mach); + ret = snd_sof_machine_register(sdev, plat_data); + if (ret < 0) goto fw_run_err; - } - dev_dbg(sdev->dev, "created machine %s\n", - dev_name(&plat_data->pdev_mach->dev)); + /* + * Some platforms in SOF, ex: BYT, may not have their platform PM + * callbacks set. Increment the usage count so as to + * prevent the device from entering runtime suspend. + */ + if (!sof_ops(sdev)->runtime_suspend || !sof_ops(sdev)->runtime_resume) + pm_runtime_get_noresume(sdev->dev); if (plat_data->sof_probe_complete) plat_data->sof_probe_complete(sdev->dev); @@ -476,6 +301,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->pdata = plat_data; sdev->first_boot = true; + sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; dev_set_drvdata(dev, sdev); /* check all mandatory ops */ @@ -534,9 +360,7 @@ int snd_sof_device_remove(struct device *dev) * will remove the component driver and unload the topology * before freeing the snd_card. */ - if (!IS_ERR_OR_NULL(pdata->pdev_mach)) - platform_device_unregister(pdata->pdev_mach); - + snd_sof_machine_unregister(sdev, pdata); /* * Unregistering the machine driver results in unloading the topology. * Some widgets, ex: scheduler, attempt to power down the core they are diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index aef6ca167b9c..b2556f5e2871 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -294,8 +294,7 @@ static int imx8_probe(struct snd_sof_dev *sdev) } sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start, - res.end - res.start + - 1); + resource_size(&res)); if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n", base, size); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index cc09bb606f7d..56a837d2cb95 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -319,6 +319,7 @@ config SND_SOC_SOF_HDA_COMMON_HDMI_CODEC bool "SOF common HDA HDMI codec driver" depends on SND_SOC_SOF_HDA_LINK depends on SND_HDA_CODEC_HDMI + default SND_HDA_CODEC_HDMI help This adds support for HDMI audio by using the common HDA HDMI/DisplayPort codec driver. diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 7daa8eb456c8..2483b15699e7 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -17,6 +17,7 @@ #include "../sof-priv.h" #include "hda.h" +#include "../sof-audio.h" static const struct snd_sof_debugfs_map apl_dsp_debugfs[] = { {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS}, @@ -41,7 +42,6 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .block_write = sof_block_write, /* doorbell */ - .irq_handler = hda_dsp_ipc_irq_handler, .irq_thread = hda_dsp_ipc_irq_thread, /* ipc */ @@ -53,6 +53,12 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .ipc_msg_data = hda_ipc_msg_data, .ipc_pcm_params = hda_ipc_pcm_params, + /* machine driver */ + .machine_select = hda_machine_select, + .machine_register = sof_machine_register, + .machine_unregister = sof_machine_unregister, + .set_mach_params = hda_set_mach_params, + /* debug */ .debug_map = apl_dsp_debugfs, .debug_map_count = ARRAY_SIZE(apl_dsp_debugfs), @@ -105,8 +111,10 @@ const struct snd_sof_dsp_ops sof_apl_ops = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + + .arch_ops = &sof_xtensa_arch_ops, }; -EXPORT_SYMBOL(sof_apl_ops); +EXPORT_SYMBOL_NS(sof_apl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); const struct sof_intel_dsp_desc apl_chip_info = { /* Apollolake */ @@ -122,4 +130,4 @@ const struct sof_intel_dsp_desc apl_chip_info = { .ssp_count = APL_SSP_COUNT, .ssp_base_offset = APL_SSP_BASE_OFFSET, }; -EXPORT_SYMBOL(apl_chip_info); +EXPORT_SYMBOL_NS(apl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 141dad554764..6c23c5769330 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -17,6 +17,7 @@ #include <sound/sof/xtensa.h> #include "../ops.h" #include "shim.h" +#include "../sof-audio.h" /* BARs */ #define BDW_DSP_BAR 0 @@ -536,6 +537,32 @@ static int bdw_probe(struct snd_sof_dev *sdev) return ret; } +static void bdw_machine_select(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *sof_pdata = sdev->pdata; + const struct sof_dev_desc *desc = sof_pdata->desc; + struct snd_soc_acpi_mach *mach; + + mach = snd_soc_acpi_find_machine(desc->machines); + if (!mach) { + dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); + return; + } + + sof_pdata->tplg_filename = mach->sof_tplg_filename; + mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc; + sof_pdata->machine = mach; +} + +static void bdw_set_mach_params(const struct snd_soc_acpi_mach *mach, + struct device *dev) +{ + struct snd_soc_acpi_mach_params *mach_params; + + mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params->platform = dev_name(dev); +} + /* Broadwell DAIs */ static struct snd_soc_dai_driver bdw_dai[] = { { @@ -574,6 +601,12 @@ const struct snd_sof_dsp_ops sof_bdw_ops = { .ipc_msg_data = intel_ipc_msg_data, .ipc_pcm_params = intel_ipc_pcm_params, + /* machine driver */ + .machine_select = bdw_machine_select, + .machine_register = sof_machine_register, + .machine_unregister = sof_machine_unregister, + .set_mach_params = bdw_set_mach_params, + /* debug */ .debug_map = bdw_debugfs, .debug_map_count = ARRAY_SIZE(bdw_debugfs), @@ -599,13 +632,17 @@ const struct snd_sof_dsp_ops sof_bdw_ops = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_BATCH, + + .arch_ops = &sof_xtensa_arch_ops, }; -EXPORT_SYMBOL(sof_bdw_ops); +EXPORT_SYMBOL_NS(sof_bdw_ops, SND_SOC_SOF_BROADWELL); const struct sof_intel_dsp_desc bdw_chip_info = { .cores_num = 1, .cores_mask = 1, }; -EXPORT_SYMBOL(bdw_chip_info); +EXPORT_SYMBOL_NS(bdw_chip_info, SND_SOC_SOF_BROADWELL); MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HIFI_EP_IPC); +MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 92ef6a796fd5..f84391294f12 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -17,6 +17,8 @@ #include <sound/sof/xtensa.h> #include "../ops.h" #include "shim.h" +#include "../sof-audio.h" +#include "../../intel/common/soc-intel-quirks.h" /* DSP memories */ #define IRAM_OFFSET 0x0C0000 @@ -383,6 +385,76 @@ static int byt_reset(struct snd_sof_dev *sdev) return 0; } +static const char *fixup_tplg_name(struct snd_sof_dev *sdev, + const char *sof_tplg_filename, + const char *ssp_str) +{ + const char *tplg_filename = NULL; + char *filename; + char *split_ext; + + filename = devm_kstrdup(sdev->dev, sof_tplg_filename, GFP_KERNEL); + if (!filename) + return NULL; + + /* this assumes a .tplg extension */ + split_ext = strsep(&filename, "."); + if (split_ext) { + tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL, + "%s-%s.tplg", + split_ext, ssp_str); + if (!tplg_filename) + return NULL; + } + return tplg_filename; +} + +static void byt_machine_select(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *sof_pdata = sdev->pdata; + const struct sof_dev_desc *desc = sof_pdata->desc; + struct snd_soc_acpi_mach *mach; + struct platform_device *pdev; + const char *tplg_filename; + + mach = snd_soc_acpi_find_machine(desc->machines); + if (!mach) { + dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); + return; + } + + pdev = to_platform_device(sdev->dev); + if (soc_intel_is_byt_cr(pdev)) { + dev_dbg(sdev->dev, + "BYT-CR detected, SSP0 used instead of SSP2\n"); + + tplg_filename = fixup_tplg_name(sdev, + mach->sof_tplg_filename, + "ssp0"); + } else { + tplg_filename = mach->sof_tplg_filename; + } + + if (!tplg_filename) { + dev_dbg(sdev->dev, + "error: no topology filename\n"); + return; + } + + sof_pdata->tplg_filename = tplg_filename; + mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc; + sof_pdata->machine = mach; +} + +static void byt_set_mach_params(const struct snd_soc_acpi_mach *mach, + struct device *dev) +{ + struct snd_soc_acpi_mach_params *mach_params; + + mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params->platform = dev_name(dev); +} + /* Baytrail DAIs */ static struct snd_soc_dai_driver byt_dai[] = { { @@ -515,6 +587,12 @@ const struct snd_sof_dsp_ops sof_tng_ops = { .ipc_msg_data = intel_ipc_msg_data, .ipc_pcm_params = intel_ipc_pcm_params, + /* machine driver */ + .machine_select = byt_machine_select, + .machine_register = sof_machine_register, + .machine_unregister = sof_machine_unregister, + .set_mach_params = byt_set_mach_params, + /* debug */ .debug_map = byt_debugfs, .debug_map_count = ARRAY_SIZE(byt_debugfs), @@ -540,14 +618,16 @@ const struct snd_sof_dsp_ops sof_tng_ops = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_BATCH, + + .arch_ops = &sof_xtensa_arch_ops, }; -EXPORT_SYMBOL(sof_tng_ops); +EXPORT_SYMBOL_NS(sof_tng_ops, SND_SOC_SOF_MERRIFIELD); const struct sof_intel_dsp_desc tng_chip_info = { .cores_num = 1, .cores_mask = 1, }; -EXPORT_SYMBOL(tng_chip_info); +EXPORT_SYMBOL_NS(tng_chip_info, SND_SOC_SOF_MERRIFIELD); #endif /* CONFIG_SND_SOC_SOF_MERRIFIELD */ @@ -683,6 +763,12 @@ const struct snd_sof_dsp_ops sof_byt_ops = { .ipc_msg_data = intel_ipc_msg_data, .ipc_pcm_params = intel_ipc_pcm_params, + /* machine driver */ + .machine_select = byt_machine_select, + .machine_register = sof_machine_register, + .machine_unregister = sof_machine_unregister, + .set_mach_params = byt_set_mach_params, + /* debug */ .debug_map = byt_debugfs, .debug_map_count = ARRAY_SIZE(byt_debugfs), @@ -708,14 +794,16 @@ const struct snd_sof_dsp_ops sof_byt_ops = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_BATCH, + + .arch_ops = &sof_xtensa_arch_ops, }; -EXPORT_SYMBOL(sof_byt_ops); +EXPORT_SYMBOL_NS(sof_byt_ops, SND_SOC_SOF_BAYTRAIL); const struct sof_intel_dsp_desc byt_chip_info = { .cores_num = 1, .cores_mask = 1, }; -EXPORT_SYMBOL(byt_chip_info); +EXPORT_SYMBOL_NS(byt_chip_info, SND_SOC_SOF_BAYTRAIL); /* cherrytrail and braswell ops */ const struct snd_sof_dsp_ops sof_cht_ops = { @@ -749,6 +837,12 @@ const struct snd_sof_dsp_ops sof_cht_ops = { .ipc_msg_data = intel_ipc_msg_data, .ipc_pcm_params = intel_ipc_pcm_params, + /* machine driver */ + .machine_select = byt_machine_select, + .machine_register = sof_machine_register, + .machine_unregister = sof_machine_unregister, + .set_mach_params = byt_set_mach_params, + /* debug */ .debug_map = cht_debugfs, .debug_map_count = ARRAY_SIZE(cht_debugfs), @@ -775,15 +869,19 @@ const struct snd_sof_dsp_ops sof_cht_ops = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_BATCH, + + .arch_ops = &sof_xtensa_arch_ops, }; -EXPORT_SYMBOL(sof_cht_ops); +EXPORT_SYMBOL_NS(sof_cht_ops, SND_SOC_SOF_BAYTRAIL); const struct sof_intel_dsp_desc cht_chip_info = { .cores_num = 1, .cores_mask = 1, }; -EXPORT_SYMBOL(cht_chip_info); +EXPORT_SYMBOL_NS(cht_chip_info, SND_SOC_SOF_BAYTRAIL); #endif /* CONFIG_SND_SOC_SOF_BAYTRAIL */ MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HIFI_EP_IPC); +MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 0e1e265f3f3b..9e2d8afe0535 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -18,6 +18,7 @@ #include "../ops.h" #include "hda.h" #include "hda-ipc.h" +#include "../sof-audio.h" static const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = { {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS}, @@ -106,10 +107,6 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) "nothing to do in IPC IRQ thread\n"); } - /* re-enable IPC interrupt */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, - HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); - return IRQ_HANDLED; } @@ -231,7 +228,6 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .block_write = sof_block_write, /* doorbell */ - .irq_handler = hda_dsp_ipc_irq_handler, .irq_thread = cnl_ipc_irq_thread, /* ipc */ @@ -243,6 +239,12 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .ipc_msg_data = hda_ipc_msg_data, .ipc_pcm_params = hda_ipc_pcm_params, + /* machine driver */ + .machine_select = hda_machine_select, + .machine_register = sof_machine_register, + .machine_unregister = sof_machine_unregister, + .set_mach_params = hda_set_mach_params, + /* debug */ .debug_map = cnl_dsp_debugfs, .debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs), @@ -295,8 +297,10 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + + .arch_ops = &sof_xtensa_arch_ops, }; -EXPORT_SYMBOL(sof_cnl_ops); +EXPORT_SYMBOL_NS(sof_cnl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); const struct sof_intel_dsp_desc cnl_chip_info = { /* Cannonlake */ @@ -315,7 +319,7 @@ const struct sof_intel_dsp_desc cnl_chip_info = { .ssp_count = CNL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, }; -EXPORT_SYMBOL(cnl_chip_info); +EXPORT_SYMBOL_NS(cnl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); const struct sof_intel_dsp_desc icl_chip_info = { /* Icelake */ @@ -334,7 +338,7 @@ const struct sof_intel_dsp_desc icl_chip_info = { .ssp_count = ICL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, }; -EXPORT_SYMBOL(icl_chip_info); +EXPORT_SYMBOL_NS(icl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); const struct sof_intel_dsp_desc tgl_chip_info = { /* Tigerlake */ @@ -350,7 +354,7 @@ const struct sof_intel_dsp_desc tgl_chip_info = { .ssp_count = ICL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, }; -EXPORT_SYMBOL(tgl_chip_info); +EXPORT_SYMBOL_NS(tgl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); const struct sof_intel_dsp_desc ehl_chip_info = { /* Elkhartlake */ @@ -366,7 +370,7 @@ const struct sof_intel_dsp_desc ehl_chip_info = { .ssp_count = ICL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, }; -EXPORT_SYMBOL(ehl_chip_info); +EXPORT_SYMBOL_NS(ehl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); const struct sof_intel_dsp_desc jsl_chip_info = { /* Jasperlake */ @@ -383,4 +387,4 @@ const struct sof_intel_dsp_desc jsl_chip_info = { .ssp_count = ICL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, }; -EXPORT_SYMBOL(jsl_chip_info); +EXPORT_SYMBOL_NS(jsl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index fbfa225d1c5a..78dfd5f5c034 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -75,16 +75,15 @@ void hda_codec_jack_check(struct snd_sof_dev *sdev) void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev) {} void hda_codec_jack_check(struct snd_sof_dev *sdev) {} #endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */ -EXPORT_SYMBOL(hda_codec_jack_wake_enable); -EXPORT_SYMBOL(hda_codec_jack_check); +EXPORT_SYMBOL_NS(hda_codec_jack_wake_enable, SND_SOC_SOF_HDA_AUDIO_CODEC); +EXPORT_SYMBOL_NS(hda_codec_jack_check, SND_SOC_SOF_HDA_AUDIO_CODEC); /* probe individual codec */ -static int hda_codec_probe(struct snd_sof_dev *sdev, int address) +static int hda_codec_probe(struct snd_sof_dev *sdev, int address, + bool hda_codec_use_common_hdmi) { #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) struct hdac_hda_priv *hda_priv; - struct snd_soc_acpi_mach_params *mach_params = NULL; - struct snd_sof_pdata *pdata = sdev->pdata; #endif struct hda_bus *hbus = sof_to_hbus(sdev); struct hdac_device *hdev; @@ -114,10 +113,6 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) if (ret < 0) return ret; - if (pdata->machine) - mach_params = (struct snd_soc_acpi_mach_params *) - &pdata->machine->mach_params; - if ((resp & 0xFFFF0000) == IDISP_VID_INTEL) hda_priv->need_display_power = true; @@ -125,7 +120,7 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) * if common HDMI codec driver is not used, codec load * is skipped here and hdac_hdmi is used instead */ - if ((mach_params && mach_params->common_hdmi_codec_drv) || + if (hda_codec_use_common_hdmi || (resp & 0xFFFF0000) != IDISP_VID_INTEL) { hdev->type = HDA_DEV_LEGACY; ret = hda_codec_load_module(&hda_priv->codec); @@ -150,7 +145,8 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) } /* Codec initialization */ -int hda_codec_probe_bus(struct snd_sof_dev *sdev) +void hda_codec_probe_bus(struct snd_sof_dev *sdev, + bool hda_codec_use_common_hdmi) { struct hdac_bus *bus = sof_to_bus(sdev); int i, ret; @@ -161,17 +157,15 @@ int hda_codec_probe_bus(struct snd_sof_dev *sdev) if (!(bus->codec_mask & (1 << i))) continue; - ret = hda_codec_probe(sdev, i); + ret = hda_codec_probe(sdev, i, hda_codec_use_common_hdmi); if (ret < 0) { - dev_err(bus->dev, "error: codec #%d probe error, ret: %d\n", - i, ret); - return ret; + dev_warn(bus->dev, "codec #%d probe error, ret: %d\n", + i, ret); + bus->codec_mask &= ~BIT(i); } } - - return 0; } -EXPORT_SYMBOL(hda_codec_probe_bus); +EXPORT_SYMBOL_NS(hda_codec_probe_bus, SND_SOC_SOF_HDA_AUDIO_CODEC); #if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) || \ IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) @@ -183,7 +177,7 @@ void hda_codec_i915_get(struct snd_sof_dev *sdev) dev_dbg(bus->dev, "Turning i915 HDAC power on\n"); snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true); } -EXPORT_SYMBOL(hda_codec_i915_get); +EXPORT_SYMBOL_NS(hda_codec_i915_get, SND_SOC_SOF_HDA_AUDIO_CODEC_I915); void hda_codec_i915_put(struct snd_sof_dev *sdev) { @@ -192,7 +186,7 @@ void hda_codec_i915_put(struct snd_sof_dev *sdev) dev_dbg(bus->dev, "Turning i915 HDAC power off\n"); snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); } -EXPORT_SYMBOL(hda_codec_i915_put); +EXPORT_SYMBOL_NS(hda_codec_i915_put, SND_SOC_SOF_HDA_AUDIO_CODEC_I915); int hda_codec_i915_init(struct snd_sof_dev *sdev) { @@ -208,7 +202,7 @@ int hda_codec_i915_init(struct snd_sof_dev *sdev) return 0; } -EXPORT_SYMBOL(hda_codec_i915_init); +EXPORT_SYMBOL_NS(hda_codec_i915_init, SND_SOC_SOF_HDA_AUDIO_CODEC_I915); int hda_codec_i915_exit(struct snd_sof_dev *sdev) { @@ -221,7 +215,7 @@ int hda_codec_i915_exit(struct snd_sof_dev *sdev) return ret; } -EXPORT_SYMBOL(hda_codec_i915_exit); +EXPORT_SYMBOL_NS(hda_codec_i915_exit, SND_SOC_SOF_HDA_AUDIO_CODEC_I915); #endif diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index df1909e1d950..871b71a15a63 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -15,11 +15,18 @@ * Hardware interface for generic Intel audio DSP HDA IP */ +#include <linux/module.h> #include <sound/hdaudio_ext.h> #include <sound/hda_register.h> #include "../ops.h" #include "hda.h" +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) +static int hda_codec_mask = -1; +module_param_named(codec_mask, hda_codec_mask, int, 0444); +MODULE_PARM_DESC(codec_mask, "SOF HDA codec mask for probing"); +#endif + /* * HDA Operations. */ @@ -206,6 +213,12 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset) bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS); dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask); } + + if (hda_codec_mask != -1) { + bus->codec_mask &= hda_codec_mask; + dev_dbg(bus->dev, "filtered codec_mask = 0x%lx\n", + bus->codec_mask); + } #endif /* clear stream status */ diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 896d21984b73..9c6e3f990ee3 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -11,6 +11,7 @@ #include <sound/pcm_params.h> #include <sound/hdaudio_ext.h> #include "../sof-priv.h" +#include "../sof-audio.h" #include "hda.h" #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) @@ -261,14 +262,11 @@ static int hda_link_pcm_prepare(struct snd_pcm_substream *substream, { struct hdac_ext_stream *link_dev = snd_soc_dai_get_dma_data(dai, substream); - struct sof_intel_hda_stream *hda_stream; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); int stream = substream->stream; - hda_stream = hstream_to_sof_hda_stream(link_dev); - if (link_dev->link_prepared) return 0; @@ -447,6 +445,10 @@ struct snd_soc_dai_driver skl_dai[] = { .ops = &hda_link_dai_ops, }, { + .name = "iDisp4 Pin", + .ops = &hda_link_dai_ops, +}, +{ .name = "Analog CPU DAI", .ops = &hda_link_dai_ops, }, diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 0fd2153c1769..1837f66e361f 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -230,22 +230,15 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) "nothing to do in IPC IRQ thread\n"); } - /* re-enable IPC interrupt */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, - HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); - return IRQ_HANDLED; } -/* is this IRQ for ADSP ? - we only care about IPC here */ -irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context) +/* Check if an IPC IRQ occurred */ +bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev) { - struct snd_sof_dev *sdev = context; - int ret = IRQ_NONE; + bool ret = false; u32 irq_status; - spin_lock(&sdev->hw_lock); - /* store status */ irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS); dev_vdbg(sdev->dev, "irq handler: irq_status:0x%x\n", irq_status); @@ -255,16 +248,10 @@ irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context) goto out; /* IPC message ? */ - if (irq_status & HDA_DSP_ADSPIS_IPC) { - /* disable IPC interrupt */ - snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, - HDA_DSP_REG_ADSPIC, - HDA_DSP_ADSPIC_IPC, 0); - ret = IRQ_WAKE_THREAD; - } + if (irq_status & HDA_DSP_ADSPIS_IPC) + ret = true; out: - spin_unlock(&sdev->hw_lock); return ret; } diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index bae7ac3581e5..8852184a2569 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -295,7 +295,6 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) /* init for booting wait */ init_waitqueue_head(&sdev->boot_wait); - sdev->boot_complete = false; /* prepare DMA for code loader stream */ tag = cl_stream_prepare(sdev, 0x40, stripped_firmware.size, diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 575f5f5877d8..23872f6e708d 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -17,6 +17,7 @@ #include <sound/hda_register.h> #include <sound/pcm_params.h> +#include "../sof-audio.h" #include "../ops.h" #include "hda.h" @@ -147,12 +148,13 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *scomp = sdev->component; struct hdac_stream *hstream = substream->runtime->private_data; struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct snd_sof_pcm *spcm; snd_pcm_uframes_t pos; - spcm = snd_sof_find_spcm_dai(sdev, rtd); + spcm = snd_sof_find_spcm_dai(scomp, rtd); if (!spcm) { dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", rtd->dai_link->id); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 29ab43281670..c0ab9bb2a797 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -20,6 +20,7 @@ #include <sound/hda_register.h> #include <sound/sof.h> #include "../ops.h" +#include "../sof-audio.h" #include "hda.h" /* @@ -549,22 +550,23 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, return 0; } -irqreturn_t hda_dsp_stream_interrupt(int irq, void *context) +bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev) { - struct hdac_bus *bus = context; - int ret = IRQ_WAKE_THREAD; + struct hdac_bus *bus = sof_to_bus(sdev); + bool ret = false; u32 status; - spin_lock(&bus->reg_lock); + /* The function can be called at irq thread, so use spin_lock_irq */ + spin_lock_irq(&bus->reg_lock); status = snd_hdac_chip_readl(bus, INTSTS); dev_vdbg(bus->dev, "stream irq, INTSTS status: 0x%x\n", status); - /* Register inaccessible, ignore it.*/ - if (status == 0xffffffff) - ret = IRQ_NONE; + /* if Register inaccessible, ignore it.*/ + if (status != 0xffffffff) + ret = true; - spin_unlock(&bus->reg_lock); + spin_unlock_irq(&bus->reg_lock); return ret; } @@ -602,7 +604,8 @@ static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status) irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) { - struct hdac_bus *bus = context; + struct snd_sof_dev *sdev = context; + struct hdac_bus *bus = sof_to_bus(sdev); #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) u32 rirb_status; #endif diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 91bd88fddac7..d08462f481de 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -168,7 +168,7 @@ void hda_dsp_dump_skl(struct snd_sof_dev *sdev, u32 flags) panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_ADSP_ERROR_CODE_SKL + 0x4); - if (sdev->boot_complete) { + if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) { hda_dsp_get_registers(sdev, &xoops, &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, @@ -195,7 +195,7 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) HDA_DSP_SRAM_REG_FW_STATUS); panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP); - if (sdev->boot_complete) { + if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) { hda_dsp_get_registers(sdev, &xoops, &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, @@ -344,16 +344,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) struct hdac_bus *bus = sof_to_bus(sdev); #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) struct hdac_ext_link *hlink; - struct snd_soc_acpi_mach_params *mach_params; - struct snd_soc_acpi_mach *hda_mach; - struct snd_sof_pdata *pdata = sdev->pdata; - struct snd_soc_acpi_mach *mach; - const char *tplg_filename; - const char *idisp_str; - const char *dmic_str; - int dmic_num; - int codec_num = 0; - int i; #endif int ret = 0; @@ -387,95 +377,8 @@ static int hda_init_caps(struct snd_sof_dev *sdev) if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(bus); - /* codec detection */ - if (!bus->codec_mask) { - dev_info(bus->dev, "no hda codecs found!\n"); - } else { - dev_info(bus->dev, "hda codecs found, mask %lx\n", - bus->codec_mask); - - for (i = 0; i < HDA_MAX_CODECS; i++) { - if (bus->codec_mask & (1 << i)) - codec_num++; - } - - /* - * If no machine driver is found, then: - * - * hda machine driver is used if : - * 1. there is one HDMI codec and one external HDAudio codec - * 2. only HDMI codec - */ - if (!pdata->machine && codec_num <= 2 && - HDA_IDISP_CODEC(bus->codec_mask)) { - hda_mach = snd_soc_acpi_intel_hda_machines; - pdata->machine = hda_mach; - - /* topology: use the info from hda_machines */ - pdata->tplg_filename = - hda_mach->sof_tplg_filename; - - /* - * firmware: pick the first in machine list, - * or use nocodec firmware name if list is empty - */ - mach = pdata->desc->machines; - if (mach->id[0]) - pdata->fw_filename = mach->sof_fw_filename; - else - pdata->fw_filename = - pdata->desc->nocodec_fw_filename; - - dev_info(bus->dev, "using HDA machine driver %s now\n", - hda_mach->drv_name); - - if (codec_num == 1) - idisp_str = "-idisp"; - else - idisp_str = ""; - - /* first check NHLT for DMICs */ - dmic_num = check_nhlt_dmic(sdev); - - /* allow for module parameter override */ - if (hda_dmic_num != -1) - dmic_num = hda_dmic_num; - - switch (dmic_num) { - case 2: - dmic_str = "-2ch"; - break; - case 4: - dmic_str = "-4ch"; - break; - default: - dmic_num = 0; - dmic_str = ""; - break; - } - - tplg_filename = pdata->tplg_filename; - tplg_filename = fixup_tplg_name(sdev, tplg_filename, - idisp_str, dmic_str); - if (!tplg_filename) { - hda_codec_i915_exit(sdev); - return ret; - } - pdata->tplg_filename = tplg_filename; - } - } - - /* used by hda machine driver to create dai links */ - if (pdata->machine) { - mach_params = (struct snd_soc_acpi_mach_params *) - &pdata->machine->mach_params; - mach_params->codec_mask = bus->codec_mask; - mach_params->platform = dev_name(sdev->dev); - mach_params->common_hdmi_codec_drv = hda_codec_use_common_hdmi; - } - /* create codec instances */ - hda_codec_probe_bus(sdev); + hda_codec_probe_bus(sdev, hda_codec_use_common_hdmi); hda_codec_i915_put(sdev); @@ -499,6 +402,49 @@ static const struct sof_intel_dsp_desc return chip_info; } +static irqreturn_t hda_dsp_interrupt_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = context; + + /* + * Get global interrupt status. It includes all hardware interrupt + * sources in the Intel HD Audio controller. + */ + if (snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS) & + SOF_HDA_INTSTS_GIS) { + + /* disable GIE interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + SOF_HDA_INTCTL, + SOF_HDA_INT_GLOBAL_EN, + 0); + + return IRQ_WAKE_THREAD; + } + + return IRQ_NONE; +} + +static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = context; + + /* deal with streams and controller first */ + if (hda_dsp_check_stream_irq(sdev)) + hda_dsp_stream_threaded_handler(irq, sdev); + + if (hda_dsp_check_ipc_irq(sdev)) + sof_ops(sdev)->irq_thread(irq, sdev); + + /* enable GIE interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + SOF_HDA_INTCTL, + SOF_HDA_INT_GLOBAL_EN, + SOF_HDA_INT_GLOBAL_EN); + + return IRQ_HANDLED; +} + int hda_dsp_probe(struct snd_sof_dev *sdev) { struct pci_dev *pci = to_pci_dev(sdev->dev); @@ -603,9 +549,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) */ if (hda_use_msi && pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI) > 0) { dev_info(sdev->dev, "use msi interrupt mode\n"); - hdev->irq = pci_irq_vector(pci, 0); - /* ipc irq number is the same of hda irq */ - sdev->ipc_irq = hdev->irq; + sdev->ipc_irq = pci_irq_vector(pci, 0); /* initialised to "false" by kzalloc() */ sdev->msi_enabled = true; } @@ -616,28 +560,17 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) * in IO-APIC mode, hda->irq and ipc_irq are using the same * irq number of pci->irq */ - hdev->irq = pci->irq; sdev->ipc_irq = pci->irq; } - dev_dbg(sdev->dev, "using HDA IRQ %d\n", hdev->irq); - ret = request_threaded_irq(hdev->irq, hda_dsp_stream_interrupt, - hda_dsp_stream_threaded_handler, - IRQF_SHARED, "AudioHDA", bus); - if (ret < 0) { - dev_err(sdev->dev, "error: failed to register HDA IRQ %d\n", - hdev->irq); - goto free_irq_vector; - } - dev_dbg(sdev->dev, "using IPC IRQ %d\n", sdev->ipc_irq); - ret = request_threaded_irq(sdev->ipc_irq, hda_dsp_ipc_irq_handler, - sof_ops(sdev)->irq_thread, IRQF_SHARED, - "AudioDSP", sdev); + ret = request_threaded_irq(sdev->ipc_irq, hda_dsp_interrupt_handler, + hda_dsp_interrupt_thread, + IRQF_SHARED, "AudioDSP", sdev); if (ret < 0) { dev_err(sdev->dev, "error: failed to register IPC IRQ %d\n", sdev->ipc_irq); - goto free_hda_irq; + goto free_irq_vector; } pci_set_master(pci); @@ -668,8 +601,6 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) free_ipc_irq: free_irq(sdev->ipc_irq, sdev); -free_hda_irq: - free_irq(hdev->irq, bus); free_irq_vector: if (sdev->msi_enabled) pci_free_irq_vectors(pci); @@ -715,7 +646,6 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) SOF_HDA_PPCTL_GPROCEN, 0); free_irq(sdev->ipc_irq, sdev); - free_irq(hda->irq, bus); if (sdev->msi_enabled) pci_free_irq_vectors(pci); @@ -735,4 +665,136 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) return 0; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) +static int hda_generic_machine_select(struct snd_sof_dev *sdev) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + struct snd_soc_acpi_mach_params *mach_params; + struct snd_soc_acpi_mach *hda_mach; + struct snd_sof_pdata *pdata = sdev->pdata; + const char *tplg_filename; + const char *idisp_str; + const char *dmic_str; + int dmic_num = 0; + int codec_num = 0; + int i; + + /* codec detection */ + if (!bus->codec_mask) { + dev_info(bus->dev, "no hda codecs found!\n"); + } else { + dev_info(bus->dev, "hda codecs found, mask %lx\n", + bus->codec_mask); + + for (i = 0; i < HDA_MAX_CODECS; i++) { + if (bus->codec_mask & (1 << i)) + codec_num++; + } + + /* + * If no machine driver is found, then: + * + * hda machine driver is used if : + * 1. there is one HDMI codec and one external HDAudio codec + * 2. only HDMI codec + */ + if (!pdata->machine && codec_num <= 2 && + HDA_IDISP_CODEC(bus->codec_mask)) { + hda_mach = snd_soc_acpi_intel_hda_machines; + + /* topology: use the info from hda_machines */ + pdata->tplg_filename = + hda_mach->sof_tplg_filename; + + dev_info(bus->dev, "using HDA machine driver %s now\n", + hda_mach->drv_name); + + if (codec_num == 1) + idisp_str = "-idisp"; + else + idisp_str = ""; + + /* first check NHLT for DMICs */ + dmic_num = check_nhlt_dmic(sdev); + + /* allow for module parameter override */ + if (hda_dmic_num != -1) + dmic_num = hda_dmic_num; + + switch (dmic_num) { + case 2: + dmic_str = "-2ch"; + break; + case 4: + dmic_str = "-4ch"; + break; + default: + dmic_num = 0; + dmic_str = ""; + break; + } + + tplg_filename = pdata->tplg_filename; + tplg_filename = fixup_tplg_name(sdev, tplg_filename, + idisp_str, dmic_str); + if (!tplg_filename) + return -EINVAL; + + pdata->machine = hda_mach; + pdata->tplg_filename = tplg_filename; + } + } + + /* used by hda machine driver to create dai links */ + if (pdata->machine) { + mach_params = (struct snd_soc_acpi_mach_params *) + &pdata->machine->mach_params; + mach_params->codec_mask = bus->codec_mask; + mach_params->common_hdmi_codec_drv = hda_codec_use_common_hdmi; + mach_params->dmic_num = dmic_num; + } + + return 0; +} +#else +static int hda_generic_machine_select(struct snd_sof_dev *sdev) +{ + return 0; +} +#endif + +void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, + struct device *dev) +{ + struct snd_soc_acpi_mach_params *mach_params; + + mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params->platform = dev_name(dev); +} + +void hda_machine_select(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *sof_pdata = sdev->pdata; + const struct sof_dev_desc *desc = sof_pdata->desc; + struct snd_soc_acpi_mach *mach; + + mach = snd_soc_acpi_find_machine(desc->machines); + if (mach) { + sof_pdata->tplg_filename = mach->sof_tplg_filename; + sof_pdata->machine = mach; + } + + /* + * Choose HDA generic machine driver if mach is NULL. + * Otherwise, set certain mach params. + */ + hda_generic_machine_select(sdev); + + if (!sof_pdata->machine) + dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); +} + MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_HDA_AUDIO_CODEC); +MODULE_IMPORT_NS(SND_SOC_SOF_HDA_AUDIO_CODEC_I915); +MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 18d7e72bf9b7..a4d030bfeee1 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -43,11 +43,14 @@ /* SOF_HDA_GCTL register bist */ #define SOF_HDA_GCTL_RESET BIT(0) -/* SOF_HDA_INCTL and SOF_HDA_INTSTS regs */ +/* SOF_HDA_INCTL regs */ #define SOF_HDA_INT_GLOBAL_EN BIT(31) #define SOF_HDA_INT_CTRL_EN BIT(30) #define SOF_HDA_INT_ALL_STREAM 0xff +/* SOF_HDA_INTSTS regs */ +#define SOF_HDA_INTSTS_GIS BIT(31) + #define SOF_HDA_MAX_CAPS 10 #define SOF_HDA_CAP_ID_OFF 16 #define SOF_HDA_CAP_ID_MASK GENMASK(SOF_HDA_CAP_ID_OFF + 11,\ @@ -345,7 +348,7 @@ /* Number of DAIs */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) -#define SOF_SKL_NUM_DAIS 14 +#define SOF_SKL_NUM_DAIS 15 #else #define SOF_SKL_NUM_DAIS 8 #endif @@ -406,8 +409,6 @@ struct sof_intel_hda_dev { /* the maximum number of streams (playback + capture) supported */ u32 stream_max; - int irq; - /* PM related */ bool l1_support_changed;/* during suspend, is L1SEN changed or not */ @@ -511,11 +512,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params); int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, int cmd); -irqreturn_t hda_dsp_stream_interrupt(int irq, void *context); irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context); int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct hdac_stream *stream); +bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev); +bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev); struct hdac_ext_stream * hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction); @@ -540,7 +542,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev); int hda_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev); int hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id); -irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context); irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context); int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir); @@ -574,7 +575,8 @@ void sof_hda_bus_init(struct hdac_bus *bus, struct device *dev); /* * HDA Codec operations. */ -int hda_codec_probe_bus(struct snd_sof_dev *sdev); +void hda_codec_probe_bus(struct snd_sof_dev *sdev, + bool hda_codec_use_common_hdmi); void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev); void hda_codec_jack_check(struct snd_sof_dev *sdev); @@ -622,4 +624,9 @@ extern const struct sof_intel_dsp_desc tgl_chip_info; extern const struct sof_intel_dsp_desc ehl_chip_info; extern const struct sof_intel_dsp_desc jsl_chip_info; +/* machine driver select */ +void hda_machine_select(struct snd_sof_dev *sdev); +void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, + struct device *dev); + #endif diff --git a/sound/soc/sof/intel/intel-ipc.c b/sound/soc/sof/intel/intel-ipc.c index 4edd92151fd5..e935f70d611b 100644 --- a/sound/soc/sof/intel/intel-ipc.c +++ b/sound/soc/sof/intel/intel-ipc.c @@ -39,7 +39,7 @@ void intel_ipc_msg_data(struct snd_sof_dev *sdev, sof_mailbox_read(sdev, stream->posn_offset, p, sz); } } -EXPORT_SYMBOL(intel_ipc_msg_data); +EXPORT_SYMBOL_NS(intel_ipc_msg_data, SND_SOC_SOF_INTEL_HIFI_EP_IPC); int intel_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, @@ -60,7 +60,7 @@ int intel_ipc_pcm_params(struct snd_sof_dev *sdev, return 0; } -EXPORT_SYMBOL(intel_ipc_pcm_params); +EXPORT_SYMBOL_NS(intel_ipc_pcm_params, SND_SOC_SOF_INTEL_HIFI_EP_IPC); int intel_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) @@ -75,7 +75,7 @@ int intel_pcm_open(struct snd_sof_dev *sdev, return 0; } -EXPORT_SYMBOL(intel_pcm_open); +EXPORT_SYMBOL_NS(intel_pcm_open, SND_SOC_SOF_INTEL_HIFI_EP_IPC); int intel_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) @@ -87,6 +87,6 @@ int intel_pcm_close(struct snd_sof_dev *sdev, return 0; } -EXPORT_SYMBOL(intel_pcm_close); +EXPORT_SYMBOL_NS(intel_pcm_close, SND_SOC_SOF_INTEL_HIFI_EP_IPC); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h index f7a3f62e45d4..daaf3364c177 100644 --- a/sound/soc/sof/intel/shim.h +++ b/sound/soc/sof/intel/shim.h @@ -12,7 +12,7 @@ #define __SOF_INTEL_SHIM_H /* - * SHIM registers for BYT, BSW, CHT, HSW, BDW + * SHIM registers for BYT, BSW, CHT, BDW */ #define SHIM_CSR (SHIM_OFFSET + 0x00) @@ -38,7 +38,7 @@ #define SHIM_PWMCTRL 0x1000 /* - * SST SHIM register bits for BYT, BSW, CHT HSW, BDW + * SST SHIM register bits for BYT, BSW, CHT, BDW * Register bit naming and functionaility can differ between devices. */ @@ -169,13 +169,11 @@ struct sof_intel_dsp_desc { extern const struct snd_sof_dsp_ops sof_tng_ops; extern const struct snd_sof_dsp_ops sof_byt_ops; extern const struct snd_sof_dsp_ops sof_cht_ops; -extern const struct snd_sof_dsp_ops sof_hsw_ops; extern const struct snd_sof_dsp_ops sof_bdw_ops; extern const struct sof_intel_dsp_desc byt_chip_info; extern const struct sof_intel_dsp_desc cht_chip_info; extern const struct sof_intel_dsp_desc bdw_chip_info; -extern const struct sof_intel_dsp_desc hsw_chip_info; extern const struct sof_intel_dsp_desc tng_chip_info; struct sof_intel_stream { diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 5fdfbaa8c4ed..b63fc529b456 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -15,6 +15,7 @@ #include <linux/types.h> #include "sof-priv.h" +#include "sof-audio.h" #include "ops.h" static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id); @@ -346,19 +347,12 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) break; case SOF_IPC_FW_READY: /* check for FW boot completion */ - if (!sdev->boot_complete) { + if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) { err = sof_ops(sdev)->fw_ready(sdev, cmd); - if (err < 0) { - /* - * this indicates a mismatch in ABI - * between the driver and fw - */ - dev_err(sdev->dev, "error: ABI mismatch %d\n", - err); - } else { - /* firmware boot completed OK */ - sdev->boot_complete = true; - } + if (err < 0) + sdev->fw_state = SOF_FW_BOOT_READY_FAILED; + else + sdev->fw_state = SOF_FW_BOOT_COMPLETE; /* wake up firmware loader */ wake_up(&sdev->boot_wait); @@ -412,12 +406,13 @@ static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id) static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) { + struct snd_soc_component *scomp = sdev->component; struct snd_sof_pcm_stream *stream; struct sof_ipc_stream_posn posn; struct snd_sof_pcm *spcm; int direction; - spcm = snd_sof_find_spcm_comp(sdev, msg_id, &direction); + spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); if (!spcm) { dev_err(sdev->dev, "error: period elapsed for unknown stream, msg_id %d\n", @@ -441,12 +436,13 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) /* DSP notifies host of an XRUN within FW */ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) { + struct snd_soc_component *scomp = sdev->component; struct snd_sof_pcm_stream *stream; struct sof_ipc_stream_posn posn; struct snd_sof_pcm *spcm; int direction; - spcm = snd_sof_find_spcm_comp(sdev, msg_id, &direction); + spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); if (!spcm) { dev_err(sdev->dev, "error: XRUN for unknown stream, msg_id %d\n", msg_id); @@ -488,10 +484,11 @@ static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd) } /* get stream position IPC - use faster MMIO method if available on platform */ -int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev, +int snd_sof_ipc_stream_posn(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, int direction, struct sof_ipc_stream_posn *posn) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc_stream stream; int err; @@ -620,15 +617,15 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, /* * IPC get()/set() for kcontrols. */ -int snd_sof_ipc_set_get_comp_data(struct snd_sof_ipc *ipc, - struct snd_sof_control *scontrol, +int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, u32 ipc_cmd, enum sof_ipc_ctrl_type ctrl_type, enum sof_ipc_ctrl_cmd ctrl_cmd, bool send) { + struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; - struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc_fw_ready *ready = &sdev->fw_ready; struct sof_ipc_fw_version *v = &ready->version; struct sof_ipc_ctrl_data_params sparams; diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 432d12bd4937..fc4ab51bacf4 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -32,6 +32,42 @@ static int get_ext_windows(struct snd_sof_dev *sdev, return 0; } +static int get_cc_info(struct snd_sof_dev *sdev, + struct sof_ipc_ext_data_hdr *ext_hdr) +{ + int ret; + + struct sof_ipc_cc_version *cc = + container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr); + + dev_dbg(sdev->dev, "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n", + cc->name, cc->major, cc->minor, cc->micro, cc->desc, + cc->optim); + + /* create read-only cc_version debugfs to store compiler version info */ + /* use local copy of the cc_version to prevent data corruption */ + if (sdev->first_boot) { + sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size, + GFP_KERNEL); + + if (!sdev->cc_version) + return -ENOMEM; + + memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size); + ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version, + cc->ext_hdr.hdr.size, + "cc_version", 0444); + + /* errors are only due to memory allocation, not debugfs */ + if (ret < 0) { + dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n"); + return ret; + } + } + + return 0; +} + /* parse the extended FW boot data structures from FW boot message */ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) { @@ -65,6 +101,9 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) case SOF_IPC_EXT_WINDOW: ret = get_ext_windows(sdev, ext_hdr); break; + case SOF_IPC_EXT_CC_INFO: + ret = get_cc_info(sdev, ext_hdr); + break; default: dev_warn(sdev->dev, "warning: unknown ext header type %d size 0x%x\n", ext_hdr->type, ext_hdr->hdr.size); @@ -448,6 +487,9 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: request firmware %s failed err: %d\n", fw_filename, ret); + } else { + dev_dbg(sdev->dev, "request_firmware %s successful\n", + fw_filename); } kfree(fw_filename); @@ -512,7 +554,6 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) int init_core_mask; init_waitqueue_head(&sdev->boot_wait); - sdev->boot_complete = false; /* create read-only fw_version debugfs to store boot version info */ if (sdev->first_boot) { @@ -544,19 +585,27 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) init_core_mask = ret; - /* now wait for the DSP to boot */ - ret = wait_event_timeout(sdev->boot_wait, sdev->boot_complete, + /* + * now wait for the DSP to boot. There are 3 possible outcomes: + * 1. Boot wait times out indicating FW boot failure. + * 2. FW boots successfully and fw_ready op succeeds. + * 3. FW boots but fw_ready op fails. + */ + ret = wait_event_timeout(sdev->boot_wait, + sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, msecs_to_jiffies(sdev->boot_timeout)); if (ret == 0) { dev_err(sdev->dev, "error: firmware boot failure\n"); snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX | SOF_DBG_TEXT | SOF_DBG_PCI); - /* after this point FW_READY msg should be ignored */ - sdev->boot_complete = true; + sdev->fw_state = SOF_FW_BOOT_FAILED; return -EIO; } - dev_info(sdev->dev, "firmware boot complete\n"); + if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) + dev_info(sdev->dev, "firmware boot complete\n"); + else + return -EIO; /* FW boots but fw_ready op failed */ /* perform post fw run operations */ ret = snd_sof_dsp_post_fw_run(sdev); diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index 3d128e5a132c..2233146386cc 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -63,23 +63,11 @@ static int sof_nocodec_bes_setup(struct device *dev, } int sof_nocodec_setup(struct device *dev, - struct snd_sof_pdata *sof_pdata, - struct snd_soc_acpi_mach *mach, - const struct sof_dev_desc *desc, const struct snd_sof_dsp_ops *ops) { struct snd_soc_dai_link *links; int ret; - if (!mach) - return -EINVAL; - - sof_pdata->drv_name = "sof-nocodec"; - - mach->drv_name = "sof-nocodec"; - sof_pdata->fw_filename = desc->nocodec_fw_filename; - sof_pdata->tplg_filename = desc->nocodec_tplg_filename; - /* create dummy BE dai_links */ links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * ops->num_drv, GFP_KERNEL); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 93512dcbaacd..e929a6e0058f 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -391,6 +391,40 @@ snd_sof_pcm_platform_pointer(struct snd_sof_dev *sdev, return 0; } +/* machine driver */ +static inline int +snd_sof_machine_register(struct snd_sof_dev *sdev, void *pdata) +{ + if (sof_ops(sdev) && sof_ops(sdev)->machine_register) + return sof_ops(sdev)->machine_register(sdev, pdata); + + return 0; +} + +static inline void +snd_sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata) +{ + if (sof_ops(sdev) && sof_ops(sdev)->machine_unregister) + sof_ops(sdev)->machine_unregister(sdev, pdata); +} + +static inline void +snd_sof_machine_select(struct snd_sof_dev *sdev) +{ + if (sof_ops(sdev) && sof_ops(sdev)->machine_select) + sof_ops(sdev)->machine_select(sdev); +} + +static inline void +snd_sof_set_mach_params(const struct snd_soc_acpi_mach *mach, + struct device *dev) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + + if (sof_ops(sdev) && sof_ops(sdev)->set_mach_params) + sof_ops(sdev)->set_mach_params(mach, dev); +} + static inline const struct snd_sof_dsp_ops *sof_get_ops(const struct sof_dev_desc *d, const struct sof_ops_table mach_ops[], int asize) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 549238a98b2a..314f3095c12f 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -14,38 +14,38 @@ #include <sound/pcm_params.h> #include <sound/sof.h> #include "sof-priv.h" +#include "sof-audio.h" #include "ops.h" -#define DRV_NAME "sof-audio-component" - /* Create DMA buffer page table for DSP */ static int create_page_table(struct snd_soc_component *component, struct snd_pcm_substream *substream, unsigned char *dma_area, size_t size) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm; struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); int stream = substream->stream; - spcm = snd_sof_find_spcm_dai(sdev, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return -EINVAL; - return snd_sof_create_page_table(sdev, dmab, + return snd_sof_create_page_table(component->dev, dmab, spcm->stream[stream].page_table.area, size); } static int sof_pcm_dsp_params(struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream, const struct sof_ipc_pcm_params_reply *reply) { - struct snd_sof_dev *sdev = spcm->sdev; + struct snd_soc_component *scomp = spcm->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + /* validate offset */ int ret = snd_sof_ipc_pcm_params(sdev, substream, reply); if (ret < 0) - dev_err(sdev->dev, "error: got wrong reply for PCM %d\n", + dev_err(scomp->dev, "error: got wrong reply for PCM %d\n", spcm->pcm.pcm_id); return ret; @@ -70,13 +70,12 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); struct snd_sof_pcm *spcm; - spcm = snd_sof_find_spcm_dai(sdev, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { - dev_err(sdev->dev, + dev_err(component->dev, "error: period elapsed for unknown stream!\n"); return; } @@ -93,7 +92,27 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); -/* this may get called several times by oss emulation */ +static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, + struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm) +{ + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + int ret; + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; + stream.comp_id = spcm->stream[substream->stream].comp_id; + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); + if (!ret) + spcm->prepared[substream->stream] = false; + + return ret; +} + static int sof_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -110,29 +129,27 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, if (rtd->dai_link->no_pcm) return 0; - spcm = snd_sof_find_spcm_dai(sdev, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return -EINVAL; - dev_dbg(sdev->dev, "pcm: hw params stream %d dir %d\n", + /* + * Handle repeated calls to hw_params() without free_pcm() in + * between. At least ALSA OSS emulation depends on this. + */ + if (spcm->prepared[substream->stream]) { + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + return ret; + } + + dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); memset(&pcm, 0, sizeof(pcm)); - /* allocate audio buffer pages */ - ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); - if (ret < 0) { - dev_err(sdev->dev, "error: could not allocate %d bytes for PCM %d\n", - params_buffer_bytes(params), spcm->pcm.pcm_id); - return ret; - } - if (ret) { - /* - * ret == 1 means the buffer is changed - * create compressed page table for audio firmware - * ret == 0 means the buffer is not changed - * so no need to regenerate the page table - */ + /* create compressed page table for audio firmware */ + if (runtime->buffer_changed) { ret = create_page_table(component, substream, runtime->dma_area, runtime->dma_bytes); if (ret < 0) @@ -187,17 +204,17 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, params, &pcm.params); if (ret < 0) { - dev_err(sdev->dev, "error: platform hw params failed\n"); + dev_err(component->dev, "error: platform hw params failed\n"); return ret; } - dev_dbg(sdev->dev, "stream_tag %d", pcm.params.stream_tag); + dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag); /* send IPC to the DSP */ ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), &ipc_params_reply, sizeof(ipc_params_reply)); if (ret < 0) { - dev_err(sdev->dev, "error: hw params ipc failed for stream %d\n", + dev_err(component->dev, "error: hw params ipc failed for stream %d\n", pcm.params.stream_tag); return ret; } @@ -214,27 +231,6 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, return ret; } -static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, - struct snd_sof_dev *sdev, - struct snd_sof_pcm *spcm) -{ - struct sof_ipc_stream stream; - struct sof_ipc_reply reply; - int ret; - - stream.hdr.size = sizeof(stream); - stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; - stream.comp_id = spcm->stream[substream->stream].comp_id; - - /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, - sizeof(stream), &reply, sizeof(reply)); - if (!ret) - spcm->prepared[substream->stream] = false; - - return ret; -} - static int sof_pcm_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -247,12 +243,12 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, if (rtd->dai_link->no_pcm) return 0; - spcm = snd_sof_find_spcm_dai(sdev, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return -EINVAL; - dev_dbg(sdev->dev, "pcm: free stream %d dir %d\n", spcm->pcm.pcm_id, - substream->stream); + dev_dbg(component->dev, "pcm: free stream %d dir %d\n", + spcm->pcm.pcm_id, substream->stream); if (spcm->prepared[substream->stream]) { ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); @@ -260,13 +256,11 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, err = ret; } - snd_pcm_lib_free_pages(substream); - cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); ret = snd_sof_pcm_platform_hw_free(sdev, substream); if (ret < 0) { - dev_err(sdev->dev, "error: platform hw free failed\n"); + dev_err(component->dev, "error: platform hw free failed\n"); err = ret; } @@ -277,7 +271,6 @@ static int sof_pcm_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm; int ret; @@ -285,21 +278,22 @@ static int sof_pcm_prepare(struct snd_soc_component *component, if (rtd->dai_link->no_pcm) return 0; - spcm = snd_sof_find_spcm_dai(sdev, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return -EINVAL; if (spcm->prepared[substream->stream]) return 0; - dev_dbg(sdev->dev, "pcm: prepare stream %d dir %d\n", spcm->pcm.pcm_id, - substream->stream); + dev_dbg(component->dev, "pcm: prepare stream %d dir %d\n", + spcm->pcm.pcm_id, substream->stream); /* set hw_params */ ret = sof_pcm_hw_params(component, substream, &spcm->params[substream->stream]); if (ret < 0) { - dev_err(sdev->dev, "error: set pcm hw_params after resume\n"); + dev_err(component->dev, + "error: set pcm hw_params after resume\n"); return ret; } @@ -326,11 +320,11 @@ static int sof_pcm_trigger(struct snd_soc_component *component, if (rtd->dai_link->no_pcm) return 0; - spcm = snd_sof_find_spcm_dai(sdev, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return -EINVAL; - dev_dbg(sdev->dev, "pcm: trigger stream %d dir %d cmd %d\n", + dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n", spcm->pcm.pcm_id, substream->stream, cmd); stream.hdr.size = sizeof(stream); @@ -359,7 +353,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, /* set up hw_params */ ret = sof_pcm_prepare(component, substream); if (ret < 0) { - dev_err(sdev->dev, + dev_err(component->dev, "error: failed to set up hw_params upon resume\n"); return ret; } @@ -396,7 +390,8 @@ static int sof_pcm_trigger(struct snd_soc_component *component, reset_hw_params = true; break; default: - dev_err(sdev->dev, "error: unhandled trigger cmd %d\n", cmd); + dev_err(component->dev, "error: unhandled trigger cmd %d\n", + cmd); return -EINVAL; } @@ -438,7 +433,7 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, if (sof_ops(sdev)->pcm_pointer) return sof_ops(sdev)->pcm_pointer(sdev, substream); - spcm = snd_sof_find_spcm_dai(sdev, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return -EINVAL; @@ -448,7 +443,8 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, dai = bytes_to_frames(substream->runtime, spcm->stream[substream->stream].posn.dai_posn); - dev_dbg(sdev->dev, "PCM: stream %d dir %d DMA position %lu DAI position %lu\n", + dev_dbg(component->dev, + "PCM: stream %d dir %d DMA position %lu DAI position %lu\n", spcm->pcm.pcm_id, substream->stream, host, dai); return host; @@ -469,12 +465,12 @@ static int sof_pcm_open(struct snd_soc_component *component, if (rtd->dai_link->no_pcm) return 0; - spcm = snd_sof_find_spcm_dai(sdev, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return -EINVAL; - dev_dbg(sdev->dev, "pcm: open stream %d dir %d\n", spcm->pcm.pcm_id, - substream->stream); + dev_dbg(component->dev, "pcm: open stream %d dir %d\n", + spcm->pcm.pcm_id, substream->stream); INIT_WORK(&spcm->stream[substream->stream].period_elapsed_work, sof_pcm_period_elapsed_work); @@ -504,13 +500,13 @@ static int sof_pcm_open(struct snd_soc_component *component, */ runtime->hw.buffer_bytes_max = le32_to_cpu(caps->buffer_size_max); - dev_dbg(sdev->dev, "period min %zd max %zd bytes\n", + dev_dbg(component->dev, "period min %zd max %zd bytes\n", runtime->hw.period_bytes_min, runtime->hw.period_bytes_max); - dev_dbg(sdev->dev, "period count %d max %d\n", + dev_dbg(component->dev, "period count %d max %d\n", runtime->hw.periods_min, runtime->hw.periods_max); - dev_dbg(sdev->dev, "buffer max %zd bytes\n", + dev_dbg(component->dev, "buffer max %zd bytes\n", runtime->hw.buffer_bytes_max); /* set wait time - TODO: come from topology */ @@ -523,7 +519,7 @@ static int sof_pcm_open(struct snd_soc_component *component, ret = snd_sof_pcm_platform_open(sdev, substream); if (ret < 0) - dev_err(sdev->dev, "error: pcm open failed %d\n", ret); + dev_err(component->dev, "error: pcm open failed %d\n", ret); return ret; } @@ -540,16 +536,16 @@ static int sof_pcm_close(struct snd_soc_component *component, if (rtd->dai_link->no_pcm) return 0; - spcm = snd_sof_find_spcm_dai(sdev, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return -EINVAL; - dev_dbg(sdev->dev, "pcm: close stream %d dir %d\n", spcm->pcm.pcm_id, - substream->stream); + dev_dbg(component->dev, "pcm: close stream %d dir %d\n", + spcm->pcm.pcm_id, substream->stream); err = snd_sof_pcm_platform_close(sdev, substream); if (err < 0) { - dev_err(sdev->dev, "error: pcm close failed %d\n", + dev_err(component->dev, "error: pcm close failed %d\n", err); /* * keep going, no point in preventing the close @@ -575,14 +571,14 @@ static int sof_pcm_new(struct snd_soc_component *component, int stream = SNDRV_PCM_STREAM_PLAYBACK; /* find SOF PCM for this RTD */ - spcm = snd_sof_find_spcm_dai(sdev, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { - dev_warn(sdev->dev, "warn: can't find PCM with DAI ID %d\n", + dev_warn(component->dev, "warn: can't find PCM with DAI ID %d\n", rtd->dai_link->id); return 0; } - dev_dbg(sdev->dev, "creating new PCM %s\n", spcm->pcm.pcm_name); + dev_dbg(component->dev, "creating new PCM %s\n", spcm->pcm.pcm_name); /* do we need to pre-allocate playback audio buffer pages */ if (!spcm->pcm.playback) @@ -591,13 +587,14 @@ static int sof_pcm_new(struct snd_soc_component *component, caps = &spcm->pcm.caps[stream]; /* pre-allocate playback audio buffer pages */ - dev_dbg(sdev->dev, "spcm: allocate %s playback DMA buffer size 0x%x max 0x%x\n", + dev_dbg(component->dev, + "spcm: allocate %s playback DMA buffer size 0x%x max 0x%x\n", caps->name, caps->buffer_size_min, caps->buffer_size_max); - snd_pcm_lib_preallocate_pages(pcm->streams[stream].substream, - SNDRV_DMA_TYPE_DEV_SG, sdev->dev, - le32_to_cpu(caps->buffer_size_min), - le32_to_cpu(caps->buffer_size_max)); + snd_pcm_set_managed_buffer(pcm->streams[stream].substream, + SNDRV_DMA_TYPE_DEV_SG, sdev->dev, + le32_to_cpu(caps->buffer_size_min), + le32_to_cpu(caps->buffer_size_max)); capture: stream = SNDRV_PCM_STREAM_CAPTURE; @@ -608,13 +605,14 @@ capture: caps = &spcm->pcm.caps[stream]; /* pre-allocate capture audio buffer pages */ - dev_dbg(sdev->dev, "spcm: allocate %s capture DMA buffer size 0x%x max 0x%x\n", + dev_dbg(component->dev, + "spcm: allocate %s capture DMA buffer size 0x%x max 0x%x\n", caps->name, caps->buffer_size_min, caps->buffer_size_max); - snd_pcm_lib_preallocate_pages(pcm->streams[stream].substream, - SNDRV_DMA_TYPE_DEV_SG, sdev->dev, - le32_to_cpu(caps->buffer_size_min), - le32_to_cpu(caps->buffer_size_max)); + snd_pcm_set_managed_buffer(pcm->streams[stream].substream, + SNDRV_DMA_TYPE_DEV_SG, sdev->dev, + le32_to_cpu(caps->buffer_size_min), + le32_to_cpu(caps->buffer_size_max)); return 0; } @@ -629,14 +627,14 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); struct snd_sof_dai *dai = - snd_sof_find_dai(sdev, (char *)rtd->dai_link->name); + snd_sof_find_dai(component, (char *)rtd->dai_link->name); /* no topology exists for this BE, try a common configuration */ if (!dai) { - dev_warn(sdev->dev, "warning: no topology found for BE DAI %s config\n", + dev_warn(component->dev, + "warning: no topology found for BE DAI %s config\n", rtd->dai_link->name); /* set 48k, stereo, 16bits by default */ @@ -666,7 +664,7 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); break; default: - dev_err(sdev->dev, "error: No available DAI format!\n"); + dev_err(component->dev, "error: No available DAI format!\n"); return -EINVAL; } @@ -678,9 +676,9 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = dai->dai_config->ssp.tdm_slots; channels->max = dai->dai_config->ssp.tdm_slots; - dev_dbg(sdev->dev, + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(sdev->dev, + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", channels->min, channels->max); @@ -688,7 +686,7 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, case SOF_DAI_INTEL_DMIC: /* DMIC only supports 16 or 32 bit formats */ if (dai->comp_dai.config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { - dev_err(sdev->dev, + dev_err(component->dev, "error: invalid fmt %d for DAI type %d\n", dai->comp_dai.config.frame_fmt, dai->dai_config->type); @@ -704,12 +702,20 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = dai->dai_config->esai.tdm_slots; channels->max = dai->dai_config->esai.tdm_slots; - dev_dbg(sdev->dev, + dev_dbg(component->dev, + "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_IMX_SAI: + channels->min = dai->dai_config->sai.tdm_slots; + channels->max = dai->dai_config->sai.tdm_slots; + + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", channels->min, channels->max); break; default: - dev_err(sdev->dev, "error: invalid DAI type %d\n", + dev_err(component->dev, "error: invalid DAI type %d\n", dai->dai_config->type); break; } @@ -734,21 +740,13 @@ static int sof_pcm_probe(struct snd_soc_component *component) if (!tplg_filename) return -ENOMEM; - ret = snd_sof_load_topology(sdev, tplg_filename); + ret = snd_sof_load_topology(component, tplg_filename); if (ret < 0) { - dev_err(sdev->dev, "error: failed to load DSP topology %d\n", + dev_err(component->dev, "error: failed to load DSP topology %d\n", ret); return ret; } - /* - * Some platforms in SOF, ex: BYT, may not have their platform PM - * callbacks set. Increment the usage count so as to - * prevent the device from entering runtime suspend. - */ - if (!sof_ops(sdev)->runtime_suspend || !sof_ops(sdev)->runtime_resume) - pm_runtime_get_noresume(sdev->dev); - return ret; } @@ -771,7 +769,6 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->remove = sof_pcm_remove; pd->open = sof_pcm_open; pd->close = sof_pcm_close; - pd->ioctl = snd_soc_pcm_lib_ioctl; pd->hw_params = sof_pcm_hw_params; pd->prepare = sof_pcm_prepare; pd->hw_free = sof_pcm_hw_free; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 0fd5567237a8..84290bbeebdd 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -10,192 +10,7 @@ #include "ops.h" #include "sof-priv.h" - -static int sof_restore_kcontrols(struct snd_sof_dev *sdev) -{ - struct snd_sof_control *scontrol; - int ipc_cmd, ctrl_type; - int ret = 0; - - /* restore kcontrol values */ - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { - /* reset readback offset for scontrol after resuming */ - scontrol->readback_offset = 0; - - /* notify DSP of kcontrol values */ - switch (scontrol->cmd) { - case SOF_CTRL_CMD_VOLUME: - case SOF_CTRL_CMD_ENUM: - case SOF_CTRL_CMD_SWITCH: - ipc_cmd = SOF_IPC_COMP_SET_VALUE; - ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; - ret = snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol, - ipc_cmd, ctrl_type, - scontrol->cmd, - true); - break; - case SOF_CTRL_CMD_BINARY: - ipc_cmd = SOF_IPC_COMP_SET_DATA; - ctrl_type = SOF_CTRL_TYPE_DATA_SET; - ret = snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol, - ipc_cmd, ctrl_type, - scontrol->cmd, - true); - break; - - default: - break; - } - - if (ret < 0) { - dev_err(sdev->dev, - "error: failed kcontrol value set for widget: %d\n", - scontrol->comp_id); - - return ret; - } - } - - return 0; -} - -static int sof_restore_pipelines(struct snd_sof_dev *sdev) -{ - struct snd_sof_widget *swidget; - struct snd_sof_route *sroute; - struct sof_ipc_pipe_new *pipeline; - struct snd_sof_dai *dai; - struct sof_ipc_comp_dai *comp_dai; - struct sof_ipc_cmd_hdr *hdr; - int ret; - - /* restore pipeline components */ - list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { - struct sof_ipc_comp_reply r; - - /* skip if there is no private data */ - if (!swidget->private) - continue; - - switch (swidget->id) { - case snd_soc_dapm_dai_in: - case snd_soc_dapm_dai_out: - dai = swidget->private; - comp_dai = &dai->comp_dai; - ret = sof_ipc_tx_message(sdev->ipc, - comp_dai->comp.hdr.cmd, - comp_dai, sizeof(*comp_dai), - &r, sizeof(r)); - break; - case snd_soc_dapm_scheduler: - - /* - * During suspend, all DSP cores are powered off. - * Therefore upon resume, create the pipeline comp - * and power up the core that the pipeline is - * scheduled on. - */ - pipeline = swidget->private; - ret = sof_load_pipeline_ipc(sdev, pipeline, &r); - break; - default: - hdr = swidget->private; - ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, - swidget->private, hdr->size, - &r, sizeof(r)); - break; - } - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to load widget type %d with ID: %d\n", - swidget->widget->id, swidget->comp_id); - - return ret; - } - } - - /* restore pipeline connections */ - list_for_each_entry_reverse(sroute, &sdev->route_list, list) { - struct sof_ipc_pipe_comp_connect *connect; - struct sof_ipc_reply reply; - - /* skip if there's no private data */ - if (!sroute->private) - continue; - - connect = sroute->private; - - /* send ipc */ - ret = sof_ipc_tx_message(sdev->ipc, - connect->hdr.cmd, - connect, sizeof(*connect), - &reply, sizeof(reply)); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to load route sink %s control %s source %s\n", - sroute->route->sink, - sroute->route->control ? sroute->route->control - : "none", - sroute->route->source); - - return ret; - } - } - - /* restore dai links */ - list_for_each_entry_reverse(dai, &sdev->dai_list, list) { - struct sof_ipc_reply reply; - struct sof_ipc_dai_config *config = dai->dai_config; - - if (!config) { - dev_err(sdev->dev, "error: no config for DAI %s\n", - dai->name); - continue; - } - - /* - * The link DMA channel would be invalidated for running - * streams but not for streams that were in the PAUSED - * state during suspend. So invalidate it here before setting - * the dai config in the DSP. - */ - if (config->type == SOF_DAI_INTEL_HDA) - config->hda.link_dma_ch = DMA_CHAN_INVALID; - - ret = sof_ipc_tx_message(sdev->ipc, - config->hdr.cmd, config, - config->hdr.size, - &reply, sizeof(reply)); - - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to set dai config for %s\n", - dai->name); - - return ret; - } - } - - /* complete pipeline */ - list_for_each_entry(swidget, &sdev->widget_list, list) { - switch (swidget->id) { - case snd_soc_dapm_scheduler: - swidget->complete = - snd_sof_complete_pipeline(sdev, swidget); - break; - default: - break; - } - } - - /* restore pipeline kcontrols */ - ret = sof_restore_kcontrols(sdev); - if (ret < 0) - dev_err(sdev->dev, - "error: restoring kcontrols after resume\n"); - - return ret; -} +#include "sof-audio.h" static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd) { @@ -213,34 +28,6 @@ static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd) sizeof(pm_ctx), &reply, sizeof(reply)); } -static int sof_set_hw_params_upon_resume(struct snd_sof_dev *sdev) -{ - struct snd_pcm_substream *substream; - struct snd_sof_pcm *spcm; - snd_pcm_state_t state; - int dir; - - /* - * SOF requires hw_params to be set-up internally upon resume. - * So, set the flag to indicate this for those streams that - * have been suspended. - */ - list_for_each_entry(spcm, &sdev->pcm_list, list) { - for (dir = 0; dir <= SNDRV_PCM_STREAM_CAPTURE; dir++) { - substream = spcm->stream[dir].substream; - if (!substream || !substream->runtime) - continue; - - state = substream->runtime->status->state; - if (state == SNDRV_PCM_STATE_SUSPENDED) - spcm->prepared[dir] = false; - } - } - - /* set internal flag for BE */ - return snd_sof_dsp_hw_params_upon_resume(sdev); -} - #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) static void sof_cache_debugfs(struct snd_sof_dev *sdev) { @@ -283,6 +70,8 @@ static int sof_resume(struct device *dev, bool runtime_resume) return ret; } + sdev->fw_state = SOF_FW_BOOT_PREPARE; + /* load the firmware */ ret = snd_sof_load_firmware(sdev); if (ret < 0) { @@ -292,7 +81,12 @@ static int sof_resume(struct device *dev, bool runtime_resume) return ret; } - /* boot the firmware */ + sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; + + /* + * Boot the firmware. The FW boot status will be modified + * in snd_sof_run_firmware() depending on the outcome. + */ ret = snd_sof_run_firmware(sdev); if (ret < 0) { dev_err(sdev->dev, @@ -311,7 +105,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) } /* restore pipelines */ - ret = sof_restore_pipelines(sdev); + ret = sof_restore_pipelines(sdev->dev); if (ret < 0) { dev_err(sdev->dev, "error: failed to restore pipeline after resume %d\n", @@ -341,12 +135,15 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) if (!sof_ops(sdev)->suspend) return 0; + if (sdev->fw_state != SOF_FW_BOOT_COMPLETE) + goto power_down; + /* release trace */ snd_sof_release_trace(sdev); /* set restore_stream for all streams during system suspend */ if (!runtime_suspend) { - ret = sof_set_hw_params_upon_resume(sdev); + ret = sof_set_hw_params_upon_resume(sdev->dev); if (ret < 0) { dev_err(sdev->dev, "error: setting hw_params flag during suspend %d\n", @@ -378,6 +175,12 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) ret); } +power_down: + + /* return if the DSP was not probed successfully */ + if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED) + return 0; + /* power down all DSP cores */ if (runtime_suspend) ret = snd_sof_dsp_runtime_suspend(sdev); @@ -388,6 +191,9 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) "error: failed to power down DSP during suspend %d\n", ret); + /* reset FW state */ + sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + return ret; } diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index df318f50dd0b..1278aa95effa 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -35,23 +35,6 @@ MODULE_PARM_DESC(sof_acpi_debug, "SOF ACPI debug options (0x0 all off)"); #define SOF_ACPI_DISABLE_PM_RUNTIME BIT(0) -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL) -static const struct sof_dev_desc sof_acpi_haswell_desc = { - .machines = snd_soc_acpi_intel_haswell_machines, - .resindex_lpe_base = 0, - .resindex_pcicfg_base = 1, - .resindex_imr_base = -1, - .irqindex_host_ipc = 0, - .chip_info = &hsw_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-hsw.ri", - .nocodec_tplg_filename = "sof-hsw-nocodec.tplg", - .ops = &sof_hsw_ops, - .arch_ops = &sof_xtensa_arch_ops -}; -#endif - #if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) static const struct sof_dev_desc sof_acpi_broadwell_desc = { .machines = snd_soc_acpi_intel_broadwell_machines, @@ -62,10 +45,9 @@ static const struct sof_dev_desc sof_acpi_broadwell_desc = { .chip_info = &bdw_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-bdw.ri", + .default_fw_filename = "sof-bdw.ri", .nocodec_tplg_filename = "sof-bdw-nocodec.tplg", .ops = &sof_bdw_ops, - .arch_ops = &sof_xtensa_arch_ops }; #endif @@ -81,10 +63,9 @@ static const struct sof_dev_desc sof_acpi_baytrailcr_desc = { .chip_info = &byt_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-byt.ri", + .default_fw_filename = "sof-byt.ri", .nocodec_tplg_filename = "sof-byt-nocodec.tplg", .ops = &sof_byt_ops, - .arch_ops = &sof_xtensa_arch_ops }; static const struct sof_dev_desc sof_acpi_baytrail_desc = { @@ -96,10 +77,9 @@ static const struct sof_dev_desc sof_acpi_baytrail_desc = { .chip_info = &byt_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-byt.ri", + .default_fw_filename = "sof-byt.ri", .nocodec_tplg_filename = "sof-byt-nocodec.tplg", .ops = &sof_byt_ops, - .arch_ops = &sof_xtensa_arch_ops }; static const struct sof_dev_desc sof_acpi_cherrytrail_desc = { @@ -111,10 +91,9 @@ static const struct sof_dev_desc sof_acpi_cherrytrail_desc = { .chip_info = &cht_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-cht.ri", + .default_fw_filename = "sof-cht.ri", .nocodec_tplg_filename = "sof-cht-nocodec.tplg", .ops = &sof_cht_ops, - .arch_ops = &sof_xtensa_arch_ops }; #endif @@ -140,7 +119,6 @@ static int sof_acpi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct sof_dev_desc *desc; - struct snd_soc_acpi_mach *mach; struct snd_sof_pdata *sof_pdata; const struct snd_sof_dsp_ops *ops; int ret; @@ -167,35 +145,9 @@ static int sof_acpi_probe(struct platform_device *pdev) return -ENODEV; } -#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) - /* force nocodec mode */ - dev_warn(dev, "Force to use nocodec mode\n"); - mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); - if (!mach) - return -ENOMEM; - ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops); - if (ret < 0) - return ret; -#else - /* find machine */ - mach = snd_soc_acpi_find_machine(desc->machines); - if (!mach) { - dev_warn(dev, "warning: No matching ASoC machine driver found\n"); - } else { - sof_pdata->fw_filename = mach->sof_fw_filename; - sof_pdata->tplg_filename = mach->sof_tplg_filename; - } -#endif - - if (mach) { - mach->mach_params.platform = dev_name(dev); - mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc; - } - - sof_pdata->machine = mach; sof_pdata->desc = desc; sof_pdata->dev = &pdev->dev; - sof_pdata->platform = dev_name(dev); + sof_pdata->fw_filename = desc->default_fw_filename; /* alternate fw and tplg filenames ? */ if (fw_path) @@ -240,9 +192,6 @@ static int sof_acpi_remove(struct platform_device *pdev) } static const struct acpi_device_id sof_acpi_match[] = { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL) - { "INT33C8", (unsigned long)&sof_acpi_haswell_desc }, -#endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) { "INT3438", (unsigned long)&sof_acpi_broadwell_desc }, #endif @@ -267,3 +216,5 @@ static struct platform_driver snd_sof_acpi_driver = { module_platform_driver(snd_sof_acpi_driver); MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_BAYTRAIL); +MODULE_IMPORT_NS(SND_SOC_SOF_BROADWELL); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c new file mode 100644 index 000000000000..0d8f65b9ae25 --- /dev/null +++ b/sound/soc/sof/sof-audio.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2019 Intel Corporation. All rights reserved. +// +// Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> +// + +#include "sof-audio.h" +#include "ops.h" + +bool snd_sof_dsp_d0i3_on_suspend(struct snd_sof_dev *sdev) +{ + struct snd_sof_pcm *spcm; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored || + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored) + return true; + } + + return false; +} + +int sof_set_hw_params_upon_resume(struct device *dev) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct snd_pcm_substream *substream; + struct snd_sof_pcm *spcm; + snd_pcm_state_t state; + int dir; + + /* + * SOF requires hw_params to be set-up internally upon resume. + * So, set the flag to indicate this for those streams that + * have been suspended. + */ + list_for_each_entry(spcm, &sdev->pcm_list, list) { + for (dir = 0; dir <= SNDRV_PCM_STREAM_CAPTURE; dir++) { + substream = spcm->stream[dir].substream; + if (!substream || !substream->runtime) + continue; + + state = substream->runtime->status->state; + if (state == SNDRV_PCM_STATE_SUSPENDED) + spcm->prepared[dir] = false; + } + } + + /* set internal flag for BE */ + return snd_sof_dsp_hw_params_upon_resume(sdev); +} + +static int sof_restore_kcontrols(struct device *dev) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct snd_sof_control *scontrol; + int ipc_cmd, ctrl_type; + int ret = 0; + + /* restore kcontrol values */ + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + /* reset readback offset for scontrol after resuming */ + scontrol->readback_offset = 0; + + /* notify DSP of kcontrol values */ + switch (scontrol->cmd) { + case SOF_CTRL_CMD_VOLUME: + case SOF_CTRL_CMD_ENUM: + case SOF_CTRL_CMD_SWITCH: + ipc_cmd = SOF_IPC_COMP_SET_VALUE; + ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; + ret = snd_sof_ipc_set_get_comp_data(scontrol, + ipc_cmd, ctrl_type, + scontrol->cmd, + true); + break; + case SOF_CTRL_CMD_BINARY: + ipc_cmd = SOF_IPC_COMP_SET_DATA; + ctrl_type = SOF_CTRL_TYPE_DATA_SET; + ret = snd_sof_ipc_set_get_comp_data(scontrol, + ipc_cmd, ctrl_type, + scontrol->cmd, + true); + break; + + default: + break; + } + + if (ret < 0) { + dev_err(dev, + "error: failed kcontrol value set for widget: %d\n", + scontrol->comp_id); + + return ret; + } + } + + return 0; +} + +int sof_restore_pipelines(struct device *dev) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct snd_sof_widget *swidget; + struct snd_sof_route *sroute; + struct sof_ipc_pipe_new *pipeline; + struct snd_sof_dai *dai; + struct sof_ipc_comp_dai *comp_dai; + struct sof_ipc_cmd_hdr *hdr; + int ret; + + /* restore pipeline components */ + list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { + struct sof_ipc_comp_reply r; + + /* skip if there is no private data */ + if (!swidget->private) + continue; + + switch (swidget->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + dai = swidget->private; + comp_dai = &dai->comp_dai; + ret = sof_ipc_tx_message(sdev->ipc, + comp_dai->comp.hdr.cmd, + comp_dai, sizeof(*comp_dai), + &r, sizeof(r)); + break; + case snd_soc_dapm_scheduler: + + /* + * During suspend, all DSP cores are powered off. + * Therefore upon resume, create the pipeline comp + * and power up the core that the pipeline is + * scheduled on. + */ + pipeline = swidget->private; + ret = sof_load_pipeline_ipc(dev, pipeline, &r); + break; + default: + hdr = swidget->private; + ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, + swidget->private, hdr->size, + &r, sizeof(r)); + break; + } + if (ret < 0) { + dev_err(dev, + "error: failed to load widget type %d with ID: %d\n", + swidget->widget->id, swidget->comp_id); + + return ret; + } + } + + /* restore pipeline connections */ + list_for_each_entry_reverse(sroute, &sdev->route_list, list) { + struct sof_ipc_pipe_comp_connect *connect; + struct sof_ipc_reply reply; + + /* skip if there's no private data */ + if (!sroute->private) + continue; + + connect = sroute->private; + + /* send ipc */ + ret = sof_ipc_tx_message(sdev->ipc, + connect->hdr.cmd, + connect, sizeof(*connect), + &reply, sizeof(reply)); + if (ret < 0) { + dev_err(dev, + "error: failed to load route sink %s control %s source %s\n", + sroute->route->sink, + sroute->route->control ? sroute->route->control + : "none", + sroute->route->source); + + return ret; + } + } + + /* restore dai links */ + list_for_each_entry_reverse(dai, &sdev->dai_list, list) { + struct sof_ipc_reply reply; + struct sof_ipc_dai_config *config = dai->dai_config; + + if (!config) { + dev_err(dev, "error: no config for DAI %s\n", + dai->name); + continue; + } + + /* + * The link DMA channel would be invalidated for running + * streams but not for streams that were in the PAUSED + * state during suspend. So invalidate it here before setting + * the dai config in the DSP. + */ + if (config->type == SOF_DAI_INTEL_HDA) + config->hda.link_dma_ch = DMA_CHAN_INVALID; + + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, + config->hdr.size, + &reply, sizeof(reply)); + + if (ret < 0) { + dev_err(dev, + "error: failed to set dai config for %s\n", + dai->name); + + return ret; + } + } + + /* complete pipeline */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + switch (swidget->id) { + case snd_soc_dapm_scheduler: + swidget->complete = + snd_sof_complete_pipeline(dev, swidget); + break; + default: + break; + } + } + + /* restore pipeline kcontrols */ + ret = sof_restore_kcontrols(dev); + if (ret < 0) + dev_err(dev, + "error: restoring kcontrols after resume\n"); + + return ret; +} + +/* + * Generic object lookup APIs. + */ + +struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp, + const char *name) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_pcm *spcm; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + /* match with PCM dai name */ + if (strcmp(spcm->pcm.dai_name, name) == 0) + return spcm; + + /* match with playback caps name if set */ + if (*spcm->pcm.caps[0].name && + !strcmp(spcm->pcm.caps[0].name, name)) + return spcm; + + /* match with capture caps name if set */ + if (*spcm->pcm.caps[1].name && + !strcmp(spcm->pcm.caps[1].name, name)) + return spcm; + } + + return NULL; +} + +struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, + unsigned int comp_id, + int *direction) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_pcm *spcm; + int dir; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + dir = SNDRV_PCM_STREAM_PLAYBACK; + if (spcm->stream[dir].comp_id == comp_id) { + *direction = dir; + return spcm; + } + + dir = SNDRV_PCM_STREAM_CAPTURE; + if (spcm->stream[dir].comp_id == comp_id) { + *direction = dir; + return spcm; + } + } + + return NULL; +} + +struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp, + unsigned int pcm_id) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_pcm *spcm; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id) + return spcm; + } + + return NULL; +} + +struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp, + const char *name) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *swidget; + + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (strcmp(name, swidget->widget->name) == 0) + return swidget; + } + + return NULL; +} + +/* find widget by stream name and direction */ +struct snd_sof_widget * +snd_sof_find_swidget_sname(struct snd_soc_component *scomp, + const char *pcm_name, int dir) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *swidget; + enum snd_soc_dapm_type type; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + type = snd_soc_dapm_aif_in; + else + type = snd_soc_dapm_aif_out; + + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (!strcmp(pcm_name, swidget->widget->sname) && + swidget->id == type) + return swidget; + } + + return NULL; +} + +struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp, + const char *name) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dai *dai; + + list_for_each_entry(dai, &sdev->dai_list, list) { + if (dai->name && (strcmp(name, dai->name) == 0)) + return dai; + } + + return NULL; +} + +/* + * SOF Driver enumeration. + */ +int sof_machine_check(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *sof_pdata = sdev->pdata; + const struct sof_dev_desc *desc = sof_pdata->desc; + struct snd_soc_acpi_mach *mach; + int ret; + + /* force nocodec mode */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) + dev_warn(sdev->dev, "Force to use nocodec mode\n"); + goto nocodec; +#endif + + /* find machine */ + snd_sof_machine_select(sdev); + if (sof_pdata->machine) { + snd_sof_set_mach_params(sof_pdata->machine, sdev->dev); + return 0; + } + +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC) + dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n"); + return -ENODEV; +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) +nocodec: +#endif + /* select nocodec mode */ + dev_warn(sdev->dev, "Using nocodec machine driver\n"); + mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL); + if (!mach) + return -ENOMEM; + + mach->drv_name = "sof-nocodec"; + sof_pdata->tplg_filename = desc->nocodec_tplg_filename; + + ret = sof_nocodec_setup(sdev->dev, desc->ops); + if (ret < 0) + return ret; + + sof_pdata->machine = mach; + snd_sof_set_mach_params(sof_pdata->machine, sdev->dev); + + return 0; +} +EXPORT_SYMBOL(sof_machine_check); + +int sof_machine_register(struct snd_sof_dev *sdev, void *pdata) +{ + struct snd_sof_pdata *plat_data = (struct snd_sof_pdata *)pdata; + const char *drv_name; + const void *mach; + int size; + + drv_name = plat_data->machine->drv_name; + mach = (const void *)plat_data->machine; + size = sizeof(*plat_data->machine); + + /* register machine driver, pass machine info as pdata */ + plat_data->pdev_mach = + platform_device_register_data(sdev->dev, drv_name, + PLATFORM_DEVID_NONE, mach, size); + if (IS_ERR(plat_data->pdev_mach)) + return PTR_ERR(plat_data->pdev_mach); + + dev_dbg(sdev->dev, "created machine %s\n", + dev_name(&plat_data->pdev_mach->dev)); + + return 0; +} +EXPORT_SYMBOL(sof_machine_register); + +void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata) +{ + struct snd_sof_pdata *plat_data = (struct snd_sof_pdata *)pdata; + + if (!IS_ERR_OR_NULL(plat_data->pdev_mach)) + platform_device_unregister(plat_data->pdev_mach); +} +EXPORT_SYMBOL(sof_machine_unregister); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h new file mode 100644 index 000000000000..a62fb2da6a6e --- /dev/null +++ b/sound/soc/sof/sof-audio.h @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2019 Intel Corporation. All rights reserved. + * + * Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> + */ + +#ifndef __SOUND_SOC_SOF_AUDIO_H +#define __SOUND_SOC_SOF_AUDIO_H + +#include <sound/soc.h> +#include <sound/control.h> +#include <sound/sof/stream.h> /* needs to be included before control.h */ +#include <sound/sof/control.h> +#include <sound/sof/dai.h> +#include <sound/sof/topology.h> +#include "sof-priv.h" + +#define SOF_AUDIO_PCM_DRV_NAME "sof-audio-component" + +/* max number of FE PCMs before BEs */ +#define SOF_BE_PCM_BASE 16 + +#define DMA_CHAN_INVALID 0xFFFFFFFF + +/* PCM stream, mapped to FW component */ +struct snd_sof_pcm_stream { + u32 comp_id; + struct snd_dma_buffer page_table; + struct sof_ipc_stream_posn posn; + struct snd_pcm_substream *substream; + struct work_struct period_elapsed_work; + bool d0i3_compatible; /* DSP can be in D0I3 when this pcm is opened */ + /* + * flag to indicate that the DSP pipelines should be kept + * active or not while suspending the stream + */ + bool suspend_ignored; +}; + +/* ALSA SOF PCM device */ +struct snd_sof_pcm { + struct snd_soc_component *scomp; + struct snd_soc_tplg_pcm pcm; + struct snd_sof_pcm_stream stream[2]; + struct list_head list; /* list in sdev pcm list */ + struct snd_pcm_hw_params params[2]; + bool prepared[2]; /* PCM_PARAMS set successfully */ +}; + +struct snd_sof_led_control { + unsigned int use_led; + unsigned int direction; + unsigned int led_value; +}; + +/* ALSA SOF Kcontrol device */ +struct snd_sof_control { + struct snd_soc_component *scomp; + int comp_id; + int min_volume_step; /* min volume step for volume_table */ + int max_volume_step; /* max volume step for volume_table */ + int num_channels; + u32 readback_offset; /* offset to mmapped data if used */ + struct sof_ipc_ctrl_data *control_data; + u32 size; /* cdata size */ + enum sof_ipc_ctrl_cmd cmd; + u32 *volume_table; /* volume table computed from tlv data*/ + + struct list_head list; /* list in sdev control list */ + + struct snd_sof_led_control led_ctl; +}; + +/* ASoC SOF DAPM widget */ +struct snd_sof_widget { + struct snd_soc_component *scomp; + int comp_id; + int pipeline_id; + int complete; + int id; + + struct snd_soc_dapm_widget *widget; + struct list_head list; /* list in sdev widget list */ + + void *private; /* core does not touch this */ +}; + +/* ASoC SOF DAPM route */ +struct snd_sof_route { + struct snd_soc_component *scomp; + + struct snd_soc_dapm_route *route; + struct list_head list; /* list in sdev route list */ + + void *private; +}; + +/* ASoC DAI device */ +struct snd_sof_dai { + struct snd_soc_component *scomp; + const char *name; + const char *cpu_dai_name; + + struct sof_ipc_comp_dai comp_dai; + struct sof_ipc_dai_config *dai_config; + struct list_head list; /* list in sdev dai list */ +}; + +/* + * Kcontrols. + */ + +int snd_sof_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, + const unsigned int __user *binary_data, + unsigned int size); +int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, + unsigned int __user *binary_data, + unsigned int size); + +/* + * Topology. + * There is no snd_sof_free_topology since topology components will + * be freed by snd_soc_unregister_component, + */ +int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file); +int snd_sof_complete_pipeline(struct device *dev, + struct snd_sof_widget *swidget); + +int sof_load_pipeline_ipc(struct device *dev, + struct sof_ipc_pipe_new *pipeline, + struct sof_ipc_comp_reply *r); + +/* + * Stream IPC + */ +int snd_sof_ipc_stream_posn(struct snd_soc_component *scomp, + struct snd_sof_pcm *spcm, int direction, + struct sof_ipc_stream_posn *posn); + +struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp, + const char *name); +struct snd_sof_widget * +snd_sof_find_swidget_sname(struct snd_soc_component *scomp, + const char *pcm_name, int dir); +struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp, + const char *name); + +static inline +struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_soc_component *scomp, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + + struct snd_sof_pcm *spcm = NULL; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (le32_to_cpu(spcm->pcm.dai_id) == rtd->dai_link->id) + return spcm; + } + + return NULL; +} + +struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp, + const char *name); +struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, + unsigned int comp_id, + int *direction); +struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp, + unsigned int pcm_id); +void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream); + +/* + * Mixer IPC + */ +int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, + u32 ipc_cmd, + enum sof_ipc_ctrl_type ctrl_type, + enum sof_ipc_ctrl_cmd ctrl_cmd, + bool send); + +/* PM */ +int sof_restore_pipelines(struct device *dev); +int sof_set_hw_params_upon_resume(struct device *dev); +bool snd_sof_dsp_d0i3_on_suspend(struct snd_sof_dev *sdev); + +/* Machine driver enumeration */ +int sof_machine_register(struct snd_sof_dev *sdev, void *pdata); +void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata); + +#endif diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index 28a9692974e5..39ea8af6213f 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -19,7 +19,7 @@ extern struct snd_sof_dsp_ops sof_imx8_ops; static struct sof_dev_desc sof_of_imx8qxp_desc = { .default_fw_path = "imx/sof", .default_tplg_path = "imx/sof-tplg", - .nocodec_fw_filename = "sof-imx8.ri", + .default_fw_filename = "sof-imx8.ri", .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", .ops = &sof_imx8_ops, }; @@ -43,8 +43,6 @@ static int sof_of_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct sof_dev_desc *desc; - /*TODO: create a generic snd_soc_xxx_mach */ - struct snd_soc_acpi_mach *mach; struct snd_sof_pdata *sof_pdata; const struct snd_sof_dsp_ops *ops; int ret; @@ -66,27 +64,9 @@ static int sof_of_probe(struct platform_device *pdev) return -ENODEV; } -#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) - /* force nocodec mode */ - dev_warn(dev, "Force to use nocodec mode\n"); - mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); - if (!mach) - return -ENOMEM; - ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops); - if (ret < 0) - return ret; -#else - /* TODO: implement case where we actually have a codec */ - return -ENODEV; -#endif - - if (mach) - mach->mach_params.platform = dev_name(dev); - - sof_pdata->machine = mach; sof_pdata->desc = desc; sof_pdata->dev = &pdev->dev; - sof_pdata->platform = dev_name(dev); + sof_pdata->fw_filename = desc->default_fw_filename; /* TODO: read alternate fw and tplg filenames from DT */ sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path; diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index bbeffd932de7..d855bc2b76ad 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -9,6 +9,7 @@ // #include <linux/firmware.h> +#include <linux/dmi.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/pm_runtime.h> @@ -36,6 +37,23 @@ MODULE_PARM_DESC(sof_pci_debug, "SOF PCI debug options (0x0 all off)"); #define SOF_PCI_DISABLE_PM_RUNTIME BIT(0) +static const struct dmi_system_id community_key_platforms[] = { + { + .ident = "Up Squared", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), + DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"), + } + }, + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {}, +}; + #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) static const struct sof_dev_desc bxt_desc = { .machines = snd_soc_acpi_intel_bxt_machines, @@ -47,10 +65,9 @@ static const struct sof_dev_desc bxt_desc = { .chip_info = &apl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-apl.ri", + .default_fw_filename = "sof-apl.ri", .nocodec_tplg_filename = "sof-apl-nocodec.tplg", .ops = &sof_apl_ops, - .arch_ops = &sof_xtensa_arch_ops }; #endif @@ -65,10 +82,9 @@ static const struct sof_dev_desc glk_desc = { .chip_info = &apl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-glk.ri", + .default_fw_filename = "sof-glk.ri", .nocodec_tplg_filename = "sof-glk-nocodec.tplg", .ops = &sof_apl_ops, - .arch_ops = &sof_xtensa_arch_ops }; #endif @@ -93,16 +109,16 @@ static const struct sof_dev_desc tng_desc = { .chip_info = &tng_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-byt.ri", + .default_fw_filename = "sof-byt.ri", .nocodec_tplg_filename = "sof-byt.tplg", .ops = &sof_tng_ops, - .arch_ops = &sof_xtensa_arch_ops }; #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) static const struct sof_dev_desc cnl_desc = { .machines = snd_soc_acpi_intel_cnl_machines, + .alt_machines = snd_soc_acpi_intel_cnl_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -111,16 +127,16 @@ static const struct sof_dev_desc cnl_desc = { .chip_info = &cnl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-cnl.ri", + .default_fw_filename = "sof-cnl.ri", .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, - .arch_ops = &sof_xtensa_arch_ops }; #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE) static const struct sof_dev_desc cfl_desc = { .machines = snd_soc_acpi_intel_cfl_machines, + .alt_machines = snd_soc_acpi_intel_cfl_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -129,10 +145,9 @@ static const struct sof_dev_desc cfl_desc = { .chip_info = &cnl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-cfl.ri", + .default_fw_filename = "sof-cfl.ri", .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, - .arch_ops = &sof_xtensa_arch_ops }; #endif @@ -141,6 +156,7 @@ static const struct sof_dev_desc cfl_desc = { static const struct sof_dev_desc cml_desc = { .machines = snd_soc_acpi_intel_cml_machines, + .alt_machines = snd_soc_acpi_intel_cml_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -149,16 +165,16 @@ static const struct sof_dev_desc cml_desc = { .chip_info = &cnl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-cml.ri", + .default_fw_filename = "sof-cml.ri", .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, - .arch_ops = &sof_xtensa_arch_ops }; #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) static const struct sof_dev_desc icl_desc = { .machines = snd_soc_acpi_intel_icl_machines, + .alt_machines = snd_soc_acpi_intel_icl_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -167,16 +183,16 @@ static const struct sof_dev_desc icl_desc = { .chip_info = &icl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-icl.ri", + .default_fw_filename = "sof-icl.ri", .nocodec_tplg_filename = "sof-icl-nocodec.tplg", .ops = &sof_cnl_ops, - .arch_ops = &sof_xtensa_arch_ops }; #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE) static const struct sof_dev_desc tgl_desc = { .machines = snd_soc_acpi_intel_tgl_machines, + .alt_machines = snd_soc_acpi_intel_tgl_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -185,10 +201,9 @@ static const struct sof_dev_desc tgl_desc = { .chip_info = &tgl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-tgl.ri", + .default_fw_filename = "sof-tgl.ri", .nocodec_tplg_filename = "sof-tgl-nocodec.tplg", .ops = &sof_cnl_ops, - .arch_ops = &sof_xtensa_arch_ops }; #endif @@ -203,10 +218,9 @@ static const struct sof_dev_desc ehl_desc = { .chip_info = &ehl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-ehl.ri", + .default_fw_filename = "sof-ehl.ri", .nocodec_tplg_filename = "sof-ehl-nocodec.tplg", .ops = &sof_cnl_ops, - .arch_ops = &sof_xtensa_arch_ops }; #endif @@ -221,10 +235,8 @@ static const struct sof_dev_desc jsl_desc = { .chip_info = &jsl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-jsl.ri", .nocodec_tplg_filename = "sof-jsl-nocodec.tplg", .ops = &sof_cnl_ops, - .arch_ops = &sof_xtensa_arch_ops }; #endif @@ -266,7 +278,6 @@ static int sof_pci_probe(struct pci_dev *pci, struct device *dev = &pci->dev; const struct sof_dev_desc *desc = (const struct sof_dev_desc *)pci_id->driver_data; - struct snd_soc_acpi_mach *mach; struct snd_sof_pdata *sof_pdata; const struct snd_sof_dsp_ops *ops; int ret; @@ -297,42 +308,39 @@ static int sof_pci_probe(struct pci_dev *pci, if (ret < 0) return ret; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) - /* force nocodec mode */ - dev_warn(dev, "Force to use nocodec mode\n"); - mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); - if (!mach) { - ret = -ENOMEM; - goto release_regions; - } - ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops); - if (ret < 0) - goto release_regions; - -#else - /* find machine */ - mach = snd_soc_acpi_find_machine(desc->machines); - if (!mach) { - dev_warn(dev, "warning: No matching ASoC machine driver found\n"); - } else { - mach->mach_params.platform = dev_name(dev); - sof_pdata->fw_filename = mach->sof_fw_filename; - sof_pdata->tplg_filename = mach->sof_tplg_filename; - } -#endif /* CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE */ - sof_pdata->name = pci_name(pci); - sof_pdata->machine = mach; sof_pdata->desc = (struct sof_dev_desc *)pci_id->driver_data; sof_pdata->dev = dev; - sof_pdata->platform = dev_name(dev); + sof_pdata->fw_filename = desc->default_fw_filename; + + /* + * for platforms using the SOF community key, change the + * default path automatically to pick the right files from the + * linux-firmware tree. This can be overridden with the + * fw_path kernel parameter, e.g. for developers. + */ /* alternate fw and tplg filenames ? */ - if (fw_path) + if (fw_path) { sof_pdata->fw_filename_prefix = fw_path; - else + + dev_dbg(dev, + "Module parameter used, changed fw path to %s\n", + sof_pdata->fw_filename_prefix); + + } else if (dmi_check_system(community_key_platforms)) { + sof_pdata->fw_filename_prefix = + devm_kasprintf(dev, GFP_KERNEL, "%s/%s", + sof_pdata->desc->default_fw_path, + "community"); + + dev_dbg(dev, + "Platform uses community key, changed fw path to %s\n", + sof_pdata->fw_filename_prefix); + } else { sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path; + } if (tplg_path) sof_pdata->tplg_filename_prefix = tplg_path; @@ -442,3 +450,5 @@ static struct pci_driver snd_sof_pci_driver = { module_pci_driver(snd_sof_pci_driver); MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_MERRIFIELD); +MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index c7c2c70ee4d0..bc2337cf1142 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -12,20 +12,11 @@ #define __SOUND_SOC_SOF_PRIV_H #include <linux/device.h> - #include <sound/hdaudio.h> -#include <sound/soc.h> -#include <sound/control.h> - #include <sound/sof.h> -#include <sound/sof/stream.h> /* needs to be included before control.h */ -#include <sound/sof/control.h> -#include <sound/sof/dai.h> #include <sound/sof/info.h> #include <sound/sof/pm.h> -#include <sound/sof/topology.h> #include <sound/sof/trace.h> - #include <uapi/sound/sof/fw.h> /* debug flags */ @@ -48,9 +39,6 @@ extern int sof_core_debug; /* DMA buffer size for trace */ #define DMA_BUF_SIZE_FOR_TRACE (PAGE_SIZE * 16) -/* max number of FE PCMs before BEs */ -#define SOF_BE_PCM_BASE 16 - #define SOF_IPC_DSP_REPLY 0 #define SOF_IPC_HOST_REPLY 1 @@ -66,8 +54,6 @@ extern int sof_core_debug; (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) || \ IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)) -#define DMA_CHAN_INVALID 0xFFFFFFFF - /* DSP D0ix sub-state */ enum sof_d0_substate { SOF_DSP_D0I0 = 0, /* DSP default D0 substate */ @@ -216,12 +202,23 @@ struct snd_sof_dsp_ops { int (*get_window_offset)(struct snd_sof_dev *sdev, u32 id);/* mandatory for common loader code */ + /* machine driver ops */ + int (*machine_register)(struct snd_sof_dev *sdev, + void *pdata); /* optional */ + void (*machine_unregister)(struct snd_sof_dev *sdev, + void *pdata); /* optional */ + void (*machine_select)(struct snd_sof_dev *sdev); /* optional */ + void (*set_mach_params)(const struct snd_soc_acpi_mach *mach, + struct device *dev); /* optional */ + /* DAI ops */ struct snd_soc_dai_driver *drv; int num_drv; /* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */ u32 hw_info; + + const struct sof_arch_ops *arch_ops; }; /* DSP architecture specific callbacks for oops and stack dumps */ @@ -231,7 +228,7 @@ struct sof_arch_ops { u32 *stack, u32 stack_words); }; -#define sof_arch_ops(sdev) ((sdev)->pdata->desc->arch_ops) +#define sof_arch_ops(sdev) ((sdev)->pdata->desc->ops->arch_ops) /* DSP device HW descriptor mapping between bus ID and ops */ struct sof_ops_table { @@ -303,88 +300,13 @@ struct snd_sof_ipc_msg { bool ipc_complete; }; -/* PCM stream, mapped to FW component */ -struct snd_sof_pcm_stream { - u32 comp_id; - struct snd_dma_buffer page_table; - struct sof_ipc_stream_posn posn; - struct snd_pcm_substream *substream; - struct work_struct period_elapsed_work; - bool d0i3_compatible; /* DSP can be in D0I3 when this pcm is opened */ - /* - * flag to indicate that the DSP pipelines should be kept - * active or not while suspending the stream - */ - bool suspend_ignored; -}; - -/* ALSA SOF PCM device */ -struct snd_sof_pcm { - struct snd_sof_dev *sdev; - struct snd_soc_tplg_pcm pcm; - struct snd_sof_pcm_stream stream[2]; - struct list_head list; /* list in sdev pcm list */ - struct snd_pcm_hw_params params[2]; - bool prepared[2]; /* PCM_PARAMS set successfully */ -}; - -struct snd_sof_led_control { - unsigned int use_led; - unsigned int direction; - unsigned int led_value; -}; - -/* ALSA SOF Kcontrol device */ -struct snd_sof_control { - struct snd_sof_dev *sdev; - int comp_id; - int min_volume_step; /* min volume step for volume_table */ - int max_volume_step; /* max volume step for volume_table */ - int num_channels; - u32 readback_offset; /* offset to mmaped data if used */ - struct sof_ipc_ctrl_data *control_data; - u32 size; /* cdata size */ - enum sof_ipc_ctrl_cmd cmd; - u32 *volume_table; /* volume table computed from tlv data*/ - - struct list_head list; /* list in sdev control list */ - - struct snd_sof_led_control led_ctl; -}; - -/* ASoC SOF DAPM widget */ -struct snd_sof_widget { - struct snd_sof_dev *sdev; - int comp_id; - int pipeline_id; - int complete; - int id; - - struct snd_soc_dapm_widget *widget; - struct list_head list; /* list in sdev widget list */ - - void *private; /* core does not touch this */ -}; - -/* ASoC SOF DAPM route */ -struct snd_sof_route { - struct snd_sof_dev *sdev; - - struct snd_soc_dapm_route *route; - struct list_head list; /* list in sdev route list */ - - void *private; -}; - -/* ASoC DAI device */ -struct snd_sof_dai { - struct snd_sof_dev *sdev; - const char *name; - const char *cpu_dai_name; - - struct sof_ipc_comp_dai comp_dai; - struct sof_ipc_dai_config *dai_config; - struct list_head list; /* list in sdev dai list */ +enum snd_sof_fw_state { + SOF_FW_BOOT_NOT_STARTED = 0, + SOF_FW_BOOT_PREPARE, + SOF_FW_BOOT_IN_PROGRESS, + SOF_FW_BOOT_FAILED, + SOF_FW_BOOT_READY_FAILED, /* firmware booted but fw_ready op failed */ + SOF_FW_BOOT_COMPLETE, }; /* @@ -408,7 +330,7 @@ struct snd_sof_dev { /* DSP firmware boot */ wait_queue_head_t boot_wait; - u32 boot_complete; + enum snd_sof_fw_state fw_state; u32 first_boot; /* work queue in case the probe is implemented in two steps */ @@ -441,6 +363,7 @@ struct snd_sof_dev { struct snd_dma_buffer dmab_bdl; struct sof_ipc_fw_ready fw_ready; struct sof_ipc_fw_version fw_version; + struct sof_ipc_cc_version *cc_version; /* topology */ struct snd_soc_tplg_ops *tplg_ops; @@ -499,7 +422,7 @@ int snd_sof_set_d0_substate(struct snd_sof_dev *sdev, void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); -int snd_sof_create_page_table(struct snd_sof_dev *sdev, +int snd_sof_create_page_table(struct device *dev, struct snd_dma_buffer *dmab, unsigned char *page_table, size_t size); @@ -531,69 +454,6 @@ int snd_sof_ipc_valid(struct snd_sof_dev *sdev); int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes); -struct snd_sof_widget *snd_sof_find_swidget(struct snd_sof_dev *sdev, - const char *name); -struct snd_sof_widget *snd_sof_find_swidget_sname(struct snd_sof_dev *sdev, - const char *pcm_name, - int dir); -struct snd_sof_dai *snd_sof_find_dai(struct snd_sof_dev *sdev, - const char *name); - -static inline -struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_sof_dev *sdev, - struct snd_soc_pcm_runtime *rtd) -{ - struct snd_sof_pcm *spcm = NULL; - - list_for_each_entry(spcm, &sdev->pcm_list, list) { - if (le32_to_cpu(spcm->pcm.dai_id) == rtd->dai_link->id) - return spcm; - } - - return NULL; -} - -bool snd_sof_dsp_d0i3_on_suspend(struct snd_sof_dev *sdev); - -struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_sof_dev *sdev, - const char *name); -struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, - unsigned int comp_id, - int *direction); -struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, - unsigned int pcm_id); -void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream); - -/* - * Stream IPC - */ -int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev, - struct snd_sof_pcm *spcm, int direction, - struct sof_ipc_stream_posn *posn); - -/* - * Mixer IPC - */ -int snd_sof_ipc_set_get_comp_data(struct snd_sof_ipc *ipc, - struct snd_sof_control *scontrol, u32 ipc_cmd, - enum sof_ipc_ctrl_type ctrl_type, - enum sof_ipc_ctrl_cmd ctrl_cmd, - bool send); - -/* - * Topology. - * There is no snd_sof_free_topology since topology components will - * be freed by snd_soc_unregister_component, - */ -int snd_sof_init_topology(struct snd_sof_dev *sdev, - struct snd_soc_tplg_ops *ops); -int snd_sof_load_topology(struct snd_sof_dev *sdev, const char *file); -int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget); - -int sof_load_pipeline_ipc(struct snd_sof_dev *sdev, - struct sof_ipc_pipe_new *pipeline, - struct sof_ipc_comp_reply *r); /* * Trace/debug @@ -626,39 +486,11 @@ void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev); extern struct snd_compr_ops sof_compressed_ops; /* - * Kcontrols. - */ - -int snd_sof_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_sof_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_sof_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_sof_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_sof_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_sof_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, - const unsigned int __user *binary_data, - unsigned int size); -int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, - unsigned int __user *binary_data, - unsigned int size); - -/* * DSP Architectures. */ static inline void sof_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, u32 stack_words) { - if (sof_arch_ops(sdev)->dsp_stack) sof_arch_ops(sdev)->dsp_stack(sdev, oops, stack, stack_words); } @@ -700,4 +532,6 @@ int intel_pcm_open(struct snd_sof_dev *sdev, int intel_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); +int sof_machine_check(struct snd_sof_dev *sdev); + #endif diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index e20b806ec80f..9f4f8868b386 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -13,6 +13,7 @@ #include <sound/pcm_params.h> #include <uapi/sound/sof/tokens.h> #include "sof-priv.h" +#include "sof-audio.h" #include "ops.h" #define COMP_ID_UNASSIGNED 0xffffffff @@ -53,7 +54,8 @@ struct sof_widget_data { static int ipc_pcm_params(struct snd_sof_widget *swidget, int dir) { struct sof_ipc_pcm_params_reply ipc_params_reply; - struct snd_sof_dev *sdev = swidget->sdev; + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc_pcm_params pcm; struct snd_pcm_hw_params *params; struct snd_sof_pcm *spcm; @@ -62,9 +64,9 @@ static int ipc_pcm_params(struct snd_sof_widget *swidget, int dir) memset(&pcm, 0, sizeof(pcm)); /* get runtime PCM params using widget's stream name */ - spcm = snd_sof_find_spcm_name(sdev, swidget->widget->sname); + spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname); if (!spcm) { - dev_err(sdev->dev, "error: cannot find PCM for %s\n", + dev_err(scomp->dev, "error: cannot find PCM for %s\n", swidget->widget->name); return -EINVAL; } @@ -102,7 +104,7 @@ static int ipc_pcm_params(struct snd_sof_widget *swidget, int dir) ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), &ipc_params_reply, sizeof(ipc_params_reply)); if (ret < 0) - dev_err(sdev->dev, "error: pcm params failed for %s\n", + dev_err(scomp->dev, "error: pcm params failed for %s\n", swidget->widget->name); return ret; @@ -111,7 +113,8 @@ static int ipc_pcm_params(struct snd_sof_widget *swidget, int dir) /* send stream trigger ipc */ static int ipc_trigger(struct snd_sof_widget *swidget, int cmd) { - struct snd_sof_dev *sdev = swidget->sdev; + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc_stream stream; struct sof_ipc_reply reply; int ret = 0; @@ -125,7 +128,7 @@ static int ipc_trigger(struct snd_sof_widget *swidget, int cmd) ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, sizeof(stream), &reply, sizeof(reply)); if (ret < 0) - dev_err(sdev->dev, "error: failed to trigger %s\n", + dev_err(scomp->dev, "error: failed to trigger %s\n", swidget->widget->name); return ret; @@ -135,23 +138,23 @@ static int sof_keyword_dapm_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { struct snd_sof_widget *swidget = w->dobj.private; + struct snd_soc_component *scomp; int stream = SNDRV_PCM_STREAM_CAPTURE; - struct snd_sof_dev *sdev; struct snd_sof_pcm *spcm; int ret = 0; if (!swidget) return 0; - sdev = swidget->sdev; + scomp = swidget->scomp; - dev_dbg(sdev->dev, "received event %d for widget %s\n", + dev_dbg(scomp->dev, "received event %d for widget %s\n", event, w->name); /* get runtime PCM params using widget's stream name */ - spcm = snd_sof_find_spcm_name(sdev, swidget->widget->sname); + spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname); if (!spcm) { - dev_err(sdev->dev, "error: cannot find PCM for %s\n", + dev_err(scomp->dev, "error: cannot find PCM for %s\n", swidget->widget->name); return -EINVAL; } @@ -160,14 +163,14 @@ static int sof_keyword_dapm_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: if (spcm->stream[stream].suspend_ignored) { - dev_dbg(sdev->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n"); + dev_dbg(scomp->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n"); return 0; } /* set pcm params */ ret = ipc_pcm_params(swidget, stream); if (ret < 0) { - dev_err(sdev->dev, + dev_err(scomp->dev, "error: failed to set pcm params for widget %s\n", swidget->widget->name); break; @@ -176,27 +179,27 @@ static int sof_keyword_dapm_event(struct snd_soc_dapm_widget *w, /* start trigger */ ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_START); if (ret < 0) - dev_err(sdev->dev, + dev_err(scomp->dev, "error: failed to trigger widget %s\n", swidget->widget->name); break; case SND_SOC_DAPM_POST_PMD: if (spcm->stream[stream].suspend_ignored) { - dev_dbg(sdev->dev, "POST_PMD even ignored, KWD pipeline will remain RUNNING\n"); + dev_dbg(scomp->dev, "POST_PMD even ignored, KWD pipeline will remain RUNNING\n"); return 0; } /* stop trigger */ ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP); if (ret < 0) - dev_err(sdev->dev, + dev_err(scomp->dev, "error: failed to trigger widget %s\n", swidget->widget->name); /* pcm free */ ret = ipc_trigger(swidget, SOF_IPC_STREAM_PCM_FREE); if (ret < 0) - dev_err(sdev->dev, + dev_err(scomp->dev, "error: failed to trigger widget %s\n", swidget->widget->name); break; @@ -570,6 +573,20 @@ static const struct sof_topology_token src_tokens[] = { offsetof(struct sof_ipc_comp_src, sink_rate), 0}, }; +/* ASRC */ +static const struct sof_topology_token asrc_tokens[] = { + {SOF_TKN_ASRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_asrc, source_rate), 0}, + {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_asrc, sink_rate), 0}, + {SOF_TKN_ASRC_ASYNCHRONOUS_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_u32, + offsetof(struct sof_ipc_comp_asrc, asynchronous_mode), 0}, + {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_u32, + offsetof(struct sof_ipc_comp_asrc, operation_mode), 0}, +}; + /* Tone */ static const struct sof_topology_token tone_tokens[] = { }; @@ -678,6 +695,13 @@ static const struct sof_topology_token esai_tokens[] = { offsetof(struct sof_ipc_dai_esai_params, mclk_id), 0}, }; +/* SAI */ +static const struct sof_topology_token sai_tokens[] = { + {SOF_TKN_IMX_SAI_MCLK_ID, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_sai_params, mclk_id), 0}, +}; + /* * DMIC PDM Tokens * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token @@ -838,7 +862,7 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, /* check if array index is valid */ if (!index || *index == 0) { - dev_err(sdev->dev, + dev_err(scomp->dev, "error: invalid array offset\n"); continue; } else { @@ -866,7 +890,6 @@ static int sof_parse_tokens(struct snd_soc_component *scomp, struct snd_soc_tplg_vendor_array *array, int priv_size) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); int asize; while (priv_size > 0) { @@ -874,7 +897,7 @@ static int sof_parse_tokens(struct snd_soc_component *scomp, /* validate asize */ if (asize < 0) { /* FIXME: A zero-size array makes no sense */ - dev_err(sdev->dev, "error: invalid array size 0x%x\n", + dev_err(scomp->dev, "error: invalid array size 0x%x\n", asize); return -EINVAL; } @@ -882,7 +905,7 @@ static int sof_parse_tokens(struct snd_soc_component *scomp, /* make sure there is enough data before parsing */ priv_size -= asize; if (priv_size < 0) { - dev_err(sdev->dev, "error: invalid array size 0x%x\n", + dev_err(scomp->dev, "error: invalid array size 0x%x\n", asize); return -EINVAL; } @@ -905,7 +928,7 @@ static int sof_parse_tokens(struct snd_soc_component *scomp, array); break; default: - dev_err(sdev->dev, "error: unknown token type %d\n", + dev_err(scomp->dev, "error: unknown token type %d\n", array->type); return -EINVAL; } @@ -920,9 +943,7 @@ static int sof_parse_tokens(struct snd_soc_component *scomp, static void sof_dbg_comp_config(struct snd_soc_component *scomp, struct sof_ipc_comp_config *config) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - - dev_dbg(sdev->dev, " config: periods snk %d src %d fmt %d\n", + dev_dbg(scomp->dev, " config: periods snk %d src %d fmt %d\n", config->periods_sink, config->periods_source, config->frame_fmt); } @@ -974,7 +995,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, /* extract tlv data */ if (get_tlv_data(kc->tlv.p, tlv) < 0) { - dev_err(sdev->dev, "error: invalid TLV data\n"); + dev_err(scomp->dev, "error: invalid TLV data\n"); ret = -EINVAL; goto out_free; } @@ -982,7 +1003,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, /* set up volume table */ ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1); if (ret < 0) { - dev_err(sdev->dev, "error: setting up volume table\n"); + dev_err(scomp->dev, "error: setting up volume table\n"); goto out_free; } @@ -999,12 +1020,12 @@ skip: ARRAY_SIZE(led_tokens), mc->priv.array, le32_to_cpu(mc->priv.size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse led tokens failed %d\n", + dev_err(scomp->dev, "error: parse led tokens failed %d\n", le32_to_cpu(mc->priv.size)); goto out_free_table; } - dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", + dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", scontrol->comp_id, scontrol->num_channels); return ret; @@ -1043,7 +1064,7 @@ static int sof_control_load_enum(struct snd_soc_component *scomp, scontrol->cmd = SOF_CTRL_CMD_ENUM; - dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", + dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", scontrol->comp_id, scontrol->num_channels, scontrol->comp_id); return 0; @@ -1067,7 +1088,7 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, le32_to_cpu(control->priv.size); if (scontrol->size > max_size) { - dev_err(sdev->dev, "err: bytes data size %d exceeds max %d.\n", + dev_err(scomp->dev, "err: bytes data size %d exceeds max %d.\n", scontrol->size, max_size); ret = -EINVAL; goto out; @@ -1083,7 +1104,7 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, scontrol->comp_id = sdev->next_comp_id; scontrol->cmd = SOF_CTRL_CMD_BINARY; - dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", + dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", scontrol->comp_id, scontrol->num_channels); if (le32_to_cpu(control->priv.size) > 0) { @@ -1091,14 +1112,14 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, le32_to_cpu(control->priv.size)); if (cdata->data->magic != SOF_ABI_MAGIC) { - dev_err(sdev->dev, "error: Wrong ABI magic 0x%08x.\n", + dev_err(scomp->dev, "error: Wrong ABI magic 0x%08x.\n", cdata->data->magic); ret = -EINVAL; goto out_free; } if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { - dev_err(sdev->dev, + dev_err(scomp->dev, "error: Incompatible ABI version 0x%08x.\n", cdata->data->abi); ret = -EINVAL; @@ -1106,7 +1127,7 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, } if (cdata->data->size + sizeof(const struct sof_abi_hdr) != le32_to_cpu(control->priv.size)) { - dev_err(sdev->dev, + dev_err(scomp->dev, "error: Conflict in bytes vs. priv size.\n"); ret = -EINVAL; goto out_free; @@ -1134,14 +1155,14 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, struct snd_sof_control *scontrol; int ret = -EINVAL; - dev_dbg(sdev->dev, "tplg: load control type %d name : %s\n", + dev_dbg(scomp->dev, "tplg: load control type %d name : %s\n", hdr->type, hdr->name); scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL); if (!scontrol) return -ENOMEM; - scontrol->sdev = sdev; + scontrol->scomp = scomp; switch (le32_to_cpu(hdr->ops.info)) { case SND_SOC_TPLG_CTL_VOLSW: @@ -1170,7 +1191,7 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: case SND_SOC_TPLG_DAPM_CTL_PIN: default: - dev_warn(sdev->dev, "control type not supported %d:%d:%d\n", + dev_warn(scomp->dev, "control type not supported %d:%d:%d\n", hdr->ops.get, hdr->ops.put, hdr->ops.info); kfree(scontrol); return 0; @@ -1193,7 +1214,7 @@ static int sof_control_unload(struct snd_soc_component *scomp, struct sof_ipc_free fcomp; struct snd_sof_control *scontrol = dobj->private; - dev_dbg(sdev->dev, "tplg: unload control name : %s\n", scomp->name); + dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scomp->name); fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE; fcomp.hdr.size = sizeof(fcomp); @@ -1217,12 +1238,11 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, struct snd_soc_tplg_dapm_widget *tw, struct snd_sof_dai *dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_card *card = scomp->card; struct snd_soc_pcm_runtime *rtd; list_for_each_entry(rtd, &card->rtd_list, list) { - dev_vdbg(sdev->dev, "tplg: check widget: %s stream: %s dai stream: %s\n", + dev_vdbg(scomp->dev, "tplg: check widget: %s stream: %s dai stream: %s\n", w->name, w->sname, rtd->dai_link->stream_name); if (!w->sname || !rtd->dai_link->stream_name) @@ -1236,13 +1256,13 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, case snd_soc_dapm_dai_out: rtd->cpu_dai->capture_widget = w; dai->name = rtd->dai_link->name; - dev_dbg(sdev->dev, "tplg: connected widget %s -> DAI link %s\n", + dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n", w->name, rtd->dai_link->name); break; case snd_soc_dapm_dai_in: rtd->cpu_dai->playback_widget = w; dai->name = rtd->dai_link->name; - dev_dbg(sdev->dev, "tplg: connected widget %s -> DAI link %s\n", + dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n", w->name, rtd->dai_link->name); break; default: @@ -1252,7 +1272,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, /* check we have a connection */ if (!dai->name) { - dev_err(sdev->dev, "error: can't connect DAI %s stream %s\n", + dev_err(scomp->dev, "error: can't connect DAI %s stream %s\n", w->name, w->sname); return -EINVAL; } @@ -1284,7 +1304,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, ARRAY_SIZE(dai_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse dai tokens failed %d\n", + dev_err(scomp->dev, "error: parse dai tokens failed %d\n", le32_to_cpu(private->size)); return ret; } @@ -1293,12 +1313,12 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse dai.cfg tokens failed %d\n", + dev_err(scomp->dev, "error: parse dai.cfg tokens failed %d\n", private->size); return ret; } - dev_dbg(sdev->dev, "dai %s: type %d index %d\n", + dev_dbg(scomp->dev, "dai %s: type %d index %d\n", swidget->widget->name, comp_dai.type, comp_dai.dai_index); sof_dbg_comp_config(scomp, &comp_dai.config); @@ -1306,7 +1326,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, &comp_dai, sizeof(comp_dai), r, sizeof(*r)); if (ret == 0 && dai) { - dai->sdev = sdev; + dai->scomp = scomp; memcpy(&dai->comp_dai, &comp_dai, sizeof(comp_dai)); } @@ -1342,13 +1362,13 @@ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, ARRAY_SIZE(buffer_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse buffer tokens failed %d\n", + dev_err(scomp->dev, "error: parse buffer tokens failed %d\n", private->size); kfree(buffer); return ret; } - dev_dbg(sdev->dev, "buffer %s: size %d caps 0x%x\n", + dev_dbg(scomp->dev, "buffer %s: size %d caps 0x%x\n", swidget->widget->name, buffer->size, buffer->caps); swidget->private = buffer; @@ -1356,7 +1376,7 @@ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, ret = sof_ipc_tx_message(sdev->ipc, buffer->comp.hdr.cmd, buffer, sizeof(*buffer), r, sizeof(*r)); if (ret < 0) { - dev_err(sdev->dev, "error: buffer %s load failed\n", + dev_err(scomp->dev, "error: buffer %s load failed\n", swidget->widget->name); kfree(buffer); } @@ -1365,16 +1385,16 @@ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, } /* bind PCM ID to host component ID */ -static int spcm_bind(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, +static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, int dir) { struct snd_sof_widget *host_widget; - host_widget = snd_sof_find_swidget_sname(sdev, + host_widget = snd_sof_find_swidget_sname(scomp, spcm->pcm.caps[dir].name, dir); if (!host_widget) { - dev_err(sdev->dev, "can't find host comp to bind pcm\n"); + dev_err(scomp->dev, "can't find host comp to bind pcm\n"); return -EINVAL; } @@ -1415,7 +1435,7 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, ARRAY_SIZE(pcm_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse host tokens failed %d\n", + dev_err(scomp->dev, "error: parse host tokens failed %d\n", private->size); goto err; } @@ -1424,12 +1444,12 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse host.cfg tokens failed %d\n", + dev_err(scomp->dev, "error: parse host.cfg tokens failed %d\n", le32_to_cpu(private->size)); goto err; } - dev_dbg(sdev->dev, "loaded host %s\n", swidget->widget->name); + dev_dbg(scomp->dev, "loaded host %s\n", swidget->widget->name); sof_dbg_comp_config(scomp, &host->config); swidget->private = host; @@ -1446,24 +1466,25 @@ err: /* * Pipeline Topology */ -int sof_load_pipeline_ipc(struct snd_sof_dev *sdev, +int sof_load_pipeline_ipc(struct device *dev, struct sof_ipc_pipe_new *pipeline, struct sof_ipc_comp_reply *r) { + struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct sof_ipc_pm_core_config pm_core_config; int ret; ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, sizeof(*pipeline), r, sizeof(*r)); if (ret < 0) { - dev_err(sdev->dev, "error: load pipeline ipc failure\n"); + dev_err(dev, "error: load pipeline ipc failure\n"); return ret; } /* power up the core that this pipeline is scheduled on */ ret = snd_sof_dsp_core_power_up(sdev, 1 << pipeline->core); if (ret < 0) { - dev_err(sdev->dev, "error: powering up pipeline schedule core %d\n", + dev_err(dev, "error: powering up pipeline schedule core %d\n", pipeline->core); return ret; } @@ -1487,7 +1508,7 @@ int sof_load_pipeline_ipc(struct snd_sof_dev *sdev, &pm_core_config, sizeof(pm_core_config), &pm_core_config, sizeof(pm_core_config)); if (ret < 0) - dev_err(sdev->dev, "error: core enable ipc failure\n"); + dev_err(dev, "error: core enable ipc failure\n"); return ret; } @@ -1497,7 +1518,6 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_pipe_new *pipeline; struct snd_sof_widget *comp_swidget; @@ -1514,9 +1534,9 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, pipeline->comp_id = swidget->comp_id; /* component at start of pipeline is our stream id */ - comp_swidget = snd_sof_find_swidget(sdev, tw->sname); + comp_swidget = snd_sof_find_swidget(scomp, tw->sname); if (!comp_swidget) { - dev_err(sdev->dev, "error: widget %s refers to non existent widget %s\n", + dev_err(scomp->dev, "error: widget %s refers to non existent widget %s\n", tw->name, tw->sname); ret = -EINVAL; goto err; @@ -1524,26 +1544,26 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, pipeline->sched_id = comp_swidget->comp_id; - dev_dbg(sdev->dev, "tplg: pipeline id %d comp %d scheduling comp id %d\n", + dev_dbg(scomp->dev, "tplg: pipeline id %d comp %d scheduling comp id %d\n", pipeline->pipeline_id, pipeline->comp_id, pipeline->sched_id); ret = sof_parse_tokens(scomp, pipeline, sched_tokens, ARRAY_SIZE(sched_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse pipeline tokens failed %d\n", + dev_err(scomp->dev, "error: parse pipeline tokens failed %d\n", private->size); goto err; } - dev_dbg(sdev->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d\n", + dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d\n", swidget->widget->name, pipeline->period, pipeline->priority, pipeline->period_mips, pipeline->core, pipeline->frames_per_sched); swidget->private = pipeline; /* send ipc's to create pipeline comp and power up schedule core */ - ret = sof_load_pipeline_ipc(sdev, pipeline, r); + ret = sof_load_pipeline_ipc(scomp->dev, pipeline, r); if (ret >= 0) return ret; err: @@ -1581,7 +1601,7 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse mixer.cfg tokens failed %d\n", + dev_err(scomp->dev, "error: parse mixer.cfg tokens failed %d\n", private->size); kfree(mixer); return ret; @@ -1628,7 +1648,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse mux.cfg tokens failed %d\n", + dev_err(scomp->dev, "error: parse mux.cfg tokens failed %d\n", private->size); kfree(mux); return ret; @@ -1668,7 +1688,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, return -ENOMEM; if (!le32_to_cpu(tw->num_kcontrols)) { - dev_err(sdev->dev, "error: invalid kcontrol count %d for volume\n", + dev_err(scomp->dev, "error: invalid kcontrol count %d for volume\n", tw->num_kcontrols); ret = -EINVAL; goto err; @@ -1686,7 +1706,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, ARRAY_SIZE(volume_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse volume tokens failed %d\n", + dev_err(scomp->dev, "error: parse volume tokens failed %d\n", private->size); goto err; } @@ -1694,7 +1714,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse volume.cfg tokens failed %d\n", + dev_err(scomp->dev, "error: parse volume.cfg tokens failed %d\n", le32_to_cpu(private->size)); goto err; } @@ -1754,7 +1774,7 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, ARRAY_SIZE(src_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse src tokens failed %d\n", + dev_err(scomp->dev, "error: parse src tokens failed %d\n", private->size); goto err; } @@ -1763,12 +1783,12 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse src.cfg tokens failed %d\n", + dev_err(scomp->dev, "error: parse src.cfg tokens failed %d\n", le32_to_cpu(private->size)); goto err; } - dev_dbg(sdev->dev, "src %s: source rate %d sink rate %d\n", + dev_dbg(scomp->dev, "src %s: source rate %d sink rate %d\n", swidget->widget->name, src->source_rate, src->sink_rate); sof_dbg_comp_config(scomp, &src->config); @@ -1784,6 +1804,67 @@ err: } /* + * ASRC Topology + */ + +static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_asrc *asrc; + int ret; + + asrc = kzalloc(sizeof(*asrc), GFP_KERNEL); + if (!asrc) + return -ENOMEM; + + /* configure ASRC IPC message */ + asrc->comp.hdr.size = sizeof(*asrc); + asrc->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + asrc->comp.id = swidget->comp_id; + asrc->comp.type = SOF_COMP_ASRC; + asrc->comp.pipeline_id = index; + asrc->config.hdr.size = sizeof(asrc->config); + + ret = sof_parse_tokens(scomp, asrc, asrc_tokens, + ARRAY_SIZE(asrc_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(scomp->dev, "error: parse asrc tokens failed %d\n", + private->size); + goto err; + } + + ret = sof_parse_tokens(scomp, &asrc->config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(scomp->dev, "error: parse asrc.cfg tokens failed %d\n", + le32_to_cpu(private->size)); + goto err; + } + + dev_dbg(scomp->dev, "asrc %s: source rate %d sink rate %d " + "asynch %d operation %d\n", + swidget->widget->name, asrc->source_rate, asrc->sink_rate, + asrc->asynchronous_mode, asrc->operation_mode); + sof_dbg_comp_config(scomp, &asrc->config); + + swidget->private = asrc; + + ret = sof_ipc_tx_message(sdev->ipc, asrc->comp.hdr.cmd, asrc, + sizeof(*asrc), r, sizeof(*r)); + if (ret >= 0) + return ret; +err: + kfree(asrc); + return ret; +} + +/* * Signal Generator Topology */ @@ -1813,7 +1894,7 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, ARRAY_SIZE(tone_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse tone tokens failed %d\n", + dev_err(scomp->dev, "error: parse tone tokens failed %d\n", le32_to_cpu(private->size)); goto err; } @@ -1822,12 +1903,12 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse tone.cfg tokens failed %d\n", + dev_err(scomp->dev, "error: parse tone.cfg tokens failed %d\n", le32_to_cpu(private->size)); goto err; } - dev_dbg(sdev->dev, "tone %s: frequency %d amplitude %d\n", + dev_dbg(scomp->dev, "tone %s: frequency %d amplitude %d\n", swidget->widget->name, tone->frequency, tone->amplitude); sof_dbg_comp_config(scomp, &tone->config); @@ -1842,7 +1923,7 @@ err: return ret; } -static int sof_get_control_data(struct snd_sof_dev *sdev, +static int sof_get_control_data(struct snd_soc_component *scomp, struct snd_soc_dapm_widget *widget, struct sof_widget_data *wdata, size_t *size) @@ -1872,14 +1953,14 @@ static int sof_get_control_data(struct snd_sof_dev *sdev, wdata[i].control = se->dobj.private; break; default: - dev_err(sdev->dev, "error: unknown kcontrol type %d in widget %s\n", + dev_err(scomp->dev, "error: unknown kcontrol type %d in widget %s\n", widget->dobj.widget.kcontrol_type, widget->name); return -EINVAL; } if (!wdata[i].control) { - dev_err(sdev->dev, "error: no scontrol for widget %s\n", + dev_err(scomp->dev, "error: no scontrol for widget %s\n", widget->name); return -EINVAL; } @@ -1932,7 +2013,7 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, int i; if (type == SOF_COMP_NONE) { - dev_err(sdev->dev, "error: invalid process comp type %d\n", + dev_err(scomp->dev, "error: invalid process comp type %d\n", type); return -EINVAL; } @@ -1947,7 +2028,7 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, return -ENOMEM; /* get possible component controls and get size of all pdata */ - ret = sof_get_control_data(sdev, widget, wdata, + ret = sof_get_control_data(scomp, widget, wdata, &ipc_data_size); if (ret < 0) @@ -1982,7 +2063,7 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse process.cfg tokens failed %d\n", + dev_err(scomp->dev, "error: parse process.cfg tokens failed %d\n", le32_to_cpu(private->size)); goto err; } @@ -2010,7 +2091,7 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, ipc_size, r, sizeof(*r)); if (ret < 0) { - dev_err(sdev->dev, "error: create process failed\n"); + dev_err(scomp->dev, "error: create process failed\n"); goto err; } @@ -2021,13 +2102,13 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, /* send control data with large message supported method */ for (i = 0; i < widget->num_kcontrols; i++) { wdata[i].control->readback_offset = 0; - ret = snd_sof_ipc_set_get_comp_data(sdev->ipc, wdata[i].control, + ret = snd_sof_ipc_set_get_comp_data(wdata[i].control, wdata[i].ipc_cmd, wdata[i].ctrl_type, wdata[i].control->cmd, true); if (ret != 0) { - dev_err(sdev->dev, "error: send control failed\n"); + dev_err(scomp->dev, "error: send control failed\n"); break; } } @@ -2050,14 +2131,13 @@ static int sof_widget_load_process(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_process config; int ret; /* check we have some tokens - we need at least process type */ if (le32_to_cpu(private->size) == 0) { - dev_err(sdev->dev, "error: process tokens not found\n"); + dev_err(scomp->dev, "error: process tokens not found\n"); return -EINVAL; } @@ -2068,7 +2148,7 @@ static int sof_widget_load_process(struct snd_soc_component *scomp, int index, ARRAY_SIZE(process_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse process tokens failed %d\n", + dev_err(scomp->dev, "error: parse process tokens failed %d\n", le32_to_cpu(private->size)); return ret; } @@ -2077,14 +2157,14 @@ static int sof_widget_load_process(struct snd_soc_component *scomp, int index, ret = sof_process_load(scomp, index, swidget, tw, r, find_process_comp_type(config.type)); if (ret < 0) { - dev_err(sdev->dev, "error: process loading failed\n"); + dev_err(scomp->dev, "error: process loading failed\n"); return ret; } return 0; } -static int sof_widget_bind_event(struct snd_sof_dev *sdev, +static int sof_widget_bind_event(struct snd_soc_component *scomp, struct snd_sof_widget *swidget, u16 event_type) { @@ -2110,7 +2190,7 @@ static int sof_widget_bind_event(struct snd_sof_dev *sdev, break; } - dev_err(sdev->dev, + dev_err(scomp->dev, "error: invalid event type %d for widget %s\n", event_type, swidget->widget->name); return -EINVAL; @@ -2132,7 +2212,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, if (!swidget) return -ENOMEM; - swidget->sdev = sdev; + swidget->scomp = scomp; swidget->widget = w; swidget->comp_id = sdev->next_comp_id++; swidget->complete = 0; @@ -2141,7 +2221,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, swidget->private = NULL; memset(&reply, 0, sizeof(reply)); - dev_dbg(sdev->dev, "tplg: ready widget id %d pipe %d type %d name : %s stream %s\n", + dev_dbg(scomp->dev, "tplg: ready widget id %d pipe %d type %d name : %s stream %s\n", swidget->comp_id, index, swidget->id, tw->name, strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 ? tw->sname : "none"); @@ -2197,6 +2277,9 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, case snd_soc_dapm_src: ret = sof_widget_load_src(scomp, index, swidget, tw, &reply); break; + case snd_soc_dapm_asrc: + ret = sof_widget_load_asrc(scomp, index, swidget, tw, &reply); + break; case snd_soc_dapm_siggen: ret = sof_widget_load_siggen(scomp, index, swidget, tw, &reply); break; @@ -2212,14 +2295,14 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, case snd_soc_dapm_dai_link: case snd_soc_dapm_kcontrol: default: - dev_warn(sdev->dev, "warning: widget type %d name %s not handled\n", + dev_warn(scomp->dev, "warning: widget type %d name %s not handled\n", swidget->id, tw->name); break; } /* check IPC reply */ if (ret < 0 || reply.rhdr.error < 0) { - dev_err(sdev->dev, + dev_err(scomp->dev, "error: DSP failed to add widget id %d type %d name : %s stream %s reply %d\n", tw->shift, swidget->id, tw->name, strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 @@ -2230,10 +2313,10 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, /* bind widget to external event */ if (tw->event_type) { - ret = sof_widget_bind_event(sdev, swidget, + ret = sof_widget_bind_event(scomp, swidget, le16_to_cpu(tw->event_type)); if (ret) { - dev_err(sdev->dev, "error: widget event binding failed\n"); + dev_err(scomp->dev, "error: widget event binding failed\n"); kfree(swidget->private); kfree(swidget); return ret; @@ -2301,7 +2384,7 @@ static int sof_widget_unload(struct snd_soc_component *scomp, pipeline = swidget->private; ret = snd_sof_dsp_core_power_down(sdev, 1 << pipeline->core); if (ret < 0) - dev_err(sdev->dev, "error: powering down pipeline schedule core %d\n", + dev_err(scomp->dev, "error: powering down pipeline schedule core %d\n", pipeline->core); /* update enabled cores mask */ @@ -2329,7 +2412,7 @@ static int sof_widget_unload(struct snd_soc_component *scomp, scontrol = sbe->dobj.private; break; default: - dev_warn(sdev->dev, "unsupported kcontrol_type\n"); + dev_warn(scomp->dev, "unsupported kcontrol_type\n"); goto out; } kfree(scontrol->control_data); @@ -2372,12 +2455,12 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, if (!spcm) return -ENOMEM; - spcm->sdev = sdev; + spcm->scomp = scomp; spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = COMP_ID_UNASSIGNED; spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = COMP_ID_UNASSIGNED; spcm->pcm = *pcm; - dev_dbg(sdev->dev, "tplg: load pcm %s\n", pcm->dai_name); + dev_dbg(scomp->dev, "tplg: load pcm %s\n", pcm->dai_name); dai_drv->dobj.private = spcm; list_add(&spcm->list, &sdev->pcm_list); @@ -2386,7 +2469,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, ARRAY_SIZE(stream_tokens), private->array, le32_to_cpu(private->size)); if (ret) { - dev_err(sdev->dev, "error: parse stream tokens failed %d\n", + dev_err(scomp->dev, "error: parse stream tokens failed %d\n", le32_to_cpu(private->size)); return ret; } @@ -2395,7 +2478,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, if (!spcm->pcm.playback) goto capture; - dev_vdbg(sdev->dev, "tplg: pcm %s stream tokens: playback d0i3:%d\n", + dev_vdbg(scomp->dev, "tplg: pcm %s stream tokens: playback d0i3:%d\n", spcm->pcm.pcm_name, spcm->stream[0].d0i3_compatible); caps = &spcm->pcm.caps[stream]; @@ -2404,16 +2487,16 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, PAGE_SIZE, &spcm->stream[stream].page_table); if (ret < 0) { - dev_err(sdev->dev, "error: can't alloc page table for %s %d\n", + dev_err(scomp->dev, "error: can't alloc page table for %s %d\n", caps->name, ret); return ret; } /* bind pcm to host comp */ - ret = spcm_bind(sdev, spcm, stream); + ret = spcm_bind(scomp, spcm, stream); if (ret) { - dev_err(sdev->dev, + dev_err(scomp->dev, "error: can't bind pcm to host\n"); goto free_playback_tables; } @@ -2425,7 +2508,7 @@ capture: if (!spcm->pcm.capture) return ret; - dev_vdbg(sdev->dev, "tplg: pcm %s stream tokens: capture d0i3:%d\n", + dev_vdbg(scomp->dev, "tplg: pcm %s stream tokens: capture d0i3:%d\n", spcm->pcm.pcm_name, spcm->stream[1].d0i3_compatible); caps = &spcm->pcm.caps[stream]; @@ -2434,15 +2517,15 @@ capture: ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, PAGE_SIZE, &spcm->stream[stream].page_table); if (ret < 0) { - dev_err(sdev->dev, "error: can't alloc page table for %s %d\n", + dev_err(scomp->dev, "error: can't alloc page table for %s %d\n", caps->name, ret); goto free_playback_tables; } /* bind pcm to host comp */ - ret = spcm_bind(sdev, spcm, stream); + ret = spcm_bind(scomp, spcm, stream); if (ret) { - dev_err(sdev->dev, + dev_err(scomp->dev, "error: can't bind pcm to host\n"); snd_dma_free_pages(&spcm->stream[stream].page_table); goto free_playback_tables; @@ -2568,7 +2651,7 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, ARRAY_SIZE(ssp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse ssp tokens failed %d\n", + dev_err(scomp->dev, "error: parse ssp tokens failed %d\n", le32_to_cpu(private->size)); return ret; } @@ -2582,7 +2665,7 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, config->ssp.rx_slots = le32_to_cpu(hw_config->rx_slots); config->ssp.tx_slots = le32_to_cpu(hw_config->tx_slots); - dev_dbg(sdev->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d\n", + dev_dbg(scomp->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d\n", config->dai_index, config->format, config->ssp.mclk_rate, config->ssp.bclk_rate, config->ssp.fsync_rate, config->ssp.sample_valid_bits, @@ -2591,13 +2674,13 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, /* validate SSP fsync rate and channel count */ if (config->ssp.fsync_rate < 8000 || config->ssp.fsync_rate > 192000) { - dev_err(sdev->dev, "error: invalid fsync rate for SSP%d\n", + dev_err(scomp->dev, "error: invalid fsync rate for SSP%d\n", config->dai_index); return -EINVAL; } if (config->ssp.tdm_slots < 1 || config->ssp.tdm_slots > 8) { - dev_err(sdev->dev, "error: invalid channel count for SSP%d\n", + dev_err(scomp->dev, "error: invalid channel count for SSP%d\n", config->dai_index); return -EINVAL; } @@ -2608,7 +2691,7 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, sizeof(reply)); if (ret < 0) { - dev_err(sdev->dev, "error: failed to set DAI config for SSP%d\n", + dev_err(scomp->dev, "error: failed to set DAI config for SSP%d\n", config->dai_index); return ret; } @@ -2616,7 +2699,7 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, /* set config for all DAI's with name matching the link name */ ret = sof_set_dai_config(sdev, size, link, config); if (ret < 0) - dev_err(sdev->dev, "error: failed to save DAI config for SSP%d\n", + dev_err(scomp->dev, "error: failed to save DAI config for SSP%d\n", config->dai_index); return ret; @@ -2628,8 +2711,66 @@ static int sof_link_sai_load(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_hw_config *hw_config, struct sof_ipc_dai_config *config) { - /*TODO: Add implementation */ - return 0; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_reply reply; + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->sai, 0, sizeof(struct sof_ipc_dai_sai_params)); + config->hdr.size = size; + + ret = sof_parse_tokens(scomp, &config->sai, sai_tokens, + ARRAY_SIZE(sai_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(scomp->dev, "error: parse sai tokens failed %d\n", + le32_to_cpu(private->size)); + return ret; + } + + config->sai.mclk_rate = le32_to_cpu(hw_config->mclk_rate); + config->sai.mclk_direction = hw_config->mclk_direction; + + config->sai.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + config->sai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); + config->sai.rx_slots = le32_to_cpu(hw_config->rx_slots); + config->sai.tx_slots = le32_to_cpu(hw_config->tx_slots); + + dev_info(scomp->dev, + "tplg: config SAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n", + config->dai_index, config->format, + config->sai.mclk_rate, config->sai.tdm_slot_width, + config->sai.tdm_slots, config->sai.mclk_id); + + if (config->sai.tdm_slots < 1 || config->sai.tdm_slots > 8) { + dev_err(scomp->dev, "error: invalid channel count for SAI%d\n", + config->dai_index); + return -EINVAL; + } + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, size, &reply, + sizeof(reply)); + + if (ret < 0) { + dev_err(scomp->dev, "error: failed to set DAI config for SAI%d\n", + config->dai_index); + return ret; + } + + /* set config for all DAI's with name matching the link name */ + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(scomp->dev, "error: failed to save DAI config for SAI%d\n", + config->dai_index); + + return ret; } static int sof_link_esai_load(struct snd_soc_component *scomp, int index, @@ -2655,7 +2796,7 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index, ARRAY_SIZE(esai_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse esai tokens failed %d\n", + dev_err(scomp->dev, "error: parse esai tokens failed %d\n", le32_to_cpu(private->size)); return ret; } @@ -2669,14 +2810,14 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index, config->esai.rx_slots = le32_to_cpu(hw_config->rx_slots); config->esai.tx_slots = le32_to_cpu(hw_config->tx_slots); - dev_info(sdev->dev, + dev_info(scomp->dev, "tplg: config ESAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n", config->dai_index, config->format, config->esai.mclk_rate, config->esai.tdm_slot_width, config->esai.tdm_slots, config->esai.mclk_id); if (config->esai.tdm_slots < 1 || config->esai.tdm_slots > 8) { - dev_err(sdev->dev, "error: invalid channel count for ESAI%d\n", + dev_err(scomp->dev, "error: invalid channel count for ESAI%d\n", config->dai_index); return -EINVAL; } @@ -2686,7 +2827,7 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index, config->hdr.cmd, config, size, &reply, sizeof(reply)); if (ret < 0) { - dev_err(sdev->dev, "error: failed to set DAI config for ESAI%d\n", + dev_err(scomp->dev, "error: failed to set DAI config for ESAI%d\n", config->dai_index); return ret; } @@ -2694,7 +2835,7 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index, /* set config for all DAI's with name matching the link name */ ret = sof_set_dai_config(sdev, size, link, config); if (ret < 0) - dev_err(sdev->dev, "error: failed to save DAI config for ESAI%d\n", + dev_err(scomp->dev, "error: failed to save DAI config for ESAI%d\n", config->dai_index); return ret; @@ -2727,7 +2868,7 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, ARRAY_SIZE(dmic_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse dmic tokens failed %d\n", + dev_err(scomp->dev, "error: parse dmic tokens failed %d\n", le32_to_cpu(private->size)); return ret; } @@ -2762,7 +2903,7 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, ARRAY_SIZE(dmic_pdm_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse dmic pdm tokens failed %d\n", + dev_err(scomp->dev, "error: parse dmic pdm tokens failed %d\n", le32_to_cpu(private->size)); goto err; } @@ -2771,27 +2912,27 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, ipc_config->hdr.size = size; /* debug messages */ - dev_dbg(sdev->dev, "tplg: config DMIC%d driver version %d\n", + dev_dbg(scomp->dev, "tplg: config DMIC%d driver version %d\n", ipc_config->dai_index, ipc_config->dmic.driver_ipc_version); - dev_dbg(sdev->dev, "pdmclk_min %d pdm_clkmax %d duty_min %hd\n", + dev_dbg(scomp->dev, "pdmclk_min %d pdm_clkmax %d duty_min %hd\n", ipc_config->dmic.pdmclk_min, ipc_config->dmic.pdmclk_max, ipc_config->dmic.duty_min); - dev_dbg(sdev->dev, "duty_max %hd fifo_fs %d num_pdms active %d\n", + dev_dbg(scomp->dev, "duty_max %hd fifo_fs %d num_pdms active %d\n", ipc_config->dmic.duty_max, ipc_config->dmic.fifo_fs, ipc_config->dmic.num_pdm_active); - dev_dbg(sdev->dev, "fifo word length %hd\n", + dev_dbg(scomp->dev, "fifo word length %hd\n", ipc_config->dmic.fifo_bits); for (j = 0; j < ipc_config->dmic.num_pdm_active; j++) { - dev_dbg(sdev->dev, "pdm %hd mic a %hd mic b %hd\n", + dev_dbg(scomp->dev, "pdm %hd mic a %hd mic b %hd\n", ipc_config->dmic.pdm[j].id, ipc_config->dmic.pdm[j].enable_mic_a, ipc_config->dmic.pdm[j].enable_mic_b); - dev_dbg(sdev->dev, "pdm %hd polarity a %hd polarity b %hd\n", + dev_dbg(scomp->dev, "pdm %hd polarity a %hd polarity b %hd\n", ipc_config->dmic.pdm[j].id, ipc_config->dmic.pdm[j].polarity_mic_a, ipc_config->dmic.pdm[j].polarity_mic_b); - dev_dbg(sdev->dev, "pdm %hd clk_edge %hd skew %hd\n", + dev_dbg(scomp->dev, "pdm %hd clk_edge %hd skew %hd\n", ipc_config->dmic.pdm[j].id, ipc_config->dmic.pdm[j].clk_edge, ipc_config->dmic.pdm[j].skew); @@ -2808,7 +2949,7 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, sizeof(reply)); if (ret < 0) { - dev_err(sdev->dev, + dev_err(scomp->dev, "error: failed to set DAI config for DMIC%d\n", config->dai_index); goto err; @@ -2817,7 +2958,7 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, /* set config for all DAI's with name matching the link name */ ret = sof_set_dai_config(sdev, size, link, ipc_config); if (ret < 0) - dev_err(sdev->dev, "error: failed to save DAI config for DMIC%d\n", + dev_err(scomp->dev, "error: failed to save DAI config for DMIC%d\n", config->dai_index); err: @@ -2908,21 +3049,21 @@ static int sof_link_hda_load(struct snd_soc_component *scomp, int index, ARRAY_SIZE(hda_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse hda tokens failed %d\n", + dev_err(scomp->dev, "error: parse hda tokens failed %d\n", le32_to_cpu(private->size)); return ret; } dai = snd_soc_find_dai(link->cpus); if (!dai) { - dev_err(sdev->dev, "error: failed to find dai %s in %s", + dev_err(scomp->dev, "error: failed to find dai %s in %s", link->cpus->dai_name, __func__); return -EINVAL; } ret = sof_link_hda_process(sdev, link, config); if (ret < 0) - dev_err(sdev->dev, "error: failed to process hda dai link %s", + dev_err(scomp->dev, "error: failed to process hda dai link %s", link->name); return ret; @@ -2948,7 +3089,7 @@ static int sof_link_alh_load(struct snd_soc_component *scomp, int index, sizeof(reply)); if (ret < 0) { - dev_err(sdev->dev, "error: failed to set DAI config for ALH %d\n", + dev_err(scomp->dev, "error: failed to set DAI config for ALH %d\n", config->dai_index); return ret; } @@ -2956,7 +3097,7 @@ static int sof_link_alh_load(struct snd_soc_component *scomp, int index, /* set config for all DAI's with name matching the link name */ ret = sof_set_dai_config(sdev, size, link, config); if (ret < 0) - dev_err(sdev->dev, "error: failed to save DAI config for ALH %d\n", + dev_err(scomp->dev, "error: failed to save DAI config for ALH %d\n", config->dai_index); return ret; @@ -2967,7 +3108,6 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &cfg->priv; struct sof_ipc_dai_config config; struct snd_soc_tplg_hw_config *hw_config; @@ -2976,10 +3116,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, int i = 0; if (!link->platforms) { - dev_err(sdev->dev, "error: no platforms\n"); + dev_err(scomp->dev, "error: no platforms\n"); return -EINVAL; } - link->platforms->name = dev_name(sdev->dev); + link->platforms->name = dev_name(scomp->dev); /* * Set nonatomic property for FE dai links as their trigger action @@ -2998,7 +3138,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, /* check we have some tokens - we need at least DAI type */ if (le32_to_cpu(private->size) == 0) { - dev_err(sdev->dev, "error: expected tokens for DAI, none found\n"); + dev_err(scomp->dev, "error: expected tokens for DAI, none found\n"); return -EINVAL; } @@ -3010,7 +3150,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, ARRAY_SIZE(dai_link_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse link tokens failed %d\n", + dev_err(scomp->dev, "error: parse link tokens failed %d\n", le32_to_cpu(private->size)); return ret; } @@ -3022,12 +3162,12 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, num_hw_configs = le32_to_cpu(cfg->num_hw_configs); if (!num_hw_configs) { if (config.type != SOF_DAI_INTEL_HDA) { - dev_err(sdev->dev, "error: unexpected DAI config count %d!\n", + dev_err(scomp->dev, "error: unexpected DAI config count %d!\n", le32_to_cpu(cfg->num_hw_configs)); return -EINVAL; } } else { - dev_dbg(sdev->dev, "tplg: %d hw_configs found, default id: %d!\n", + dev_dbg(scomp->dev, "tplg: %d hw_configs found, default id: %d!\n", cfg->num_hw_configs, le32_to_cpu(cfg->default_hw_config_id)); for (i = 0; i < num_hw_configs; i++) { @@ -3036,7 +3176,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, } if (i == num_hw_configs) { - dev_err(sdev->dev, "error: default hw_config id: %d not found!\n", + dev_err(scomp->dev, "error: default hw_config id: %d not found!\n", le32_to_cpu(cfg->default_hw_config_id)); return -EINVAL; } @@ -3075,7 +3215,8 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, &config); break; default: - dev_err(sdev->dev, "error: invalid DAI type %d\n", config.type); + dev_err(scomp->dev, "error: invalid DAI type %d\n", + config.type); ret = -EINVAL; break; } @@ -3123,7 +3264,7 @@ static int sof_link_unload(struct snd_soc_component *scomp, goto found; } - dev_err(sdev->dev, "error: failed to find dai %s in %s", + dev_err(scomp->dev, "error: failed to find dai %s in %s", link->name, __func__); return -EINVAL; found: @@ -3140,7 +3281,7 @@ found: ret = sof_link_hda_unload(sdev, link); break; default: - dev_err(sdev->dev, "error: invalid DAI type %d\n", + dev_err(scomp->dev, "error: invalid DAI type %d\n", sof_dai->dai_config->type); ret = -EINVAL; break; @@ -3166,7 +3307,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, if (!sroute) return -ENOMEM; - sroute->sdev = sdev; + sroute->scomp = scomp; connect = kzalloc(sizeof(*connect), GFP_KERNEL); if (!connect) { @@ -3177,14 +3318,14 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, connect->hdr.size = sizeof(*connect); connect->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; - dev_dbg(sdev->dev, "sink %s control %s source %s\n", + dev_dbg(scomp->dev, "sink %s control %s source %s\n", route->sink, route->control ? route->control : "none", route->source); /* source component */ - source_swidget = snd_sof_find_swidget(sdev, (char *)route->source); + source_swidget = snd_sof_find_swidget(scomp, (char *)route->source); if (!source_swidget) { - dev_err(sdev->dev, "error: source %s not found\n", + dev_err(scomp->dev, "error: source %s not found\n", route->source); ret = -EINVAL; goto err; @@ -3203,9 +3344,9 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, connect->source_id = source_swidget->comp_id; /* sink component */ - sink_swidget = snd_sof_find_swidget(sdev, (char *)route->sink); + sink_swidget = snd_sof_find_swidget(scomp, (char *)route->sink); if (!sink_swidget) { - dev_err(sdev->dev, "error: sink %s not found\n", + dev_err(scomp->dev, "error: sink %s not found\n", route->sink); ret = -EINVAL; goto err; @@ -3229,7 +3370,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, */ if (source_swidget->id != snd_soc_dapm_buffer && sink_swidget->id != snd_soc_dapm_buffer) { - dev_dbg(sdev->dev, "warning: neither Linked source component %s nor sink component %s is of buffer type, ignoring link\n", + dev_dbg(scomp->dev, "warning: neither Linked source component %s nor sink component %s is of buffer type, ignoring link\n", route->source, route->sink); ret = 0; goto err; @@ -3241,7 +3382,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, /* check IPC return value */ if (ret < 0) { - dev_err(sdev->dev, "error: failed to add route sink %s control %s source %s\n", + dev_err(scomp->dev, "error: failed to add route sink %s control %s source %s\n", route->sink, route->control ? route->control : "none", route->source); @@ -3250,7 +3391,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, /* check IPC reply */ if (reply.error < 0) { - dev_err(sdev->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n", + dev_err(scomp->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n", route->sink, route->control ? route->control : "none", route->source, reply.error); @@ -3277,8 +3418,9 @@ err: /* Function to set the initial value of SOF kcontrols. * The value will be stored in scontrol->control_data */ -static int snd_sof_cache_kcontrol_val(struct snd_sof_dev *sdev) +static int snd_sof_cache_kcontrol_val(struct snd_soc_component *scomp) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_control *scontrol = NULL; int ipc_cmd, ctrl_type; int ret = 0; @@ -3298,33 +3440,34 @@ static int snd_sof_cache_kcontrol_val(struct snd_sof_dev *sdev) ctrl_type = SOF_CTRL_TYPE_DATA_GET; break; default: - dev_err(sdev->dev, + dev_err(scomp->dev, "error: Invalid scontrol->cmd: %d\n", scontrol->cmd); return -EINVAL; } - ret = snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol, + ret = snd_sof_ipc_set_get_comp_data(scontrol, ipc_cmd, ctrl_type, scontrol->cmd, false); if (ret < 0) { - dev_warn(sdev->dev, - "error: kcontrol value get for widget: %d\n", - scontrol->comp_id); + dev_warn(scomp->dev, + "error: kcontrol value get for widget: %d\n", + scontrol->comp_id); } } return ret; } -int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, +int snd_sof_complete_pipeline(struct device *dev, struct snd_sof_widget *swidget) { + struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct sof_ipc_pipe_ready ready; struct sof_ipc_reply reply; int ret; - dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n", + dev_dbg(dev, "tplg: complete pipeline %s id %d\n", swidget->widget->name, swidget->comp_id); memset(&ready, 0, sizeof(ready)); @@ -3354,7 +3497,7 @@ static void sof_complete(struct snd_soc_component *scomp) switch (swidget->id) { case snd_soc_dapm_scheduler: swidget->complete = - snd_sof_complete_pipeline(sdev, swidget); + snd_sof_complete_pipeline(scomp->dev, swidget); break; default: break; @@ -3364,14 +3507,13 @@ static void sof_complete(struct snd_soc_component *scomp) * cache initial values of SOF kcontrols by reading DSP value over * IPC. It may be overwritten by alsa-mixer after booting up */ - snd_sof_cache_kcontrol_val(sdev); + snd_sof_cache_kcontrol_val(scomp); } /* manifest - optional to inform component of manifest */ static int sof_manifest(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_manifest *man) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); u32 size; u32 abi_version; @@ -3379,16 +3521,16 @@ static int sof_manifest(struct snd_soc_component *scomp, int index, /* backward compatible with tplg without ABI info */ if (!size) { - dev_dbg(sdev->dev, "No topology ABI info\n"); + dev_dbg(scomp->dev, "No topology ABI info\n"); return 0; } if (size != SOF_TPLG_ABI_SIZE) { - dev_err(sdev->dev, "error: invalid topology ABI size\n"); + dev_err(scomp->dev, "error: invalid topology ABI size\n"); return -EINVAL; } - dev_info(sdev->dev, + dev_info(scomp->dev, "Topology: ABI %d:%d:%d Kernel ABI %d:%d:%d\n", man->priv.data[0], man->priv.data[1], man->priv.data[2], SOF_ABI_MAJOR, SOF_ABI_MINOR, @@ -3399,15 +3541,15 @@ static int sof_manifest(struct snd_soc_component *scomp, int index, man->priv.data[2]); if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, abi_version)) { - dev_err(sdev->dev, "error: incompatible topology ABI version\n"); + dev_err(scomp->dev, "error: incompatible topology ABI version\n"); return -EINVAL; } if (abi_version > SOF_ABI_VERSION) { if (!IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS)) { - dev_warn(sdev->dev, "warn: topology ABI is more recent than kernel\n"); + dev_warn(scomp->dev, "warn: topology ABI is more recent than kernel\n"); } else { - dev_err(sdev->dev, "error: topology ABI is more recent than kernel\n"); + dev_err(scomp->dev, "error: topology ABI is more recent than kernel\n"); return -EINVAL; } } @@ -3465,34 +3607,25 @@ static struct snd_soc_tplg_ops sof_tplg_ops = { .bytes_ext_ops_count = ARRAY_SIZE(sof_bytes_ext_ops), }; -int snd_sof_init_topology(struct snd_sof_dev *sdev, - struct snd_soc_tplg_ops *ops) -{ - /* TODO: support linked list of topologies */ - sdev->tplg_ops = ops; - return 0; -} -EXPORT_SYMBOL(snd_sof_init_topology); - -int snd_sof_load_topology(struct snd_sof_dev *sdev, const char *file) +int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) { const struct firmware *fw; int ret; - dev_dbg(sdev->dev, "loading topology:%s\n", file); + dev_dbg(scomp->dev, "loading topology:%s\n", file); - ret = request_firmware(&fw, file, sdev->dev); + ret = request_firmware(&fw, file, scomp->dev); if (ret < 0) { - dev_err(sdev->dev, "error: tplg request firmware %s failed err: %d\n", + dev_err(scomp->dev, "error: tplg request firmware %s failed err: %d\n", file, ret); return ret; } - ret = snd_soc_tplg_component_load(sdev->component, + ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw, SND_SOC_TPLG_INDEX_ALL); if (ret < 0) { - dev_err(sdev->dev, "error: tplg component load failed %d\n", + dev_err(scomp->dev, "error: tplg component load failed %d\n", ret); ret = -EINVAL; } diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index b0e4556c8536..4bb65030819d 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -250,8 +250,8 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev) } /* create compressed page table for audio firmware */ - ret = snd_sof_create_page_table(sdev, &sdev->dmatb, sdev->dmatp.area, - sdev->dmatb.bytes); + ret = snd_sof_create_page_table(sdev->dev, &sdev->dmatb, + sdev->dmatp.area, sdev->dmatb.bytes); if (ret < 0) goto table_err; diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/utils.c index 2ac4c3da0320..9831eb57df6c 100644 --- a/sound/soc/sof/utils.c +++ b/sound/soc/sof/utils.c @@ -10,6 +10,7 @@ #include <linux/io-64-nonatomic-lo-hi.h> #include <linux/platform_device.h> +#include <asm/unaligned.h> #include <sound/soc.h> #include <sound/sof.h> #include "sof-priv.h" @@ -110,3 +111,62 @@ void sof_block_read(struct snd_sof_dev *sdev, u32 bar, u32 offset, void *dest, memcpy_fromio(dest, src, size); } EXPORT_SYMBOL(sof_block_read); + +/* + * Generic buffer page table creation. + * Take the each physical page address and drop the least significant unused + * bits from each (based on PAGE_SIZE). Then pack valid page address bits + * into compressed page table. + */ + +int snd_sof_create_page_table(struct device *dev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size) +{ + int i, pages; + + pages = snd_sgbuf_aligned_pages(size); + + dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n", + dmab->area, size, pages); + + for (i = 0; i < pages; i++) { + /* + * The number of valid address bits for each page is 20. + * idx determines the byte position within page_table + * where the current page's address is stored + * in the compressed page_table. + * This can be calculated by multiplying the page number by 2.5. + */ + u32 idx = (5 * i) >> 1; + u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; + u8 *pg_table; + + dev_vdbg(dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); + + pg_table = (u8 *)(page_table + idx); + + /* + * pagetable compression: + * byte 0 byte 1 byte 2 byte 3 byte 4 byte 5 + * ___________pfn 0__________ __________pfn 1___________ _pfn 2... + * .... .... .... .... .... .... .... .... .... .... .... + * It is created by: + * 1. set current location to 0, PFN index i to 0 + * 2. put pfn[i] at current location in Little Endian byte order + * 3. calculate an intermediate value as + * x = (pfn[i+1] << 4) | (pfn[i] & 0xf) + * 4. put x at offset (current location + 2) in LE byte order + * 5. increment current location by 5 bytes, increment i by 2 + * 6. continue to (2) + */ + if (i & 1) + put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4, + pg_table); + else + put_unaligned_le32(pfn, pg_table); + } + + return pages; +} +EXPORT_SYMBOL(snd_sof_create_page_table); diff --git a/sound/soc/sof/xtensa/core.c b/sound/soc/sof/xtensa/core.c index 46a4905a9dce..ea08651f0bb3 100644 --- a/sound/soc/sof/xtensa/core.c +++ b/sound/soc/sof/xtensa/core.c @@ -132,7 +132,7 @@ const struct sof_arch_ops sof_xtensa_arch_ops = { .dsp_oops = xtensa_dsp_oops, .dsp_stack = xtensa_stack, }; -EXPORT_SYMBOL(sof_xtensa_arch_ops); +EXPORT_SYMBOL_NS(sof_xtensa_arch_ops, SND_SOC_SOF_XTENSA); MODULE_DESCRIPTION("SOF Xtensa DSP support"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c index da4b8f5f192b..2284558684bc 100644 --- a/sound/soc/sprd/sprd-pcm-dma.c +++ b/sound/soc/sprd/sprd-pcm-dma.c @@ -508,7 +508,6 @@ static const struct snd_soc_component_driver sprd_soc_component = { .name = DRV_NAME, .open = sprd_pcm_open, .close = sprd_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = sprd_pcm_hw_params, .hw_free = sprd_pcm_hw_free, .trigger = sprd_pcm_trigger, diff --git a/sound/soc/sti/sti_uniperif.c b/sound/soc/sti/sti_uniperif.c index ee4a0151e63e..7b9169f04d6e 100644 --- a/sound/soc/sti/sti_uniperif.c +++ b/sound/soc/sti/sti_uniperif.c @@ -308,9 +308,9 @@ int sti_uniperiph_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static int sti_uniperiph_dai_suspend(struct snd_soc_dai *dai) +static int sti_uniperiph_suspend(struct snd_soc_component *component) { - struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct sti_uniperiph_data *priv = snd_soc_component_get_drvdata(component); struct uniperif *uni = priv->dai_data.uni; int ret; @@ -330,9 +330,9 @@ static int sti_uniperiph_dai_suspend(struct snd_soc_dai *dai) return ret; } -static int sti_uniperiph_dai_resume(struct snd_soc_dai *dai) +static int sti_uniperiph_resume(struct snd_soc_component *component) { - struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); + struct sti_uniperiph_data *priv = snd_soc_component_get_drvdata(component); struct uniperif *uni = priv->dai_data.uni; int ret; @@ -370,12 +370,12 @@ static int sti_uniperiph_dai_probe(struct snd_soc_dai *dai) static const struct snd_soc_dai_driver sti_uniperiph_dai_template = { .probe = sti_uniperiph_dai_probe, - .suspend = sti_uniperiph_dai_suspend, - .resume = sti_uniperiph_dai_resume }; static const struct snd_soc_component_driver sti_uniperiph_dai_component = { .name = "sti_cpu_dai", + .suspend = sti_uniperiph_suspend, + .resume = sti_uniperiph_resume }; static int sti_uniperiph_cpu_dai_of(struct device_node *node, diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index 08696a4adb69..51407a21c440 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -252,7 +252,6 @@ static int stm32_adfsdm_pcm_close(struct snd_soc_component *component, struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); - snd_pcm_lib_free_pages(substream); priv->substream = NULL; return 0; @@ -276,25 +275,13 @@ static int stm32_adfsdm_pcm_hw_params(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); - int ret; - ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); - if (ret < 0) - return ret; priv->pcm_buff = substream->runtime->dma_area; return iio_channel_cb_set_buffer_watermark(priv->iio_cb, params_period_size(params)); } -static int stm32_adfsdm_pcm_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - snd_pcm_lib_free_pages(substream); - - return 0; -} - static int stm32_adfsdm_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { @@ -303,30 +290,18 @@ static int stm32_adfsdm_pcm_new(struct snd_soc_component *component, snd_soc_dai_get_drvdata(rtd->cpu_dai); unsigned int size = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE; - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - priv->dev, size, size); + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, + priv->dev, size, size); return 0; } -static void stm32_adfsdm_pcm_free(struct snd_soc_component *component, - struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - - substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - if (substream) - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static struct snd_soc_component_driver stm32_adfsdm_soc_platform = { .open = stm32_adfsdm_pcm_open, .close = stm32_adfsdm_pcm_close, .hw_params = stm32_adfsdm_pcm_hw_params, - .hw_free = stm32_adfsdm_pcm_hw_free, .trigger = stm32_adfsdm_trigger, .pointer = stm32_adfsdm_pcm_pointer, .pcm_construct = stm32_adfsdm_pcm_new, - .pcm_destruct = stm32_adfsdm_pcm_free, }; static const struct of_device_id stm32_adfsdm_of_match[] = { diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index 09c8516d7c4d..06c728ae17ed 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -219,7 +219,6 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver tegra20_ac97_dai = { .name = "tegra-ac97-pcm", - .bus_control = true, .probe = tegra20_ac97_probe, .playback = { .stream_name = "PCM Playback", diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index 6211dfda2195..f08d3489c3cf 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -186,7 +186,7 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) static int tegra_wm8903_remove(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd = - snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + snd_soc_get_pcm_runtime(card, &card->dai_link[0]); struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_component *component = codec_dai->component; diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 8e5371801d88..e1e937eb1dc1 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -664,18 +664,39 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); pm_runtime_get_sync(mcasp->dev); - if (dir == SND_SOC_CLOCK_OUT) { + + if (dir == SND_SOC_CLOCK_IN) { + switch (clk_id) { + case MCASP_CLK_HCLK_AHCLK: + mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRE); + clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + break; + case MCASP_CLK_HCLK_AUXCLK: + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRE); + set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + break; + default: + dev_err(mcasp->dev, "Invalid clk id: %d\n", clk_id); + goto out; + } + } else { + /* Select AUXCLK as HCLK */ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); - } else { - mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); - clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir); } - + /* + * When AHCLK X/R is selected to be output it means that the HCLK is + * the same clock - coming via AUXCLK. + */ mcasp->sysclk_freq = freq; - +out: pm_runtime_put(mcasp->dev); return 0; } diff --git a/sound/soc/ti/davinci-mcasp.h b/sound/soc/ti/davinci-mcasp.h index bc705d6ca48b..5de2b8a31061 100644 --- a/sound/soc/ti/davinci-mcasp.h +++ b/sound/soc/ti/davinci-mcasp.h @@ -295,6 +295,10 @@ #define NUMEVT(x) (((x) & 0xFF) << 8) #define NUMDMA_MASK (0xFF) +/* Source of High-frequency transmit/receive clock */ +#define MCASP_CLK_HCLK_AHCLK 0 /* AHCLKX/R */ +#define MCASP_CLK_HCLK_AUXCLK 1 /* Internal functional clock */ + /* clock divider IDs */ #define MCASP_CLKDIV_AUXCLK 0 /* HCLK divider from AUXCLK */ #define MCASP_CLKDIV_BCLK 1 /* BCLK divider from HCLK */ diff --git a/sound/soc/ti/omap-mcpdm.c b/sound/soc/ti/omap-mcpdm.c index b8c8290265c7..a726cd7a8252 100644 --- a/sound/soc/ti/omap-mcpdm.c +++ b/sound/soc/ti/omap-mcpdm.c @@ -458,11 +458,11 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai) } #ifdef CONFIG_PM_SLEEP -static int omap_mcpdm_suspend(struct snd_soc_dai *dai) +static int omap_mcpdm_suspend(struct snd_soc_component *component) { - struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); + struct omap_mcpdm *mcpdm = snd_soc_component_get_drvdata(component); - if (dai->active) { + if (component->active) { omap_mcpdm_stop(mcpdm); omap_mcpdm_close_streams(mcpdm); } @@ -476,15 +476,15 @@ static int omap_mcpdm_suspend(struct snd_soc_dai *dai) return 0; } -static int omap_mcpdm_resume(struct snd_soc_dai *dai) +static int omap_mcpdm_resume(struct snd_soc_component *component) { - struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); + struct omap_mcpdm *mcpdm = snd_soc_component_get_drvdata(component); if (mcpdm->pm_active_count) { while (mcpdm->pm_active_count--) pm_runtime_get_sync(mcpdm->dev); - if (dai->active) { + if (component->active) { omap_mcpdm_open_streams(mcpdm); omap_mcpdm_start(mcpdm); } @@ -504,8 +504,6 @@ static int omap_mcpdm_resume(struct snd_soc_dai *dai) static struct snd_soc_dai_driver omap_mcpdm_dai = { .probe = omap_mcpdm_probe, .remove = omap_mcpdm_remove, - .suspend = omap_mcpdm_suspend, - .resume = omap_mcpdm_resume, .probe_order = SND_SOC_COMP_ORDER_LATE, .remove_order = SND_SOC_COMP_ORDER_EARLY, .playback = { @@ -527,6 +525,8 @@ static struct snd_soc_dai_driver omap_mcpdm_dai = { static const struct snd_soc_component_driver omap_mcpdm_component = { .name = "omap-mcpdm", + .suspend = omap_mcpdm_suspend, + .resume = omap_mcpdm_resume, }; void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd, diff --git a/sound/soc/ti/rx51.c b/sound/soc/ti/rx51.c index 588f680a9c24..fdb0dc85fe67 100644 --- a/sound/soc/ti/rx51.c +++ b/sound/soc/ti/rx51.c @@ -328,11 +328,11 @@ static struct snd_soc_aux_dev rx51_aux_dev[] = { static struct snd_soc_codec_conf rx51_codec_conf[] = { { - .dev_name = "tlv320aic3x-codec.2-0019", + .dlc = COMP_CODEC_CONF("tlv320aic3x-codec.2-0019"), .name_prefix = "b", }, { - .dev_name = "tpa6130a2.2-0060", + .dlc = COMP_CODEC_CONF("tpa6130a2.2-0060"), .name_prefix = "TPA6130A2", }, }; @@ -397,8 +397,8 @@ static int rx51_soc_probe(struct platform_device *pdev) } rx51_aux_dev[0].dlc.name = NULL; rx51_aux_dev[0].dlc.of_node = dai_node; - rx51_codec_conf[0].dev_name = NULL; - rx51_codec_conf[0].of_node = dai_node; + rx51_codec_conf[0].dlc.name = NULL; + rx51_codec_conf[0].dlc.of_node = dai_node; dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0); if (!dai_node) { @@ -407,8 +407,8 @@ static int rx51_soc_probe(struct platform_device *pdev) } rx51_aux_dev[1].dlc.name = NULL; rx51_aux_dev[1].dlc.of_node = dai_node; - rx51_codec_conf[1].dev_name = NULL; - rx51_codec_conf[1].of_node = dai_node; + rx51_codec_conf[1].dlc.name = NULL; + rx51_codec_conf[1].dlc.of_node = dai_node; } pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c index bfaa9b3fda43..b1d9615f2375 100644 --- a/sound/soc/txx9/txx9aclc-ac97.c +++ b/sound/soc/txx9/txx9aclc-ac97.c @@ -148,7 +148,6 @@ static int txx9aclc_ac97_remove(struct snd_soc_dai *dai) } static struct snd_soc_dai_driver txx9aclc_ac97_dai = { - .bus_control = true, .probe = txx9aclc_ac97_probe, .remove = txx9aclc_ac97_remove, .playback = { diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index 33c78d33e5a1..985487cc3a55 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -51,14 +51,8 @@ static int txx9aclc_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct txx9aclc_dmadata *dmadata = runtime->private_data; - int ret; - - ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); - if (ret < 0) - return ret; dev_dbg(component->dev, "runtime->dma_area = %#lx dma_addr = %#lx dma_bytes = %zd " @@ -76,12 +70,6 @@ static int txx9aclc_pcm_hw_params(struct snd_soc_component *component, return 0; } -static int txx9aclc_pcm_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); -} - static int txx9aclc_pcm_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -306,7 +294,7 @@ static int txx9aclc_pcm_new(struct snd_soc_component *component, goto exit; } - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev, 64 * 1024, 4 * 1024 * 1024); return 0; @@ -406,9 +394,7 @@ static const struct snd_soc_component_driver txx9aclc_soc_component = { .remove = txx9aclc_pcm_remove, .open = txx9aclc_pcm_open, .close = txx9aclc_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = txx9aclc_pcm_hw_params, - .hw_free = txx9aclc_pcm_hw_free, .prepare = txx9aclc_pcm_prepare, .trigger = txx9aclc_pcm_trigger, .pointer = txx9aclc_pcm_pointer, diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c index 2ae582a99b63..fdaa6522720f 100644 --- a/sound/soc/uniphier/aio-cpu.c +++ b/sound/soc/uniphier/aio-cpu.c @@ -420,25 +420,37 @@ int uniphier_aio_dai_remove(struct snd_soc_dai *dai) } EXPORT_SYMBOL_GPL(uniphier_aio_dai_remove); -int uniphier_aio_dai_suspend(struct snd_soc_dai *dai) +static void uniphier_aio_dai_suspend(struct snd_soc_dai *dai) { struct uniphier_aio *aio = uniphier_priv(dai); + if (!dai->active) + return; + aio->chip->num_wup_aios--; if (!aio->chip->num_wup_aios) { reset_control_assert(aio->chip->rst); clk_disable_unprepare(aio->chip->clk); } +} + +static int uniphier_aio_suspend(struct snd_soc_component *component) +{ + struct snd_soc_dai *dai; + for_each_component_dais(component, dai) + uniphier_aio_dai_suspend(dai); return 0; } -EXPORT_SYMBOL_GPL(uniphier_aio_dai_suspend); -int uniphier_aio_dai_resume(struct snd_soc_dai *dai) +static int uniphier_aio_dai_resume(struct snd_soc_dai *dai) { struct uniphier_aio *aio = uniphier_priv(dai); int ret, i; + if (!dai->active) + return 0; + if (!aio->chip->active) return 0; @@ -484,7 +496,16 @@ err_out_clock: return ret; } -EXPORT_SYMBOL_GPL(uniphier_aio_dai_resume); + +static int uniphier_aio_resume(struct snd_soc_component *component) +{ + struct snd_soc_dai *dai; + int ret = 0; + + for_each_component_dais(component, dai) + ret |= uniphier_aio_dai_resume(dai); + return ret; +} static int uniphier_aio_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -596,6 +617,8 @@ static const struct snd_soc_component_driver uniphier_aio_component = { .name = "uniphier-aio", .controls = uniphier_aio_controls, .num_controls = ARRAY_SIZE(uniphier_aio_controls), + .suspend = uniphier_aio_suspend, + .resume = uniphier_aio_resume, }; int uniphier_aio_probe(struct platform_device *pdev) diff --git a/sound/soc/uniphier/aio-dma.c b/sound/soc/uniphier/aio-dma.c index 700d936ed94e..da83423c52e2 100644 --- a/sound/soc/uniphier/aio-dma.c +++ b/sound/soc/uniphier/aio-dma.c @@ -104,25 +104,6 @@ static int uniphier_aiodma_open(struct snd_soc_component *component, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256); } -static int uniphier_aiodma_hw_params(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - substream->runtime->dma_bytes = params_buffer_bytes(params); - - return 0; -} - -static int uniphier_aiodma_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - snd_pcm_set_runtime_buffer(substream, NULL); - substream->runtime->dma_bytes = 0; - - return 0; -} - static int uniphier_aiodma_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -232,30 +213,20 @@ static int uniphier_aiodma_new(struct snd_soc_component *component, if (ret) return ret; - snd_pcm_lib_preallocate_pages_for_all(pcm, + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, dev, uniphier_aiodma_hw.buffer_bytes_max, uniphier_aiodma_hw.buffer_bytes_max); return 0; } -static void uniphier_aiodma_free(struct snd_soc_component *component, - struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static const struct snd_soc_component_driver uniphier_soc_platform = { .open = uniphier_aiodma_open, - .ioctl = snd_soc_pcm_lib_ioctl, - .hw_params = uniphier_aiodma_hw_params, - .hw_free = uniphier_aiodma_hw_free, .prepare = uniphier_aiodma_prepare, .trigger = uniphier_aiodma_trigger, .pointer = uniphier_aiodma_pointer, .mmap = uniphier_aiodma_mmap, .pcm_construct = uniphier_aiodma_new, - .pcm_destruct = uniphier_aiodma_free, .compr_ops = &uniphier_aio_compr_ops, }; diff --git a/sound/soc/uniphier/aio-ld11.c b/sound/soc/uniphier/aio-ld11.c index de962df245ba..8b44f8dc4970 100644 --- a/sound/soc/uniphier/aio-ld11.c +++ b/sound/soc/uniphier/aio-ld11.c @@ -218,8 +218,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .name = AUD_GNAME_HDMI, .probe = uniphier_aio_ld11_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .playback = { .stream_name = AUD_NAME_PCMOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -242,8 +240,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .name = AUD_NAME_PCMIN2, .probe = uniphier_aio_ld11_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .capture = { .stream_name = AUD_NAME_PCMIN2, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -257,8 +253,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .name = AUD_GNAME_LINE, .probe = uniphier_aio_ld11_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .playback = { .stream_name = AUD_NAME_PCMOUT2, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -279,8 +273,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .name = AUD_NAME_HPCMOUT1, .probe = uniphier_aio_ld11_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .playback = { .stream_name = AUD_NAME_HPCMOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -294,8 +286,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .name = AUD_NAME_PCMOUT3, .probe = uniphier_aio_ld11_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .playback = { .stream_name = AUD_NAME_PCMOUT3, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -309,8 +299,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .name = AUD_NAME_HIECOUT1, .probe = uniphier_aio_ld11_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .playback = { .stream_name = AUD_NAME_HIECOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -324,8 +312,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .name = AUD_NAME_EPCMOUT2, .probe = uniphier_aio_ld11_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .playback = { .stream_name = AUD_NAME_EPCMOUT2, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -341,8 +327,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .name = AUD_NAME_EPCMOUT3, .probe = uniphier_aio_ld11_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .playback = { .stream_name = AUD_NAME_EPCMOUT3, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -358,8 +342,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .name = AUD_NAME_HIECCOMPOUT1, .probe = uniphier_aio_ld11_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .compress_new = snd_soc_new_compress, .playback = { .stream_name = AUD_NAME_HIECCOMPOUT1, diff --git a/sound/soc/uniphier/aio-pxs2.c b/sound/soc/uniphier/aio-pxs2.c index 69cd5b0af948..a1d05fe9d3c2 100644 --- a/sound/soc/uniphier/aio-pxs2.c +++ b/sound/soc/uniphier/aio-pxs2.c @@ -171,8 +171,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .name = AUD_GNAME_HDMI, .probe = uniphier_aio_pxs2_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .playback = { .stream_name = AUD_NAME_HPCMOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -186,8 +184,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .name = AUD_GNAME_LINE, .probe = uniphier_aio_pxs2_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .playback = { .stream_name = AUD_NAME_PCMOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -208,8 +204,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .name = AUD_GNAME_AUX, .probe = uniphier_aio_pxs2_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .playback = { .stream_name = AUD_NAME_PCMOUT2, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -230,8 +224,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .name = AUD_NAME_HIECOUT1, .probe = uniphier_aio_pxs2_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .playback = { .stream_name = AUD_NAME_HIECOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -245,8 +237,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .name = AUD_NAME_IECOUT1, .probe = uniphier_aio_pxs2_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .playback = { .stream_name = AUD_NAME_IECOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -260,8 +250,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .name = AUD_NAME_HIECCOMPOUT1, .probe = uniphier_aio_pxs2_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .compress_new = snd_soc_new_compress, .playback = { .stream_name = AUD_NAME_HIECCOMPOUT1, @@ -274,8 +262,6 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .name = AUD_NAME_IECCOMPOUT1, .probe = uniphier_aio_pxs2_probe, .remove = uniphier_aio_dai_remove, - .suspend = uniphier_aio_dai_suspend, - .resume = uniphier_aio_dai_resume, .compress_new = snd_soc_new_compress, .playback = { .stream_name = AUD_NAME_IECCOMPOUT1, diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h index a7ff7e556429..694ac030950e 100644 --- a/sound/soc/uniphier/aio.h +++ b/sound/soc/uniphier/aio.h @@ -308,8 +308,6 @@ extern const struct snd_compr_ops uniphier_aio_compr_ops; int uniphier_aio_dai_probe(struct snd_soc_dai *dai); int uniphier_aio_dai_remove(struct snd_soc_dai *dai); -int uniphier_aio_dai_suspend(struct snd_soc_dai *dai); -int uniphier_aio_dai_resume(struct snd_soc_dai *dai); int uniphier_aio_probe(struct platform_device *pdev); int uniphier_aio_remove(struct platform_device *pdev); extern const struct snd_soc_dai_ops uniphier_aio_i2s_ops; diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index dec065fb3e54..21052378a32e 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -719,8 +719,6 @@ static const struct snd_soc_dai_ops ux500_msp_dai_ops[] = { static struct snd_soc_dai_driver ux500_msp_dai_drv = { .probe = ux500_msp_dai_probe, - .suspend = NULL, - .resume = NULL, .playback.channels_min = UX500_MSP_MIN_CHANNELS, .playback.channels_max = UX500_MSP_MAX_CHANNELS, .playback.rates = UX500_I2S_RATES, diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c index 296c4caf96a0..1d59fb668c77 100644 --- a/sound/soc/xilinx/xlnx_formatter_pcm.c +++ b/sound/soc/xilinx/xlnx_formatter_pcm.c @@ -426,7 +426,6 @@ static int xlnx_formatter_pcm_hw_params(struct snd_soc_component *component, { u32 low, high, active_ch, val, bytes_per_ch, bits_per_sample; u32 aes_reg1_val, aes_reg2_val; - int status; u64 size; struct snd_pcm_runtime *runtime = substream->runtime; struct xlnx_pcm_stream_param *stream_data = runtime->private_data; @@ -450,9 +449,6 @@ static int xlnx_formatter_pcm_hw_params(struct snd_soc_component *component, } size = params_buffer_bytes(params); - status = snd_pcm_lib_malloc_pages(substream, size); - if (status < 0) - return status; stream_data->buffer_size = size; @@ -495,12 +491,6 @@ static int xlnx_formatter_pcm_hw_params(struct snd_soc_component *component, return 0; } -static int xlnx_formatter_pcm_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); -} - static int xlnx_formatter_pcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) @@ -532,7 +522,7 @@ static int xlnx_formatter_pcm_trigger(struct snd_soc_component *component, static int xlnx_formatter_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { - snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, xlnx_pcm_hardware.buffer_bytes_max, xlnx_pcm_hardware.buffer_bytes_max); @@ -543,9 +533,7 @@ static const struct snd_soc_component_driver xlnx_asoc_component = { .name = DRV_NAME, .open = xlnx_formatter_pcm_open, .close = xlnx_formatter_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = xlnx_formatter_pcm_hw_params, - .hw_free = xlnx_formatter_pcm_hw_free, .trigger = xlnx_formatter_pcm_trigger, .pointer = xlnx_formatter_pcm_pointer, .pcm_construct = xlnx_formatter_pcm_new, diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c index e08f4fee932a..bcf442faff7c 100644 --- a/sound/soc/xtensa/xtfpga-i2s.c +++ b/sound/soc/xtensa/xtfpga-i2s.c @@ -390,7 +390,6 @@ static int xtfpga_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - int ret; struct snd_pcm_runtime *runtime = substream->runtime; struct xtfpga_i2s *i2s = runtime->private_data; unsigned channels = params_channels(hw_params); @@ -422,9 +421,7 @@ static int xtfpga_pcm_hw_params(struct snd_soc_component *component, return -EINVAL; } - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - return ret; + return 0; } static int xtfpga_pcm_trigger(struct snd_soc_component *component, @@ -472,8 +469,8 @@ static int xtfpga_pcm_new(struct snd_soc_component *component, struct snd_card *card = rtd->card->snd_card; size_t size = xtfpga_pcm_hardware.buffer_bytes_max; - snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, - card->dev, size, size); + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, + card->dev, size, size); return 0; } @@ -481,7 +478,6 @@ static const struct snd_soc_component_driver xtfpga_i2s_component = { .name = DRV_NAME, .open = xtfpga_pcm_open, .close = xtfpga_pcm_close, - .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = xtfpga_pcm_hw_params, .trigger = xtfpga_pcm_trigger, .pointer = xtfpga_pcm_pointer, |