177f61444SVijendar Mukunda // SPDX-License-Identifier: GPL-2.0+ 277f61444SVijendar Mukunda // 377f61444SVijendar Mukunda // AMD ALSA SoC PCM Driver 477f61444SVijendar Mukunda // 577f61444SVijendar Mukunda // Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved. 677f61444SVijendar Mukunda 777f61444SVijendar Mukunda #include <linux/platform_device.h> 877f61444SVijendar Mukunda #include <linux/module.h> 977f61444SVijendar Mukunda #include <linux/err.h> 1077f61444SVijendar Mukunda #include <linux/io.h> 11361414dcSVijendar Mukunda #include <linux/pm_runtime.h> 1277f61444SVijendar Mukunda #include <sound/pcm.h> 1377f61444SVijendar Mukunda #include <sound/pcm_params.h> 1477f61444SVijendar Mukunda #include <sound/soc.h> 1577f61444SVijendar Mukunda #include <sound/soc-dai.h> 1677f61444SVijendar Mukunda 1777f61444SVijendar Mukunda #include "acp5x.h" 1877f61444SVijendar Mukunda 1977f61444SVijendar Mukunda #define DRV_NAME "acp5x_i2s_dma" 2077f61444SVijendar Mukunda 21cab396d8SVijendar Mukunda static const struct snd_pcm_hardware acp5x_pcm_hardware_playback = { 22cab396d8SVijendar Mukunda .info = SNDRV_PCM_INFO_INTERLEAVED | 23cab396d8SVijendar Mukunda SNDRV_PCM_INFO_BLOCK_TRANSFER | 24cab396d8SVijendar Mukunda SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 25cab396d8SVijendar Mukunda SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 26cab396d8SVijendar Mukunda .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 27cab396d8SVijendar Mukunda SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, 28cab396d8SVijendar Mukunda .channels_min = 2, 29cab396d8SVijendar Mukunda .channels_max = 2, 30cab396d8SVijendar Mukunda .rates = SNDRV_PCM_RATE_8000_96000, 31cab396d8SVijendar Mukunda .rate_min = 8000, 32cab396d8SVijendar Mukunda .rate_max = 96000, 33cab396d8SVijendar Mukunda .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE, 34cab396d8SVijendar Mukunda .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, 35cab396d8SVijendar Mukunda .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, 36cab396d8SVijendar Mukunda .periods_min = PLAYBACK_MIN_NUM_PERIODS, 37cab396d8SVijendar Mukunda .periods_max = PLAYBACK_MAX_NUM_PERIODS, 38cab396d8SVijendar Mukunda }; 39cab396d8SVijendar Mukunda 40cab396d8SVijendar Mukunda static const struct snd_pcm_hardware acp5x_pcm_hardware_capture = { 41cab396d8SVijendar Mukunda .info = SNDRV_PCM_INFO_INTERLEAVED | 42cab396d8SVijendar Mukunda SNDRV_PCM_INFO_BLOCK_TRANSFER | 43cab396d8SVijendar Mukunda SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 44cab396d8SVijendar Mukunda SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 45cab396d8SVijendar Mukunda .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 46cab396d8SVijendar Mukunda SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, 47cab396d8SVijendar Mukunda .channels_min = 2, 48cab396d8SVijendar Mukunda .channels_max = 2, 49cab396d8SVijendar Mukunda .rates = SNDRV_PCM_RATE_8000_96000, 50cab396d8SVijendar Mukunda .rate_min = 8000, 51cab396d8SVijendar Mukunda .rate_max = 96000, 52cab396d8SVijendar Mukunda .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE, 53cab396d8SVijendar Mukunda .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, 54cab396d8SVijendar Mukunda .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, 55cab396d8SVijendar Mukunda .periods_min = CAPTURE_MIN_NUM_PERIODS, 56cab396d8SVijendar Mukunda .periods_max = CAPTURE_MAX_NUM_PERIODS, 5777f61444SVijendar Mukunda }; 5877f61444SVijendar Mukunda 59fc2c8067SVijendar Mukunda static irqreturn_t i2s_irq_handler(int irq, void *dev_id) 60fc2c8067SVijendar Mukunda { 61fc2c8067SVijendar Mukunda struct i2s_dev_data *vg_i2s_data; 62fc2c8067SVijendar Mukunda u16 irq_flag; 63fc2c8067SVijendar Mukunda u32 val; 64fc2c8067SVijendar Mukunda 65fc2c8067SVijendar Mukunda vg_i2s_data = dev_id; 66fc2c8067SVijendar Mukunda if (!vg_i2s_data) 67fc2c8067SVijendar Mukunda return IRQ_NONE; 68fc2c8067SVijendar Mukunda 69fc2c8067SVijendar Mukunda irq_flag = 0; 70fc2c8067SVijendar Mukunda val = acp_readl(vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT); 71fc2c8067SVijendar Mukunda if ((val & BIT(HS_TX_THRESHOLD)) && vg_i2s_data->play_stream) { 72fc2c8067SVijendar Mukunda acp_writel(BIT(HS_TX_THRESHOLD), vg_i2s_data->acp5x_base + 73fc2c8067SVijendar Mukunda ACP_EXTERNAL_INTR_STAT); 74fc2c8067SVijendar Mukunda snd_pcm_period_elapsed(vg_i2s_data->play_stream); 75fc2c8067SVijendar Mukunda irq_flag = 1; 76fc2c8067SVijendar Mukunda } 77fc2c8067SVijendar Mukunda if ((val & BIT(I2S_TX_THRESHOLD)) && vg_i2s_data->i2ssp_play_stream) { 78fc2c8067SVijendar Mukunda acp_writel(BIT(I2S_TX_THRESHOLD), 79fc2c8067SVijendar Mukunda vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT); 80fc2c8067SVijendar Mukunda snd_pcm_period_elapsed(vg_i2s_data->i2ssp_play_stream); 81fc2c8067SVijendar Mukunda irq_flag = 1; 82fc2c8067SVijendar Mukunda } 83fc2c8067SVijendar Mukunda 84fc2c8067SVijendar Mukunda if ((val & BIT(HS_RX_THRESHOLD)) && vg_i2s_data->capture_stream) { 85fc2c8067SVijendar Mukunda acp_writel(BIT(HS_RX_THRESHOLD), vg_i2s_data->acp5x_base + 86fc2c8067SVijendar Mukunda ACP_EXTERNAL_INTR_STAT); 87fc2c8067SVijendar Mukunda snd_pcm_period_elapsed(vg_i2s_data->capture_stream); 88fc2c8067SVijendar Mukunda irq_flag = 1; 89fc2c8067SVijendar Mukunda } 90fc2c8067SVijendar Mukunda if ((val & BIT(I2S_RX_THRESHOLD)) && vg_i2s_data->i2ssp_capture_stream) { 91fc2c8067SVijendar Mukunda acp_writel(BIT(I2S_RX_THRESHOLD), 92fc2c8067SVijendar Mukunda vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT); 93fc2c8067SVijendar Mukunda snd_pcm_period_elapsed(vg_i2s_data->i2ssp_capture_stream); 94fc2c8067SVijendar Mukunda irq_flag = 1; 95fc2c8067SVijendar Mukunda } 96fc2c8067SVijendar Mukunda 97fc2c8067SVijendar Mukunda if (irq_flag) 98fc2c8067SVijendar Mukunda return IRQ_HANDLED; 99fc2c8067SVijendar Mukunda else 100fc2c8067SVijendar Mukunda return IRQ_NONE; 101fc2c8067SVijendar Mukunda } 102fc2c8067SVijendar Mukunda 103cab396d8SVijendar Mukunda static void config_acp5x_dma(struct i2s_stream_instance *rtd, int direction) 104cab396d8SVijendar Mukunda { 105cab396d8SVijendar Mukunda u16 page_idx; 106cab396d8SVijendar Mukunda u32 low, high, val, acp_fifo_addr, reg_fifo_addr; 107cab396d8SVijendar Mukunda u32 reg_dma_size, reg_fifo_size; 108cab396d8SVijendar Mukunda dma_addr_t addr; 109cab396d8SVijendar Mukunda 110cab396d8SVijendar Mukunda addr = rtd->dma_addr; 111cab396d8SVijendar Mukunda if (direction == SNDRV_PCM_STREAM_PLAYBACK) { 112cab396d8SVijendar Mukunda switch (rtd->i2s_instance) { 113cab396d8SVijendar Mukunda case I2S_HS_INSTANCE: 114cab396d8SVijendar Mukunda val = ACP_SRAM_HS_PB_PTE_OFFSET; 115cab396d8SVijendar Mukunda break; 116cab396d8SVijendar Mukunda case I2S_SP_INSTANCE: 117cab396d8SVijendar Mukunda default: 118cab396d8SVijendar Mukunda val = ACP_SRAM_SP_PB_PTE_OFFSET; 119cab396d8SVijendar Mukunda } 120cab396d8SVijendar Mukunda } else { 121cab396d8SVijendar Mukunda switch (rtd->i2s_instance) { 122cab396d8SVijendar Mukunda case I2S_HS_INSTANCE: 123cab396d8SVijendar Mukunda val = ACP_SRAM_HS_CP_PTE_OFFSET; 124cab396d8SVijendar Mukunda break; 125cab396d8SVijendar Mukunda case I2S_SP_INSTANCE: 126cab396d8SVijendar Mukunda default: 127cab396d8SVijendar Mukunda val = ACP_SRAM_SP_CP_PTE_OFFSET; 128cab396d8SVijendar Mukunda } 129cab396d8SVijendar Mukunda } 130cab396d8SVijendar Mukunda /* Group Enable */ 131cab396d8SVijendar Mukunda acp_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp5x_base + 132cab396d8SVijendar Mukunda ACPAXI2AXI_ATU_BASE_ADDR_GRP_1); 133cab396d8SVijendar Mukunda acp_writel(PAGE_SIZE_4K_ENABLE, rtd->acp5x_base + 134cab396d8SVijendar Mukunda ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1); 135cab396d8SVijendar Mukunda 136cab396d8SVijendar Mukunda for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) { 137cab396d8SVijendar Mukunda /* Load the low address of page int ACP SRAM through SRBM */ 138cab396d8SVijendar Mukunda low = lower_32_bits(addr); 139cab396d8SVijendar Mukunda high = upper_32_bits(addr); 140cab396d8SVijendar Mukunda 141cab396d8SVijendar Mukunda acp_writel(low, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val); 142cab396d8SVijendar Mukunda high |= BIT(31); 143cab396d8SVijendar Mukunda acp_writel(high, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val + 4); 144cab396d8SVijendar Mukunda /* Move to next physically contiguous page */ 145cab396d8SVijendar Mukunda val += 8; 146cab396d8SVijendar Mukunda addr += PAGE_SIZE; 147cab396d8SVijendar Mukunda } 148cab396d8SVijendar Mukunda 149cab396d8SVijendar Mukunda if (direction == SNDRV_PCM_STREAM_PLAYBACK) { 150cab396d8SVijendar Mukunda switch (rtd->i2s_instance) { 151cab396d8SVijendar Mukunda case I2S_HS_INSTANCE: 152cab396d8SVijendar Mukunda reg_dma_size = ACP_HS_TX_DMA_SIZE; 153cab396d8SVijendar Mukunda acp_fifo_addr = ACP_SRAM_PTE_OFFSET + 154cab396d8SVijendar Mukunda HS_PB_FIFO_ADDR_OFFSET; 155cab396d8SVijendar Mukunda reg_fifo_addr = ACP_HS_TX_FIFOADDR; 156cab396d8SVijendar Mukunda reg_fifo_size = ACP_HS_TX_FIFOSIZE; 157cab396d8SVijendar Mukunda acp_writel(I2S_HS_TX_MEM_WINDOW_START, 158cab396d8SVijendar Mukunda rtd->acp5x_base + ACP_HS_TX_RINGBUFADDR); 159cab396d8SVijendar Mukunda break; 160cab396d8SVijendar Mukunda 161cab396d8SVijendar Mukunda case I2S_SP_INSTANCE: 162cab396d8SVijendar Mukunda default: 163cab396d8SVijendar Mukunda reg_dma_size = ACP_I2S_TX_DMA_SIZE; 164cab396d8SVijendar Mukunda acp_fifo_addr = ACP_SRAM_PTE_OFFSET + 165cab396d8SVijendar Mukunda SP_PB_FIFO_ADDR_OFFSET; 166cab396d8SVijendar Mukunda reg_fifo_addr = ACP_I2S_TX_FIFOADDR; 167cab396d8SVijendar Mukunda reg_fifo_size = ACP_I2S_TX_FIFOSIZE; 168cab396d8SVijendar Mukunda acp_writel(I2S_SP_TX_MEM_WINDOW_START, 169cab396d8SVijendar Mukunda rtd->acp5x_base + ACP_I2S_TX_RINGBUFADDR); 170cab396d8SVijendar Mukunda } 171cab396d8SVijendar Mukunda } else { 172cab396d8SVijendar Mukunda switch (rtd->i2s_instance) { 173cab396d8SVijendar Mukunda case I2S_HS_INSTANCE: 174cab396d8SVijendar Mukunda reg_dma_size = ACP_HS_RX_DMA_SIZE; 175cab396d8SVijendar Mukunda acp_fifo_addr = ACP_SRAM_PTE_OFFSET + 176cab396d8SVijendar Mukunda HS_CAPT_FIFO_ADDR_OFFSET; 177cab396d8SVijendar Mukunda reg_fifo_addr = ACP_HS_RX_FIFOADDR; 178cab396d8SVijendar Mukunda reg_fifo_size = ACP_HS_RX_FIFOSIZE; 179cab396d8SVijendar Mukunda acp_writel(I2S_HS_RX_MEM_WINDOW_START, 180cab396d8SVijendar Mukunda rtd->acp5x_base + ACP_HS_RX_RINGBUFADDR); 181cab396d8SVijendar Mukunda break; 182cab396d8SVijendar Mukunda 183cab396d8SVijendar Mukunda case I2S_SP_INSTANCE: 184cab396d8SVijendar Mukunda default: 185cab396d8SVijendar Mukunda reg_dma_size = ACP_I2S_RX_DMA_SIZE; 186cab396d8SVijendar Mukunda acp_fifo_addr = ACP_SRAM_PTE_OFFSET + 187cab396d8SVijendar Mukunda SP_CAPT_FIFO_ADDR_OFFSET; 188cab396d8SVijendar Mukunda reg_fifo_addr = ACP_I2S_RX_FIFOADDR; 189cab396d8SVijendar Mukunda reg_fifo_size = ACP_I2S_RX_FIFOSIZE; 190cab396d8SVijendar Mukunda acp_writel(I2S_SP_RX_MEM_WINDOW_START, 191cab396d8SVijendar Mukunda rtd->acp5x_base + ACP_I2S_RX_RINGBUFADDR); 192cab396d8SVijendar Mukunda } 193cab396d8SVijendar Mukunda } 194cab396d8SVijendar Mukunda acp_writel(DMA_SIZE, rtd->acp5x_base + reg_dma_size); 195cab396d8SVijendar Mukunda acp_writel(acp_fifo_addr, rtd->acp5x_base + reg_fifo_addr); 196cab396d8SVijendar Mukunda acp_writel(FIFO_SIZE, rtd->acp5x_base + reg_fifo_size); 197cab396d8SVijendar Mukunda acp_writel(BIT(I2S_RX_THRESHOLD) | BIT(HS_RX_THRESHOLD) 198cab396d8SVijendar Mukunda | BIT(I2S_TX_THRESHOLD) | BIT(HS_TX_THRESHOLD), 199cab396d8SVijendar Mukunda rtd->acp5x_base + ACP_EXTERNAL_INTR_CNTL); 200cab396d8SVijendar Mukunda } 201cab396d8SVijendar Mukunda 202cab396d8SVijendar Mukunda static int acp5x_dma_open(struct snd_soc_component *component, 203cab396d8SVijendar Mukunda struct snd_pcm_substream *substream) 204cab396d8SVijendar Mukunda { 205cab396d8SVijendar Mukunda struct snd_pcm_runtime *runtime; 206cab396d8SVijendar Mukunda struct snd_soc_pcm_runtime *prtd; 207cab396d8SVijendar Mukunda struct i2s_dev_data *adata; 208cab396d8SVijendar Mukunda struct i2s_stream_instance *i2s_data; 209cab396d8SVijendar Mukunda int ret; 210cab396d8SVijendar Mukunda 211cab396d8SVijendar Mukunda runtime = substream->runtime; 212cab396d8SVijendar Mukunda prtd = asoc_substream_to_rtd(substream); 213cab396d8SVijendar Mukunda component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); 214cab396d8SVijendar Mukunda adata = dev_get_drvdata(component->dev); 215cab396d8SVijendar Mukunda 216cab396d8SVijendar Mukunda i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL); 217cab396d8SVijendar Mukunda if (!i2s_data) 218cab396d8SVijendar Mukunda return -ENOMEM; 219cab396d8SVijendar Mukunda 220cab396d8SVijendar Mukunda if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 221cab396d8SVijendar Mukunda runtime->hw = acp5x_pcm_hardware_playback; 222cab396d8SVijendar Mukunda else 223cab396d8SVijendar Mukunda runtime->hw = acp5x_pcm_hardware_capture; 224cab396d8SVijendar Mukunda 225cab396d8SVijendar Mukunda ret = snd_pcm_hw_constraint_integer(runtime, 226cab396d8SVijendar Mukunda SNDRV_PCM_HW_PARAM_PERIODS); 227cab396d8SVijendar Mukunda if (ret < 0) { 228cab396d8SVijendar Mukunda dev_err(component->dev, "set integer constraint failed\n"); 229cab396d8SVijendar Mukunda kfree(i2s_data); 230cab396d8SVijendar Mukunda return ret; 231cab396d8SVijendar Mukunda } 232cab396d8SVijendar Mukunda i2s_data->acp5x_base = adata->acp5x_base; 233cab396d8SVijendar Mukunda runtime->private_data = i2s_data; 234cab396d8SVijendar Mukunda return ret; 235cab396d8SVijendar Mukunda } 236cab396d8SVijendar Mukunda 237cab396d8SVijendar Mukunda static int acp5x_dma_hw_params(struct snd_soc_component *component, 238cab396d8SVijendar Mukunda struct snd_pcm_substream *substream, 239cab396d8SVijendar Mukunda struct snd_pcm_hw_params *params) 240cab396d8SVijendar Mukunda { 241cab396d8SVijendar Mukunda struct i2s_stream_instance *rtd; 242cab396d8SVijendar Mukunda struct snd_soc_pcm_runtime *prtd; 243cab396d8SVijendar Mukunda struct snd_soc_card *card; 244cab396d8SVijendar Mukunda struct acp5x_platform_info *pinfo; 245cab396d8SVijendar Mukunda struct i2s_dev_data *adata; 246cab396d8SVijendar Mukunda u64 size; 247cab396d8SVijendar Mukunda 248cab396d8SVijendar Mukunda prtd = asoc_substream_to_rtd(substream); 249cab396d8SVijendar Mukunda card = prtd->card; 250cab396d8SVijendar Mukunda pinfo = snd_soc_card_get_drvdata(card); 251cab396d8SVijendar Mukunda adata = dev_get_drvdata(component->dev); 252cab396d8SVijendar Mukunda rtd = substream->runtime->private_data; 253cab396d8SVijendar Mukunda 254cab396d8SVijendar Mukunda if (!rtd) 255cab396d8SVijendar Mukunda return -EINVAL; 256cab396d8SVijendar Mukunda 257cab396d8SVijendar Mukunda if (pinfo) { 258cab396d8SVijendar Mukunda if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 259cab396d8SVijendar Mukunda rtd->i2s_instance = pinfo->play_i2s_instance; 260cab396d8SVijendar Mukunda switch (rtd->i2s_instance) { 261cab396d8SVijendar Mukunda case I2S_HS_INSTANCE: 262cab396d8SVijendar Mukunda adata->play_stream = substream; 263cab396d8SVijendar Mukunda break; 264cab396d8SVijendar Mukunda case I2S_SP_INSTANCE: 265cab396d8SVijendar Mukunda default: 266cab396d8SVijendar Mukunda adata->i2ssp_play_stream = substream; 267cab396d8SVijendar Mukunda } 268cab396d8SVijendar Mukunda } else { 269cab396d8SVijendar Mukunda rtd->i2s_instance = pinfo->cap_i2s_instance; 270cab396d8SVijendar Mukunda switch (rtd->i2s_instance) { 271cab396d8SVijendar Mukunda case I2S_HS_INSTANCE: 272cab396d8SVijendar Mukunda adata->capture_stream = substream; 273cab396d8SVijendar Mukunda break; 274cab396d8SVijendar Mukunda case I2S_SP_INSTANCE: 275cab396d8SVijendar Mukunda default: 276cab396d8SVijendar Mukunda adata->i2ssp_capture_stream = substream; 277cab396d8SVijendar Mukunda } 278cab396d8SVijendar Mukunda } 279cab396d8SVijendar Mukunda } else { 280cab396d8SVijendar Mukunda dev_err(component->dev, "pinfo failed\n"); 281cab396d8SVijendar Mukunda return -EINVAL; 282cab396d8SVijendar Mukunda } 283cab396d8SVijendar Mukunda size = params_buffer_bytes(params); 28454e1bf9fSMeng Tang rtd->dma_addr = substream->runtime->dma_addr; 285cab396d8SVijendar Mukunda rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); 286cab396d8SVijendar Mukunda config_acp5x_dma(rtd, substream->stream); 287cab396d8SVijendar Mukunda return 0; 288cab396d8SVijendar Mukunda } 289cab396d8SVijendar Mukunda 290cab396d8SVijendar Mukunda static snd_pcm_uframes_t acp5x_dma_pointer(struct snd_soc_component *component, 291cab396d8SVijendar Mukunda struct snd_pcm_substream *substream) 292cab396d8SVijendar Mukunda { 293cab396d8SVijendar Mukunda struct i2s_stream_instance *rtd; 294cab396d8SVijendar Mukunda u32 pos; 295cab396d8SVijendar Mukunda u32 buffersize; 296cab396d8SVijendar Mukunda u64 bytescount; 297cab396d8SVijendar Mukunda 298cab396d8SVijendar Mukunda rtd = substream->runtime->private_data; 299cab396d8SVijendar Mukunda buffersize = frames_to_bytes(substream->runtime, 300cab396d8SVijendar Mukunda substream->runtime->buffer_size); 301cab396d8SVijendar Mukunda bytescount = acp_get_byte_count(rtd, substream->stream); 302cab396d8SVijendar Mukunda if (bytescount > rtd->bytescount) 303cab396d8SVijendar Mukunda bytescount -= rtd->bytescount; 304cab396d8SVijendar Mukunda pos = do_div(bytescount, buffersize); 305cab396d8SVijendar Mukunda return bytes_to_frames(substream->runtime, pos); 306cab396d8SVijendar Mukunda } 307cab396d8SVijendar Mukunda 308cab396d8SVijendar Mukunda static int acp5x_dma_new(struct snd_soc_component *component, 309cab396d8SVijendar Mukunda struct snd_soc_pcm_runtime *rtd) 310cab396d8SVijendar Mukunda { 311cab396d8SVijendar Mukunda struct device *parent = component->dev->parent; 312cab396d8SVijendar Mukunda 313cab396d8SVijendar Mukunda snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, 314cab396d8SVijendar Mukunda parent, MIN_BUFFER, MAX_BUFFER); 315cab396d8SVijendar Mukunda return 0; 316cab396d8SVijendar Mukunda } 317cab396d8SVijendar Mukunda 318cab396d8SVijendar Mukunda static int acp5x_dma_close(struct snd_soc_component *component, 319cab396d8SVijendar Mukunda struct snd_pcm_substream *substream) 320cab396d8SVijendar Mukunda { 321cab396d8SVijendar Mukunda struct snd_soc_pcm_runtime *prtd; 322cab396d8SVijendar Mukunda struct i2s_dev_data *adata; 323cab396d8SVijendar Mukunda struct i2s_stream_instance *ins; 324cab396d8SVijendar Mukunda 325cab396d8SVijendar Mukunda prtd = asoc_substream_to_rtd(substream); 326cab396d8SVijendar Mukunda component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); 327cab396d8SVijendar Mukunda adata = dev_get_drvdata(component->dev); 328cab396d8SVijendar Mukunda ins = substream->runtime->private_data; 329cab396d8SVijendar Mukunda if (!ins) 330cab396d8SVijendar Mukunda return -EINVAL; 331cab396d8SVijendar Mukunda if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 332cab396d8SVijendar Mukunda switch (ins->i2s_instance) { 333cab396d8SVijendar Mukunda case I2S_HS_INSTANCE: 334cab396d8SVijendar Mukunda adata->play_stream = NULL; 335cab396d8SVijendar Mukunda break; 336cab396d8SVijendar Mukunda case I2S_SP_INSTANCE: 337cab396d8SVijendar Mukunda default: 338cab396d8SVijendar Mukunda adata->i2ssp_play_stream = NULL; 339cab396d8SVijendar Mukunda } 340cab396d8SVijendar Mukunda } else { 341cab396d8SVijendar Mukunda switch (ins->i2s_instance) { 342cab396d8SVijendar Mukunda case I2S_HS_INSTANCE: 343cab396d8SVijendar Mukunda adata->capture_stream = NULL; 344cab396d8SVijendar Mukunda break; 345cab396d8SVijendar Mukunda case I2S_SP_INSTANCE: 346cab396d8SVijendar Mukunda default: 347cab396d8SVijendar Mukunda adata->i2ssp_capture_stream = NULL; 348cab396d8SVijendar Mukunda } 349cab396d8SVijendar Mukunda } 350cab396d8SVijendar Mukunda kfree(ins); 351cab396d8SVijendar Mukunda return 0; 352cab396d8SVijendar Mukunda } 353cab396d8SVijendar Mukunda 354cab396d8SVijendar Mukunda static const struct snd_soc_component_driver acp5x_i2s_component = { 355cab396d8SVijendar Mukunda .name = DRV_NAME, 356cab396d8SVijendar Mukunda .open = acp5x_dma_open, 357cab396d8SVijendar Mukunda .close = acp5x_dma_close, 358cab396d8SVijendar Mukunda .hw_params = acp5x_dma_hw_params, 359cab396d8SVijendar Mukunda .pointer = acp5x_dma_pointer, 360cab396d8SVijendar Mukunda .pcm_construct = acp5x_dma_new, 361cab396d8SVijendar Mukunda }; 362cab396d8SVijendar Mukunda 36377f61444SVijendar Mukunda static int acp5x_audio_probe(struct platform_device *pdev) 36477f61444SVijendar Mukunda { 36577f61444SVijendar Mukunda struct resource *res; 36677f61444SVijendar Mukunda struct i2s_dev_data *adata; 367fc2c8067SVijendar Mukunda unsigned int irqflags; 36877f61444SVijendar Mukunda int status; 36977f61444SVijendar Mukunda 37077f61444SVijendar Mukunda if (!pdev->dev.platform_data) { 37177f61444SVijendar Mukunda dev_err(&pdev->dev, "platform_data not retrieved\n"); 37277f61444SVijendar Mukunda return -ENODEV; 37377f61444SVijendar Mukunda } 37477f61444SVijendar Mukunda irqflags = *((unsigned int *)(pdev->dev.platform_data)); 37577f61444SVijendar Mukunda 37677f61444SVijendar Mukunda res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 37777f61444SVijendar Mukunda if (!res) { 37877f61444SVijendar Mukunda dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); 37977f61444SVijendar Mukunda return -ENODEV; 38077f61444SVijendar Mukunda } 38177f61444SVijendar Mukunda 38277f61444SVijendar Mukunda adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL); 38377f61444SVijendar Mukunda if (!adata) 38477f61444SVijendar Mukunda return -ENOMEM; 38577f61444SVijendar Mukunda 38677f61444SVijendar Mukunda adata->acp5x_base = devm_ioremap(&pdev->dev, res->start, 38777f61444SVijendar Mukunda resource_size(res)); 38877f61444SVijendar Mukunda if (!adata->acp5x_base) 38977f61444SVijendar Mukunda return -ENOMEM; 390fc2c8067SVijendar Mukunda 391468f2529SDan Carpenter status = platform_get_irq(pdev, 0); 392468f2529SDan Carpenter if (status < 0) 393468f2529SDan Carpenter return status; 394468f2529SDan Carpenter adata->i2s_irq = status; 395fc2c8067SVijendar Mukunda 39677f61444SVijendar Mukunda dev_set_drvdata(&pdev->dev, adata); 39777f61444SVijendar Mukunda status = devm_snd_soc_register_component(&pdev->dev, 39877f61444SVijendar Mukunda &acp5x_i2s_component, 39977f61444SVijendar Mukunda NULL, 0); 400fc2c8067SVijendar Mukunda if (status) { 40177f61444SVijendar Mukunda dev_err(&pdev->dev, "Fail to register acp i2s component\n"); 402fc2c8067SVijendar Mukunda return status; 403fc2c8067SVijendar Mukunda } 404fc2c8067SVijendar Mukunda status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler, 405fc2c8067SVijendar Mukunda irqflags, "ACP5x_I2S_IRQ", adata); 406361414dcSVijendar Mukunda if (status) { 407fc2c8067SVijendar Mukunda dev_err(&pdev->dev, "ACP5x I2S IRQ request failed\n"); 40877f61444SVijendar Mukunda return status; 40977f61444SVijendar Mukunda } 410361414dcSVijendar Mukunda pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); 411361414dcSVijendar Mukunda pm_runtime_use_autosuspend(&pdev->dev); 412361414dcSVijendar Mukunda pm_runtime_enable(&pdev->dev); 413361414dcSVijendar Mukunda pm_runtime_allow(&pdev->dev); 414361414dcSVijendar Mukunda 415361414dcSVijendar Mukunda return 0; 416361414dcSVijendar Mukunda } 417361414dcSVijendar Mukunda 4188564d4f2SUwe Kleine-König static void acp5x_audio_remove(struct platform_device *pdev) 419361414dcSVijendar Mukunda { 420361414dcSVijendar Mukunda pm_runtime_disable(&pdev->dev); 421361414dcSVijendar Mukunda } 422361414dcSVijendar Mukunda 423361414dcSVijendar Mukunda static int __maybe_unused acp5x_pcm_resume(struct device *dev) 424361414dcSVijendar Mukunda { 425361414dcSVijendar Mukunda struct i2s_dev_data *adata; 42683b71361SVijendar Mukunda struct i2s_stream_instance *rtd; 42783b71361SVijendar Mukunda u32 val; 428361414dcSVijendar Mukunda 429361414dcSVijendar Mukunda adata = dev_get_drvdata(dev); 430361414dcSVijendar Mukunda 431361414dcSVijendar Mukunda if (adata->play_stream && adata->play_stream->runtime) { 43283b71361SVijendar Mukunda rtd = adata->play_stream->runtime->private_data; 433361414dcSVijendar Mukunda config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK); 43483b71361SVijendar Mukunda acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_ITER); 43583b71361SVijendar Mukunda if (adata->tdm_mode == TDM_ENABLE) { 43683b71361SVijendar Mukunda acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_TXFRMT); 43783b71361SVijendar Mukunda val = acp_readl(adata->acp5x_base + ACP_HSTDM_ITER); 43883b71361SVijendar Mukunda acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_ITER); 439361414dcSVijendar Mukunda } 44083b71361SVijendar Mukunda } 44183b71361SVijendar Mukunda if (adata->i2ssp_play_stream && adata->i2ssp_play_stream->runtime) { 44283b71361SVijendar Mukunda rtd = adata->i2ssp_play_stream->runtime->private_data; 44383b71361SVijendar Mukunda config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK); 44483b71361SVijendar Mukunda acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_ITER); 44583b71361SVijendar Mukunda if (adata->tdm_mode == TDM_ENABLE) { 44683b71361SVijendar Mukunda acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_TXFRMT); 44783b71361SVijendar Mukunda val = acp_readl(adata->acp5x_base + ACP_I2STDM_ITER); 44883b71361SVijendar Mukunda acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_ITER); 44983b71361SVijendar Mukunda } 450361414dcSVijendar Mukunda } 451361414dcSVijendar Mukunda 452361414dcSVijendar Mukunda if (adata->capture_stream && adata->capture_stream->runtime) { 45383b71361SVijendar Mukunda rtd = adata->capture_stream->runtime->private_data; 454361414dcSVijendar Mukunda config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE); 45583b71361SVijendar Mukunda acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_IRER); 456361414dcSVijendar Mukunda if (adata->tdm_mode == TDM_ENABLE) { 45783b71361SVijendar Mukunda acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_RXFRMT); 45883b71361SVijendar Mukunda val = acp_readl(adata->acp5x_base + ACP_HSTDM_IRER); 45983b71361SVijendar Mukunda acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_IRER); 46083b71361SVijendar Mukunda } 46183b71361SVijendar Mukunda } 46283b71361SVijendar Mukunda if (adata->i2ssp_capture_stream && adata->i2ssp_capture_stream->runtime) { 46383b71361SVijendar Mukunda rtd = adata->i2ssp_capture_stream->runtime->private_data; 46483b71361SVijendar Mukunda config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE); 46583b71361SVijendar Mukunda acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_IRER); 46683b71361SVijendar Mukunda if (adata->tdm_mode == TDM_ENABLE) { 46783b71361SVijendar Mukunda acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_RXFRMT); 46883b71361SVijendar Mukunda val = acp_readl(adata->acp5x_base + ACP_I2STDM_IRER); 46983b71361SVijendar Mukunda acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_IRER); 47083b71361SVijendar Mukunda } 471361414dcSVijendar Mukunda } 472361414dcSVijendar Mukunda acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB); 473361414dcSVijendar Mukunda return 0; 474361414dcSVijendar Mukunda } 475361414dcSVijendar Mukunda 476361414dcSVijendar Mukunda static int __maybe_unused acp5x_pcm_suspend(struct device *dev) 477361414dcSVijendar Mukunda { 478361414dcSVijendar Mukunda struct i2s_dev_data *adata; 479361414dcSVijendar Mukunda 480361414dcSVijendar Mukunda adata = dev_get_drvdata(dev); 481361414dcSVijendar Mukunda acp_writel(0, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB); 482361414dcSVijendar Mukunda return 0; 483361414dcSVijendar Mukunda } 484361414dcSVijendar Mukunda 485361414dcSVijendar Mukunda static int __maybe_unused acp5x_pcm_runtime_resume(struct device *dev) 486361414dcSVijendar Mukunda { 487361414dcSVijendar Mukunda struct i2s_dev_data *adata; 488361414dcSVijendar Mukunda 489361414dcSVijendar Mukunda adata = dev_get_drvdata(dev); 490361414dcSVijendar Mukunda acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB); 491361414dcSVijendar Mukunda return 0; 492361414dcSVijendar Mukunda } 493361414dcSVijendar Mukunda 494361414dcSVijendar Mukunda static const struct dev_pm_ops acp5x_pm_ops = { 495361414dcSVijendar Mukunda SET_RUNTIME_PM_OPS(acp5x_pcm_suspend, 496361414dcSVijendar Mukunda acp5x_pcm_runtime_resume, NULL) 497361414dcSVijendar Mukunda SET_SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_resume) 498361414dcSVijendar Mukunda }; 49977f61444SVijendar Mukunda 50077f61444SVijendar Mukunda static struct platform_driver acp5x_dma_driver = { 50177f61444SVijendar Mukunda .probe = acp5x_audio_probe, 5028564d4f2SUwe Kleine-König .remove_new = acp5x_audio_remove, 50377f61444SVijendar Mukunda .driver = { 50477f61444SVijendar Mukunda .name = "acp5x_i2s_dma", 505361414dcSVijendar Mukunda .pm = &acp5x_pm_ops, 50677f61444SVijendar Mukunda }, 50777f61444SVijendar Mukunda }; 50877f61444SVijendar Mukunda 50977f61444SVijendar Mukunda module_platform_driver(acp5x_dma_driver); 51077f61444SVijendar Mukunda 51177f61444SVijendar Mukunda MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); 51277f61444SVijendar Mukunda MODULE_DESCRIPTION("AMD ACP 5.x PCM Driver"); 51377f61444SVijendar Mukunda MODULE_LICENSE("GPL v2"); 51477f61444SVijendar Mukunda MODULE_ALIAS("platform:" DRV_NAME); 515