ASoC: SOF: amd: Add PCM stream callback for Renoir dai's
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>
This commit is contained in:
parent
bda93076d1
commit
e8afccf8fb
@ -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
|
||||
|
82
sound/soc/sof/amd/acp-pcm.c
Normal file
82
sound/soc/sof/amd/acp-pcm.c
Normal file
@ -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);
|
181
sound/soc/sof/amd/acp-stream.c
Normal file
181
sound/soc/sof/amd/acp-stream.c
Normal file
@ -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);
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user