162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2022 Advanced Micro Devices, Inc. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> 962306a36Sopenharmony_ci// Vijendar Mukunda <Vijendar.Mukunda@amd.com> 1062306a36Sopenharmony_ci// 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* 1362306a36Sopenharmony_ci * Generic Hardware interface for ACP Audio PDM controller 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/err.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <sound/pcm_params.h> 2162306a36Sopenharmony_ci#include <sound/soc.h> 2262306a36Sopenharmony_ci#include <sound/soc-dai.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "amd.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DRV_NAME "acp-pdm" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int acp_dmic_prepare(struct snd_pcm_substream *substream, 2962306a36Sopenharmony_ci struct snd_soc_dai *dai) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct acp_stream *stream = substream->runtime->private_data; 3262306a36Sopenharmony_ci struct device *dev = dai->component->dev; 3362306a36Sopenharmony_ci struct acp_dev_data *adata = dev_get_drvdata(dev); 3462306a36Sopenharmony_ci u32 physical_addr, size_dmic, period_bytes; 3562306a36Sopenharmony_ci unsigned int dmic_ctrl; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci /* Enable default DMIC clk */ 3862306a36Sopenharmony_ci writel(PDM_CLK_FREQ_MASK, adata->acp_base + ACP_WOV_CLK_CTRL); 3962306a36Sopenharmony_ci dmic_ctrl = readl(adata->acp_base + ACP_WOV_MISC_CTRL); 4062306a36Sopenharmony_ci dmic_ctrl |= PDM_MISC_CTRL_MASK; 4162306a36Sopenharmony_ci writel(dmic_ctrl, adata->acp_base + ACP_WOV_MISC_CTRL); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci period_bytes = frames_to_bytes(substream->runtime, 4462306a36Sopenharmony_ci substream->runtime->period_size); 4562306a36Sopenharmony_ci size_dmic = frames_to_bytes(substream->runtime, 4662306a36Sopenharmony_ci substream->runtime->buffer_size); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci physical_addr = stream->reg_offset + MEM_WINDOW_START; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* Init DMIC Ring buffer */ 5162306a36Sopenharmony_ci writel(physical_addr, adata->acp_base + ACP_WOV_RX_RINGBUFADDR); 5262306a36Sopenharmony_ci writel(size_dmic, adata->acp_base + ACP_WOV_RX_RINGBUFSIZE); 5362306a36Sopenharmony_ci writel(period_bytes, adata->acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE); 5462306a36Sopenharmony_ci writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int acp_dmic_dai_trigger(struct snd_pcm_substream *substream, 6062306a36Sopenharmony_ci int cmd, struct snd_soc_dai *dai) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct device *dev = dai->component->dev; 6362306a36Sopenharmony_ci struct acp_dev_data *adata = dev_get_drvdata(dev); 6462306a36Sopenharmony_ci unsigned int dma_enable; 6562306a36Sopenharmony_ci int ret = 0; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci switch (cmd) { 6862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 6962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 7062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 7162306a36Sopenharmony_ci dma_enable = readl(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE); 7262306a36Sopenharmony_ci if (!(dma_enable & DMA_EN_MASK)) { 7362306a36Sopenharmony_ci writel(PDM_ENABLE, adata->acp_base + ACP_WOV_PDM_ENABLE); 7462306a36Sopenharmony_ci writel(PDM_ENABLE, adata->acp_base + ACP_WOV_PDM_DMA_ENABLE); 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE, 7862306a36Sopenharmony_ci dma_enable, (dma_enable & DMA_EN_MASK), 7962306a36Sopenharmony_ci DELAY_US, PDM_TIMEOUT); 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 8262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 8362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 8462306a36Sopenharmony_ci dma_enable = readl(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE); 8562306a36Sopenharmony_ci if ((dma_enable & DMA_EN_MASK)) { 8662306a36Sopenharmony_ci writel(PDM_DISABLE, adata->acp_base + ACP_WOV_PDM_ENABLE); 8762306a36Sopenharmony_ci writel(PDM_DISABLE, adata->acp_base + ACP_WOV_PDM_DMA_ENABLE); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE, 9262306a36Sopenharmony_ci dma_enable, !(dma_enable & DMA_EN_MASK), 9362306a36Sopenharmony_ci DELAY_US, PDM_TIMEOUT); 9462306a36Sopenharmony_ci break; 9562306a36Sopenharmony_ci default: 9662306a36Sopenharmony_ci ret = -EINVAL; 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return ret; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int acp_dmic_hwparams(struct snd_pcm_substream *substream, 10462306a36Sopenharmony_ci struct snd_pcm_hw_params *hwparams, struct snd_soc_dai *dai) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct device *dev = dai->component->dev; 10762306a36Sopenharmony_ci struct acp_dev_data *adata = dev_get_drvdata(dev); 10862306a36Sopenharmony_ci unsigned int channels, ch_mask; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci channels = params_channels(hwparams); 11162306a36Sopenharmony_ci switch (channels) { 11262306a36Sopenharmony_ci case 2: 11362306a36Sopenharmony_ci ch_mask = 0; 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci case 4: 11662306a36Sopenharmony_ci ch_mask = 1; 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci case 6: 11962306a36Sopenharmony_ci ch_mask = 2; 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci default: 12262306a36Sopenharmony_ci dev_err(dev, "Invalid channels %d\n", channels); 12362306a36Sopenharmony_ci return -EINVAL; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci adata->ch_mask = ch_mask; 12762306a36Sopenharmony_ci if (params_format(hwparams) != SNDRV_PCM_FORMAT_S32_LE) { 12862306a36Sopenharmony_ci dev_err(dai->dev, "Invalid format:%d\n", params_format(hwparams)); 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci writel(ch_mask, adata->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS); 13362306a36Sopenharmony_ci writel(PDM_DEC_64, adata->acp_base + ACP_WOV_PDM_DECIMATION_FACTOR); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int acp_dmic_dai_startup(struct snd_pcm_substream *substream, 13962306a36Sopenharmony_ci struct snd_soc_dai *dai) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct acp_stream *stream = substream->runtime->private_data; 14262306a36Sopenharmony_ci struct device *dev = dai->component->dev; 14362306a36Sopenharmony_ci struct acp_dev_data *adata = dev_get_drvdata(dev); 14462306a36Sopenharmony_ci u32 ext_int_ctrl; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci stream->dai_id = DMIC_INSTANCE; 14762306a36Sopenharmony_ci stream->irq_bit = BIT(PDM_DMA_STAT); 14862306a36Sopenharmony_ci stream->pte_offset = ACP_SRAM_PDM_PTE_OFFSET; 14962306a36Sopenharmony_ci stream->reg_offset = ACP_REGION2_OFFSET; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Enable DMIC Interrupts */ 15262306a36Sopenharmony_ci ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0)); 15362306a36Sopenharmony_ci ext_int_ctrl |= PDM_DMA_INTR_MASK; 15462306a36Sopenharmony_ci writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0)); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void acp_dmic_dai_shutdown(struct snd_pcm_substream *substream, 16062306a36Sopenharmony_ci struct snd_soc_dai *dai) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct device *dev = dai->component->dev; 16362306a36Sopenharmony_ci struct acp_dev_data *adata = dev_get_drvdata(dev); 16462306a36Sopenharmony_ci u32 ext_int_ctrl; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* Disable DMIC interrupts */ 16762306a36Sopenharmony_ci ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0)); 16862306a36Sopenharmony_ci ext_int_ctrl &= ~PDM_DMA_INTR_MASK; 16962306a36Sopenharmony_ci writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0)); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciconst struct snd_soc_dai_ops acp_dmic_dai_ops = { 17362306a36Sopenharmony_ci .prepare = acp_dmic_prepare, 17462306a36Sopenharmony_ci .hw_params = acp_dmic_hwparams, 17562306a36Sopenharmony_ci .trigger = acp_dmic_dai_trigger, 17662306a36Sopenharmony_ci .startup = acp_dmic_dai_startup, 17762306a36Sopenharmony_ci .shutdown = acp_dmic_dai_shutdown, 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(acp_dmic_dai_ops, SND_SOC_ACP_COMMON); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 18262306a36Sopenharmony_ciMODULE_ALIAS(DRV_NAME); 183