18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// AMD ALSA SoC PCM Driver 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci//Copyright 2016 Advanced Micro Devices, Inc. 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 128c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 138c2ecf20Sopenharmony_ci#include <sound/soc.h> 148c2ecf20Sopenharmony_ci#include <sound/soc-dai.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "acp3x.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define DRV_NAME "acp3x_rv_i2s_dma" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware acp3x_pcm_hardware_playback = { 218c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_INTERLEAVED | 228c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 238c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BATCH | 248c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 258c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 268c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 278c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | 288c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 298c2ecf20Sopenharmony_ci .channels_min = 2, 308c2ecf20Sopenharmony_ci .channels_max = 8, 318c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_96000, 328c2ecf20Sopenharmony_ci .rate_min = 8000, 338c2ecf20Sopenharmony_ci .rate_max = 96000, 348c2ecf20Sopenharmony_ci .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE, 358c2ecf20Sopenharmony_ci .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, 368c2ecf20Sopenharmony_ci .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, 378c2ecf20Sopenharmony_ci .periods_min = PLAYBACK_MIN_NUM_PERIODS, 388c2ecf20Sopenharmony_ci .periods_max = PLAYBACK_MAX_NUM_PERIODS, 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware acp3x_pcm_hardware_capture = { 428c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_INTERLEAVED | 438c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 448c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BATCH | 458c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 468c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 478c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 488c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | 498c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 508c2ecf20Sopenharmony_ci .channels_min = 2, 518c2ecf20Sopenharmony_ci .channels_max = 2, 528c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 538c2ecf20Sopenharmony_ci .rate_min = 8000, 548c2ecf20Sopenharmony_ci .rate_max = 48000, 558c2ecf20Sopenharmony_ci .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE, 568c2ecf20Sopenharmony_ci .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, 578c2ecf20Sopenharmony_ci .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, 588c2ecf20Sopenharmony_ci .periods_min = CAPTURE_MIN_NUM_PERIODS, 598c2ecf20Sopenharmony_ci .periods_max = CAPTURE_MAX_NUM_PERIODS, 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic irqreturn_t i2s_irq_handler(int irq, void *dev_id) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct i2s_dev_data *rv_i2s_data; 658c2ecf20Sopenharmony_ci u16 play_flag, cap_flag; 668c2ecf20Sopenharmony_ci u32 val; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci rv_i2s_data = dev_id; 698c2ecf20Sopenharmony_ci if (!rv_i2s_data) 708c2ecf20Sopenharmony_ci return IRQ_NONE; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci play_flag = 0; 738c2ecf20Sopenharmony_ci cap_flag = 0; 748c2ecf20Sopenharmony_ci val = rv_readl(rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT); 758c2ecf20Sopenharmony_ci if ((val & BIT(BT_TX_THRESHOLD)) && rv_i2s_data->play_stream) { 768c2ecf20Sopenharmony_ci rv_writel(BIT(BT_TX_THRESHOLD), rv_i2s_data->acp3x_base + 778c2ecf20Sopenharmony_ci mmACP_EXTERNAL_INTR_STAT); 788c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(rv_i2s_data->play_stream); 798c2ecf20Sopenharmony_ci play_flag = 1; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci if ((val & BIT(I2S_TX_THRESHOLD)) && 828c2ecf20Sopenharmony_ci rv_i2s_data->i2ssp_play_stream) { 838c2ecf20Sopenharmony_ci rv_writel(BIT(I2S_TX_THRESHOLD), 848c2ecf20Sopenharmony_ci rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT); 858c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(rv_i2s_data->i2ssp_play_stream); 868c2ecf20Sopenharmony_ci play_flag = 1; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if ((val & BIT(BT_RX_THRESHOLD)) && rv_i2s_data->capture_stream) { 908c2ecf20Sopenharmony_ci rv_writel(BIT(BT_RX_THRESHOLD), rv_i2s_data->acp3x_base + 918c2ecf20Sopenharmony_ci mmACP_EXTERNAL_INTR_STAT); 928c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(rv_i2s_data->capture_stream); 938c2ecf20Sopenharmony_ci cap_flag = 1; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci if ((val & BIT(I2S_RX_THRESHOLD)) && 968c2ecf20Sopenharmony_ci rv_i2s_data->i2ssp_capture_stream) { 978c2ecf20Sopenharmony_ci rv_writel(BIT(I2S_RX_THRESHOLD), 988c2ecf20Sopenharmony_ci rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT); 998c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(rv_i2s_data->i2ssp_capture_stream); 1008c2ecf20Sopenharmony_ci cap_flag = 1; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (play_flag | cap_flag) 1048c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1058c2ecf20Sopenharmony_ci else 1068c2ecf20Sopenharmony_ci return IRQ_NONE; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci u16 page_idx; 1128c2ecf20Sopenharmony_ci u32 low, high, val, acp_fifo_addr, reg_fifo_addr; 1138c2ecf20Sopenharmony_ci u32 reg_dma_size, reg_fifo_size; 1148c2ecf20Sopenharmony_ci dma_addr_t addr; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci addr = rtd->dma_addr; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (direction == SNDRV_PCM_STREAM_PLAYBACK) { 1198c2ecf20Sopenharmony_ci switch (rtd->i2s_instance) { 1208c2ecf20Sopenharmony_ci case I2S_BT_INSTANCE: 1218c2ecf20Sopenharmony_ci val = ACP_SRAM_BT_PB_PTE_OFFSET; 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci case I2S_SP_INSTANCE: 1248c2ecf20Sopenharmony_ci default: 1258c2ecf20Sopenharmony_ci val = ACP_SRAM_SP_PB_PTE_OFFSET; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci } else { 1288c2ecf20Sopenharmony_ci switch (rtd->i2s_instance) { 1298c2ecf20Sopenharmony_ci case I2S_BT_INSTANCE: 1308c2ecf20Sopenharmony_ci val = ACP_SRAM_BT_CP_PTE_OFFSET; 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci case I2S_SP_INSTANCE: 1338c2ecf20Sopenharmony_ci default: 1348c2ecf20Sopenharmony_ci val = ACP_SRAM_SP_CP_PTE_OFFSET; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci /* Group Enable */ 1388c2ecf20Sopenharmony_ci rv_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp3x_base + 1398c2ecf20Sopenharmony_ci mmACPAXI2AXI_ATU_BASE_ADDR_GRP_1); 1408c2ecf20Sopenharmony_ci rv_writel(PAGE_SIZE_4K_ENABLE, rtd->acp3x_base + 1418c2ecf20Sopenharmony_ci mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_1); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) { 1448c2ecf20Sopenharmony_ci /* Load the low address of page int ACP SRAM through SRBM */ 1458c2ecf20Sopenharmony_ci low = lower_32_bits(addr); 1468c2ecf20Sopenharmony_ci high = upper_32_bits(addr); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci rv_writel(low, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val); 1498c2ecf20Sopenharmony_ci high |= BIT(31); 1508c2ecf20Sopenharmony_ci rv_writel(high, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val 1518c2ecf20Sopenharmony_ci + 4); 1528c2ecf20Sopenharmony_ci /* Move to next physically contiguos page */ 1538c2ecf20Sopenharmony_ci val += 8; 1548c2ecf20Sopenharmony_ci addr += PAGE_SIZE; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (direction == SNDRV_PCM_STREAM_PLAYBACK) { 1588c2ecf20Sopenharmony_ci switch (rtd->i2s_instance) { 1598c2ecf20Sopenharmony_ci case I2S_BT_INSTANCE: 1608c2ecf20Sopenharmony_ci reg_dma_size = mmACP_BT_TX_DMA_SIZE; 1618c2ecf20Sopenharmony_ci acp_fifo_addr = ACP_SRAM_PTE_OFFSET + 1628c2ecf20Sopenharmony_ci BT_PB_FIFO_ADDR_OFFSET; 1638c2ecf20Sopenharmony_ci reg_fifo_addr = mmACP_BT_TX_FIFOADDR; 1648c2ecf20Sopenharmony_ci reg_fifo_size = mmACP_BT_TX_FIFOSIZE; 1658c2ecf20Sopenharmony_ci rv_writel(I2S_BT_TX_MEM_WINDOW_START, 1668c2ecf20Sopenharmony_ci rtd->acp3x_base + mmACP_BT_TX_RINGBUFADDR); 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci case I2S_SP_INSTANCE: 1708c2ecf20Sopenharmony_ci default: 1718c2ecf20Sopenharmony_ci reg_dma_size = mmACP_I2S_TX_DMA_SIZE; 1728c2ecf20Sopenharmony_ci acp_fifo_addr = ACP_SRAM_PTE_OFFSET + 1738c2ecf20Sopenharmony_ci SP_PB_FIFO_ADDR_OFFSET; 1748c2ecf20Sopenharmony_ci reg_fifo_addr = mmACP_I2S_TX_FIFOADDR; 1758c2ecf20Sopenharmony_ci reg_fifo_size = mmACP_I2S_TX_FIFOSIZE; 1768c2ecf20Sopenharmony_ci rv_writel(I2S_SP_TX_MEM_WINDOW_START, 1778c2ecf20Sopenharmony_ci rtd->acp3x_base + mmACP_I2S_TX_RINGBUFADDR); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci } else { 1808c2ecf20Sopenharmony_ci switch (rtd->i2s_instance) { 1818c2ecf20Sopenharmony_ci case I2S_BT_INSTANCE: 1828c2ecf20Sopenharmony_ci reg_dma_size = mmACP_BT_RX_DMA_SIZE; 1838c2ecf20Sopenharmony_ci acp_fifo_addr = ACP_SRAM_PTE_OFFSET + 1848c2ecf20Sopenharmony_ci BT_CAPT_FIFO_ADDR_OFFSET; 1858c2ecf20Sopenharmony_ci reg_fifo_addr = mmACP_BT_RX_FIFOADDR; 1868c2ecf20Sopenharmony_ci reg_fifo_size = mmACP_BT_RX_FIFOSIZE; 1878c2ecf20Sopenharmony_ci rv_writel(I2S_BT_RX_MEM_WINDOW_START, 1888c2ecf20Sopenharmony_ci rtd->acp3x_base + mmACP_BT_RX_RINGBUFADDR); 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci case I2S_SP_INSTANCE: 1928c2ecf20Sopenharmony_ci default: 1938c2ecf20Sopenharmony_ci reg_dma_size = mmACP_I2S_RX_DMA_SIZE; 1948c2ecf20Sopenharmony_ci acp_fifo_addr = ACP_SRAM_PTE_OFFSET + 1958c2ecf20Sopenharmony_ci SP_CAPT_FIFO_ADDR_OFFSET; 1968c2ecf20Sopenharmony_ci reg_fifo_addr = mmACP_I2S_RX_FIFOADDR; 1978c2ecf20Sopenharmony_ci reg_fifo_size = mmACP_I2S_RX_FIFOSIZE; 1988c2ecf20Sopenharmony_ci rv_writel(I2S_SP_RX_MEM_WINDOW_START, 1998c2ecf20Sopenharmony_ci rtd->acp3x_base + mmACP_I2S_RX_RINGBUFADDR); 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci rv_writel(DMA_SIZE, rtd->acp3x_base + reg_dma_size); 2038c2ecf20Sopenharmony_ci rv_writel(acp_fifo_addr, rtd->acp3x_base + reg_fifo_addr); 2048c2ecf20Sopenharmony_ci rv_writel(FIFO_SIZE, rtd->acp3x_base + reg_fifo_size); 2058c2ecf20Sopenharmony_ci rv_writel(BIT(I2S_RX_THRESHOLD) | BIT(BT_RX_THRESHOLD) 2068c2ecf20Sopenharmony_ci | BIT(I2S_TX_THRESHOLD) | BIT(BT_TX_THRESHOLD), 2078c2ecf20Sopenharmony_ci rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int acp3x_dma_open(struct snd_soc_component *component, 2118c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 2148c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *prtd; 2158c2ecf20Sopenharmony_ci struct i2s_dev_data *adata; 2168c2ecf20Sopenharmony_ci struct i2s_stream_instance *i2s_data; 2178c2ecf20Sopenharmony_ci int ret; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci runtime = substream->runtime; 2208c2ecf20Sopenharmony_ci prtd = asoc_substream_to_rtd(substream); 2218c2ecf20Sopenharmony_ci component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); 2228c2ecf20Sopenharmony_ci adata = dev_get_drvdata(component->dev); 2238c2ecf20Sopenharmony_ci i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL); 2248c2ecf20Sopenharmony_ci if (!i2s_data) 2258c2ecf20Sopenharmony_ci return -EINVAL; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 2288c2ecf20Sopenharmony_ci runtime->hw = acp3x_pcm_hardware_playback; 2298c2ecf20Sopenharmony_ci else 2308c2ecf20Sopenharmony_ci runtime->hw = acp3x_pcm_hardware_capture; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_integer(runtime, 2338c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIODS); 2348c2ecf20Sopenharmony_ci if (ret < 0) { 2358c2ecf20Sopenharmony_ci dev_err(component->dev, "set integer constraint failed\n"); 2368c2ecf20Sopenharmony_ci kfree(i2s_data); 2378c2ecf20Sopenharmony_ci return ret; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci i2s_data->acp3x_base = adata->acp3x_base; 2418c2ecf20Sopenharmony_ci runtime->private_data = i2s_data; 2428c2ecf20Sopenharmony_ci return ret; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic int acp3x_dma_hw_params(struct snd_soc_component *component, 2478c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 2488c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct i2s_stream_instance *rtd; 2518c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *prtd; 2528c2ecf20Sopenharmony_ci struct snd_soc_card *card; 2538c2ecf20Sopenharmony_ci struct acp3x_platform_info *pinfo; 2548c2ecf20Sopenharmony_ci struct i2s_dev_data *adata; 2558c2ecf20Sopenharmony_ci u64 size; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci prtd = asoc_substream_to_rtd(substream); 2588c2ecf20Sopenharmony_ci card = prtd->card; 2598c2ecf20Sopenharmony_ci pinfo = snd_soc_card_get_drvdata(card); 2608c2ecf20Sopenharmony_ci adata = dev_get_drvdata(component->dev); 2618c2ecf20Sopenharmony_ci rtd = substream->runtime->private_data; 2628c2ecf20Sopenharmony_ci if (!rtd) 2638c2ecf20Sopenharmony_ci return -EINVAL; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (pinfo) { 2668c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 2678c2ecf20Sopenharmony_ci rtd->i2s_instance = pinfo->play_i2s_instance; 2688c2ecf20Sopenharmony_ci switch (rtd->i2s_instance) { 2698c2ecf20Sopenharmony_ci case I2S_BT_INSTANCE: 2708c2ecf20Sopenharmony_ci adata->play_stream = substream; 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci case I2S_SP_INSTANCE: 2738c2ecf20Sopenharmony_ci default: 2748c2ecf20Sopenharmony_ci adata->i2ssp_play_stream = substream; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci } else { 2778c2ecf20Sopenharmony_ci rtd->i2s_instance = pinfo->cap_i2s_instance; 2788c2ecf20Sopenharmony_ci switch (rtd->i2s_instance) { 2798c2ecf20Sopenharmony_ci case I2S_BT_INSTANCE: 2808c2ecf20Sopenharmony_ci adata->capture_stream = substream; 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci case I2S_SP_INSTANCE: 2838c2ecf20Sopenharmony_ci default: 2848c2ecf20Sopenharmony_ci adata->i2ssp_capture_stream = substream; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci } else { 2888c2ecf20Sopenharmony_ci pr_err("pinfo failed\n"); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci size = params_buffer_bytes(params); 2918c2ecf20Sopenharmony_ci rtd->dma_addr = substream->runtime->dma_addr; 2928c2ecf20Sopenharmony_ci rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); 2938c2ecf20Sopenharmony_ci config_acp3x_dma(rtd, substream->stream); 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t acp3x_dma_pointer(struct snd_soc_component *component, 2988c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct i2s_stream_instance *rtd; 3018c2ecf20Sopenharmony_ci u32 pos; 3028c2ecf20Sopenharmony_ci u32 buffersize; 3038c2ecf20Sopenharmony_ci u64 bytescount; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci rtd = substream->runtime->private_data; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci buffersize = frames_to_bytes(substream->runtime, 3088c2ecf20Sopenharmony_ci substream->runtime->buffer_size); 3098c2ecf20Sopenharmony_ci bytescount = acp_get_byte_count(rtd, substream->stream); 3108c2ecf20Sopenharmony_ci if (bytescount > rtd->bytescount) 3118c2ecf20Sopenharmony_ci bytescount -= rtd->bytescount; 3128c2ecf20Sopenharmony_ci pos = do_div(bytescount, buffersize); 3138c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, pos); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int acp3x_dma_new(struct snd_soc_component *component, 3178c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct device *parent = component->dev->parent; 3208c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, 3218c2ecf20Sopenharmony_ci parent, MIN_BUFFER, MAX_BUFFER); 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int acp3x_dma_mmap(struct snd_soc_component *component, 3268c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 3278c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci return snd_pcm_lib_default_mmap(substream, vma); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int acp3x_dma_close(struct snd_soc_component *component, 3338c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *prtd; 3368c2ecf20Sopenharmony_ci struct i2s_dev_data *adata; 3378c2ecf20Sopenharmony_ci struct i2s_stream_instance *ins; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci prtd = asoc_substream_to_rtd(substream); 3408c2ecf20Sopenharmony_ci component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); 3418c2ecf20Sopenharmony_ci adata = dev_get_drvdata(component->dev); 3428c2ecf20Sopenharmony_ci ins = substream->runtime->private_data; 3438c2ecf20Sopenharmony_ci if (!ins) 3448c2ecf20Sopenharmony_ci return -EINVAL; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 3478c2ecf20Sopenharmony_ci switch (ins->i2s_instance) { 3488c2ecf20Sopenharmony_ci case I2S_BT_INSTANCE: 3498c2ecf20Sopenharmony_ci adata->play_stream = NULL; 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci case I2S_SP_INSTANCE: 3528c2ecf20Sopenharmony_ci default: 3538c2ecf20Sopenharmony_ci adata->i2ssp_play_stream = NULL; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci } else { 3568c2ecf20Sopenharmony_ci switch (ins->i2s_instance) { 3578c2ecf20Sopenharmony_ci case I2S_BT_INSTANCE: 3588c2ecf20Sopenharmony_ci adata->capture_stream = NULL; 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci case I2S_SP_INSTANCE: 3618c2ecf20Sopenharmony_ci default: 3628c2ecf20Sopenharmony_ci adata->i2ssp_capture_stream = NULL; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver acp3x_i2s_component = { 3708c2ecf20Sopenharmony_ci .name = DRV_NAME, 3718c2ecf20Sopenharmony_ci .open = acp3x_dma_open, 3728c2ecf20Sopenharmony_ci .close = acp3x_dma_close, 3738c2ecf20Sopenharmony_ci .hw_params = acp3x_dma_hw_params, 3748c2ecf20Sopenharmony_ci .pointer = acp3x_dma_pointer, 3758c2ecf20Sopenharmony_ci .mmap = acp3x_dma_mmap, 3768c2ecf20Sopenharmony_ci .pcm_construct = acp3x_dma_new, 3778c2ecf20Sopenharmony_ci}; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int acp3x_audio_probe(struct platform_device *pdev) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct resource *res; 3828c2ecf20Sopenharmony_ci struct i2s_dev_data *adata; 3838c2ecf20Sopenharmony_ci unsigned int irqflags; 3848c2ecf20Sopenharmony_ci int status; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (!pdev->dev.platform_data) { 3878c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "platform_data not retrieved\n"); 3888c2ecf20Sopenharmony_ci return -ENODEV; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci irqflags = *((unsigned int *)(pdev->dev.platform_data)); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3938c2ecf20Sopenharmony_ci if (!res) { 3948c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); 3958c2ecf20Sopenharmony_ci return -ENODEV; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL); 3998c2ecf20Sopenharmony_ci if (!adata) 4008c2ecf20Sopenharmony_ci return -ENOMEM; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci adata->acp3x_base = devm_ioremap(&pdev->dev, res->start, 4038c2ecf20Sopenharmony_ci resource_size(res)); 4048c2ecf20Sopenharmony_ci if (!adata->acp3x_base) 4058c2ecf20Sopenharmony_ci return -ENOMEM; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 4088c2ecf20Sopenharmony_ci if (!res) { 4098c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); 4108c2ecf20Sopenharmony_ci return -ENODEV; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci adata->i2s_irq = res->start; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, adata); 4168c2ecf20Sopenharmony_ci status = devm_snd_soc_register_component(&pdev->dev, 4178c2ecf20Sopenharmony_ci &acp3x_i2s_component, 4188c2ecf20Sopenharmony_ci NULL, 0); 4198c2ecf20Sopenharmony_ci if (status) { 4208c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Fail to register acp i2s component\n"); 4218c2ecf20Sopenharmony_ci return -ENODEV; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler, 4248c2ecf20Sopenharmony_ci irqflags, "ACP3x_I2S_IRQ", adata); 4258c2ecf20Sopenharmony_ci if (status) { 4268c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ACP3x I2S IRQ request failed\n"); 4278c2ecf20Sopenharmony_ci return -ENODEV; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); 4318c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 4328c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 4338c2ecf20Sopenharmony_ci pm_runtime_allow(&pdev->dev); 4348c2ecf20Sopenharmony_ci return 0; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int acp3x_audio_remove(struct platform_device *pdev) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int acp3x_resume(struct device *dev) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct i2s_dev_data *adata; 4468c2ecf20Sopenharmony_ci u32 val, reg_val, frmt_val; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci reg_val = 0; 4498c2ecf20Sopenharmony_ci frmt_val = 0; 4508c2ecf20Sopenharmony_ci adata = dev_get_drvdata(dev); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (adata->play_stream && adata->play_stream->runtime) { 4538c2ecf20Sopenharmony_ci struct i2s_stream_instance *rtd = 4548c2ecf20Sopenharmony_ci adata->play_stream->runtime->private_data; 4558c2ecf20Sopenharmony_ci config_acp3x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK); 4568c2ecf20Sopenharmony_ci switch (rtd->i2s_instance) { 4578c2ecf20Sopenharmony_ci case I2S_BT_INSTANCE: 4588c2ecf20Sopenharmony_ci reg_val = mmACP_BTTDM_ITER; 4598c2ecf20Sopenharmony_ci frmt_val = mmACP_BTTDM_TXFRMT; 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci case I2S_SP_INSTANCE: 4628c2ecf20Sopenharmony_ci default: 4638c2ecf20Sopenharmony_ci reg_val = mmACP_I2STDM_ITER; 4648c2ecf20Sopenharmony_ci frmt_val = mmACP_I2STDM_TXFRMT; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci rv_writel((rtd->xfer_resolution << 3), 4678c2ecf20Sopenharmony_ci rtd->acp3x_base + reg_val); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci if (adata->capture_stream && adata->capture_stream->runtime) { 4708c2ecf20Sopenharmony_ci struct i2s_stream_instance *rtd = 4718c2ecf20Sopenharmony_ci adata->capture_stream->runtime->private_data; 4728c2ecf20Sopenharmony_ci config_acp3x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE); 4738c2ecf20Sopenharmony_ci switch (rtd->i2s_instance) { 4748c2ecf20Sopenharmony_ci case I2S_BT_INSTANCE: 4758c2ecf20Sopenharmony_ci reg_val = mmACP_BTTDM_IRER; 4768c2ecf20Sopenharmony_ci frmt_val = mmACP_BTTDM_RXFRMT; 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci case I2S_SP_INSTANCE: 4798c2ecf20Sopenharmony_ci default: 4808c2ecf20Sopenharmony_ci reg_val = mmACP_I2STDM_IRER; 4818c2ecf20Sopenharmony_ci frmt_val = mmACP_I2STDM_RXFRMT; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci rv_writel((rtd->xfer_resolution << 3), 4848c2ecf20Sopenharmony_ci rtd->acp3x_base + reg_val); 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci if (adata->tdm_mode == TDM_ENABLE) { 4878c2ecf20Sopenharmony_ci rv_writel(adata->tdm_fmt, adata->acp3x_base + frmt_val); 4888c2ecf20Sopenharmony_ci val = rv_readl(adata->acp3x_base + reg_val); 4898c2ecf20Sopenharmony_ci rv_writel(val | 0x2, adata->acp3x_base + reg_val); 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic int acp3x_pcm_runtime_suspend(struct device *dev) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct i2s_dev_data *adata; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci adata = dev_get_drvdata(dev); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int acp3x_pcm_runtime_resume(struct device *dev) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct i2s_dev_data *adata; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci adata = dev_get_drvdata(dev); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic const struct dev_pm_ops acp3x_pm_ops = { 5188c2ecf20Sopenharmony_ci .runtime_suspend = acp3x_pcm_runtime_suspend, 5198c2ecf20Sopenharmony_ci .runtime_resume = acp3x_pcm_runtime_resume, 5208c2ecf20Sopenharmony_ci .resume = acp3x_resume, 5218c2ecf20Sopenharmony_ci}; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic struct platform_driver acp3x_dma_driver = { 5248c2ecf20Sopenharmony_ci .probe = acp3x_audio_probe, 5258c2ecf20Sopenharmony_ci .remove = acp3x_audio_remove, 5268c2ecf20Sopenharmony_ci .driver = { 5278c2ecf20Sopenharmony_ci .name = "acp3x_rv_i2s_dma", 5288c2ecf20Sopenharmony_ci .pm = &acp3x_pm_ops, 5298c2ecf20Sopenharmony_ci }, 5308c2ecf20Sopenharmony_ci}; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cimodule_platform_driver(acp3x_dma_driver); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com"); 5358c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com"); 5368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vijendar.Mukunda@amd.com"); 5378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AMD ACP 3.x PCM Driver"); 5388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 5398c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:"DRV_NAME); 540