1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2021 Intel Corporation. All rights reserved.
4 //
5 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
6 // Author: 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/common.h>
12 #include <sof/drivers/idc.h>
13 #include <sof/ipc/topology.h>
14 #include <sof/ipc/common.h>
15 #include <sof/ipc/msg.h>
16 #include <sof/ipc/driver.h>
17 #include <sof/ipc/schedule.h>
18 #include <sof/lib/alloc.h>
19 #include <sof/lib/cache.h>
20 #include <sof/lib/cpu.h>
21 #include <sof/lib/mailbox.h>
22 #include <sof/list.h>
23 #include <sof/platform.h>
24 #include <sof/sof.h>
25 #include <sof/spinlock.h>
26 #include <ipc/dai.h>
27 #include <ipc/header.h>
28 #include <ipc/stream.h>
29 #include <ipc/topology.h>
30 #include <errno.h>
31 #include <stdbool.h>
32 #include <stddef.h>
33 #include <stdint.h>
34
35 /* create a new component in the pipeline */
buffer_new(const struct sof_ipc_buffer * desc)36 struct comp_buffer *buffer_new(const struct sof_ipc_buffer *desc)
37 {
38 struct comp_buffer *buffer;
39
40 tr_info(&buffer_tr, "buffer new size 0x%x id %d.%d flags 0x%x",
41 desc->size, desc->comp.pipeline_id, desc->comp.id, desc->flags);
42
43 /* allocate buffer */
44 buffer = buffer_alloc(desc->size, desc->caps, PLATFORM_DCACHE_ALIGN);
45 if (buffer) {
46 buffer->id = desc->comp.id;
47 buffer->pipeline_id = desc->comp.pipeline_id;
48 buffer->core = desc->comp.core;
49
50 buffer->stream.underrun_permitted = desc->flags &
51 SOF_BUF_UNDERRUN_PERMITTED;
52 buffer->stream.overrun_permitted = desc->flags &
53 SOF_BUF_OVERRUN_PERMITTED;
54
55 memcpy_s(&buffer->tctx, sizeof(struct tr_ctx),
56 &buffer_tr, sizeof(struct tr_ctx));
57
58 dcache_writeback_invalidate_region(buffer, sizeof(*buffer));
59 }
60
61 return buffer;
62 }
63
ipc_comp_pipe_id(const struct ipc_comp_dev * icd)64 int32_t ipc_comp_pipe_id(const struct ipc_comp_dev *icd)
65 {
66 switch (icd->type) {
67 case COMP_TYPE_COMPONENT:
68 return dev_comp_pipe_id(icd->cd);
69 case COMP_TYPE_BUFFER:
70 return icd->cb->pipeline_id;
71 case COMP_TYPE_PIPELINE:
72 return icd->pipeline->pipeline_id;
73 default:
74 tr_err(&ipc_tr, "Unknown ipc component type %u", icd->type);
75 return -EINVAL;
76 };
77
78 return 0;
79 }
80
81 /* Function overwrites PCM parameters (frame_fmt, buffer_fmt, channels, rate)
82 * with buffer parameters when specific flag is set.
83 */
comp_update_params(uint32_t flag,struct sof_ipc_stream_params * params,struct comp_buffer * buffer)84 static void comp_update_params(uint32_t flag,
85 struct sof_ipc_stream_params *params,
86 struct comp_buffer *buffer)
87 {
88 if (flag & BUFF_PARAMS_FRAME_FMT)
89 params->frame_fmt = buffer->stream.frame_fmt;
90
91 if (flag & BUFF_PARAMS_BUFFER_FMT)
92 params->buffer_fmt = buffer->buffer_fmt;
93
94 if (flag & BUFF_PARAMS_CHANNELS)
95 params->channels = buffer->stream.channels;
96
97 if (flag & BUFF_PARAMS_RATE)
98 params->rate = buffer->stream.rate;
99 }
100
comp_verify_params(struct comp_dev * dev,uint32_t flag,struct sof_ipc_stream_params * params)101 int comp_verify_params(struct comp_dev *dev, uint32_t flag,
102 struct sof_ipc_stream_params *params)
103 {
104 struct list_item *buffer_list;
105 struct list_item *source_list;
106 struct list_item *sink_list;
107 struct list_item *clist;
108 struct list_item *curr;
109 struct comp_buffer *sinkb;
110 struct comp_buffer *buf;
111 int dir = dev->direction;
112 uint32_t flags = 0;
113
114 if (!params) {
115 comp_err(dev, "comp_verify_params(): !params");
116 return -EINVAL;
117 }
118
119 source_list = comp_buffer_list(dev, PPL_DIR_UPSTREAM);
120 sink_list = comp_buffer_list(dev, PPL_DIR_DOWNSTREAM);
121
122 /* searching for endpoint component e.g. HOST, DETECT_TEST, which
123 * has only one sink or one source buffer.
124 */
125 if (list_is_empty(source_list) != list_is_empty(sink_list)) {
126 if (!list_is_empty(source_list))
127 buf = list_first_item(&dev->bsource_list,
128 struct comp_buffer,
129 sink_list);
130 else
131 buf = list_first_item(&dev->bsink_list,
132 struct comp_buffer,
133 source_list);
134
135 buffer_lock(buf, &flags);
136
137 /* update specific pcm parameter with buffer parameter if
138 * specific flag is set.
139 */
140 comp_update_params(flag, params, buf);
141
142 /* overwrite buffer parameters with modified pcm
143 * parameters
144 */
145 buffer_set_params(buf, params, BUFFER_UPDATE_FORCE);
146
147 /* set component period frames */
148 component_set_period_frames(dev, buf->stream.rate);
149
150 buffer_unlock(buf, flags);
151 } else {
152 /* for other components we iterate over all downstream buffers
153 * (for playback) or upstream buffers (for capture).
154 */
155 buffer_list = comp_buffer_list(dev, dir);
156 clist = buffer_list->next;
157
158 while (clist != buffer_list) {
159 curr = clist;
160 buf = buffer_from_list(curr, struct comp_buffer, dir);
161 buffer_lock(buf, &flags);
162 clist = clist->next;
163 comp_update_params(flag, params, buf);
164 buffer_set_params(buf, params, BUFFER_UPDATE_FORCE);
165 buffer_unlock(buf, flags);
166 }
167
168 /* fetch sink buffer in order to calculate period frames */
169 sinkb = list_first_item(&dev->bsink_list, struct comp_buffer,
170 source_list);
171
172 buffer_lock(sinkb, &flags);
173 component_set_period_frames(dev, sinkb->stream.rate);
174 buffer_unlock(sinkb, flags);
175 }
176
177 return 0;
178 }
179
comp_buffer_connect(struct comp_dev * comp,uint32_t comp_core,struct comp_buffer * buffer,uint32_t buffer_core,uint32_t dir)180 int comp_buffer_connect(struct comp_dev *comp, uint32_t comp_core,
181 struct comp_buffer *buffer, uint32_t buffer_core, uint32_t dir)
182 {
183 int ret;
184
185 /* check if it's a connection between cores */
186 if (buffer_core != comp_core) {
187 dcache_invalidate_region(buffer, sizeof(*buffer));
188
189 buffer->inter_core = true;
190
191 if (!comp->is_shared) {
192 comp = comp_make_shared(comp);
193 if (!comp)
194 return -ENOMEM;
195 }
196 }
197
198 ret = pipeline_connect(comp, buffer, dir);
199 dcache_writeback_invalidate_region(buffer, sizeof(*buffer));
200
201 return ret;
202 }
203
ipc_pipeline_complete(struct ipc * ipc,uint32_t comp_id)204 int ipc_pipeline_complete(struct ipc *ipc, uint32_t comp_id)
205 {
206 struct ipc_comp_dev *ipc_pipe;
207 struct ipc_comp_dev *icd;
208 struct pipeline *p;
209 uint32_t pipeline_id;
210 struct ipc_comp_dev *ipc_ppl_source;
211 struct ipc_comp_dev *ipc_ppl_sink;
212 int ret;
213
214 /* check whether pipeline exists */
215 ipc_pipe = ipc_get_comp_by_id(ipc, comp_id);
216 if (!ipc_pipe) {
217 tr_err(&ipc_tr, "ipc: ipc_pipeline_complete looking for pipe component id %d failed",
218 comp_id);
219 return -EINVAL;
220 }
221
222 /* check core */
223 if (!cpu_is_me(ipc_pipe->core))
224 return ipc_process_on_core(ipc_pipe->core, false);
225
226 p = ipc_pipe->pipeline;
227
228 /* find the scheduling component */
229 icd = ipc_get_comp_by_id(ipc, p->sched_id);
230 if (!icd) {
231 tr_err(&ipc_tr, "ipc_pipeline_complete(): cannot find the scheduling component, p->sched_id = %u",
232 p->sched_id);
233 return -EINVAL;
234 }
235
236 if (icd->type != COMP_TYPE_COMPONENT) {
237 tr_err(&ipc_tr, "ipc_pipeline_complete(): icd->type (%d) != COMP_TYPE_COMPONENT for pipeline scheduling component icd->id %d",
238 icd->type, icd->id);
239 return -EINVAL;
240 }
241
242 if (icd->core != ipc_pipe->core) {
243 tr_err(&ipc_tr, "ipc_pipeline_complete(): icd->core (%d) != ipc_pipe->core (%d) for pipeline scheduling component icd->id %d",
244 icd->core, ipc_pipe->core, icd->id);
245 return -EINVAL;
246 }
247
248 p->sched_comp = icd->cd;
249
250 pipeline_id = ipc_pipe->pipeline->pipeline_id;
251
252 tr_dbg(&ipc_tr, "ipc: pipe %d -> complete on comp %d", pipeline_id,
253 comp_id);
254
255 /* get pipeline source component */
256 ipc_ppl_source = ipc_get_ppl_src_comp(ipc, pipeline_id);
257 if (!ipc_ppl_source) {
258 tr_err(&ipc_tr, "ipc: ipc_pipeline_complete looking for pipeline source failed");
259 return -EINVAL;
260 }
261
262 /* get pipeline sink component */
263 ipc_ppl_sink = ipc_get_ppl_sink_comp(ipc, pipeline_id);
264 if (!ipc_ppl_sink) {
265 tr_err(&ipc_tr, "ipc: ipc_pipeline_complete looking for pipeline sink failed");
266 return -EINVAL;
267 }
268
269 ret = pipeline_complete(ipc_pipe->pipeline, ipc_ppl_source->cd,
270 ipc_ppl_sink->cd);
271
272 return ret;
273 }
274
ipc_comp_free(struct ipc * ipc,uint32_t comp_id)275 int ipc_comp_free(struct ipc *ipc, uint32_t comp_id)
276 {
277 struct ipc_comp_dev *icd;
278
279 /* check whether component exists */
280 icd = ipc_get_comp_by_id(ipc, comp_id);
281 if (!icd)
282 return -ENODEV;
283
284 /* check core */
285 if (!cpu_is_me(icd->core))
286 return ipc_process_on_core(icd->core, false);
287
288 /* check state */
289 if (icd->cd->state != COMP_STATE_READY)
290 return -EINVAL;
291
292 /* free component and remove from list */
293 comp_free(icd->cd);
294
295 icd->cd = NULL;
296
297 list_item_del(&icd->list);
298 rfree(icd);
299
300 return 0;
301 }
302