1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2016 Intel Corporation. All rights reserved.
4 //
5 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
6 // Keyon Jie <yang.jie@linux.intel.com>
7
8 #include <sof/audio/buffer.h>
9 #include <sof/audio/component_ext.h>
10 #include <sof/audio/pipeline.h>
11 #include <sof/ipc/msg.h>
12 #include <sof/list.h>
13 #include <sof/spinlock.h>
14 #include <sof/string.h>
15 #include <ipc/header.h>
16 #include <ipc/stream.h>
17 #include <ipc/topology.h>
18 #include <errno.h>
19 #include <stdbool.h>
20 #include <stddef.h>
21 #include <stdint.h>
22
23 /*
24 * This flag disables firmware-side xrun recovery.
25 * It should remain enabled in the situation when the
26 * recovery is delegated to the outside of firmware.
27 */
28 #define NO_XRUN_RECOVERY 1
29
pipeline_comp_xrun(struct comp_dev * current,struct comp_buffer * calling_buf,struct pipeline_walk_context * ctx,int dir)30 static int pipeline_comp_xrun(struct comp_dev *current,
31 struct comp_buffer *calling_buf,
32 struct pipeline_walk_context *ctx, int dir)
33 {
34 struct pipeline_data *ppl_data = ctx->comp_data;
35
36 if (dev_comp_type(current) == SOF_COMP_HOST) {
37 /* get host timestamps */
38 platform_host_timestamp(current, ppl_data->posn);
39
40 /* send XRUN to host */
41 mailbox_stream_write(ppl_data->p->posn_offset, ppl_data->posn,
42 sizeof(*ppl_data->posn));
43 ipc_msg_send(ppl_data->p->msg, ppl_data->posn, true);
44 }
45
46 return pipeline_for_each_comp(current, ctx, dir);
47 }
48
49 #if NO_XRUN_RECOVERY
50 /* recover the pipeline from a XRUN condition */
pipeline_xrun_recover(struct pipeline * p)51 int pipeline_xrun_recover(struct pipeline *p)
52 {
53 return -EINVAL;
54 }
55
56 #else
57 /* recover the pipeline from a XRUN condition */
pipeline_xrun_recover(struct pipeline * p)58 int pipeline_xrun_recover(struct pipeline *p)
59 {
60 int ret;
61
62 pipe_err(p, "pipeline_xrun_recover()");
63
64 /* prepare the pipeline */
65 ret = pipeline_prepare(p, p->source_comp);
66 if (ret < 0) {
67 pipe_err(p, "pipeline_xrun_recover(): pipeline_prepare() failed, ret = %d",
68 ret);
69 return ret;
70 }
71
72 /* reset xrun status as we already in prepared */
73 p->xrun_bytes = 0;
74
75 /* restart pipeline comps */
76 ret = pipeline_trigger(p, p->source_comp, COMP_TRIGGER_START);
77 if (ret < 0) {
78 pipe_err(p, "pipeline_xrun_recover(): pipeline_trigger() failed, ret = %d",
79 ret);
80 return ret;
81 }
82
83 return 0;
84 }
85 #endif
86
pipeline_xrun_set_limit(struct pipeline * p,uint32_t xrun_limit_usecs)87 int pipeline_xrun_set_limit(struct pipeline *p, uint32_t xrun_limit_usecs)
88 {
89 /* TODO: these could be validated against min/max permissible values */
90 p->xrun_limit_usecs = xrun_limit_usecs;
91 return 0;
92 }
93
94 /*
95 * trigger handler for pipelines in xrun, used for recovery from host only.
96 * return values:
97 * 0 -- success, further trigger in caller needed.
98 * PPL_STATUS_PATH_STOP -- done, no more further trigger needed.
99 * minus -- failed, caller should return failure.
100 */
pipeline_xrun_handle_trigger(struct pipeline * p,int cmd)101 int pipeline_xrun_handle_trigger(struct pipeline *p, int cmd)
102 {
103 int ret = 0;
104
105 /* it is expected in paused status for xrun pipeline */
106 if (!p->xrun_bytes || p->status != COMP_STATE_PAUSED)
107 return 0;
108
109 /* in xrun, handle start/stop trigger */
110 switch (cmd) {
111 case COMP_TRIGGER_START:
112 /* in xrun, prepare before trigger start needed */
113 pipe_info(p, "in xrun, prepare it first");
114 /* prepare the pipeline */
115 ret = pipeline_prepare(p, p->source_comp);
116 if (ret < 0) {
117 pipe_err(p, "prepare: ret = %d", ret);
118 return ret;
119 }
120 /* now ready for start, clear xrun_bytes */
121 p->xrun_bytes = 0;
122 break;
123 case COMP_TRIGGER_STOP:
124 /* in xrun, suppose pipeline is already stopped, ignore it */
125 pipe_info(p, "already stopped in xrun");
126 /* no more further trigger stop needed */
127 ret = PPL_STATUS_PATH_STOP;
128 break;
129 }
130
131 return ret;
132 }
133
134 /* Send an XRUN to each host for this component. */
pipeline_xrun(struct pipeline * p,struct comp_dev * dev,int32_t bytes)135 void pipeline_xrun(struct pipeline *p, struct comp_dev *dev,
136 int32_t bytes)
137 {
138 struct pipeline_data data;
139 struct pipeline_walk_context walk_ctx = {
140 .comp_func = pipeline_comp_xrun,
141 .comp_data = &data,
142 .skip_incomplete = true,
143 };
144 struct sof_ipc_stream_posn posn;
145 int ret;
146
147 /* don't flood host */
148 if (p->xrun_bytes)
149 return;
150
151 /* only send when we are running */
152 if (dev->state != COMP_STATE_ACTIVE)
153 return;
154
155 /* notify all pipeline comps we are in XRUN, and stop copying */
156 ret = pipeline_trigger(p, p->source_comp, COMP_TRIGGER_XRUN);
157 if (ret < 0)
158 pipe_err(p, "pipeline_xrun(): Pipelines notification about XRUN failed, ret = %d",
159 ret);
160
161 memset(&posn, 0, sizeof(posn));
162 ipc_build_stream_posn(&posn, SOF_IPC_STREAM_TRIG_XRUN,
163 dev_comp_id(dev));
164 p->xrun_bytes = bytes;
165 posn.xrun_size = bytes;
166 posn.xrun_comp_id = dev_comp_id(dev);
167 data.posn = &posn;
168 data.p = p;
169
170 walk_ctx.comp_func(dev, NULL, &walk_ctx, dev->direction);
171 }
172