mirror of https://github.com/thesofproject/sof.git
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:
parent
2d5f55ca5f
commit
a1da5bcc8d
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue