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
4 changes: 4 additions & 0 deletions include/sound/sof/stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,8 @@ struct sof_ipc_stream_posn {
int32_t xrun_size; /**< XRUN size in bytes */
} __packed;

struct sof_ipc_stream_drain {
struct sof_ipc_reply rhdr;
} __packed;

#endif
3 changes: 3 additions & 0 deletions sound/soc/sof/compress.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ static int sof_compr_trigger(struct snd_soc_component *component,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE;
break;
case SNDRV_PCM_TRIGGER_DRAIN:
stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_DRAIN;
break;
Copy link
Member

Choose a reason for hiding this comment

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

you probably need a different behavior for capture and playback.

From the alsa-lib PCM documentation https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html#ga49afc5b8527f30c33fafa476533c9f86

"
For playback wait for all pending frames to be played and then stop the PCM. For capture stop PCM permitting to retrieve residual frames.
"

Copy link
Author

Choose a reason for hiding this comment

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

Yeah, most likely so

default:
dev_err(component->dev, "error: unhandled trigger cmd %d\n", cmd);
break;
Expand Down
32 changes: 32 additions & 0 deletions sound/soc/sof/ipc3.c
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,35 @@ static void ipc3_xrun(struct snd_sof_dev *sdev, u32 msg_id)
#endif
}

static void ipc3_drain(struct snd_sof_dev *sdev, u32 msg_id)
{
struct snd_soc_component *scomp = sdev->component;
struct snd_sof_pcm *spcm;
struct snd_sof_pcm_stream *stream;
struct sof_ipc_stream_drain drain;
int direction;

spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction);
if (!spcm) {
dev_err(sdev->dev,
"error: drain for unknown stream, msg_id %d\n",
msg_id);
return;
}

stream = &spcm->stream[direction];
snd_sof_ipc_msg_data(sdev, stream->substream, &drain, sizeof(drain));

/* since struct sof_ipc_stream drain only contains the reply
* header there's no need for additional checks.
*
* we're assuming that since we're receiving the drain-related
* reply then the drain operation finished successfully.
*/
if (spcm->pcm.compress)
snd_compr_drain_notify(stream->cstream);
Copy link
Member

Choose a reason for hiding this comment

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

And what happens when the drain is finished? Don't we need a TRIGGER_STOP or something?

Choose a reason for hiding this comment

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

Drain can be possible without stop if there are somehow two consecutive streams being fed, you do a partial drain for the first stream before starting the next one (useful for gapless playback I guess? Not 100% sure). I'd guess stop should wait until a drain finishes though.

}

/* stream notifications from firmware */
static void ipc3_stream_message(struct snd_sof_dev *sdev, void *msg_buf)
{
Expand All @@ -912,6 +941,9 @@ static void ipc3_stream_message(struct snd_sof_dev *sdev, void *msg_buf)
case SOF_IPC_STREAM_TRIG_XRUN:
ipc3_xrun(sdev, msg_id);
break;
case SOF_IPC_STREAM_TRIG_DRAIN:
ipc3_drain(sdev, msg_id);
break;
default:
dev_err(sdev->dev, "unhandled stream message %#x\n",
msg_id);
Expand Down