pipeline: xrun: Add XRUN initial handler into pipeline

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>
This commit is contained in:
Liam Girdwood 2017-08-22 22:31:04 +01:00
parent 2d5f55ca5f
commit a1da5bcc8d
5 changed files with 148 additions and 3 deletions

View File

@ -183,6 +183,28 @@ static void disconnect_downstream(struct pipeline *p, struct comp_dev *start,
spin_unlock(&current->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, &current->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, &current->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)

View File

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

View File

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

View File

@ -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 */

View File

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