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:
commit
f23e8f3ad3
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue