1665dd181SVijendar Mukunda // SPDX-License-Identifier: GPL-2.0+ 2665dd181SVijendar Mukunda /* 3665dd181SVijendar Mukunda * AMD ALSA SoC Pink Sardine SoundWire DMA Driver 4665dd181SVijendar Mukunda * 5665dd181SVijendar Mukunda * Copyright 2023 Advanced Micro Devices, Inc. 6665dd181SVijendar Mukunda */ 7665dd181SVijendar Mukunda 8665dd181SVijendar Mukunda #include <linux/err.h> 9665dd181SVijendar Mukunda #include <linux/io.h> 10665dd181SVijendar Mukunda #include <linux/module.h> 11665dd181SVijendar Mukunda #include <linux/platform_device.h> 12665dd181SVijendar Mukunda #include <sound/pcm_params.h> 13665dd181SVijendar Mukunda #include <sound/soc.h> 14665dd181SVijendar Mukunda #include <sound/soc-dai.h> 155a06c3acSVijendar Mukunda #include <linux/pm_runtime.h> 16f7229173SVijendar Mukunda #include <linux/soundwire/sdw_amd.h> 17665dd181SVijendar Mukunda #include "acp63.h" 18665dd181SVijendar Mukunda 19665dd181SVijendar Mukunda #define DRV_NAME "amd_ps_sdw_dma" 20665dd181SVijendar Mukunda 21f7229173SVijendar Mukunda static struct sdw_dma_ring_buf_reg sdw0_dma_ring_buf_reg[ACP63_SDW0_DMA_MAX_STREAMS] = { 22f7229173SVijendar Mukunda {ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE, 23f7229173SVijendar Mukunda ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE, 24f7229173SVijendar Mukunda ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH}, 25f7229173SVijendar Mukunda {ACP_AUDIO1_TX_DMA_SIZE, ACP_AUDIO1_TX_FIFOADDR, ACP_AUDIO1_TX_FIFOSIZE, 26f7229173SVijendar Mukunda ACP_AUDIO1_TX_RINGBUFSIZE, ACP_AUDIO1_TX_RINGBUFADDR, ACP_AUDIO1_TX_INTR_WATERMARK_SIZE, 27f7229173SVijendar Mukunda ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH}, 28f7229173SVijendar Mukunda {ACP_AUDIO2_TX_DMA_SIZE, ACP_AUDIO2_TX_FIFOADDR, ACP_AUDIO2_TX_FIFOSIZE, 29f7229173SVijendar Mukunda ACP_AUDIO2_TX_RINGBUFSIZE, ACP_AUDIO2_TX_RINGBUFADDR, ACP_AUDIO2_TX_INTR_WATERMARK_SIZE, 30f7229173SVijendar Mukunda ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH}, 31f7229173SVijendar Mukunda {ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE, 32f7229173SVijendar Mukunda ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE, 33f15f6b29SVijendar Mukunda ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH}, 34f7229173SVijendar Mukunda {ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE, 35f7229173SVijendar Mukunda ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE, 36f7229173SVijendar Mukunda ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH}, 37f7229173SVijendar Mukunda {ACP_AUDIO2_RX_DMA_SIZE, ACP_AUDIO2_RX_FIFOADDR, ACP_AUDIO2_RX_FIFOSIZE, 38f7229173SVijendar Mukunda ACP_AUDIO2_RX_RINGBUFSIZE, ACP_AUDIO2_RX_RINGBUFADDR, ACP_AUDIO2_RX_INTR_WATERMARK_SIZE, 39f7229173SVijendar Mukunda ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH} 40f7229173SVijendar Mukunda }; 41f7229173SVijendar Mukunda 4246b50e51SVijendar Mukunda /* 4346b50e51SVijendar Mukunda * SDW1 instance supports one TX stream and one RX stream. 4446b50e51SVijendar Mukunda * For TX/RX streams DMA registers programming for SDW1 instance, it uses ACP_P1_AUDIO1 register 4546b50e51SVijendar Mukunda * set as per hardware register documentation 4646b50e51SVijendar Mukunda */ 47f7229173SVijendar Mukunda static struct sdw_dma_ring_buf_reg sdw1_dma_ring_buf_reg[ACP63_SDW1_DMA_MAX_STREAMS] = { 48f7229173SVijendar Mukunda {ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE, 49f7229173SVijendar Mukunda ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR, 50f7229173SVijendar Mukunda ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE, 51f7229173SVijendar Mukunda ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH}, 52f7229173SVijendar Mukunda {ACP_P1_AUDIO1_RX_DMA_SIZE, ACP_P1_AUDIO1_RX_FIFOADDR, ACP_P1_AUDIO1_RX_FIFOSIZE, 53f7229173SVijendar Mukunda ACP_P1_AUDIO1_RX_RINGBUFSIZE, ACP_P1_AUDIO1_RX_RINGBUFADDR, 54f7229173SVijendar Mukunda ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE, 55f7229173SVijendar Mukunda ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH}, 56f7229173SVijendar Mukunda }; 57f7229173SVijendar Mukunda 58f7229173SVijendar Mukunda static u32 sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = { 59f7229173SVijendar Mukunda ACP_SW0_AUDIO0_TX_EN, 60f7229173SVijendar Mukunda ACP_SW0_AUDIO1_TX_EN, 61f7229173SVijendar Mukunda ACP_SW0_AUDIO2_TX_EN, 62f7229173SVijendar Mukunda ACP_SW0_AUDIO0_RX_EN, 63f7229173SVijendar Mukunda ACP_SW0_AUDIO1_RX_EN, 64f7229173SVijendar Mukunda ACP_SW0_AUDIO2_RX_EN, 65f7229173SVijendar Mukunda }; 66f7229173SVijendar Mukunda 6746b50e51SVijendar Mukunda /* 6846b50e51SVijendar Mukunda * SDW1 instance supports one TX stream and one RX stream. 6946b50e51SVijendar Mukunda * For TX/RX streams DMA enable register programming for SDW1 instance, 7046b50e51SVijendar Mukunda * it uses ACP_SW1_AUDIO1_TX_EN and ACP_SW1_AUDIO1_RX_EN registers 7146b50e51SVijendar Mukunda * as per hardware register documentation. 7246b50e51SVijendar Mukunda */ 73f7229173SVijendar Mukunda static u32 sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = { 74f7229173SVijendar Mukunda ACP_SW1_AUDIO1_TX_EN, 75f7229173SVijendar Mukunda ACP_SW1_AUDIO1_RX_EN, 76f7229173SVijendar Mukunda }; 77f7229173SVijendar Mukunda 78f7229173SVijendar Mukunda static const struct snd_pcm_hardware acp63_sdw_hardware_playback = { 79f7229173SVijendar Mukunda .info = SNDRV_PCM_INFO_INTERLEAVED | 80f7229173SVijendar Mukunda SNDRV_PCM_INFO_BLOCK_TRANSFER | 81f7229173SVijendar Mukunda SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 82f7229173SVijendar Mukunda SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 83f7229173SVijendar Mukunda .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 84f7229173SVijendar Mukunda SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, 85f7229173SVijendar Mukunda .channels_min = 2, 86f7229173SVijendar Mukunda .channels_max = 2, 87f7229173SVijendar Mukunda .rates = SNDRV_PCM_RATE_48000, 88f7229173SVijendar Mukunda .rate_min = 48000, 89f7229173SVijendar Mukunda .rate_max = 48000, 90f7229173SVijendar Mukunda .buffer_bytes_max = SDW_PLAYBACK_MAX_NUM_PERIODS * SDW_PLAYBACK_MAX_PERIOD_SIZE, 91f7229173SVijendar Mukunda .period_bytes_min = SDW_PLAYBACK_MIN_PERIOD_SIZE, 92f7229173SVijendar Mukunda .period_bytes_max = SDW_PLAYBACK_MAX_PERIOD_SIZE, 93f7229173SVijendar Mukunda .periods_min = SDW_PLAYBACK_MIN_NUM_PERIODS, 94f7229173SVijendar Mukunda .periods_max = SDW_PLAYBACK_MAX_NUM_PERIODS, 95f7229173SVijendar Mukunda }; 96f7229173SVijendar Mukunda 97f7229173SVijendar Mukunda static const struct snd_pcm_hardware acp63_sdw_hardware_capture = { 98f7229173SVijendar Mukunda .info = SNDRV_PCM_INFO_INTERLEAVED | 99f7229173SVijendar Mukunda SNDRV_PCM_INFO_BLOCK_TRANSFER | 100f7229173SVijendar Mukunda SNDRV_PCM_INFO_MMAP | 101f7229173SVijendar Mukunda SNDRV_PCM_INFO_MMAP_VALID | 102f7229173SVijendar Mukunda SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 103f7229173SVijendar Mukunda .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 104f7229173SVijendar Mukunda SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, 105f7229173SVijendar Mukunda .channels_min = 2, 106f7229173SVijendar Mukunda .channels_max = 2, 107f7229173SVijendar Mukunda .rates = SNDRV_PCM_RATE_48000, 108f7229173SVijendar Mukunda .rate_min = 48000, 109f7229173SVijendar Mukunda .rate_max = 48000, 110f7229173SVijendar Mukunda .buffer_bytes_max = SDW_CAPTURE_MAX_NUM_PERIODS * SDW_CAPTURE_MAX_PERIOD_SIZE, 111f7229173SVijendar Mukunda .period_bytes_min = SDW_CAPTURE_MIN_PERIOD_SIZE, 112f7229173SVijendar Mukunda .period_bytes_max = SDW_CAPTURE_MAX_PERIOD_SIZE, 113f7229173SVijendar Mukunda .periods_min = SDW_CAPTURE_MIN_NUM_PERIODS, 114f7229173SVijendar Mukunda .periods_max = SDW_CAPTURE_MAX_NUM_PERIODS, 115f7229173SVijendar Mukunda }; 116f7229173SVijendar Mukunda 1175a06c3acSVijendar Mukunda static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, bool enable) 1185a06c3acSVijendar Mukunda { 1195a06c3acSVijendar Mukunda u32 ext_intr_cntl, ext_intr_cntl1; 1205a06c3acSVijendar Mukunda u32 irq_mask = ACP_SDW_DMA_IRQ_MASK; 1215a06c3acSVijendar Mukunda u32 irq_mask1 = ACP_P1_SDW_DMA_IRQ_MASK; 1225a06c3acSVijendar Mukunda 1235a06c3acSVijendar Mukunda if (enable) { 1245a06c3acSVijendar Mukunda ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL); 1255a06c3acSVijendar Mukunda ext_intr_cntl |= irq_mask; 1265a06c3acSVijendar Mukunda writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL); 1275a06c3acSVijendar Mukunda ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1); 1285a06c3acSVijendar Mukunda ext_intr_cntl1 |= irq_mask1; 1295a06c3acSVijendar Mukunda writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1); 1305a06c3acSVijendar Mukunda } else { 1315a06c3acSVijendar Mukunda ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL); 1325a06c3acSVijendar Mukunda ext_intr_cntl &= ~irq_mask; 1335a06c3acSVijendar Mukunda writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL); 1345a06c3acSVijendar Mukunda ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1); 1355a06c3acSVijendar Mukunda ext_intr_cntl1 &= ~irq_mask1; 1365a06c3acSVijendar Mukunda writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1); 1375a06c3acSVijendar Mukunda } 1385a06c3acSVijendar Mukunda } 1395a06c3acSVijendar Mukunda 140f7229173SVijendar Mukunda static void acp63_config_dma(struct acp_sdw_dma_stream *stream, void __iomem *acp_base, 141f7229173SVijendar Mukunda u32 stream_id) 142f7229173SVijendar Mukunda { 143f7229173SVijendar Mukunda u16 page_idx; 144f7229173SVijendar Mukunda u32 low, high, val; 145f7229173SVijendar Mukunda u32 sdw_dma_pte_offset; 146f7229173SVijendar Mukunda dma_addr_t addr; 147f7229173SVijendar Mukunda 148f7229173SVijendar Mukunda addr = stream->dma_addr; 149f7229173SVijendar Mukunda sdw_dma_pte_offset = SDW_PTE_OFFSET(stream->instance); 150f7229173SVijendar Mukunda val = sdw_dma_pte_offset + (stream_id * ACP_SDW_PTE_OFFSET); 151f7229173SVijendar Mukunda 152f7229173SVijendar Mukunda /* Group Enable */ 153f7229173SVijendar Mukunda writel(ACP_SDW_SRAM_PTE_OFFSET | BIT(31), acp_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_2); 154f7229173SVijendar Mukunda writel(PAGE_SIZE_4K_ENABLE, acp_base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2); 155f7229173SVijendar Mukunda for (page_idx = 0; page_idx < stream->num_pages; page_idx++) { 156f7229173SVijendar Mukunda /* Load the low address of page int ACP SRAM through SRBM */ 157f7229173SVijendar Mukunda low = lower_32_bits(addr); 158f7229173SVijendar Mukunda high = upper_32_bits(addr); 159f7229173SVijendar Mukunda 160f7229173SVijendar Mukunda writel(low, acp_base + ACP_SCRATCH_REG_0 + val); 161f7229173SVijendar Mukunda high |= BIT(31); 162f7229173SVijendar Mukunda writel(high, acp_base + ACP_SCRATCH_REG_0 + val + 4); 163f7229173SVijendar Mukunda val += 8; 164f7229173SVijendar Mukunda addr += PAGE_SIZE; 165f7229173SVijendar Mukunda } 166f7229173SVijendar Mukunda writel(0x1, acp_base + ACPAXI2AXI_ATU_CTRL); 167f7229173SVijendar Mukunda } 168f7229173SVijendar Mukunda 169f7229173SVijendar Mukunda static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, u32 size, 170f7229173SVijendar Mukunda u32 manager_instance) 171f7229173SVijendar Mukunda { 172f7229173SVijendar Mukunda u32 reg_dma_size; 173f7229173SVijendar Mukunda u32 reg_fifo_addr; 174f7229173SVijendar Mukunda u32 reg_fifo_size; 175f7229173SVijendar Mukunda u32 reg_ring_buf_size; 176f7229173SVijendar Mukunda u32 reg_ring_buf_addr; 177f7229173SVijendar Mukunda u32 sdw_fifo_addr; 178f7229173SVijendar Mukunda u32 sdw_fifo_offset; 179f7229173SVijendar Mukunda u32 sdw_ring_buf_addr; 180f7229173SVijendar Mukunda u32 sdw_ring_buf_size; 181f7229173SVijendar Mukunda u32 sdw_mem_window_offset; 182f7229173SVijendar Mukunda 183f7229173SVijendar Mukunda switch (manager_instance) { 184f7229173SVijendar Mukunda case ACP_SDW0: 185f7229173SVijendar Mukunda reg_dma_size = sdw0_dma_ring_buf_reg[stream_id].reg_dma_size; 186f7229173SVijendar Mukunda reg_fifo_addr = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_addr; 187f7229173SVijendar Mukunda reg_fifo_size = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_size; 188f7229173SVijendar Mukunda reg_ring_buf_size = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_size; 189f7229173SVijendar Mukunda reg_ring_buf_addr = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_addr; 190f7229173SVijendar Mukunda break; 191f7229173SVijendar Mukunda case ACP_SDW1: 192f7229173SVijendar Mukunda reg_dma_size = sdw1_dma_ring_buf_reg[stream_id].reg_dma_size; 193f7229173SVijendar Mukunda reg_fifo_addr = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_addr; 194f7229173SVijendar Mukunda reg_fifo_size = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_size; 195f7229173SVijendar Mukunda reg_ring_buf_size = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_size; 196f7229173SVijendar Mukunda reg_ring_buf_addr = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_addr; 197f7229173SVijendar Mukunda break; 198f7229173SVijendar Mukunda default: 199f7229173SVijendar Mukunda return -EINVAL; 200f7229173SVijendar Mukunda } 201f7229173SVijendar Mukunda sdw_fifo_offset = ACP_SDW_FIFO_OFFSET(manager_instance); 202f7229173SVijendar Mukunda sdw_mem_window_offset = SDW_MEM_WINDOW_START(manager_instance); 203f7229173SVijendar Mukunda sdw_fifo_addr = sdw_fifo_offset + (stream_id * SDW_FIFO_OFFSET); 204f7229173SVijendar Mukunda sdw_ring_buf_addr = sdw_mem_window_offset + (stream_id * ACP_SDW_RING_BUFF_ADDR_OFFSET); 205f7229173SVijendar Mukunda sdw_ring_buf_size = size; 206f7229173SVijendar Mukunda writel(sdw_ring_buf_size, acp_base + reg_ring_buf_size); 207f7229173SVijendar Mukunda writel(sdw_ring_buf_addr, acp_base + reg_ring_buf_addr); 208f7229173SVijendar Mukunda writel(sdw_fifo_addr, acp_base + reg_fifo_addr); 209f7229173SVijendar Mukunda writel(SDW_DMA_SIZE, acp_base + reg_dma_size); 210f7229173SVijendar Mukunda writel(SDW_FIFO_SIZE, acp_base + reg_fifo_size); 211f7229173SVijendar Mukunda return 0; 212f7229173SVijendar Mukunda } 213f7229173SVijendar Mukunda 214f7229173SVijendar Mukunda static int acp63_sdw_dma_open(struct snd_soc_component *component, 215f7229173SVijendar Mukunda struct snd_pcm_substream *substream) 216f7229173SVijendar Mukunda { 217f7229173SVijendar Mukunda struct snd_pcm_runtime *runtime; 218f7229173SVijendar Mukunda struct acp_sdw_dma_stream *stream; 219f7229173SVijendar Mukunda struct snd_soc_dai *cpu_dai; 220f7229173SVijendar Mukunda struct amd_sdw_manager *amd_manager; 221f7229173SVijendar Mukunda struct snd_soc_pcm_runtime *prtd = substream->private_data; 222f7229173SVijendar Mukunda int ret; 223f7229173SVijendar Mukunda 224f7229173SVijendar Mukunda runtime = substream->runtime; 225f7229173SVijendar Mukunda cpu_dai = asoc_rtd_to_cpu(prtd, 0); 226f7229173SVijendar Mukunda amd_manager = snd_soc_dai_get_drvdata(cpu_dai); 227f7229173SVijendar Mukunda stream = kzalloc(sizeof(*stream), GFP_KERNEL); 228f7229173SVijendar Mukunda if (!stream) 229f7229173SVijendar Mukunda return -ENOMEM; 230f7229173SVijendar Mukunda 231f7229173SVijendar Mukunda if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 232f7229173SVijendar Mukunda runtime->hw = acp63_sdw_hardware_playback; 233f7229173SVijendar Mukunda else 234f7229173SVijendar Mukunda runtime->hw = acp63_sdw_hardware_capture; 235f7229173SVijendar Mukunda ret = snd_pcm_hw_constraint_integer(runtime, 236f7229173SVijendar Mukunda SNDRV_PCM_HW_PARAM_PERIODS); 237f7229173SVijendar Mukunda if (ret < 0) { 238f7229173SVijendar Mukunda dev_err(component->dev, "set integer constraint failed\n"); 239f7229173SVijendar Mukunda kfree(stream); 240f7229173SVijendar Mukunda return ret; 241f7229173SVijendar Mukunda } 242f7229173SVijendar Mukunda 243f7229173SVijendar Mukunda stream->stream_id = cpu_dai->id; 244f7229173SVijendar Mukunda stream->instance = amd_manager->instance; 245f7229173SVijendar Mukunda runtime->private_data = stream; 246f7229173SVijendar Mukunda return ret; 247f7229173SVijendar Mukunda } 248f7229173SVijendar Mukunda 249f7229173SVijendar Mukunda static int acp63_sdw_dma_hw_params(struct snd_soc_component *component, 250f7229173SVijendar Mukunda struct snd_pcm_substream *substream, 251f7229173SVijendar Mukunda struct snd_pcm_hw_params *params) 252f7229173SVijendar Mukunda { 253f7229173SVijendar Mukunda struct acp_sdw_dma_stream *stream; 254f7229173SVijendar Mukunda struct sdw_dma_dev_data *sdw_data; 255f7229173SVijendar Mukunda u32 period_bytes; 256f7229173SVijendar Mukunda u32 water_mark_size_reg; 257f7229173SVijendar Mukunda u32 irq_mask, ext_intr_ctrl; 258f7229173SVijendar Mukunda u64 size; 259f7229173SVijendar Mukunda u32 stream_id; 260f7229173SVijendar Mukunda u32 acp_ext_intr_cntl_reg; 261f7229173SVijendar Mukunda int ret; 262f7229173SVijendar Mukunda 263f7229173SVijendar Mukunda sdw_data = dev_get_drvdata(component->dev); 264f7229173SVijendar Mukunda stream = substream->runtime->private_data; 265f7229173SVijendar Mukunda if (!stream) 266f7229173SVijendar Mukunda return -EINVAL; 267f7229173SVijendar Mukunda stream_id = stream->stream_id; 268f7229173SVijendar Mukunda switch (stream->instance) { 269f7229173SVijendar Mukunda case ACP_SDW0: 270f7229173SVijendar Mukunda sdw_data->sdw0_dma_stream[stream_id] = substream; 271f7229173SVijendar Mukunda water_mark_size_reg = sdw0_dma_ring_buf_reg[stream_id].water_mark_size_reg; 272f7229173SVijendar Mukunda acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL; 273f7229173SVijendar Mukunda if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 274f7229173SVijendar Mukunda irq_mask = BIT(SDW0_DMA_TX_IRQ_MASK(stream_id)); 275f7229173SVijendar Mukunda else 276f7229173SVijendar Mukunda irq_mask = BIT(SDW0_DMA_RX_IRQ_MASK(stream_id)); 277f7229173SVijendar Mukunda break; 278f7229173SVijendar Mukunda case ACP_SDW1: 279f7229173SVijendar Mukunda sdw_data->sdw1_dma_stream[stream_id] = substream; 280f7229173SVijendar Mukunda acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1; 281f7229173SVijendar Mukunda water_mark_size_reg = sdw1_dma_ring_buf_reg[stream_id].water_mark_size_reg; 282f7229173SVijendar Mukunda irq_mask = BIT(SDW1_DMA_IRQ_MASK(stream_id)); 283f7229173SVijendar Mukunda break; 284f7229173SVijendar Mukunda default: 285f7229173SVijendar Mukunda return -EINVAL; 286f7229173SVijendar Mukunda } 287f7229173SVijendar Mukunda size = params_buffer_bytes(params); 288f7229173SVijendar Mukunda period_bytes = params_period_bytes(params); 289f7229173SVijendar Mukunda stream->dma_addr = substream->runtime->dma_addr; 290f7229173SVijendar Mukunda stream->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); 291f7229173SVijendar Mukunda acp63_config_dma(stream, sdw_data->acp_base, stream_id); 292f7229173SVijendar Mukunda ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, stream_id, size, 293f7229173SVijendar Mukunda stream->instance); 294f7229173SVijendar Mukunda if (ret) { 295f7229173SVijendar Mukunda dev_err(component->dev, "Invalid DMA channel\n"); 296f7229173SVijendar Mukunda return -EINVAL; 297f7229173SVijendar Mukunda } 298f7229173SVijendar Mukunda ext_intr_ctrl = readl(sdw_data->acp_base + acp_ext_intr_cntl_reg); 299f7229173SVijendar Mukunda ext_intr_ctrl |= irq_mask; 300f7229173SVijendar Mukunda writel(ext_intr_ctrl, sdw_data->acp_base + acp_ext_intr_cntl_reg); 301f7229173SVijendar Mukunda writel(period_bytes, sdw_data->acp_base + water_mark_size_reg); 302f7229173SVijendar Mukunda return 0; 303f7229173SVijendar Mukunda } 304f7229173SVijendar Mukunda 305f7229173SVijendar Mukunda static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __iomem *acp_base) 306f7229173SVijendar Mukunda { 307f7229173SVijendar Mukunda union acp_sdw_dma_count byte_count; 308f7229173SVijendar Mukunda u32 pos_low_reg, pos_high_reg; 309f7229173SVijendar Mukunda 310f7229173SVijendar Mukunda byte_count.bytescount = 0; 311f7229173SVijendar Mukunda switch (stream->instance) { 312f7229173SVijendar Mukunda case ACP_SDW0: 313f7229173SVijendar Mukunda pos_low_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_low_reg; 314f7229173SVijendar Mukunda pos_high_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_high_reg; 315f7229173SVijendar Mukunda break; 316f7229173SVijendar Mukunda case ACP_SDW1: 317f7229173SVijendar Mukunda pos_low_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_low_reg; 318f7229173SVijendar Mukunda pos_high_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_high_reg; 319f7229173SVijendar Mukunda break; 320f7229173SVijendar Mukunda default: 32168a653abSVijendar Mukunda goto POINTER_RETURN_BYTES; 322f7229173SVijendar Mukunda } 323f7229173SVijendar Mukunda if (pos_low_reg) { 324f7229173SVijendar Mukunda byte_count.bcount.high = readl(acp_base + pos_high_reg); 325f7229173SVijendar Mukunda byte_count.bcount.low = readl(acp_base + pos_low_reg); 326f7229173SVijendar Mukunda } 32768a653abSVijendar Mukunda POINTER_RETURN_BYTES: 328f7229173SVijendar Mukunda return byte_count.bytescount; 329f7229173SVijendar Mukunda } 330f7229173SVijendar Mukunda 331f7229173SVijendar Mukunda static snd_pcm_uframes_t acp63_sdw_dma_pointer(struct snd_soc_component *comp, 332f7229173SVijendar Mukunda struct snd_pcm_substream *substream) 333f7229173SVijendar Mukunda { 334f7229173SVijendar Mukunda struct sdw_dma_dev_data *sdw_data; 335f7229173SVijendar Mukunda struct acp_sdw_dma_stream *stream; 336f7229173SVijendar Mukunda u32 pos, buffersize; 337f7229173SVijendar Mukunda u64 bytescount; 338f7229173SVijendar Mukunda 339f7229173SVijendar Mukunda sdw_data = dev_get_drvdata(comp->dev); 340f7229173SVijendar Mukunda stream = substream->runtime->private_data; 341f7229173SVijendar Mukunda buffersize = frames_to_bytes(substream->runtime, 342f7229173SVijendar Mukunda substream->runtime->buffer_size); 343f7229173SVijendar Mukunda bytescount = acp63_sdw_get_byte_count(stream, sdw_data->acp_base); 344f7229173SVijendar Mukunda if (bytescount > stream->bytescount) 345f7229173SVijendar Mukunda bytescount -= stream->bytescount; 346f7229173SVijendar Mukunda pos = do_div(bytescount, buffersize); 347f7229173SVijendar Mukunda return bytes_to_frames(substream->runtime, pos); 348f7229173SVijendar Mukunda } 349f7229173SVijendar Mukunda 350f7229173SVijendar Mukunda static int acp63_sdw_dma_new(struct snd_soc_component *component, 351f7229173SVijendar Mukunda struct snd_soc_pcm_runtime *rtd) 352f7229173SVijendar Mukunda { 353f7229173SVijendar Mukunda struct device *parent = component->dev->parent; 354f7229173SVijendar Mukunda 355f7229173SVijendar Mukunda snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, 356f7229173SVijendar Mukunda parent, SDW_MIN_BUFFER, SDW_MAX_BUFFER); 357f7229173SVijendar Mukunda return 0; 358f7229173SVijendar Mukunda } 359f7229173SVijendar Mukunda 360f7229173SVijendar Mukunda static int acp63_sdw_dma_close(struct snd_soc_component *component, 361f7229173SVijendar Mukunda struct snd_pcm_substream *substream) 362f7229173SVijendar Mukunda { 363f7229173SVijendar Mukunda struct sdw_dma_dev_data *sdw_data; 364f7229173SVijendar Mukunda struct acp_sdw_dma_stream *stream; 365f7229173SVijendar Mukunda 366f7229173SVijendar Mukunda sdw_data = dev_get_drvdata(component->dev); 367f7229173SVijendar Mukunda stream = substream->runtime->private_data; 368f7229173SVijendar Mukunda if (!stream) 369f7229173SVijendar Mukunda return -EINVAL; 370f7229173SVijendar Mukunda switch (stream->instance) { 371f7229173SVijendar Mukunda case ACP_SDW0: 372f7229173SVijendar Mukunda sdw_data->sdw0_dma_stream[stream->stream_id] = NULL; 373f7229173SVijendar Mukunda break; 374f7229173SVijendar Mukunda case ACP_SDW1: 375f7229173SVijendar Mukunda sdw_data->sdw1_dma_stream[stream->stream_id] = NULL; 376f7229173SVijendar Mukunda break; 377f7229173SVijendar Mukunda default: 378f7229173SVijendar Mukunda return -EINVAL; 379f7229173SVijendar Mukunda } 380f7229173SVijendar Mukunda kfree(stream); 381f7229173SVijendar Mukunda return 0; 382f7229173SVijendar Mukunda } 383f7229173SVijendar Mukunda 384f7229173SVijendar Mukunda static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream, 385f7229173SVijendar Mukunda void __iomem *acp_base, bool sdw_dma_enable) 386f7229173SVijendar Mukunda { 387f7229173SVijendar Mukunda struct acp_sdw_dma_stream *stream; 388f7229173SVijendar Mukunda u32 stream_id; 389f7229173SVijendar Mukunda u32 sdw_dma_en_reg; 390f7229173SVijendar Mukunda u32 sdw_dma_en_stat_reg; 391f7229173SVijendar Mukunda u32 sdw_dma_stat; 392f7229173SVijendar Mukunda u32 dma_enable; 393f7229173SVijendar Mukunda 394f7229173SVijendar Mukunda stream = substream->runtime->private_data; 395f7229173SVijendar Mukunda stream_id = stream->stream_id; 396f7229173SVijendar Mukunda switch (stream->instance) { 397f7229173SVijendar Mukunda case ACP_SDW0: 398f7229173SVijendar Mukunda sdw_dma_en_reg = sdw0_dma_enable_reg[stream_id]; 399f7229173SVijendar Mukunda break; 400f7229173SVijendar Mukunda case ACP_SDW1: 401f7229173SVijendar Mukunda sdw_dma_en_reg = sdw1_dma_enable_reg[stream_id]; 402f7229173SVijendar Mukunda break; 403f7229173SVijendar Mukunda default: 404f7229173SVijendar Mukunda return -EINVAL; 405f7229173SVijendar Mukunda } 406f7229173SVijendar Mukunda sdw_dma_en_stat_reg = sdw_dma_en_reg + 4; 407f7229173SVijendar Mukunda dma_enable = sdw_dma_enable; 408f7229173SVijendar Mukunda writel(dma_enable, acp_base + sdw_dma_en_reg); 409f7229173SVijendar Mukunda return readl_poll_timeout(acp_base + sdw_dma_en_stat_reg, sdw_dma_stat, 410f7229173SVijendar Mukunda (sdw_dma_stat == dma_enable), ACP_DELAY_US, ACP_COUNTER); 411f7229173SVijendar Mukunda } 412f7229173SVijendar Mukunda 413f7229173SVijendar Mukunda static int acp63_sdw_dma_trigger(struct snd_soc_component *comp, 414f7229173SVijendar Mukunda struct snd_pcm_substream *substream, 415f7229173SVijendar Mukunda int cmd) 416f7229173SVijendar Mukunda { 417f7229173SVijendar Mukunda struct sdw_dma_dev_data *sdw_data; 418f7229173SVijendar Mukunda int ret; 419f7229173SVijendar Mukunda 420f7229173SVijendar Mukunda sdw_data = dev_get_drvdata(comp->dev); 421f7229173SVijendar Mukunda switch (cmd) { 422f7229173SVijendar Mukunda case SNDRV_PCM_TRIGGER_START: 423f7229173SVijendar Mukunda case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 424f7229173SVijendar Mukunda case SNDRV_PCM_TRIGGER_RESUME: 425f7229173SVijendar Mukunda ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, true); 426f7229173SVijendar Mukunda break; 427f7229173SVijendar Mukunda case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 428f7229173SVijendar Mukunda case SNDRV_PCM_TRIGGER_SUSPEND: 429f7229173SVijendar Mukunda case SNDRV_PCM_TRIGGER_STOP: 430f7229173SVijendar Mukunda ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, false); 431f7229173SVijendar Mukunda break; 432f7229173SVijendar Mukunda default: 433f7229173SVijendar Mukunda ret = -EINVAL; 434f7229173SVijendar Mukunda } 435f7229173SVijendar Mukunda if (ret) 436f7229173SVijendar Mukunda dev_err(comp->dev, "trigger %d failed: %d", cmd, ret); 437f7229173SVijendar Mukunda return ret; 438f7229173SVijendar Mukunda } 439f7229173SVijendar Mukunda 440665dd181SVijendar Mukunda static const struct snd_soc_component_driver acp63_sdw_component = { 441665dd181SVijendar Mukunda .name = DRV_NAME, 442f7229173SVijendar Mukunda .open = acp63_sdw_dma_open, 443f7229173SVijendar Mukunda .close = acp63_sdw_dma_close, 444f7229173SVijendar Mukunda .hw_params = acp63_sdw_dma_hw_params, 445f7229173SVijendar Mukunda .trigger = acp63_sdw_dma_trigger, 446f7229173SVijendar Mukunda .pointer = acp63_sdw_dma_pointer, 447f7229173SVijendar Mukunda .pcm_construct = acp63_sdw_dma_new, 448665dd181SVijendar Mukunda }; 449665dd181SVijendar Mukunda 450665dd181SVijendar Mukunda static int acp63_sdw_platform_probe(struct platform_device *pdev) 451665dd181SVijendar Mukunda { 452665dd181SVijendar Mukunda struct resource *res; 453665dd181SVijendar Mukunda struct sdw_dma_dev_data *sdw_data; 454665dd181SVijendar Mukunda struct acp63_dev_data *acp_data; 455665dd181SVijendar Mukunda struct device *parent; 456665dd181SVijendar Mukunda int status; 457665dd181SVijendar Mukunda 458665dd181SVijendar Mukunda parent = pdev->dev.parent; 459665dd181SVijendar Mukunda acp_data = dev_get_drvdata(parent); 460665dd181SVijendar Mukunda res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 461665dd181SVijendar Mukunda if (!res) { 462665dd181SVijendar Mukunda dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); 463665dd181SVijendar Mukunda return -ENODEV; 464665dd181SVijendar Mukunda } 465665dd181SVijendar Mukunda 466665dd181SVijendar Mukunda sdw_data = devm_kzalloc(&pdev->dev, sizeof(*sdw_data), GFP_KERNEL); 467665dd181SVijendar Mukunda if (!sdw_data) 468665dd181SVijendar Mukunda return -ENOMEM; 469665dd181SVijendar Mukunda 470665dd181SVijendar Mukunda sdw_data->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); 471665dd181SVijendar Mukunda if (!sdw_data->acp_base) 472665dd181SVijendar Mukunda return -ENOMEM; 473665dd181SVijendar Mukunda 474665dd181SVijendar Mukunda sdw_data->acp_lock = &acp_data->acp_lock; 475665dd181SVijendar Mukunda dev_set_drvdata(&pdev->dev, sdw_data); 476665dd181SVijendar Mukunda status = devm_snd_soc_register_component(&pdev->dev, 477665dd181SVijendar Mukunda &acp63_sdw_component, 478665dd181SVijendar Mukunda NULL, 0); 4795a06c3acSVijendar Mukunda if (status) { 480665dd181SVijendar Mukunda dev_err(&pdev->dev, "Fail to register sdw dma component\n"); 481665dd181SVijendar Mukunda return status; 482665dd181SVijendar Mukunda } 4835a06c3acSVijendar Mukunda pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS); 4845a06c3acSVijendar Mukunda pm_runtime_use_autosuspend(&pdev->dev); 4855a06c3acSVijendar Mukunda pm_runtime_mark_last_busy(&pdev->dev); 4865a06c3acSVijendar Mukunda pm_runtime_set_active(&pdev->dev); 4875a06c3acSVijendar Mukunda pm_runtime_enable(&pdev->dev); 4885a06c3acSVijendar Mukunda return 0; 4895a06c3acSVijendar Mukunda } 4905a06c3acSVijendar Mukunda 491*acb5c0b1SUwe Kleine-König static void acp63_sdw_platform_remove(struct platform_device *pdev) 4925a06c3acSVijendar Mukunda { 4935a06c3acSVijendar Mukunda pm_runtime_disable(&pdev->dev); 4945a06c3acSVijendar Mukunda } 4955a06c3acSVijendar Mukunda 4965a06c3acSVijendar Mukunda static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data) 4975a06c3acSVijendar Mukunda { 4985a06c3acSVijendar Mukunda struct acp_sdw_dma_stream *stream; 4995a06c3acSVijendar Mukunda struct snd_pcm_substream *substream; 5005a06c3acSVijendar Mukunda struct snd_pcm_runtime *runtime; 5015a06c3acSVijendar Mukunda u32 period_bytes, buf_size, water_mark_size_reg; 5025a06c3acSVijendar Mukunda u32 stream_count; 5035a06c3acSVijendar Mukunda int index, instance, ret; 5045a06c3acSVijendar Mukunda 5055a06c3acSVijendar Mukunda for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) { 5065a06c3acSVijendar Mukunda if (instance == ACP_SDW0) 5075a06c3acSVijendar Mukunda stream_count = ACP63_SDW0_DMA_MAX_STREAMS; 5085a06c3acSVijendar Mukunda else 5095a06c3acSVijendar Mukunda stream_count = ACP63_SDW1_DMA_MAX_STREAMS; 5105a06c3acSVijendar Mukunda 5115a06c3acSVijendar Mukunda for (index = 0; index < stream_count; index++) { 5125a06c3acSVijendar Mukunda if (instance == ACP_SDW0) { 5135a06c3acSVijendar Mukunda substream = sdw_data->sdw0_dma_stream[index]; 5145a06c3acSVijendar Mukunda water_mark_size_reg = 5155a06c3acSVijendar Mukunda sdw0_dma_ring_buf_reg[index].water_mark_size_reg; 5165a06c3acSVijendar Mukunda } else { 5175a06c3acSVijendar Mukunda substream = sdw_data->sdw1_dma_stream[index]; 5185a06c3acSVijendar Mukunda water_mark_size_reg = 5195a06c3acSVijendar Mukunda sdw1_dma_ring_buf_reg[index].water_mark_size_reg; 5205a06c3acSVijendar Mukunda } 5215a06c3acSVijendar Mukunda 5225a06c3acSVijendar Mukunda if (substream && substream->runtime) { 5235a06c3acSVijendar Mukunda runtime = substream->runtime; 5245a06c3acSVijendar Mukunda stream = runtime->private_data; 5255a06c3acSVijendar Mukunda period_bytes = frames_to_bytes(runtime, runtime->period_size); 5265a06c3acSVijendar Mukunda buf_size = frames_to_bytes(runtime, runtime->buffer_size); 5275a06c3acSVijendar Mukunda acp63_config_dma(stream, sdw_data->acp_base, index); 5285a06c3acSVijendar Mukunda ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index, 5295a06c3acSVijendar Mukunda buf_size, instance); 5305a06c3acSVijendar Mukunda if (ret) 5315a06c3acSVijendar Mukunda return ret; 5325a06c3acSVijendar Mukunda writel(period_bytes, sdw_data->acp_base + water_mark_size_reg); 5335a06c3acSVijendar Mukunda } 5345a06c3acSVijendar Mukunda } 5355a06c3acSVijendar Mukunda } 5365a06c3acSVijendar Mukunda acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, true); 5375a06c3acSVijendar Mukunda return 0; 5385a06c3acSVijendar Mukunda } 5395a06c3acSVijendar Mukunda 5405a06c3acSVijendar Mukunda static int __maybe_unused acp63_sdw_pcm_resume(struct device *dev) 5415a06c3acSVijendar Mukunda { 5425a06c3acSVijendar Mukunda struct sdw_dma_dev_data *sdw_data; 5435a06c3acSVijendar Mukunda 5445a06c3acSVijendar Mukunda sdw_data = dev_get_drvdata(dev); 5455a06c3acSVijendar Mukunda return acp_restore_sdw_dma_config(sdw_data); 5465a06c3acSVijendar Mukunda } 5475a06c3acSVijendar Mukunda 5485a06c3acSVijendar Mukunda static const struct dev_pm_ops acp63_pm_ops = { 5495a06c3acSVijendar Mukunda SET_SYSTEM_SLEEP_PM_OPS(NULL, acp63_sdw_pcm_resume) 5505a06c3acSVijendar Mukunda }; 551665dd181SVijendar Mukunda 552665dd181SVijendar Mukunda static struct platform_driver acp63_sdw_dma_driver = { 553665dd181SVijendar Mukunda .probe = acp63_sdw_platform_probe, 554*acb5c0b1SUwe Kleine-König .remove_new = acp63_sdw_platform_remove, 555665dd181SVijendar Mukunda .driver = { 556665dd181SVijendar Mukunda .name = "amd_ps_sdw_dma", 5575a06c3acSVijendar Mukunda .pm = &acp63_pm_ops, 558665dd181SVijendar Mukunda }, 559665dd181SVijendar Mukunda }; 560665dd181SVijendar Mukunda 561665dd181SVijendar Mukunda module_platform_driver(acp63_sdw_dma_driver); 562665dd181SVijendar Mukunda 563665dd181SVijendar Mukunda MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); 564665dd181SVijendar Mukunda MODULE_DESCRIPTION("AMD ACP6.3 PS SDW DMA Driver"); 565665dd181SVijendar Mukunda MODULE_LICENSE("GPL"); 566665dd181SVijendar Mukunda MODULE_ALIAS("platform:" DRV_NAME); 567