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@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;