blob: abedb037bb9c36ee40a078b0613c7006b3f14314 [file] [log] [blame]
// 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"
#include "../sof-audio.h"
int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_sof_platform_stream_params *platform_params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct acp_dsp_stream *stream = runtime->private_data;
unsigned int buf_offset, index;
u32 size;
int ret;
size = runtime->dma_bytes;
stream->num_pages = PFN_UP(runtime->dma_bytes);
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;
}
platform_params->use_phy_address = true;
platform_params->phy_addr = stream->reg_offset;
platform_params->stream_tag = stream->stream_tag;
/* write buffer size of stream in scratch memory */
buf_offset = sdev->debug_box.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);
static const struct acp_pcm_table pcm_dev[] = {
{I2S_BT, "I2SBT"},
{I2S_SP, "I2SSP"},
{PDM_DMIC, "DMIC"},
{I2S_HS, "I2SHS"},
};
static enum acp_pcm_types get_id_from_list(const char *name)
{
int i;
for (i = 0; i < ARRAY_SIZE(pcm_dev); i++) {
if (!strcmp(name, pcm_dev[i].pcm_name))
return pcm_dev[i].pcm_index;
}
return PCM_NONE;
}
static u64 acp_get_byte_count(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
enum acp_pcm_types pcm_id)
{
u64 bytescount, low = 0, high = 0;
u32 reg1 = 0, reg2 = 0;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
switch (pcm_id) {
case I2S_BT:
reg1 = ACP_BT_TX_LINEARPOSITIONCNTR_HIGH;
reg2 = ACP_BT_TX_LINEARPOSITIONCNTR_LOW;
break;
case I2S_SP:
reg1 = ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH;
reg2 = ACP_I2S_TX_LINEARPOSITIONCNTR_LOW;
break;
case I2S_HS:
reg1 = ACP_HS_TX_LINEARPOSITIONCNTR_HIGH;
reg2 = ACP_HS_TX_LINEARPOSITIONCNTR_LOW;
break;
default:
return -EINVAL;
}
} else {
switch (pcm_id) {
case I2S_BT:
reg1 = ACP_BT_RX_LINEARPOSITIONCNTR_HIGH;
reg2 = ACP_BT_RX_LINEARPOSITIONCNTR_LOW;
break;
case I2S_SP:
reg1 = ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH;
reg2 = ACP_I2S_RX_LINEARPOSITIONCNTR_LOW;
break;
case I2S_HS:
reg1 = ACP_HS_RX_LINEARPOSITIONCNTR_HIGH;
reg2 = ACP_HS_RX_LINEARPOSITIONCNTR_LOW;
break;
case PDM_DMIC:
reg1 = ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH;
reg2 = ACP_WOV_RX_LINEARPOSITIONCNTR_LOW;
break;
default:
return -EINVAL;
}
}
high = snd_sof_dsp_read(sdev, ACP_DSP_BAR, reg1);
low = snd_sof_dsp_read(sdev, ACP_DSP_BAR, reg2);
bytescount = (high << 32) | low;
return bytescount;
}
snd_pcm_uframes_t acp_pcm_pointer(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_component *scomp = sdev->component;
enum acp_pcm_types pcm_id = PCM_NONE;
struct snd_sof_pcm *spcm;
u32 pos, buffersize;
u64 bytescount;
spcm = snd_sof_find_spcm_dai(scomp, rtd);
pcm_id = get_id_from_list(spcm->pcm.pcm_name);
bytescount = acp_get_byte_count(sdev, substream, pcm_id);
if (bytescount < 0)
return -EINVAL;
buffersize = frames_to_bytes(substream->runtime, substream->runtime->buffer_size);
pos = do_div(bytescount, buffersize);
return bytes_to_frames(substream->runtime, pos);
}
EXPORT_SYMBOL_NS(acp_pcm_pointer, SND_SOC_SOF_AMD_COMMON);