Path: blob/master/sound/soc/amd/vangogh/acp5x-pcm-dma.c
26490 views
// SPDX-License-Identifier: GPL-2.0+1//2// AMD ALSA SoC PCM Driver3//4// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.56#include <linux/platform_device.h>7#include <linux/module.h>8#include <linux/err.h>9#include <linux/io.h>10#include <linux/pm_runtime.h>11#include <sound/pcm.h>12#include <sound/pcm_params.h>13#include <sound/soc.h>14#include <sound/soc-dai.h>1516#include "acp5x.h"1718#define DRV_NAME "acp5x_i2s_dma"1920static const struct snd_pcm_hardware acp5x_pcm_hardware_playback = {21.info = SNDRV_PCM_INFO_INTERLEAVED |22SNDRV_PCM_INFO_BLOCK_TRANSFER |23SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |24SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,25.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |26SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,27.channels_min = 2,28.channels_max = 2,29.rates = SNDRV_PCM_RATE_8000_96000,30.rate_min = 8000,31.rate_max = 96000,32.buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,33.period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,34.period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,35.periods_min = PLAYBACK_MIN_NUM_PERIODS,36.periods_max = PLAYBACK_MAX_NUM_PERIODS,37};3839static const struct snd_pcm_hardware acp5x_pcm_hardware_capture = {40.info = SNDRV_PCM_INFO_INTERLEAVED |41SNDRV_PCM_INFO_BLOCK_TRANSFER |42SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |43SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,44.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |45SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,46.channels_min = 2,47.channels_max = 2,48.rates = SNDRV_PCM_RATE_8000_96000,49.rate_min = 8000,50.rate_max = 96000,51.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,52.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,53.period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,54.periods_min = CAPTURE_MIN_NUM_PERIODS,55.periods_max = CAPTURE_MAX_NUM_PERIODS,56};5758static irqreturn_t i2s_irq_handler(int irq, void *dev_id)59{60struct i2s_dev_data *vg_i2s_data;61u16 irq_flag;62u32 val;6364vg_i2s_data = dev_id;65if (!vg_i2s_data)66return IRQ_NONE;6768irq_flag = 0;69val = acp_readl(vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);70if ((val & BIT(HS_TX_THRESHOLD)) && vg_i2s_data->play_stream) {71acp_writel(BIT(HS_TX_THRESHOLD), vg_i2s_data->acp5x_base +72ACP_EXTERNAL_INTR_STAT);73snd_pcm_period_elapsed(vg_i2s_data->play_stream);74irq_flag = 1;75}76if ((val & BIT(I2S_TX_THRESHOLD)) && vg_i2s_data->i2ssp_play_stream) {77acp_writel(BIT(I2S_TX_THRESHOLD),78vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);79snd_pcm_period_elapsed(vg_i2s_data->i2ssp_play_stream);80irq_flag = 1;81}8283if ((val & BIT(HS_RX_THRESHOLD)) && vg_i2s_data->capture_stream) {84acp_writel(BIT(HS_RX_THRESHOLD), vg_i2s_data->acp5x_base +85ACP_EXTERNAL_INTR_STAT);86snd_pcm_period_elapsed(vg_i2s_data->capture_stream);87irq_flag = 1;88}89if ((val & BIT(I2S_RX_THRESHOLD)) && vg_i2s_data->i2ssp_capture_stream) {90acp_writel(BIT(I2S_RX_THRESHOLD),91vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);92snd_pcm_period_elapsed(vg_i2s_data->i2ssp_capture_stream);93irq_flag = 1;94}9596if (irq_flag)97return IRQ_HANDLED;98else99return IRQ_NONE;100}101102static void config_acp5x_dma(struct i2s_stream_instance *rtd, int direction)103{104u16 page_idx;105u32 low, high, val, acp_fifo_addr, reg_fifo_addr;106u32 reg_dma_size, reg_fifo_size;107dma_addr_t addr;108109addr = rtd->dma_addr;110if (direction == SNDRV_PCM_STREAM_PLAYBACK) {111switch (rtd->i2s_instance) {112case I2S_HS_INSTANCE:113val = ACP_SRAM_HS_PB_PTE_OFFSET;114break;115case I2S_SP_INSTANCE:116default:117val = ACP_SRAM_SP_PB_PTE_OFFSET;118}119} else {120switch (rtd->i2s_instance) {121case I2S_HS_INSTANCE:122val = ACP_SRAM_HS_CP_PTE_OFFSET;123break;124case I2S_SP_INSTANCE:125default:126val = ACP_SRAM_SP_CP_PTE_OFFSET;127}128}129/* Group Enable */130acp_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp5x_base +131ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);132acp_writel(PAGE_SIZE_4K_ENABLE, rtd->acp5x_base +133ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);134135for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {136/* Load the low address of page int ACP SRAM through SRBM */137low = lower_32_bits(addr);138high = upper_32_bits(addr);139140acp_writel(low, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val);141high |= BIT(31);142acp_writel(high, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val + 4);143/* Move to next physically contiguous page */144val += 8;145addr += PAGE_SIZE;146}147148if (direction == SNDRV_PCM_STREAM_PLAYBACK) {149switch (rtd->i2s_instance) {150case I2S_HS_INSTANCE:151reg_dma_size = ACP_HS_TX_DMA_SIZE;152acp_fifo_addr = ACP_SRAM_PTE_OFFSET +153HS_PB_FIFO_ADDR_OFFSET;154reg_fifo_addr = ACP_HS_TX_FIFOADDR;155reg_fifo_size = ACP_HS_TX_FIFOSIZE;156acp_writel(I2S_HS_TX_MEM_WINDOW_START,157rtd->acp5x_base + ACP_HS_TX_RINGBUFADDR);158break;159160case I2S_SP_INSTANCE:161default:162reg_dma_size = ACP_I2S_TX_DMA_SIZE;163acp_fifo_addr = ACP_SRAM_PTE_OFFSET +164SP_PB_FIFO_ADDR_OFFSET;165reg_fifo_addr = ACP_I2S_TX_FIFOADDR;166reg_fifo_size = ACP_I2S_TX_FIFOSIZE;167acp_writel(I2S_SP_TX_MEM_WINDOW_START,168rtd->acp5x_base + ACP_I2S_TX_RINGBUFADDR);169}170} else {171switch (rtd->i2s_instance) {172case I2S_HS_INSTANCE:173reg_dma_size = ACP_HS_RX_DMA_SIZE;174acp_fifo_addr = ACP_SRAM_PTE_OFFSET +175HS_CAPT_FIFO_ADDR_OFFSET;176reg_fifo_addr = ACP_HS_RX_FIFOADDR;177reg_fifo_size = ACP_HS_RX_FIFOSIZE;178acp_writel(I2S_HS_RX_MEM_WINDOW_START,179rtd->acp5x_base + ACP_HS_RX_RINGBUFADDR);180break;181182case I2S_SP_INSTANCE:183default:184reg_dma_size = ACP_I2S_RX_DMA_SIZE;185acp_fifo_addr = ACP_SRAM_PTE_OFFSET +186SP_CAPT_FIFO_ADDR_OFFSET;187reg_fifo_addr = ACP_I2S_RX_FIFOADDR;188reg_fifo_size = ACP_I2S_RX_FIFOSIZE;189acp_writel(I2S_SP_RX_MEM_WINDOW_START,190rtd->acp5x_base + ACP_I2S_RX_RINGBUFADDR);191}192}193acp_writel(DMA_SIZE, rtd->acp5x_base + reg_dma_size);194acp_writel(acp_fifo_addr, rtd->acp5x_base + reg_fifo_addr);195acp_writel(FIFO_SIZE, rtd->acp5x_base + reg_fifo_size);196acp_writel(BIT(I2S_RX_THRESHOLD) | BIT(HS_RX_THRESHOLD)197| BIT(I2S_TX_THRESHOLD) | BIT(HS_TX_THRESHOLD),198rtd->acp5x_base + ACP_EXTERNAL_INTR_CNTL);199}200201static int acp5x_dma_open(struct snd_soc_component *component,202struct snd_pcm_substream *substream)203{204struct snd_pcm_runtime *runtime;205struct snd_soc_pcm_runtime *prtd;206struct i2s_dev_data *adata;207struct i2s_stream_instance *i2s_data;208int ret;209210runtime = substream->runtime;211prtd = snd_soc_substream_to_rtd(substream);212component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);213adata = dev_get_drvdata(component->dev);214215i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL);216if (!i2s_data)217return -ENOMEM;218219if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)220runtime->hw = acp5x_pcm_hardware_playback;221else222runtime->hw = acp5x_pcm_hardware_capture;223224ret = snd_pcm_hw_constraint_integer(runtime,225SNDRV_PCM_HW_PARAM_PERIODS);226if (ret < 0) {227dev_err(component->dev, "set integer constraint failed\n");228kfree(i2s_data);229return ret;230}231i2s_data->acp5x_base = adata->acp5x_base;232runtime->private_data = i2s_data;233return ret;234}235236static int acp5x_dma_hw_params(struct snd_soc_component *component,237struct snd_pcm_substream *substream,238struct snd_pcm_hw_params *params)239{240struct i2s_stream_instance *rtd;241struct snd_soc_pcm_runtime *prtd;242struct snd_soc_card *card;243struct acp5x_platform_info *pinfo;244struct i2s_dev_data *adata;245u64 size;246247prtd = snd_soc_substream_to_rtd(substream);248card = prtd->card;249pinfo = snd_soc_card_get_drvdata(card);250adata = dev_get_drvdata(component->dev);251rtd = substream->runtime->private_data;252253if (!rtd)254return -EINVAL;255256if (pinfo) {257if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {258rtd->i2s_instance = pinfo->play_i2s_instance;259switch (rtd->i2s_instance) {260case I2S_HS_INSTANCE:261adata->play_stream = substream;262break;263case I2S_SP_INSTANCE:264default:265adata->i2ssp_play_stream = substream;266}267} else {268rtd->i2s_instance = pinfo->cap_i2s_instance;269switch (rtd->i2s_instance) {270case I2S_HS_INSTANCE:271adata->capture_stream = substream;272break;273case I2S_SP_INSTANCE:274default:275adata->i2ssp_capture_stream = substream;276}277}278} else {279dev_err(component->dev, "pinfo failed\n");280return -EINVAL;281}282size = params_buffer_bytes(params);283rtd->dma_addr = substream->runtime->dma_addr;284rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);285config_acp5x_dma(rtd, substream->stream);286return 0;287}288289static snd_pcm_uframes_t acp5x_dma_pointer(struct snd_soc_component *component,290struct snd_pcm_substream *substream)291{292struct i2s_stream_instance *rtd;293u32 pos;294u32 buffersize;295u64 bytescount;296297rtd = substream->runtime->private_data;298buffersize = frames_to_bytes(substream->runtime,299substream->runtime->buffer_size);300bytescount = acp_get_byte_count(rtd, substream->stream);301if (bytescount > rtd->bytescount)302bytescount -= rtd->bytescount;303pos = do_div(bytescount, buffersize);304return bytes_to_frames(substream->runtime, pos);305}306307static int acp5x_dma_new(struct snd_soc_component *component,308struct snd_soc_pcm_runtime *rtd)309{310struct device *parent = component->dev->parent;311312snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,313parent, MIN_BUFFER, MAX_BUFFER);314return 0;315}316317static int acp5x_dma_close(struct snd_soc_component *component,318struct snd_pcm_substream *substream)319{320struct snd_soc_pcm_runtime *prtd;321struct i2s_dev_data *adata;322struct i2s_stream_instance *ins;323324prtd = snd_soc_substream_to_rtd(substream);325component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);326adata = dev_get_drvdata(component->dev);327ins = substream->runtime->private_data;328if (!ins)329return -EINVAL;330if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {331switch (ins->i2s_instance) {332case I2S_HS_INSTANCE:333adata->play_stream = NULL;334break;335case I2S_SP_INSTANCE:336default:337adata->i2ssp_play_stream = NULL;338}339} else {340switch (ins->i2s_instance) {341case I2S_HS_INSTANCE:342adata->capture_stream = NULL;343break;344case I2S_SP_INSTANCE:345default:346adata->i2ssp_capture_stream = NULL;347}348}349kfree(ins);350return 0;351}352353static const struct snd_soc_component_driver acp5x_i2s_component = {354.name = DRV_NAME,355.open = acp5x_dma_open,356.close = acp5x_dma_close,357.hw_params = acp5x_dma_hw_params,358.pointer = acp5x_dma_pointer,359.pcm_construct = acp5x_dma_new,360};361362static int acp5x_audio_probe(struct platform_device *pdev)363{364struct resource *res;365struct i2s_dev_data *adata;366unsigned int irqflags;367int status;368369if (!pdev->dev.platform_data) {370dev_err(&pdev->dev, "platform_data not retrieved\n");371return -ENODEV;372}373irqflags = *((unsigned int *)(pdev->dev.platform_data));374375res = platform_get_resource(pdev, IORESOURCE_MEM, 0);376if (!res) {377dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");378return -ENODEV;379}380381adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);382if (!adata)383return -ENOMEM;384385adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,386resource_size(res));387if (!adata->acp5x_base)388return -ENOMEM;389390status = platform_get_irq(pdev, 0);391if (status < 0)392return status;393adata->i2s_irq = status;394395dev_set_drvdata(&pdev->dev, adata);396status = devm_snd_soc_register_component(&pdev->dev,397&acp5x_i2s_component,398NULL, 0);399if (status) {400dev_err(&pdev->dev, "Fail to register acp i2s component\n");401return status;402}403status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler,404irqflags, "ACP5x_I2S_IRQ", adata);405if (status) {406dev_err(&pdev->dev, "ACP5x I2S IRQ request failed\n");407return status;408}409pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);410pm_runtime_use_autosuspend(&pdev->dev);411pm_runtime_mark_last_busy(&pdev->dev);412pm_runtime_set_active(&pdev->dev);413pm_runtime_enable(&pdev->dev);414return 0;415}416417static void acp5x_audio_remove(struct platform_device *pdev)418{419pm_runtime_disable(&pdev->dev);420}421422static int acp5x_pcm_resume(struct device *dev)423{424struct i2s_dev_data *adata;425struct i2s_stream_instance *rtd;426u32 val;427428adata = dev_get_drvdata(dev);429430if (adata->play_stream && adata->play_stream->runtime) {431rtd = adata->play_stream->runtime->private_data;432config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);433acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_ITER);434if (adata->tdm_mode == TDM_ENABLE) {435acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_TXFRMT);436val = acp_readl(adata->acp5x_base + ACP_HSTDM_ITER);437acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_ITER);438}439}440if (adata->i2ssp_play_stream && adata->i2ssp_play_stream->runtime) {441rtd = adata->i2ssp_play_stream->runtime->private_data;442config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);443acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_ITER);444if (adata->tdm_mode == TDM_ENABLE) {445acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_TXFRMT);446val = acp_readl(adata->acp5x_base + ACP_I2STDM_ITER);447acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_ITER);448}449}450451if (adata->capture_stream && adata->capture_stream->runtime) {452rtd = adata->capture_stream->runtime->private_data;453config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);454acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_IRER);455if (adata->tdm_mode == TDM_ENABLE) {456acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_RXFRMT);457val = acp_readl(adata->acp5x_base + ACP_HSTDM_IRER);458acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_IRER);459}460}461if (adata->i2ssp_capture_stream && adata->i2ssp_capture_stream->runtime) {462rtd = adata->i2ssp_capture_stream->runtime->private_data;463config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);464acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_IRER);465if (adata->tdm_mode == TDM_ENABLE) {466acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_RXFRMT);467val = acp_readl(adata->acp5x_base + ACP_I2STDM_IRER);468acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_IRER);469}470}471acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);472return 0;473}474475static int acp5x_pcm_suspend(struct device *dev)476{477struct i2s_dev_data *adata;478479adata = dev_get_drvdata(dev);480acp_writel(0, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);481return 0;482}483484static int acp5x_pcm_runtime_resume(struct device *dev)485{486struct i2s_dev_data *adata;487488adata = dev_get_drvdata(dev);489acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);490return 0;491}492493static const struct dev_pm_ops acp5x_pm_ops = {494RUNTIME_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_runtime_resume, NULL)495SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_resume)496};497498static struct platform_driver acp5x_dma_driver = {499.probe = acp5x_audio_probe,500.remove = acp5x_audio_remove,501.driver = {502.name = "acp5x_i2s_dma",503.pm = &acp5x_pm_ops,504},505};506507module_platform_driver(acp5x_dma_driver);508509MODULE_AUTHOR("[email protected]");510MODULE_DESCRIPTION("AMD ACP 5.x PCM Driver");511MODULE_LICENSE("GPL v2");512MODULE_ALIAS("platform:" DRV_NAME);513514515