ASoC: SOF: ipc4-pcm: Do not reset ChainDMA if it is

Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>:

The current code will reset the ChainDMA on release unconditionally which
can result the following error when the CHainDMA is not allocated:

ipc tx      : 0xe040000|0x0: GLB_CHAIN_DMA
ipc tx reply: 0x2e000007|0x0: GLB_CHAIN_DMA
FW reported error: 7 - Unsupported operation requested
ipc error for msg 0xe040000|0x0
sof_pcm_stream_free: pcm_ops hw_free failed -22

Background:
Pulseaudio and Pipewire on startup opens all available streams and
closes them without triggering a start (after probing it's capabilities).
This commit is contained in:
Mark Brown 2024-04-09 23:26:25 +01:00
commit f23e8f3ad3
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
1 changed files with 82 additions and 39 deletions

View File

@ -37,6 +37,25 @@ struct sof_ipc4_timestamp_info {
snd_pcm_sframes_t delay;
};
/**
* struct sof_ipc4_pcm_stream_priv - IPC4 specific private data
* @time_info: pointer to time info struct if it is supported, otherwise NULL
* @chain_dma_allocated: indicates the ChainDMA allocation state
*/
struct sof_ipc4_pcm_stream_priv {
struct sof_ipc4_timestamp_info *time_info;
bool chain_dma_allocated;
};
static inline struct sof_ipc4_timestamp_info *
sof_ipc4_sps_to_time_info(struct snd_sof_pcm_stream *sps)
{
struct sof_ipc4_pcm_stream_priv *stream_priv = sps->private;
return stream_priv->time_info;
}
static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state,
struct ipc4_pipeline_set_state_data *trigger_list)
{
@ -253,14 +272,17 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd,
*/
static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
int direction,
struct snd_sof_pcm *spcm, int direction,
struct snd_sof_pcm_stream_pipeline_list *pipeline_list,
int state, int cmd)
{
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_ipc4_pcm_stream_priv *stream_priv;
bool allocate, enable, set_fifo_size;
struct sof_ipc4_msg msg = {{ 0 }};
int i;
int ret, i;
stream_priv = spcm->stream[direction].private;
switch (state) {
case SOF_IPC4_PIPE_RUNNING: /* Allocate and start chained dma */
@ -281,6 +303,11 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
set_fifo_size = false;
break;
case SOF_IPC4_PIPE_RESET: /* Disable and free chained DMA. */
/* ChainDMA can only be reset if it has been allocated */
if (!stream_priv->chain_dma_allocated)
return 0;
allocate = false;
enable = false;
set_fifo_size = false;
@ -338,7 +365,12 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
if (enable)
msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK;
return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
/* Update the ChainDMA allocation state */
if (!ret)
stream_priv->chain_dma_allocated = allocate;
return ret;
}
static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
@ -378,7 +410,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
* trigger function that handles the rest for the substream.
*/
if (pipeline->use_chain_dma)
return sof_ipc4_chain_dma_trigger(sdev, substream->stream,
return sof_ipc4_chain_dma_trigger(sdev, spcm, substream->stream,
pipeline_list, state, cmd);
/* allocate memory for the pipeline data */
@ -452,7 +484,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
* Invalidate the stream_start_offset to make sure that it is
* going to be updated if the stream resumes
*/
time_info = spcm->stream[substream->stream].private;
time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]);
if (time_info)
time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION;
@ -706,12 +738,16 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm)
{
struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
struct sof_ipc4_pcm_stream_priv *stream_priv;
int stream;
for_each_pcm_streams(stream) {
pipeline_list = &spcm->stream[stream].pipeline_list;
kfree(pipeline_list->pipelines);
pipeline_list->pipelines = NULL;
stream_priv = spcm->stream[stream].private;
kfree(stream_priv->time_info);
kfree(spcm->stream[stream].private);
spcm->stream[stream].private = NULL;
}
@ -721,7 +757,8 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm
{
struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_ipc4_timestamp_info *stream_info;
struct sof_ipc4_pcm_stream_priv *stream_priv;
struct sof_ipc4_timestamp_info *time_info;
bool support_info = true;
u32 abi_version;
u32 abi_offset;
@ -749,33 +786,41 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm
return -ENOMEM;
}
if (!support_info)
continue;
stream_info = kzalloc(sizeof(*stream_info), GFP_KERNEL);
if (!stream_info) {
stream_priv = kzalloc(sizeof(*stream_priv), GFP_KERNEL);
if (!stream_priv) {
sof_ipc4_pcm_free(sdev, spcm);
return -ENOMEM;
}
spcm->stream[stream].private = stream_info;
spcm->stream[stream].private = stream_priv;
if (!support_info)
continue;
time_info = kzalloc(sizeof(*time_info), GFP_KERNEL);
if (!time_info) {
sof_ipc4_pcm_free(sdev, spcm);
return -ENOMEM;
}
stream_priv->time_info = time_info;
}
return 0;
}
static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *spcm)
static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps)
{
struct sof_ipc4_copier *host_copier = NULL;
struct sof_ipc4_copier *dai_copier = NULL;
struct sof_ipc4_llp_reading_slot llp_slot;
struct sof_ipc4_timestamp_info *info;
struct sof_ipc4_timestamp_info *time_info;
struct snd_soc_dapm_widget *widget;
struct snd_sof_dai *dai;
int i;
/* find host & dai to locate info in memory window */
for_each_dapm_widgets(spcm->list, i, widget) {
for_each_dapm_widgets(sps->list, i, widget) {
struct snd_sof_widget *swidget = widget->dobj.private;
if (!swidget)
@ -795,44 +840,44 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc
return;
}
info = spcm->private;
info->host_copier = host_copier;
info->dai_copier = dai_copier;
info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_gpdma_reading_slots) +
sdev->fw_info_box.offset;
time_info = sof_ipc4_sps_to_time_info(sps);
time_info->host_copier = host_copier;
time_info->dai_copier = dai_copier;
time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers,
llp_gpdma_reading_slots) + sdev->fw_info_box.offset;
/* find llp slot used by current dai */
for (i = 0; i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS; i++) {
sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot));
sof_mailbox_read(sdev, time_info->llp_offset, &llp_slot, sizeof(llp_slot));
if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id)
break;
info->llp_offset += sizeof(llp_slot);
time_info->llp_offset += sizeof(llp_slot);
}
if (i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS)
return;
/* if no llp gpdma slot is used, check aggregated sdw slot */
info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_sndw_reading_slots) +
sdev->fw_info_box.offset;
time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers,
llp_sndw_reading_slots) + sdev->fw_info_box.offset;
for (i = 0; i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS; i++) {
sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot));
sof_mailbox_read(sdev, time_info->llp_offset, &llp_slot, sizeof(llp_slot));
if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id)
break;
info->llp_offset += sizeof(llp_slot);
time_info->llp_offset += sizeof(llp_slot);
}
if (i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS)
return;
/* check EVAD slot */
info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_evad_reading_slot) +
sdev->fw_info_box.offset;
sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot));
time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers,
llp_evad_reading_slot) + sdev->fw_info_box.offset;
sof_mailbox_read(sdev, time_info->llp_offset, &llp_slot, sizeof(llp_slot));
if (llp_slot.node_id != dai_copier->data.gtw_cfg.node_id)
info->llp_offset = 0;
time_info->llp_offset = 0;
}
static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component,
@ -849,7 +894,7 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
time_info = spcm->stream[substream->stream].private;
time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]);
/* delay calculation is not supported by current fw_reg ABI */
if (!time_info)
return 0;
@ -864,7 +909,7 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component,
static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
struct snd_sof_pcm_stream *stream,
struct snd_sof_pcm_stream *sps,
struct sof_ipc4_timestamp_info *time_info)
{
struct sof_ipc4_copier *host_copier = time_info->host_copier;
@ -918,7 +963,7 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
struct sof_ipc4_timestamp_info *time_info;
struct sof_ipc4_llp_reading_slot llp;
snd_pcm_uframes_t head_cnt, tail_cnt;
struct snd_sof_pcm_stream *stream;
struct snd_sof_pcm_stream *sps;
u64 dai_cnt, host_cnt, host_ptr;
struct snd_sof_pcm *spcm;
int ret;
@ -927,8 +972,8 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
if (!spcm)
return -EOPNOTSUPP;
stream = &spcm->stream[substream->stream];
time_info = stream->private;
sps = &spcm->stream[substream->stream];
time_info = sof_ipc4_sps_to_time_info(sps);
if (!time_info)
return -EOPNOTSUPP;
@ -938,7 +983,7 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
* the statistics is complete. And it will not change after the first initiailization.
*/
if (time_info->stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) {
ret = sof_ipc4_get_stream_start_offset(sdev, substream, stream, time_info);
ret = sof_ipc4_get_stream_start_offset(sdev, substream, sps, time_info);
if (ret < 0)
return -EOPNOTSUPP;
}
@ -1030,15 +1075,13 @@ static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component,
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct sof_ipc4_timestamp_info *time_info;
struct snd_sof_pcm_stream *stream;
struct snd_sof_pcm *spcm;
spcm = snd_sof_find_spcm_dai(component, rtd);
if (!spcm)
return 0;
stream = &spcm->stream[substream->stream];
time_info = stream->private;
time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]);
/*
* Report the stored delay value calculated in the pointer callback.
* In the unlikely event that the calculation was skipped/aborted, the