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 <rtos/idc.h>
13 #include <rtos/interrupt.h>
14 #include <sof/ipc/topology.h>
15 #include <sof/ipc/common.h>
16 #include <sof/ipc/msg.h>
17 #include <sof/ipc/driver.h>
18 #include <sof/ipc/schedule.h>
19 #include <rtos/alloc.h>
20 #include <rtos/cache.h>
21 #include <sof/lib/cpu.h>
22 #include <sof/lib/mailbox.h>
23 #include <sof/list.h>
24 #include <sof/platform.h>
25 #include <rtos/sof.h>
26 #include <rtos/spinlock.h>
27 #include <ipc/dai.h>
28 #include <ipc/header.h>
29 #include <ipc/stream.h>
30 #include <ipc/topology.h>
31 #include <errno.h>
32 #include <stdbool.h>
33 #include <stddef.h>
34 #include <stdint.h>
35
36 LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL);
37
38 /* create a new component in the pipeline */
buffer_new(const struct sof_ipc_buffer * desc)39 struct comp_buffer *buffer_new(const struct sof_ipc_buffer *desc)
40 {
41 struct comp_buffer *buffer;
42
43 tr_info(&buffer_tr, "buffer new size 0x%x id %d.%d flags 0x%x",
44 desc->size, desc->comp.pipeline_id, desc->comp.id, desc->flags);
45
46 /* allocate buffer */
47 buffer = buffer_alloc(desc->size, desc->caps, PLATFORM_DCACHE_ALIGN);
48 if (buffer) {
49 buffer->id = desc->comp.id;
50 buffer->pipeline_id = desc->comp.pipeline_id;
51 buffer->core = desc->comp.core;
52
53 buffer->stream.underrun_permitted = desc->flags &
54 SOF_BUF_UNDERRUN_PERMITTED;
55 buffer->stream.overrun_permitted = desc->flags &
56 SOF_BUF_OVERRUN_PERMITTED;
57
58 memcpy_s(&buffer->tctx, sizeof(struct tr_ctx),
59 &buffer_tr, sizeof(struct tr_ctx));
60 }
61
62 return buffer;
63 }
64
ipc_comp_pipe_id(const struct ipc_comp_dev * icd)65 int32_t ipc_comp_pipe_id(const struct ipc_comp_dev *icd)
66 {
67 switch (icd->type) {
68 case COMP_TYPE_COMPONENT:
69 return dev_comp_pipe_id(icd->cd);
70 case COMP_TYPE_BUFFER:
71 return icd->cb->pipeline_id;
72 case COMP_TYPE_PIPELINE:
73 return icd->pipeline->pipeline_id;
74 default:
75 tr_err(&ipc_tr, "Unknown ipc component type %u", icd->type);
76 return -EINVAL;
77 };
78
79 return 0;
80 }
81
82 /* Function overwrites PCM parameters (frame_fmt, buffer_fmt, channels, rate)
83 * with buffer parameters when specific flag is set.
84 */
comp_update_params(uint32_t flag,struct sof_ipc_stream_params * params,struct comp_buffer __sparse_cache * buffer)85 static void comp_update_params(uint32_t flag,
86 struct sof_ipc_stream_params *params,
87 struct comp_buffer __sparse_cache *buffer)
88 {
89 if (flag & BUFF_PARAMS_FRAME_FMT)
90 params->frame_fmt = buffer->stream.frame_fmt;
91
92 if (flag & BUFF_PARAMS_BUFFER_FMT)
93 params->buffer_fmt = buffer->buffer_fmt;
94
95 if (flag & BUFF_PARAMS_CHANNELS)
96 params->channels = buffer->stream.channels;
97
98 if (flag & BUFF_PARAMS_RATE)
99 params->rate = buffer->stream.rate;
100 }
101
comp_verify_params(struct comp_dev * dev,uint32_t flag,struct sof_ipc_stream_params * params)102 int comp_verify_params(struct comp_dev *dev, uint32_t flag,
103 struct sof_ipc_stream_params *params)
104 {
105 struct list_item *buffer_list;
106 struct list_item *source_list;
107 struct list_item *sink_list;
108 struct list_item *clist;
109 struct comp_buffer *sinkb;
110 struct comp_buffer *buf;
111 struct comp_buffer __sparse_cache *buf_c;
112 int dir = dev->direction;
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(sink_list))
127 buf = list_first_item(source_list,
128 struct comp_buffer,
129 sink_list);
130 else
131 buf = list_first_item(sink_list,
132 struct comp_buffer,
133 source_list);
134
135 buf_c = buffer_acquire(buf);
136
137 /* update specific pcm parameter with buffer parameter if
138 * specific flag is set.
139 */
140 comp_update_params(flag, params, buf_c);
141
142 /* overwrite buffer parameters with modified pcm
143 * parameters
144 */
145 buffer_set_params(buf_c, params, BUFFER_UPDATE_FORCE);
146
147 /* set component period frames */
148 component_set_nearest_period_frames(dev, buf_c->stream.rate);
149
150 buffer_release(buf_c);
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
157 list_for_item(clist, buffer_list) {
158 buf = buffer_from_list(clist, dir);
159 buf_c = buffer_acquire(buf);
160 comp_update_params(flag, params, buf_c);
161 buffer_set_params(buf_c, params, BUFFER_UPDATE_FORCE);
162 buffer_release(buf_c);
163 }
164
165 /* fetch sink buffer in order to calculate period frames */
166 sinkb = list_first_item(&dev->bsink_list, struct comp_buffer,
167 source_list);
168
169 buf_c = buffer_acquire(sinkb);
170 component_set_nearest_period_frames(dev, buf_c->stream.rate);
171 buffer_release(buf_c);
172 }
173
174 return 0;
175 }
176
comp_buffer_connect(struct comp_dev * comp,uint32_t comp_core,struct comp_buffer * buffer,uint32_t dir)177 int comp_buffer_connect(struct comp_dev *comp, uint32_t comp_core,
178 struct comp_buffer *buffer, uint32_t dir)
179 {
180 /* check if it's a connection between cores */
181 if (buffer->core != comp_core) {
182 /* set the buffer as a coherent object */
183 coherent_shared_thread(buffer, c);
184
185 if (!comp->is_shared)
186 comp_make_shared(comp);
187 }
188
189 return pipeline_connect(comp, buffer, dir);
190 }
191
ipc_pipeline_complete(struct ipc * ipc,uint32_t comp_id)192 int ipc_pipeline_complete(struct ipc *ipc, uint32_t comp_id)
193 {
194 struct ipc_comp_dev *ipc_pipe;
195 struct ipc_comp_dev *icd;
196 struct pipeline *p;
197 uint32_t pipeline_id;
198 struct ipc_comp_dev *ipc_ppl_source;
199 struct ipc_comp_dev *ipc_ppl_sink;
200
201 /* check whether pipeline exists */
202 ipc_pipe = ipc_get_comp_by_id(ipc, comp_id);
203 if (!ipc_pipe) {
204 tr_err(&ipc_tr, "ipc: ipc_pipeline_complete looking for pipe component id %d failed",
205 comp_id);
206 return -EINVAL;
207 }
208
209 /* check core */
210 if (!cpu_is_me(ipc_pipe->core))
211 return ipc_process_on_core(ipc_pipe->core, false);
212
213 p = ipc_pipe->pipeline;
214
215 /* get pipeline source component */
216 ipc_ppl_source = ipc_get_ppl_src_comp(ipc, p->pipeline_id);
217 if (!ipc_ppl_source) {
218 tr_err(&ipc_tr, "ipc: ipc_pipeline_complete looking for pipeline source failed");
219 return -EINVAL;
220 }
221
222 /* get pipeline sink component */
223 ipc_ppl_sink = ipc_get_ppl_sink_comp(ipc, p->pipeline_id);
224 if (!ipc_ppl_sink) {
225 tr_err(&ipc_tr, "ipc: ipc_pipeline_complete looking for pipeline sink failed");
226 return -EINVAL;
227 }
228
229 /* find the scheduling component */
230 icd = ipc_get_comp_by_id(ipc, p->sched_id);
231 if (!icd) {
232 tr_warn(&ipc_tr, "ipc_pipeline_complete(): no scheduling component specified, use comp %d",
233 ipc_ppl_sink->id);
234
235 icd = ipc_ppl_sink;
236 }
237
238 if (icd->type != COMP_TYPE_COMPONENT) {
239 tr_err(&ipc_tr, "ipc_pipeline_complete(): icd->type (%d) != COMP_TYPE_COMPONENT for pipeline scheduling component icd->id %d",
240 icd->type, icd->id);
241 return -EINVAL;
242 }
243
244 if (icd->core != ipc_pipe->core) {
245 tr_err(&ipc_tr, "ipc_pipeline_complete(): icd->core (%d) != ipc_pipe->core (%d) for pipeline scheduling component icd->id %d",
246 icd->core, ipc_pipe->core, icd->id);
247 return -EINVAL;
248 }
249
250 p->sched_comp = icd->cd;
251
252 pipeline_id = ipc_pipe->pipeline->pipeline_id;
253
254 tr_dbg(&ipc_tr, "ipc: pipe %d -> complete on comp %d", pipeline_id,
255 comp_id);
256
257 return pipeline_complete(ipc_pipe->pipeline, ipc_ppl_source->cd,
258 ipc_ppl_sink->cd);
259 }
260
ipc_comp_free(struct ipc * ipc,uint32_t comp_id)261 int ipc_comp_free(struct ipc *ipc, uint32_t comp_id)
262 {
263 struct ipc_comp_dev *icd;
264 struct list_item *clist, *tmp;
265 uint32_t flags;
266
267 /* check whether component exists */
268 icd = ipc_get_comp_by_id(ipc, comp_id);
269 if (!icd) {
270 tr_err(&ipc_tr, "ipc_comp_free(): comp id: %d is not found",
271 comp_id);
272 return -ENODEV;
273 }
274
275 /* check type */
276 if (icd->type != COMP_TYPE_COMPONENT) {
277 tr_err(&ipc_tr, "ipc_comp_free(): comp id: %d is not a COMPONENT",
278 comp_id);
279 return -EINVAL;
280 }
281
282 /* check core */
283 if (!cpu_is_me(icd->core))
284 return ipc_process_on_core(icd->core, false);
285
286 /* check state */
287 if (icd->cd->state != COMP_STATE_READY) {
288 tr_err(&ipc_tr, "ipc_comp_free(): comp id: %d state is %d cannot be freed",
289 comp_id, icd->cd->state);
290 return -EINVAL;
291 }
292
293 irq_local_disable(flags);
294 list_for_item_safe(clist, tmp, &icd->cd->bsource_list) {
295 struct comp_buffer *buffer = container_of(clist, struct comp_buffer, sink_list);
296 struct comp_buffer __sparse_cache *buffer_c = buffer_acquire(buffer);
297
298 buffer_c->sink = NULL;
299 buffer_release(buffer_c);
300 /* Also if it isn't shared - we are about to modify uncached data */
301 dcache_writeback_invalidate_region(uncache_to_cache(buffer),
302 sizeof(*buffer));
303 /* This breaks the list, but we anyway delete all buffers */
304 list_init(clist);
305 }
306
307 list_for_item_safe(clist, tmp, &icd->cd->bsink_list) {
308 struct comp_buffer *buffer = container_of(clist, struct comp_buffer, source_list);
309 struct comp_buffer __sparse_cache *buffer_c = buffer_acquire(buffer);
310
311 buffer_c->source = NULL;
312 buffer_release(buffer_c);
313 /* Also if it isn't shared - we are about to modify uncached data */
314 dcache_writeback_invalidate_region(uncache_to_cache(buffer),
315 sizeof(*buffer));
316 /* This breaks the list, but we anyway delete all buffers */
317 list_init(clist);
318 }
319 irq_local_enable(flags);
320
321 /* free component and remove from list */
322 comp_free(icd->cd);
323
324 icd->cd = NULL;
325
326 list_item_del(&icd->list);
327 rfree(icd);
328
329 return 0;
330 }
331