Skip to content
Closed
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
33 changes: 32 additions & 1 deletion sound/core/compress_offload.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,14 @@ static int snd_compr_open(struct inode *inode, struct file *f)
return -ENODEV;
}

ret = snd_card_file_add(compr->card, f);
if (ret < 0) {
snd_card_unref(compr->card);
return ret;
}

if (!try_module_get(compr->card->module)) {
snd_card_file_remove(compr->card, f);
snd_card_unref(compr->card);
return -EFAULT;
}
Expand Down Expand Up @@ -149,6 +156,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
kfree(runtime);
kfree(data);
module_put(compr->card->module);
snd_card_file_remove(compr->card, f);
}
snd_card_unref(compr->card);
return ret;
Expand Down Expand Up @@ -185,6 +193,7 @@ static int snd_compr_free(struct inode *inode, struct file *f)
module_put(data->stream.device->card->module);
kfree(data->stream.runtime);
kfree(data);
snd_card_file_remove(compr->card, f);
return 0;
}

Expand Down Expand Up @@ -938,8 +947,10 @@ static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
* It is expected that driver will notify the drain completion and then
* stream will be moved to SETUP state, even if draining resulted in an
* error. We can trigger next track after this.
*
* The state has been set to SNDRV_PCM_STATE_DRAINING by the caller,
* before the drain trigger was issued.
*/
stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
mutex_unlock(&stream->device->lock);

/* we wait for drain to complete here, drain can return when
Expand All @@ -964,6 +975,7 @@ static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)

static int snd_compr_drain(struct snd_compr_stream *stream)
{
snd_pcm_state_t state;
int retval;

switch (stream->runtime->state) {
Expand All @@ -978,9 +990,20 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
break;
}

/*
* The state must be set to draining before the trigger is issued as the
* drain can be completed by the driver either from the trigger callback
* itself or from a different context as soon as the trigger is called.
* A snd_compr_drain_notify() arriving before the state is updated would
* be lost and the wait for the drain to finish would never be woken up.
*/
state = stream->runtime->state;
stream->runtime->state = SNDRV_PCM_STATE_DRAINING;

retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
if (retval) {
pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
stream->runtime->state = state;
wake_up(&stream->runtime->sleep);
return retval;
}
Expand Down Expand Up @@ -1016,6 +1039,7 @@ static int snd_compr_next_track(struct snd_compr_stream *stream)

static int snd_compr_partial_drain(struct snd_compr_stream *stream)
{
snd_pcm_state_t state;
int retval;

switch (stream->runtime->state) {
Expand All @@ -1039,9 +1063,16 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
return -EPERM;

stream->partial_drain = true;

/* See the comment in snd_compr_drain() on the state handling */
state = stream->runtime->state;
stream->runtime->state = SNDRV_PCM_STATE_DRAINING;

retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
if (retval) {
pr_debug("Partial drain returned failure\n");
stream->partial_drain = false;
stream->runtime->state = state;
wake_up(&stream->runtime->sleep);
return retval;
}
Expand Down
29 changes: 27 additions & 2 deletions sound/soc/soc-compress.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
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);
struct snd_pcm_hw_constraints *constrs;
int i;

if (!fe_substream || fe_substream->runtime)
return 0;
Expand All @@ -155,6 +157,23 @@ static int soc_compr_alloc_fe_runtime(struct snd_soc_pcm_runtime *fe, int stream
if (!fe_substream->runtime)
return -ENOMEM;

/*
* snd_pcm_open() initializes the constraints of a PCM runtime to
* 'anything goes'. A zeroed one means an empty mask and a [0, 0]
* interval instead, which the constraint helpers refine against and
* reject, so initialize them the same way the PCM core does.
*
* The hw rules snd_pcm_hw_constraints_init() installs on top are only
* evaluated by snd_pcm_hw_refine(), which never runs for this
* substream, so they are not needed here.
*/
constrs = &fe_substream->runtime->hw_constraints;
for (i = SNDRV_PCM_HW_PARAM_FIRST_MASK; i <= SNDRV_PCM_HW_PARAM_LAST_MASK; i++)
snd_mask_any(constrs_mask(constrs, i));

for (i = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; i <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; i++)
snd_interval_any(constrs_interval(constrs, i));

return 0;
}

Expand Down Expand Up @@ -418,9 +437,15 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
bool fe_first;
int ret;

if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN ||
cmd == SND_COMPR_TRIGGER_DRAIN)
/* FE only triggers */
switch (cmd) {
case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
case SND_COMPR_TRIGGER_NEXT_TRACK:
case SND_COMPR_TRIGGER_DRAIN:
return snd_soc_component_compr_trigger(cstream, cmd);
default:
break;
}

if (fe->dai_link->trigger[stream] == SND_SOC_DPCM_TRIGGER_POST)
fe_first = false;
Expand Down
2 changes: 1 addition & 1 deletion sound/soc/sof/ipc3-dtrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ static int ipc3_dtrace_init(struct snd_sof_dev *sdev)

/* create compressed page table for audio firmware */
ret = snd_sof_create_page_table(sdev->dev, &priv->dmatb,
priv->dmatp.area, priv->dmatb.bytes);
&priv->dmatp, priv->dmatb.bytes);
if (ret < 0)
goto table_err;

Expand Down
53 changes: 50 additions & 3 deletions sound/soc/sof/ipc4-compress.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ static u32 sof_ipc4_compr_calc_min_fragment_size(struct snd_sof_pcm_stream *sps)
return max(SOF_IPC4_COMPR_MAX_PROCESSING_SIZE, host_buffer_estimate);
}

/* The buffer must be described by the page table allocated for the stream */
static u64 sof_ipc4_compr_calc_max_buffer_size(struct snd_sof_pcm_stream *sps)
{
return (u64)SOF_PAGE_TABLE_MAX_PFNS(sps->page_table.bytes) * PAGE_SIZE;
}

static int sof_ipc4_compr_open(struct snd_soc_component *component,
struct snd_compr_stream *cstream)
{
Expand Down Expand Up @@ -94,6 +100,7 @@ static int sof_ipc4_compr_open(struct snd_soc_component *component,
spcm->stream[dir].posn.dai_posn = 0;
spcm->prepared[dir] = false;
spcm->pending_stop[dir] = false;
spcm->compr_started[dir] = false;

return 0;
}
Expand Down Expand Up @@ -122,6 +129,7 @@ static int sof_ipc4_compr_stream_free(struct snd_sof_dev *sdev,

spcm->prepared[dir] = false;
spcm->pending_stop[dir] = false;
spcm->compr_started[dir] = false;
spcm->stream[dir].cstream = NULL;

/* reset the DMA */
Expand Down Expand Up @@ -193,6 +201,8 @@ static int sof_ipc4_compr_get_caps(struct snd_soc_component *component,
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_ipc4_codec_info_data *codec_info = ipc4_data->codec_info;
int dir = cstream->direction;
u64 max_buffer_size;
u64 max_fragments;
struct snd_sof_pcm *spcm;
int i;

Expand Down Expand Up @@ -233,6 +243,14 @@ static int sof_ipc4_compr_get_caps(struct snd_soc_component *component,

caps->min_fragments = SOF_IPC4_COMPR_MIN_FRAGMENTS;
caps->max_fragments = SOF_IPC4_COMPR_MAX_FRAGMENTS;
/*
* A buffer of max_fragments number of max_fragment_size fragments must
* be describable by the page table allocated for the stream
*/
max_buffer_size = sof_ipc4_compr_calc_max_buffer_size(&spcm->stream[dir]);
max_fragments = div_u64(max_buffer_size, caps->max_fragment_size);
if (caps->max_fragments > max_fragments)
caps->max_fragments = max_fragments;

spcm_dbg(spcm, dir,
"num_codecs: %u, fragment_size: %u-%u, fragments: %u-%u\n",
Expand All @@ -248,6 +266,7 @@ static int sof_ipc4_compr_alloc_pages(struct device *dev,
struct snd_compr_stream *cstream)
{
u32 min_fragment_size = sof_ipc4_compr_calc_min_fragment_size(sps);
u64 max_buffer_size = sof_ipc4_compr_calc_max_buffer_size(sps);
struct snd_compr_runtime *crtd = cstream->runtime;
u64 fragments = crtd->buffer_size;
int ret;
Expand All @@ -259,6 +278,12 @@ static int sof_ipc4_compr_alloc_pages(struct device *dev,
return -EINVAL;
}

if (crtd->buffer_size > max_buffer_size) {
dev_err(dev, "%s: Buffer size is too big: %llu (maximum is %llu)\n",
__func__, crtd->buffer_size, max_buffer_size);
return -EINVAL;
}

if (crtd->fragment_size < min_fragment_size) {
dev_err(dev, "%s: Fragment size is too small: %u (minimum is %u)\n",
__func__, crtd->fragment_size, min_fragment_size);
Expand Down Expand Up @@ -376,6 +401,12 @@ static int sof_ipc4_compr_set_params(struct snd_soc_component *component,
/* Use correct format based on the used codec */
switch (params->codec.id) {
case SND_AUDIOCODEC_PCM:
/* The format is provided by user space, it must be validated */
if (snd_pcm_format_width((snd_pcm_format_t)params->codec.format) <= 0) {
spcm_err(spcm, dir, "Invalid PCM format: %u\n",
params->codec.format);
return -EINVAL;
}
snd_mask_set_format(fmt, (snd_pcm_format_t)params->codec.format);
break;
case SND_AUDIOCODEC_VORBIS:
Expand Down Expand Up @@ -545,22 +576,35 @@ static int sof_ipc4_compr_trigger(struct snd_soc_component *component,
return -EINVAL;
}

spcm->pending_stop[dir] = false;

switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
/*
* The host DMA is left running on stop and drain to match the
* firmware, which only pauses the pipeline, and it is only
* stopped during hw_free. A stream restarted after stop or
* drain - which the compress core allows for capture - would
* continue from a position the two sides no longer agree on.
*/
if (spcm->compr_started[dir]) {
spcm_err(spcm, dir, "Stream restart is not supported\n");
return -EINVAL;
}
fallthrough;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
spcm->pending_stop[dir] = false;
trigger_platform = true;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
spcm->pending_stop[dir] = false;
break;
case SND_COMPR_TRIGGER_DRAIN:
case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
spcm->pending_stop[dir] = true;
break;
case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
case SND_COMPR_TRIGGER_NEXT_TRACK:
/* Gapless playback is not supported */
spcm_dbg(spcm, dir, "Unsupported trigger cmd: %d\n", cmd);
return -EOPNOTSUPP;
default:
Expand All @@ -584,6 +628,9 @@ static int sof_ipc4_compr_trigger(struct snd_soc_component *component,
ret);
}

if (!ret && cmd == SNDRV_PCM_TRIGGER_START)
spcm->compr_started[dir] = true;

return ret;
}

Expand Down
32 changes: 9 additions & 23 deletions sound/soc/sof/ipc4-loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -422,45 +422,31 @@ static int sof_ipc4_query_sof_info(struct snd_sof_dev *sdev,
{
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_ipc4_tuple *tuple;
size_t tuple_size;
size_t offset = 0;
int ret = 0;

/*
* No size validation is needed as the sof_info data is coming from
* firmware as part of the firmware config, it is guarantied to be
* correct.
Comment on lines +428 to +430
*/
while (offset < sof_info_size) {
if (sof_info_size - offset < sizeof(*tuple)) {
dev_err(sdev->dev, "Invalid SOF info tuple header at offset %zu\n", offset);
ret = -EINVAL;
goto out;
}

tuple = (struct sof_ipc4_tuple *)((u8 *)sof_info_data + offset);
tuple_size = sizeof(*tuple) + tuple->size;
if (tuple_size < sizeof(*tuple) || tuple_size > sof_info_size - offset) {
dev_err(sdev->dev,
"Invalid SOF info tuple size %u at offset %zu\n",
tuple->size, offset);
ret = -EINVAL;
goto out;
}

switch (tuple->type) {
case SOF_IPC4_SOF_CODEC_INFO:
ipc4_data->codec_info = devm_kmemdup(sdev->dev, tuple->value,
tuple->size, GFP_KERNEL);
if (!ipc4_data->codec_info) {
ret = -ENOMEM;
goto out;
}
if (!ipc4_data->codec_info)
return -ENOMEM;
break;
default:
break;
}

offset += tuple_size;
offset += sizeof(*tuple) + tuple->size;
}

out:
return ret;
return 0;
}

int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
Expand Down
11 changes: 0 additions & 11 deletions sound/soc/sof/ipc4-pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -419,9 +419,6 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
return 0;

spipe = pipeline_list->pipelines[0];
if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private)
return 0;

pipe_widget = spipe->pipe_widget;
pipeline = pipe_widget->private;

Expand Down Expand Up @@ -485,16 +482,12 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
if (state == SOF_IPC4_PIPE_RUNNING || state == SOF_IPC4_PIPE_RESET)
for (i = pipeline_list->count - 1; i >= 0; i--) {
spipe = pipeline_list->pipelines[i];
if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private)
continue;
sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list,
pipe_priority);
}
else
for (i = 0; i < pipeline_list->count; i++) {
spipe = pipeline_list->pipelines[i];
if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private)
continue;
sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list,
pipe_priority);
}
Expand Down Expand Up @@ -535,8 +528,6 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
/* update PAUSED state for all pipelines just triggered */
for (i = 0; i < pipeline_list->count ; i++) {
spipe = pipeline_list->pipelines[i];
if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private)
continue;
sof_ipc4_update_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, cmd, spipe,
trigger_list);
}
Expand Down Expand Up @@ -580,8 +571,6 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
/* update RUNNING/RESET state for all pipelines that were just triggered */
for (i = 0; i < pipeline_list->count; i++) {
spipe = pipeline_list->pipelines[i];
if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private)
continue;
sof_ipc4_update_pipeline_state(sdev, state, cmd, spipe, trigger_list);
}

Expand Down
Loading
Loading