[Sound-open-firmware] [PATCH] pipeline: xrun: Add XRUN initial handler into pipeline
Liam Girdwood
liam.r.girdwood at linux.intel.com
Tue Aug 22 23:35:05 CEST 2017
Add an XRUN handler into the pipeline to report bag overrun and underrun
from component buffers to the host. An XRUN on a component will now cause
a IPC XRUN message to each PCM interface source/sink to the component.
Signed-off-by: Liam Girdwood <liam.r.girdwood at linux.intel.com>
---
src/audio/pipeline.c | 116 +++++++++++++++++++++++++++++++++++++-
src/include/reef/audio/pipeline.h | 6 ++
src/include/reef/ipc.h | 3 +
src/include/uapi/ipc.h | 12 +++-
src/ipc/intel-ipc.c | 14 +++++
5 files changed, 148 insertions(+), 3 deletions(-)
diff --git a/src/audio/pipeline.c b/src/audio/pipeline.c
index 282bec5..f213dc4 100644
--- a/src/audio/pipeline.c
+++ b/src/audio/pipeline.c
@@ -183,6 +183,28 @@ static void disconnect_downstream(struct pipeline *p, struct comp_dev *start,
spin_unlock(¤t->lock);
}
+/* update pipeline state based on cmd */
+static void pipeline_cmd_update(struct pipeline *p, int cmd)
+{
+ switch (cmd) {
+ case COMP_CMD_PAUSE:
+ break;
+ case COMP_CMD_STOP:
+ break;
+ case COMP_CMD_RELEASE:
+ p->xrun_bytes = 0;
+ break;
+ case COMP_CMD_START:
+ p->xrun_bytes = 0;
+ break;
+ case COMP_CMD_SUSPEND:
+ break;
+ case COMP_CMD_RESUME:
+ p->xrun_bytes = 0;
+ break;
+ }
+}
+
/* create new pipeline - returns pipeline id or negative error */
struct pipeline *pipeline_new(struct sof_ipc_pipe_new *pipe_desc,
struct comp_dev *cd)
@@ -311,8 +333,10 @@ static int component_op_downstream(struct op_data *op_data,
err = comp_params(current);
break;
case COMP_OPS_CMD:
- /* send command to the component */
+ /* send command to the component and update pipeline state */
err = comp_cmd(current, op_data->cmd, op_data->cmd_data);
+ if (err == 0)
+ pipeline_cmd_update(current->pipeline, op_data->cmd);
break;
case COMP_OPS_PREPARE:
/* prepare the component */
@@ -383,8 +407,10 @@ static int component_op_upstream(struct op_data *op_data,
err = comp_params(current);
break;
case COMP_OPS_CMD:
- /* send command to the component */
+ /* send command to the component and update pipeline state */
err = comp_cmd(current, op_data->cmd, op_data->cmd_data);
+ if (err == 0)
+ pipeline_cmd_update(current->pipeline, op_data->cmd);
break;
case COMP_OPS_PREPARE:
/* prepare the component */
@@ -818,6 +844,92 @@ void pipeline_get_timestamp(struct pipeline *p, struct comp_dev *host,
}
}
+static void xrun(struct comp_dev *dev, void *data)
+{
+ struct sof_ipc_stream_posn *posn = data;
+
+ /* get host timestamps */
+ platform_host_timestamp(dev, posn);
+
+ /* send XRUN to host */
+ ipc_stream_send_xrun(dev, posn);
+}
+
+
+/* travel down stream from start and run func for each component of type */
+static void pipeline_for_each_downstream(struct pipeline *p,
+ enum sof_comp_type type, struct comp_dev *current,
+ void (*func)(struct comp_dev *, void *), void *data)
+{
+ struct list_item *clist;
+
+ if (current->comp.type == type)
+ func(current, data);
+
+ /* travel downstream to sink end point(s) */
+ list_for_item(clist, ¤t->bsink_list) {
+ struct comp_buffer *buffer;
+
+ buffer = container_of(clist, struct comp_buffer, source_list);
+
+ /* dont go downstream if this component is not connected */
+ if (!buffer->connected)
+ continue;
+
+ /* continue downstream */
+ pipeline_for_each_downstream(p, type, buffer->sink,
+ func, data);
+ }
+}
+
+/* travel up stream from start and run func for each component of type */
+static void pipeline_for_each_upstream(struct pipeline *p,
+ enum sof_comp_type type, struct comp_dev *current,
+ void (*func)(struct comp_dev *, void *), void *data)
+{
+ struct list_item *clist;
+
+ if (current->comp.type == type)
+ func(current, data);
+
+ /* travel upstream to sink end point(s) */
+ list_for_item(clist, ¤t->bsource_list) {
+ struct comp_buffer *buffer;
+
+ buffer = container_of(clist, struct comp_buffer, sink_list);
+
+ /* dont go downstream if this component is not connected */
+ if (!buffer->connected)
+ continue;
+
+ /* continue downstream */
+ pipeline_for_each_upstream(p, type, buffer->source,
+ func, data);
+ }
+}
+
+/*
+ * Send an XRUN to each host for this component.
+ */
+void pipeline_xrun(struct pipeline *p, struct comp_dev *dev,
+ int32_t bytes)
+{
+ struct sof_ipc_stream_posn posn;
+
+ /* dont flood host */
+ if (p->xrun_bytes)
+ return;
+
+ memset(&posn, 0, sizeof(posn));
+ p->xrun_bytes = posn.xrun_size = bytes;
+ posn.xrun_comp_id = dev->comp.id;
+
+ if (dev->params.direction == SOF_IPC_STREAM_PLAYBACK) {
+ pipeline_for_each_upstream(p, SOF_COMP_HOST, dev, xrun, &posn);
+ } else {
+ pipeline_for_each_downstream(p, SOF_COMP_HOST, dev, xrun, &posn);
+ }
+}
/* notify pipeline that this component requires buffers emptied/filled */
void pipeline_schedule_copy(struct pipeline *p, struct comp_dev *dev)
diff --git a/src/include/reef/audio/pipeline.h b/src/include/reef/audio/pipeline.h
index 17e8a34..ad01dfd 100644
--- a/src/include/reef/audio/pipeline.h
+++ b/src/include/reef/audio/pipeline.h
@@ -57,6 +57,9 @@ struct pipeline {
spinlock_t lock;
struct sof_ipc_pipe_new ipc_pipe;
+ /* runtime status */
+ int32_t xrun_bytes; /* last xrun length */
+
/* lists */
struct list_item comp_list; /* list of components */
struct list_item buffer_list; /* list of buffers */
@@ -117,4 +120,7 @@ void pipeline_get_timestamp(struct pipeline *p, struct comp_dev *host_dev,
void pipeline_schedule(void *arg);
+/* notify host that we have XRUN */
+void pipeline_xrun(struct pipeline *p, struct comp_dev *dev, int32_t bytes);
+
#endif
diff --git a/src/include/reef/ipc.h b/src/include/reef/ipc.h
index 253f4f4..3eb94d8 100644
--- a/src/include/reef/ipc.h
+++ b/src/include/reef/ipc.h
@@ -116,6 +116,9 @@ int ipc_process_msg_queue(void);
int ipc_stream_send_position(struct comp_dev *cdev,
struct sof_ipc_stream_posn *posn);
+int ipc_stream_send_xrun(struct comp_dev *cdev,
+ struct sof_ipc_stream_posn *posn);
+
int ipc_queue_host_message(struct ipc *ipc, uint32_t header,
void *tx_data, size_t tx_bytes, void *rx_data,
size_t rx_bytes, void (*cb)(void*, void*), void *cb_data);
diff --git a/src/include/uapi/ipc.h b/src/include/uapi/ipc.h
index ddca551..ae212be 100644
--- a/src/include/uapi/ipc.h
+++ b/src/include/uapi/ipc.h
@@ -412,14 +412,17 @@ struct sof_ipc_stream {
struct sof_ipc_stream_posn {
struct sof_ipc_reply rhdr;
- uint32_t comp_id;
+ uint32_t comp_id; /* host component ID */
uint32_t flags; /* SOF_TIME_ */
uint32_t wallclock_hz; /* frequency of wallclock in Hz */
uint32_t timestamp_ns; /* resolution of timestamp in ns */
uint64_t host_posn; /* host DMA position in bytes */
uint64_t dai_posn; /* DAI DMA position in bytes */
+ uint64_t comp_posn; /* comp position in bytes */
uint64_t wallclock; /* audio wall clock */
uint64_t timestamp; /* system time stamp */
+ uint32_t xrun_comp_id; /* comp ID of XRUN component */
+ int32_t xrun_size; /* XRUN size in bytes */
} __attribute__((packed));
/*
@@ -467,6 +470,11 @@ enum sof_comp_type {
SOF_COMP_EQ_FIR,
};
+/* XRUN action for component */
+#define SOF_XRUN_STOP 1 /* stop stream */
+#define SOF_XRUN_UNDER_ZERO 2 /* send 0s to sink */
+#define SOF_XRUN_OVER_NULL 4 /* send data to NULL */
+
/* create new generic component - SOF_IPC_TPLG_COMP_NEW */
struct sof_ipc_comp {
struct sof_ipc_hdr hdr;
@@ -492,6 +500,7 @@ struct sof_ipc_comp_config {
uint32_t periods_source; /* 0 means variable */
uint32_t preload_count; /* how many periods to preload */
enum sof_ipc_frame frame_fmt;
+ uint32_t xrun_action;
} __attribute__((packed));
/* generic host component */
@@ -638,6 +647,7 @@ struct sof_ipc_pipe_new {
uint32_t priority; /* priority level 0 (low) to 10 (max) */
uint32_t mips; /* worst case instruction count per period */
uint32_t frames_per_sched; /* output frames of pipeline, 0 is variable */
+ uint32_t xrun_limit_usecs; /* report xruns greater than limit */
} __attribute__((packed));
/* pipeline construction complete - SOF_IPC_TPLG_PIPE_COMPLETE */
diff --git a/src/ipc/intel-ipc.c b/src/ipc/intel-ipc.c
index 681bf64..db48ef0 100644
--- a/src/ipc/intel-ipc.c
+++ b/src/ipc/intel-ipc.c
@@ -328,6 +328,20 @@ int ipc_stream_send_position(struct comp_dev *cdev,
NULL, 0, NULL, NULL);
}
+/* send stream position TODO: send compound message */
+int ipc_stream_send_xrun(struct comp_dev *cdev,
+ struct sof_ipc_stream_posn *posn)
+{
+ uint32_t header;
+
+ header = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_TRIG_XRUN;
+ posn->rhdr.hdr.cmd = header;
+ posn->rhdr.hdr.size = sizeof(*posn);
+
+ return ipc_queue_host_message(_ipc, header, posn, sizeof(*posn),
+ NULL, 0, NULL, NULL);
+}
+
static int ipc_stream_trigger(uint32_t header)
{
struct ipc_comp_dev *pcm_dev;
--
2.11.0
More information about the Sound-open-firmware
mailing list