Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions sound/soc/soc-compress.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <sound/core.h>
#include <sound/compress_params.h>
#include <sound/compress_driver.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/soc-dpcm.h>
Expand Down Expand Up @@ -130,6 +131,85 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
return ret;
}

/*
* The internal PCM of a compressed FE is never opened via the PCM API, so its
* substream has no runtime attached to it.
*
* DPCM lends the runtime of the FE to every BE it opens and re-points it at
* another FE when the lending one goes away, so the compressed FE must provide
* one as well. Without it the BEs are left with a NULL runtime, which oopses
* in BE DAI and CODEC drivers looking at substream->runtime, and a BE can not
* be shared between a compressed and a PCM FE at all.
*
* The runtime is owned by the FE for as long as the compressed stream is open.
*/
static int soc_compr_alloc_fe_runtime(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_pcm_substream *fe_substream = snd_soc_dpcm_get_substream(fe, stream);

if (!fe_substream || fe_substream->runtime)
return 0;

fe_substream->runtime = kzalloc_obj(*fe_substream->runtime);
if (!fe_substream->runtime)
return -ENOMEM;
Comment on lines +146 to +155

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is false positive, the BE side is no_pcm and we need the runtime for the random codec drivers that are out of our control.


return 0;
}

static void soc_compr_free_fe_runtime(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_pcm_substream *fe_substream = snd_soc_dpcm_get_substream(fe, stream);

if (!fe_substream || !fe_substream->runtime)
return;

/* BE startup callbacks may have added hw constraint rules */
kfree(fe_substream->runtime->hw_constraints.rules);
kfree(fe_substream->runtime);
fe_substream->runtime = NULL;
}

/*
* The BE parameters of a compressed FE are set up by the machine level
* be_hw_params_fixup(), which is mandatory for a compressed BE, see
* soc_compr_set_params_fe(). Once they are fixed up, use them to fill in the
* runtime the BEs have been lent, so that a BE DAI or CODEC driver sees the
* format it is being configured for.
*/
static void soc_compr_set_fe_runtime(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_pcm_substream *fe_substream = snd_soc_dpcm_get_substream(fe, stream);
struct snd_pcm_runtime *runtime;
struct snd_soc_dpcm *dpcm;

snd_soc_dpcm_mutex_assert_held(fe);

if (!fe_substream || !fe_substream->runtime)
return;

runtime = fe_substream->runtime;

for_each_dpcm_be(fe, stream, dpcm) {
struct snd_pcm_hw_params *params = &dpcm->be->dpcm[stream].hw_params;
int bits = snd_pcm_format_physical_width(params_format(params));

/* skip a BE which has not been fixed up */
if (bits <= 0)
continue;

runtime->access = params_access(params);
runtime->format = params_format(params);
runtime->subformat = params_subformat(params);
runtime->channels = params_channels(params);
runtime->rate = params_rate(params);

runtime->sample_bits = bits;
runtime->frame_bits = bits * runtime->channels;
break;
}
}

static int soc_compr_open_fe(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *fe = cstream->private_data;
Expand All @@ -141,6 +221,10 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)

snd_soc_card_mutex_lock(fe->card);

ret = soc_compr_alloc_fe_runtime(fe, stream);
if (ret < 0)
goto be_err;

ret = dpcm_path_get(fe, stream, &list);
if (ret < 0)
goto be_err;
Expand Down Expand Up @@ -195,6 +279,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
dpcm_path_put(&list);
snd_soc_dpcm_mutex_unlock(fe);
be_err:
soc_compr_free_fe_runtime(fe, stream);
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
snd_soc_card_mutex_unlock(fe->card);
return ret;
Expand Down Expand Up @@ -247,6 +332,9 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)

snd_soc_dai_compr_shutdown(cpu_dai, cstream, 0);

/* all BEs are shut down and disconnected, the runtime is unused now */
soc_compr_free_fe_runtime(fe, stream);

snd_soc_card_mutex_unlock(fe->card);
return 0;
}
Expand Down Expand Up @@ -450,6 +538,8 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,

snd_soc_dpcm_mutex_lock(fe);
ret = dpcm_be_dai_hw_params(fe, stream);
if (!ret)
soc_compr_set_fe_runtime(fe, stream);
snd_soc_dpcm_mutex_unlock(fe);
if (ret < 0)
goto out;
Expand Down
34 changes: 8 additions & 26 deletions sound/soc/soc-pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1790,8 +1790,6 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream,
}

__soc_pcm_close(be, be_substream);
if (fe->fe_compr)
kfree(be_substream->runtime);
be_substream->runtime = NULL;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
}
Expand Down Expand Up @@ -1839,26 +1837,18 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
dev_dbg(be->dev, "ASoC: open %s BE %s\n",
snd_pcm_direction_name(stream), be->dai_link->name);

if (!fe->fe_compr) {
be_substream->runtime = fe_substream->runtime;
} else {
be_substream->runtime = kzalloc(sizeof(*be_substream->runtime),
GFP_KERNEL);
if (!be_substream->runtime) {
err = -ENOMEM;
goto unwind;
}
}

be_substream->runtime = fe_substream->runtime;
err = __soc_pcm_open(be, be_substream);
if (err < 0) {
if (fe->fe_compr) {
kfree(be_substream->runtime);
be_substream->runtime = NULL;
}
be->dpcm[stream].users--;
if (be->dpcm[stream].users < 0)
dev_err(be->dev, "ASoC: no users %s at unwind %s\n",
snd_pcm_direction_name(stream),
dpcm_state_string(be->dpcm[stream].state));

be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
goto unwind;
}

be->dpcm[stream].be_start = 0;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
count++;
Expand All @@ -1867,14 +1857,6 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
return count;

unwind:
be->dpcm[stream].users--;
if (be->dpcm[stream].users < 0)
dev_err(be->dev, "ASoC: no users %s at unwind %s\n",
snd_pcm_direction_name(stream),
dpcm_state_string(be->dpcm[stream].state));

be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;

dpcm_be_dai_startup_rollback(fe, stream, dpcm);

return soc_pcm_ret(fe, err);
Expand Down
7 changes: 2 additions & 5 deletions sound/soc/sof/ipc4-compress.c
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,6 @@ static int sof_ipc4_compr_set_params(struct snd_soc_component *component,
struct snd_sof_platform_stream_params *platform_params;
struct sof_ipc4_timestamp_info *time_info;
struct snd_compr_params *compr_params;
struct snd_soc_dapm_widget_list *list;
struct snd_sof_widget *host_swidget;
struct sof_ipc4_process *process;
struct snd_pcm_hw_params p = {0};
Expand Down Expand Up @@ -497,9 +496,7 @@ static int sof_ipc4_compr_set_params(struct snd_soc_component *component,
process->init_ext_module_size = 0;

free_list:
list = spcm->stream[dir].list;
spcm->stream[dir].list = NULL;
snd_soc_dapm_dai_free_widgets(&list);
sof_widget_list_unprepare(sdev, spcm, dir);

free_pages:
snd_compr_free_pages(cstream);
Expand Down Expand Up @@ -735,7 +732,7 @@ void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message)
return;
}

if (!swidget->spipe)
if (!swidget->spipe || !swidget->spipe->pipe_widget)
return;

/* Find the swidget of the host copier on the same pipeline */
Expand Down
32 changes: 28 additions & 4 deletions sound/soc/sof/ipc4-topology.c
Original file line number Diff line number Diff line change
Expand Up @@ -3296,14 +3296,30 @@ static void sof_ipc4_add_init_ext_dp_memory_data(struct snd_sof_dev *sdev,
*ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32));
}

static void
static int
sof_ipc4_add_init_ext_module_data(struct snd_sof_dev *sdev,
struct sof_ipc4_process *process,
u32 *payload, u32 *ext_pos,
struct sof_ipc4_module_init_ext_object **hdr)
{
u32 data_size = process->init_ext_module_size;
void *data = process->init_ext_module_data;
size_t needed;

/*
* Unlike the other objects, the module data is of variable size,
* provided by the module which is being set up.
* Make sure that the object fits into the payload buffer before any of
* it is written.
*/
needed = ((size_t)*ext_pos + DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)) +
DIV_ROUND_UP(data_size, sizeof(u32))) * sizeof(u32);
if (needed > sdev->ipc->max_payload_size) {
dev_err(sdev->dev,
"Max ipc payload size %zu exceeded by module data: %zu\n",
sdev->ipc->max_payload_size, needed);
return -EINVAL;
}

*hdr = (struct sof_ipc4_module_init_ext_object *)&payload[*ext_pos];
(*hdr)->header = SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_MODULE_DATA) |
Expand All @@ -3313,6 +3329,8 @@ sof_ipc4_add_init_ext_module_data(struct snd_sof_dev *sdev,
memcpy(&payload[*ext_pos], data, data_size);

*ext_pos += DIV_ROUND_UP(data_size, sizeof(u32));

return 0;
}

static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev,
Expand All @@ -3329,6 +3347,7 @@ static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev,
int new_size;
u32 *payload;
u32 ext_pos;
int ret;

if (!in_dp_domain && !has_ext_data)
return 0;
Expand All @@ -3347,9 +3366,14 @@ static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev,
sof_ipc4_add_init_ext_dp_memory_data(sdev, swidget, payload,
&ext_pos, &hdr);

if (has_ext_data)
sof_ipc4_add_init_ext_module_data(sdev, process, payload,
&ext_pos, &hdr);
if (has_ext_data) {
ret = sof_ipc4_add_init_ext_module_data(sdev, process, payload,
&ext_pos, &hdr);
if (ret) {
kfree(payload);
return ret;
}
}

/* Set last bit for the last object in the array */
hdr->header |= SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK;
Expand Down
52 changes: 6 additions & 46 deletions sound/soc/sof/sof-audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -547,20 +547,15 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
* free all widgets in the sink path starting from the source widget
* (DAI type for capture, AIF type for playback)
*/
static int sof_free_widgets_in_path_internal(struct snd_sof_dev *sdev,
struct snd_soc_dapm_widget *widget,
int dir, struct snd_sof_pcm *spcm,
struct snd_soc_dapm_widget_list *list)
static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
int dir, struct snd_sof_pcm *spcm)
{
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
struct snd_sof_widget *swidget = widget->dobj.private;
struct snd_soc_dapm_path *p;
struct snd_soc_dapm_path *next_p;
int err;
int ret = 0;

if (!list)
return 0;

if (is_virtual_widget(sdev, widget, __func__))
return 0;

Expand All @@ -580,52 +575,23 @@ static int sof_free_widgets_in_path_internal(struct snd_sof_dev *sdev,
ret = err;
sink_free:
/* free all widgets in the sink paths even in case of error to keep use counts balanced */
snd_soc_dapm_widget_for_each_path_safe(widget, SND_SOC_DAPM_DIR_IN, p, next_p) {
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!p->walking) {
if (!p->sink)
continue;

if (!widget_in_list(list, p->sink))
continue;

p->walking = true;

err = sof_free_widgets_in_path_internal(sdev, p->sink,
dir, spcm, list);
err = sof_free_widgets_in_path(sdev, p->sink, dir, spcm);
if (err < 0)
ret = err;
p->walking = false;
}
}

return ret;
}

static int sof_free_widgets_in_path(struct snd_sof_dev *sdev,
struct snd_soc_dapm_widget *widget,
int dir, struct snd_sof_pcm *spcm)
{
return sof_free_widgets_in_path_internal(sdev, widget, dir, spcm,
spcm->stream[dir].list);
}

static void sof_reset_path_walking_flags(struct snd_soc_dapm_widget_list *list)
{
struct snd_soc_dapm_widget *widget;
struct snd_soc_dapm_path *p;
int i;

if (!list)
return;

for_each_dapm_widgets(list, i, widget) {
snd_soc_dapm_widget_for_each_sink_path(widget, p)
p->walking = false;

snd_soc_dapm_widget_for_each_source_path(widget, p)
p->walking = false;
}
}

/*
* set up all widgets in the sink path starting from the source widget
* (DAI type for capture, AIF type for playback).
Expand Down Expand Up @@ -760,17 +726,11 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
return -EINVAL;
}
if (ret < 0) {
if (op == SOF_WIDGET_FREE)
sof_reset_path_walking_flags(list);

dev_err(sdev->dev, "Failed to %s connected widgets\n", str);
return ret;
}
}

if (op == SOF_WIDGET_FREE)
sof_reset_path_walking_flags(list);

return 0;
}

Expand Down
Loading