Commit f23e8f3a authored by Mark Brown's avatar Mark Brown

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).
parents a93830a9 7211814f
...@@ -37,6 +37,25 @@ struct sof_ipc4_timestamp_info { ...@@ -37,6 +37,25 @@ struct sof_ipc4_timestamp_info {
snd_pcm_sframes_t delay; 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, static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state,
struct ipc4_pipeline_set_state_data *trigger_list) 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, ...@@ -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, 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, struct snd_sof_pcm_stream_pipeline_list *pipeline_list,
int state, int cmd) int state, int cmd)
{ {
struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_ipc4_pcm_stream_priv *stream_priv;
bool allocate, enable, set_fifo_size; bool allocate, enable, set_fifo_size;
struct sof_ipc4_msg msg = {{ 0 }}; struct sof_ipc4_msg msg = {{ 0 }};
int i; int ret, i;
stream_priv = spcm->stream[direction].private;
switch (state) { switch (state) {
case SOF_IPC4_PIPE_RUNNING: /* Allocate and start chained dma */ 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, ...@@ -281,6 +303,11 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
set_fifo_size = false; set_fifo_size = false;
break; break;
case SOF_IPC4_PIPE_RESET: /* Disable and free chained DMA. */ 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; allocate = false;
enable = false; enable = false;
set_fifo_size = false; set_fifo_size = false;
...@@ -338,7 +365,12 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, ...@@ -338,7 +365,12 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
if (enable) if (enable)
msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK; 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, 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, ...@@ -378,7 +410,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
* trigger function that handles the rest for the substream. * trigger function that handles the rest for the substream.
*/ */
if (pipeline->use_chain_dma) 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); pipeline_list, state, cmd);
/* allocate memory for the pipeline data */ /* allocate memory for the pipeline data */
...@@ -452,7 +484,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, ...@@ -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 * Invalidate the stream_start_offset to make sure that it is
* going to be updated if the stream resumes * 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) if (time_info)
time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION; 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, ...@@ -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) 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 snd_sof_pcm_stream_pipeline_list *pipeline_list;
struct sof_ipc4_pcm_stream_priv *stream_priv;
int stream; int stream;
for_each_pcm_streams(stream) { for_each_pcm_streams(stream) {
pipeline_list = &spcm->stream[stream].pipeline_list; pipeline_list = &spcm->stream[stream].pipeline_list;
kfree(pipeline_list->pipelines); kfree(pipeline_list->pipelines);
pipeline_list->pipelines = NULL; pipeline_list->pipelines = NULL;
stream_priv = spcm->stream[stream].private;
kfree(stream_priv->time_info);
kfree(spcm->stream[stream].private); kfree(spcm->stream[stream].private);
spcm->stream[stream].private = NULL; 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 ...@@ -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 snd_sof_pcm_stream_pipeline_list *pipeline_list;
struct sof_ipc4_fw_data *ipc4_data = sdev->private; 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; bool support_info = true;
u32 abi_version; u32 abi_version;
u32 abi_offset; u32 abi_offset;
...@@ -749,33 +786,41 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm ...@@ -749,33 +786,41 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm
return -ENOMEM; return -ENOMEM;
} }
stream_priv = kzalloc(sizeof(*stream_priv), GFP_KERNEL);
if (!stream_priv) {
sof_ipc4_pcm_free(sdev, spcm);
return -ENOMEM;
}
spcm->stream[stream].private = stream_priv;
if (!support_info) if (!support_info)
continue; continue;
stream_info = kzalloc(sizeof(*stream_info), GFP_KERNEL); time_info = kzalloc(sizeof(*time_info), GFP_KERNEL);
if (!stream_info) { if (!time_info) {
sof_ipc4_pcm_free(sdev, spcm); sof_ipc4_pcm_free(sdev, spcm);
return -ENOMEM; return -ENOMEM;
} }
spcm->stream[stream].private = stream_info; stream_priv->time_info = time_info;
} }
return 0; 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 *host_copier = NULL;
struct sof_ipc4_copier *dai_copier = NULL; struct sof_ipc4_copier *dai_copier = NULL;
struct sof_ipc4_llp_reading_slot llp_slot; 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_soc_dapm_widget *widget;
struct snd_sof_dai *dai; struct snd_sof_dai *dai;
int i; int i;
/* find host & dai to locate info in memory window */ /* 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; struct snd_sof_widget *swidget = widget->dobj.private;
if (!swidget) if (!swidget)
...@@ -795,44 +840,44 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc ...@@ -795,44 +840,44 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc
return; return;
} }
info = spcm->private; time_info = sof_ipc4_sps_to_time_info(sps);
info->host_copier = host_copier; time_info->host_copier = host_copier;
info->dai_copier = dai_copier; time_info->dai_copier = dai_copier;
info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_gpdma_reading_slots) + time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers,
sdev->fw_info_box.offset; llp_gpdma_reading_slots) + sdev->fw_info_box.offset;
/* find llp slot used by current dai */ /* find llp slot used by current dai */
for (i = 0; i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS; i++) { 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) if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id)
break; break;
info->llp_offset += sizeof(llp_slot); time_info->llp_offset += sizeof(llp_slot);
} }
if (i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS) if (i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS)
return; return;
/* if no llp gpdma slot is used, check aggregated sdw slot */ /* if no llp gpdma slot is used, check aggregated sdw slot */
info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_sndw_reading_slots) + time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers,
sdev->fw_info_box.offset; llp_sndw_reading_slots) + sdev->fw_info_box.offset;
for (i = 0; i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS; i++) { 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) if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id)
break; break;
info->llp_offset += sizeof(llp_slot); time_info->llp_offset += sizeof(llp_slot);
} }
if (i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS) if (i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS)
return; return;
/* check EVAD slot */ /* check EVAD slot */
info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_evad_reading_slot) + time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers,
sdev->fw_info_box.offset; llp_evad_reading_slot) + sdev->fw_info_box.offset;
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) 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, 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, ...@@ -849,7 +894,7 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component,
if (!spcm) if (!spcm)
return -EINVAL; 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 */ /* delay calculation is not supported by current fw_reg ABI */
if (!time_info) if (!time_info)
return 0; return 0;
...@@ -864,7 +909,7 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component, ...@@ -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, static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream, 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_timestamp_info *time_info)
{ {
struct sof_ipc4_copier *host_copier = time_info->host_copier; 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, ...@@ -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_timestamp_info *time_info;
struct sof_ipc4_llp_reading_slot llp; struct sof_ipc4_llp_reading_slot llp;
snd_pcm_uframes_t head_cnt, tail_cnt; 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; u64 dai_cnt, host_cnt, host_ptr;
struct snd_sof_pcm *spcm; struct snd_sof_pcm *spcm;
int ret; int ret;
...@@ -927,8 +972,8 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, ...@@ -927,8 +972,8 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component,
if (!spcm) if (!spcm)
return -EOPNOTSUPP; return -EOPNOTSUPP;
stream = &spcm->stream[substream->stream]; sps = &spcm->stream[substream->stream];
time_info = stream->private; time_info = sof_ipc4_sps_to_time_info(sps);
if (!time_info) if (!time_info)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -938,7 +983,7 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, ...@@ -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. * the statistics is complete. And it will not change after the first initiailization.
*/ */
if (time_info->stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) { 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) if (ret < 0)
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -1030,15 +1075,13 @@ static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component, ...@@ -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 snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct sof_ipc4_timestamp_info *time_info; struct sof_ipc4_timestamp_info *time_info;
struct snd_sof_pcm_stream *stream;
struct snd_sof_pcm *spcm; struct snd_sof_pcm *spcm;
spcm = snd_sof_find_spcm_dai(component, rtd); spcm = snd_sof_find_spcm_dai(component, rtd);
if (!spcm) if (!spcm)
return 0; return 0;
stream = &spcm->stream[substream->stream]; time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]);
time_info = stream->private;
/* /*
* Report the stored delay value calculated in the pointer callback. * Report the stored delay value calculated in the pointer callback.
* In the unlikely event that the calculation was skipped/aborted, the * In the unlikely event that the calculation was skipped/aborted, the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment