ASoC: SOF: amd: Add PCM stream callback for Renoir dai's
authorAjit Kumar Pandey <AjitKumar.Pandey@amd.com>
Wed, 17 Nov 2021 09:37:19 +0000 (11:37 +0200)
committerMark Brown <broonie@kernel.org>
Wed, 17 Nov 2021 17:35:49 +0000 (17:35 +0000)
Add module to support ALSA pcm stream configurations for ACP I2S
and DMIC endpoints

Signed-off-by: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
Reviewed-by: Bard Liao <bard.liao@intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com>
Link: https://lore.kernel.org/r/20211117093734.17407-7-daniel.baluta@oss.nxp.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sof/amd/Makefile
sound/soc/sof/amd/acp-pcm.c [new file with mode: 0644]
sound/soc/sof/amd/acp-stream.c [new file with mode: 0644]
sound/soc/sof/amd/acp.c
sound/soc/sof/amd/acp.h
sound/soc/sof/amd/renoir.c

index 29928b1..7b88db9 100644 (file)
@@ -4,7 +4,7 @@
 #
 # Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
 
-snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o
+snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o acp-pcm.o acp-stream.o
 snd-sof-amd-renoir-objs := renoir.o
 
 obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o
diff --git a/sound/soc/sof/amd/acp-pcm.c b/sound/soc/sof/amd/acp-pcm.c
new file mode 100644 (file)
index 0000000..5b23830
--- /dev/null
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/*
+ * PCM interface for generic AMD audio ACP DSP block
+ */
+#include <sound/pcm_params.h>
+
+#include "../ops.h"
+#include "acp.h"
+#include "acp-dsp-offset.h"
+
+int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+                     struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params)
+{
+       struct acp_dsp_stream *stream = substream->runtime->private_data;
+       unsigned int buf_offset, index;
+       u32 size;
+       int ret;
+
+       size = ipc_params->buffer.size;
+       stream->num_pages = ipc_params->buffer.pages;
+       stream->dmab = substream->runtime->dma_buffer_p;
+
+       ret = acp_dsp_stream_config(sdev, stream);
+       if (ret < 0) {
+               dev_err(sdev->dev, "stream configuration failed\n");
+               return ret;
+       }
+
+       ipc_params->buffer.phy_addr = stream->reg_offset;
+       ipc_params->stream_tag = stream->stream_tag;
+
+       /* write buffer size of stream in scratch memory */
+
+       buf_offset = offsetof(struct scratch_reg_conf, buf_size);
+       index = stream->stream_tag - 1;
+       buf_offset = buf_offset + index * 4;
+
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + buf_offset, size);
+
+       return 0;
+}
+EXPORT_SYMBOL_NS(acp_pcm_hw_params, SND_SOC_SOF_AMD_COMMON);
+
+int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
+{
+       struct acp_dsp_stream *stream;
+
+       stream = acp_dsp_stream_get(sdev, 0);
+       if (!stream)
+               return -ENODEV;
+
+       substream->runtime->private_data = stream;
+       stream->substream = substream;
+
+       return 0;
+}
+EXPORT_SYMBOL_NS(acp_pcm_open, SND_SOC_SOF_AMD_COMMON);
+
+int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
+{
+       struct acp_dsp_stream *stream;
+
+       stream = substream->runtime->private_data;
+       if (!stream) {
+               dev_err(sdev->dev, "No open stream\n");
+               return -EINVAL;
+       }
+
+       stream->substream = NULL;
+       substream->runtime->private_data = NULL;
+
+       return acp_dsp_stream_put(sdev, stream);
+}
+EXPORT_SYMBOL_NS(acp_pcm_close, SND_SOC_SOF_AMD_COMMON);
diff --git a/sound/soc/sof/amd/acp-stream.c b/sound/soc/sof/amd/acp-stream.c
new file mode 100644 (file)
index 0000000..f2837bf
--- /dev/null
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/*
+ * Hardware interface for generic AMD audio DSP ACP IP
+ */
+
+#include "../ops.h"
+#include "acp-dsp-offset.h"
+#include "acp.h"
+
+#define PTE_GRP1_OFFSET                0x00000000
+#define PTE_GRP2_OFFSET                0x00800000
+#define PTE_GRP3_OFFSET                0x01000000
+#define PTE_GRP4_OFFSET                0x01800000
+#define PTE_GRP5_OFFSET                0x02000000
+#define PTE_GRP6_OFFSET                0x02800000
+#define PTE_GRP7_OFFSET                0x03000000
+#define PTE_GRP8_OFFSET                0x03800000
+
+int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *stream)
+{
+       unsigned int pte_reg, pte_size, phy_addr_offset, index;
+       int stream_tag = stream->stream_tag;
+       u32 low, high, offset, reg_val;
+       dma_addr_t addr;
+       int page_idx;
+
+       switch (stream_tag) {
+       case 1:
+               pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_1;
+               pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1;
+               offset = offsetof(struct scratch_reg_conf, grp1_pte);
+               stream->reg_offset = PTE_GRP1_OFFSET;
+               break;
+       case 2:
+               pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_2;
+               pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2;
+               offset = offsetof(struct scratch_reg_conf, grp2_pte);
+               stream->reg_offset = PTE_GRP2_OFFSET;
+               break;
+       case 3:
+               pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_3;
+               pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3;
+               offset = offsetof(struct scratch_reg_conf, grp3_pte);
+               stream->reg_offset = PTE_GRP3_OFFSET;
+               break;
+       case 4:
+               pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_4;
+               pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4;
+               offset = offsetof(struct scratch_reg_conf, grp4_pte);
+               stream->reg_offset = PTE_GRP4_OFFSET;
+               break;
+       case 5:
+               pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5;
+               pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5;
+               offset = offsetof(struct scratch_reg_conf, grp5_pte);
+               stream->reg_offset = PTE_GRP5_OFFSET;
+               break;
+       case 6:
+               pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_6;
+               pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6;
+               offset = offsetof(struct scratch_reg_conf, grp6_pte);
+               stream->reg_offset = PTE_GRP6_OFFSET;
+               break;
+       case 7:
+               pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_7;
+               pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7;
+               offset = offsetof(struct scratch_reg_conf, grp7_pte);
+               stream->reg_offset = PTE_GRP7_OFFSET;
+               break;
+       case 8:
+               pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_8;
+               pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8;
+               offset = offsetof(struct scratch_reg_conf, grp8_pte);
+               stream->reg_offset = PTE_GRP8_OFFSET;
+               break;
+       default:
+               dev_err(sdev->dev, "Invalid stream tag %d\n", stream_tag);
+               return -EINVAL;
+       }
+
+       /* write phy_addr in scratch memory */
+
+       phy_addr_offset = offsetof(struct scratch_reg_conf, reg_offset);
+       index = stream_tag - 1;
+       phy_addr_offset = phy_addr_offset + index * 4;
+
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 +
+                         phy_addr_offset, stream->reg_offset);
+
+       /* Group Enable */
+       reg_val = ACP_SRAM_PTE_OFFSET + offset;
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_reg, reg_val | BIT(31));
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_size, PAGE_SIZE_4K_ENABLE);
+
+       for (page_idx = 0; page_idx < stream->num_pages; page_idx++) {
+               addr = snd_sgbuf_get_addr(stream->dmab, page_idx * PAGE_SIZE);
+
+               /* Load the low address of page int ACP SRAM through SRBM */
+               low = lower_32_bits(addr);
+               high = upper_32_bits(addr);
+
+               snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low);
+
+               high |= BIT(31);
+               snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high);
+               /* Move to next physically contiguous page */
+               offset += 8;
+       }
+
+       return 0;
+}
+
+struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag)
+{
+       struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+       struct acp_dsp_stream *stream = adata->stream_buf;
+       int i;
+
+       for (i = 0; i < ACP_MAX_STREAM; i++, stream++) {
+               if (stream->active)
+                       continue;
+
+               /* return stream if tag not specified*/
+               if (!tag) {
+                       stream->active = 1;
+                       return stream;
+               }
+
+               /* check if this is the requested stream tag */
+               if (stream->stream_tag == tag) {
+                       stream->active = 1;
+                       return stream;
+               }
+       }
+
+       dev_err(sdev->dev, "stream %d active or no inactive stream\n", tag);
+       return NULL;
+}
+EXPORT_SYMBOL_NS(acp_dsp_stream_get, SND_SOC_SOF_AMD_COMMON);
+
+int acp_dsp_stream_put(struct snd_sof_dev *sdev,
+                      struct acp_dsp_stream *acp_stream)
+{
+       struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+       struct acp_dsp_stream *stream = adata->stream_buf;
+       int i;
+
+       /* Free an active stream */
+       for (i = 0; i < ACP_MAX_STREAM; i++, stream++) {
+               if (stream == acp_stream) {
+                       stream->active = 0;
+                       return 0;
+               }
+       }
+
+       dev_err(sdev->dev, "Cannot find active stream tag %d\n", acp_stream->stream_tag);
+       return -EINVAL;
+}
+EXPORT_SYMBOL_NS(acp_dsp_stream_put, SND_SOC_SOF_AMD_COMMON);
+
+int acp_dsp_stream_init(struct snd_sof_dev *sdev)
+{
+       struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+       int i;
+
+       for (i = 0; i < ACP_MAX_STREAM; i++) {
+               adata->stream_buf[i].sdev = sdev;
+               adata->stream_buf[i].active = 0;
+               adata->stream_buf[i].stream_tag = i + 1;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_NS(acp_dsp_stream_init, SND_SOC_SOF_AMD_COMMON);
index 43a57d1..74ede28 100644 (file)
@@ -363,6 +363,8 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
 
        acp_memory_init(sdev);
 
+       acp_dsp_stream_init(sdev);
+
        return 0;
 }
 EXPORT_SYMBOL_NS(amd_sof_acp_probe, SND_SOC_SOF_AMD_COMMON);
index ac83401..36d000c 100644 (file)
@@ -13,6 +13,8 @@
 
 #include "../sof-priv.h"
 
+#define ACP_MAX_STREAM 8
+
 #define ACP_DSP_BAR    0
 
 #define ACP_REG_POLL_INTERVAL                   500
@@ -114,6 +116,17 @@ struct  scratch_reg_conf {
        unsigned int    reserve[];
 };
 
+struct acp_dsp_stream {
+       struct list_head list;
+       struct snd_sof_dev *sdev;
+       struct snd_pcm_substream *substream;
+       struct snd_dma_buffer *dmab;
+       int num_pages;
+       int stream_tag;
+       int active;
+       unsigned int reg_offset;
+};
+
 /* Common device data struct for ACP devices */
 struct acp_dev_data {
        struct snd_sof_dev  *dev;
@@ -125,6 +138,7 @@ struct acp_dev_data {
        dma_addr_t dma_addr;
        u8 *data_buf;
        struct dma_descriptor dscr_info[ACP_MAX_DESC];
+       struct acp_dsp_stream stream_buf[ACP_MAX_STREAM];
 };
 
 void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes);
@@ -165,5 +179,19 @@ int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *s
 void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes);
 void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes);
 
+/* ACP - DSP  stream callbacks */
+int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *stream);
+int acp_dsp_stream_init(struct snd_sof_dev *sdev);
+struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag);
+int acp_dsp_stream_put(struct snd_sof_dev *sdev, struct acp_dsp_stream *acp_stream);
+
+/*
+ * DSP PCM Operations.
+ */
+int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream);
+int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream);
+int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+                     struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params);
+
 extern const struct snd_sof_dsp_ops sof_renoir_ops;
 #endif
index ca5582b..0241c5d 100644 (file)
@@ -140,6 +140,17 @@ const struct snd_sof_dsp_ops sof_renoir_ops = {
        /* DAI drivers */
        .drv                    = renoir_sof_dai,
        .num_drv                = ARRAY_SIZE(renoir_sof_dai),
+
+       /* stream callbacks */
+       .pcm_open               = acp_pcm_open,
+       .pcm_close              = acp_pcm_close,
+       .pcm_hw_params          = acp_pcm_hw_params,
+
+       .hw_info                = SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_PAUSE |
+                                 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
 };
 EXPORT_SYMBOL(sof_renoir_ops);