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: Rander Wang <rander.wang@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/ipc/topology.h>
13 #include <sof/ipc/common.h>
14 #include <sof/ipc/msg.h>
15 #include <sof/lib/mailbox.h>
16 #include <sof/list.h>
17 #include <sof/platform.h>
18 #include <sof/sof.h>
19 #include <sof/spinlock.h>
20 #include <rimage/cavs/cavs_ext_manifest.h>
21 #include <rimage/sof/user/manifest.h>
22 #include <ipc4/base-config.h>
23 #include <ipc4/header.h>
24 #include <ipc4/pipeline.h>
25 #include <ipc4/module.h>
26 #include <ipc4/error_status.h>
27 #include <errno.h>
28 #include <stdbool.h>
29 #include <stddef.h>
30 #include <stdint.h>
31
32 #define IPC4_MOD_ID(x) ((x) >> 16)
33
34 extern struct tr_ctx comp_tr;
35
ipc_build_stream_posn(struct sof_ipc_stream_posn * posn,uint32_t type,uint32_t id)36 void ipc_build_stream_posn(struct sof_ipc_stream_posn *posn, uint32_t type,
37 uint32_t id)
38 {
39 }
40
ipc_build_comp_event(struct sof_ipc_comp_event * event,uint32_t type,uint32_t id)41 void ipc_build_comp_event(struct sof_ipc_comp_event *event, uint32_t type,
42 uint32_t id)
43 {
44 }
45
ipc_build_trace_posn(struct sof_ipc_dma_trace_posn * posn)46 void ipc_build_trace_posn(struct sof_ipc_dma_trace_posn *posn)
47 {
48 }
49
comp_new(struct sof_ipc_comp * comp)50 struct comp_dev *comp_new(struct sof_ipc_comp *comp)
51 {
52 struct comp_ipc_config ipc_config;
53 const struct comp_driver *drv;
54 struct comp_dev *dev;
55
56 drv = ipc4_get_comp_drv(IPC4_MOD_ID(comp->id));
57 if (!drv)
58 return NULL;
59
60 if (ipc4_get_comp_dev(comp->id)) {
61 tr_err(&ipc_tr, "comp %d exists", comp->id);
62 return NULL;
63 }
64
65 if (comp->core >= CONFIG_CORE_COUNT) {
66 tr_err(&ipc_tr, "ipc: comp->core = %u", comp->core);
67 return NULL;
68 }
69
70 memset(&ipc_config, 0, sizeof(ipc_config));
71 ipc_config.id = comp->id;
72 ipc_config.pipeline_id = comp->pipeline_id;
73 ipc_config.core = comp->core;
74
75 dev = drv->ops.create(drv, &ipc_config, (void *)MAILBOX_HOSTBOX_BASE);
76 if (!dev)
77 return NULL;
78
79 list_init(&dev->bsource_list);
80 list_init(&dev->bsink_list);
81
82 ipc4_add_comp_dev(dev);
83
84 return dev;
85 }
86
ipc_pipeline_new(struct ipc * ipc,ipc_pipe_new * _pipe_desc)87 int ipc_pipeline_new(struct ipc *ipc, ipc_pipe_new *_pipe_desc)
88 {
89 struct ipc4_pipeline_create *pipe_desc = ipc_from_pipe_new(_pipe_desc);
90 struct ipc_comp_dev *ipc_pipe;
91 struct pipeline *pipe;
92
93 tr_dbg(&ipc_tr, "ipc: pipeline id = %u", (uint32_t)pipe_desc->header.r.instance_id);
94
95 /* check whether pipeline id is already taken or in use */
96 ipc_pipe = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_PIPELINE,
97 pipe_desc->header.r.instance_id);
98 if (ipc_pipe) {
99 tr_err(&ipc_tr, "ipc: pipeline id is already taken, pipe_desc->instance_id = %u",
100 (uint32_t)pipe_desc->header.r.instance_id);
101 return IPC4_INVALID_RESOURCE_ID;
102 }
103
104 /* create the pipeline */
105 pipe = pipeline_new(pipe_desc->header.r.instance_id,
106 pipe_desc->header.r.ppl_priority, 0);
107 if (!pipe) {
108 tr_err(&ipc_tr, "ipc: pipeline_new() failed");
109 return IPC4_OUT_OF_MEMORY;
110 }
111
112 pipe->time_domain = SOF_TIME_DOMAIN_TIMER;
113 /* 1ms
114 * TODO: add DP scheduler support. Now only the
115 * LL scheduler tasks is supported.
116 */
117 pipe->period = 1000;
118
119 /* allocate the IPC pipeline container */
120 ipc_pipe = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM,
121 sizeof(struct ipc_comp_dev));
122 if (!ipc_pipe) {
123 pipeline_free(pipe);
124 return IPC4_OUT_OF_MEMORY;
125 }
126
127 ipc_pipe->pipeline = pipe;
128 ipc_pipe->type = COMP_TYPE_PIPELINE;
129 ipc_pipe->id = pipe_desc->header.r.instance_id;
130
131 /* add new pipeline to the list */
132 list_item_append(&ipc_pipe->list, &ipc->comp_list);
133
134 return 0;
135 }
136
ipc_pipeline_module_free(uint32_t pipeline_id)137 static int ipc_pipeline_module_free(uint32_t pipeline_id)
138 {
139 struct ipc *ipc = ipc_get();
140 struct ipc_comp_dev *icd;
141 int ret;
142
143 icd = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_COMPONENT, pipeline_id);
144 while (icd) {
145 ret = ipc_comp_free(ipc, icd->id);
146 if (ret)
147 return ret;
148
149 icd = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_COMPONENT, pipeline_id);
150 }
151
152 return IPC4_SUCCESS;
153 }
154
ipc_pipeline_free(struct ipc * ipc,uint32_t comp_id)155 int ipc_pipeline_free(struct ipc *ipc, uint32_t comp_id)
156 {
157 struct ipc_comp_dev *ipc_pipe;
158 int ret;
159
160 /* check whether pipeline exists */
161 ipc_pipe = ipc_get_comp_by_id(ipc, comp_id);
162 if (!ipc_pipe)
163 return -ENODEV;
164
165 /* check core */
166 if (!cpu_is_me(ipc_pipe->core))
167 return ipc_process_on_core(ipc_pipe->core, false);
168
169 ret = ipc_pipeline_module_free(ipc_pipe->pipeline->pipeline_id);
170 if (ret) {
171 tr_err(&ipc_tr, "ipc_pipeline_free(): module free () failed");
172 return ret;
173 }
174
175 /* free buffer and remove from list */
176 ret = pipeline_free(ipc_pipe->pipeline);
177 if (ret < 0) {
178 tr_err(&ipc_tr, "ipc_pipeline_free(): pipeline_free() failed");
179 return IPC4_INVALID_RESOURCE_STATE;
180 }
181
182 ipc_pipe->pipeline = NULL;
183 list_item_del(&ipc_pipe->list);
184 rfree(ipc_pipe);
185
186 return IPC4_SUCCESS;
187 }
188
ipc4_create_buffer(struct comp_dev * src,struct comp_dev * sink,uint32_t src_queue,uint32_t dst_queue)189 static struct comp_buffer *ipc4_create_buffer(struct comp_dev *src, struct comp_dev *sink,
190 uint32_t src_queue, uint32_t dst_queue)
191 {
192 struct ipc4_base_module_cfg *src_cfg, *sink_cfg;
193 struct comp_buffer *buffer = NULL;
194 struct sof_ipc_buffer ipc_buf;
195 int buf_size;
196
197 src_cfg = (struct ipc4_base_module_cfg *)comp_get_drvdata(src);
198 sink_cfg = (struct ipc4_base_module_cfg *)comp_get_drvdata(sink);
199
200 buf_size = MAX(src_cfg->obs, sink_cfg->ibs);
201 memset(&ipc_buf, 0, sizeof(ipc_buf));
202 ipc_buf.size = buf_size;
203 ipc_buf.comp.id = IPC4_COMP_ID(src_queue, dst_queue);
204 ipc_buf.comp.pipeline_id = src->ipc_config.pipeline_id;
205 ipc_buf.comp.core = src->ipc_config.core;
206 buffer = buffer_new(&ipc_buf);
207
208 return buffer;
209 }
210
ipc_comp_connect(struct ipc * ipc,ipc_pipe_comp_connect * _connect)211 int ipc_comp_connect(struct ipc *ipc, ipc_pipe_comp_connect *_connect)
212 {
213 struct ipc4_module_bind_unbind *bu;
214 struct comp_buffer *buffer = NULL;
215 struct comp_dev *source;
216 struct comp_dev *sink;
217 int src_id, sink_id;
218 int ret;
219
220 bu = (struct ipc4_module_bind_unbind *)_connect;
221 src_id = IPC4_COMP_ID(bu->header.r.module_id, bu->header.r.instance_id);
222 sink_id = IPC4_COMP_ID(bu->data.r.dst_module_id, bu->data.r.dst_instance_id);
223 source = ipc4_get_comp_dev(src_id);
224 sink = ipc4_get_comp_dev(sink_id);
225
226 if (!source || !sink) {
227 tr_err(&ipc_tr, "failed to find src %x, or dst %x", src_id, sink_id);
228 return IPC4_INVALID_RESOURCE_ID;
229 }
230
231 buffer = ipc4_create_buffer(source, sink, bu->data.r.src_queue,
232 bu->data.r.dst_queue);
233 if (!buffer) {
234 tr_err(&ipc_tr, "failed to allocate buffer to bind %d to %d", src_id, sink_id);
235 return IPC4_OUT_OF_MEMORY;
236 }
237
238 ret = comp_buffer_connect(source, source->ipc_config.core, buffer, buffer->core,
239 PPL_CONN_DIR_COMP_TO_BUFFER);
240 if (ret < 0) {
241 tr_err(&ipc_tr, "failed to connect src %d to internal buffer", src_id);
242 goto err;
243 }
244
245 ret = comp_buffer_connect(sink, sink->ipc_config.core, buffer, buffer->core,
246 PPL_CONN_DIR_BUFFER_TO_COMP);
247 if (ret < 0) {
248 tr_err(&ipc_tr, "failed to connect internal buffer to sink %d", sink_id);
249 goto err;
250 }
251
252 ret = comp_bind(source, bu);
253 if (ret < 0)
254 return IPC4_INVALID_RESOURCE_ID;
255
256 ret = comp_bind(sink, bu);
257 if (ret < 0)
258 return IPC4_INVALID_RESOURCE_ID;
259
260 return 0;
261
262 err:
263 buffer_free(buffer);
264 return IPC4_INVALID_RESOURCE_STATE;
265 }
266
267 /* when both module instances are parts of the same pipeline Unbind IPC would
268 * be ignored by FW since FW does not support changing internal topology of pipeline
269 * during run-time. The only way to change pipeline topology is to delete the whole
270 * pipeline and create it in modified form.
271 */
ipc_comp_disconnect(struct ipc * ipc,ipc_pipe_comp_connect * _connect)272 int ipc_comp_disconnect(struct ipc *ipc, ipc_pipe_comp_connect *_connect)
273 {
274 struct ipc4_module_bind_unbind *bu;
275 struct comp_buffer *buffer = NULL;
276 struct comp_dev *src, *sink;
277 struct list_item *sink_list;
278 uint32_t src_id, sink_id, buffer_id;
279 uint32_t flags;
280 int ret;
281
282 bu = (struct ipc4_module_bind_unbind *)_connect;
283 src_id = IPC4_COMP_ID(bu->header.r.module_id, bu->header.r.instance_id);
284 sink_id = IPC4_COMP_ID(bu->data.r.dst_module_id, bu->data.r.dst_instance_id);
285 src = ipc4_get_comp_dev(src_id);
286 sink = ipc4_get_comp_dev(sink_id);
287 if (!src || !sink) {
288 tr_err(&ipc_tr, "failed to find src %x, or dst %x", src_id, sink_id);
289 return IPC4_INVALID_RESOURCE_ID;
290 }
291
292 if (src->pipeline == sink->pipeline)
293 return IPC4_INVALID_REQUEST;
294
295 buffer_id = IPC4_COMP_ID(bu->data.r.src_queue, bu->data.r.dst_queue);
296 list_for_item(sink_list, &src->bsink_list) {
297 struct comp_buffer *buf;
298
299 buf = container_of(sink_list, struct comp_buffer, source_list);
300 if (buf->id == buffer_id) {
301 buffer = buf;
302 break;
303 }
304 }
305
306 if (!buffer)
307 return IPC4_INVALID_RESOURCE_ID;
308
309 irq_local_disable(flags);
310 list_item_del(buffer_comp_list(buffer, PPL_CONN_DIR_COMP_TO_BUFFER));
311 list_item_del(buffer_comp_list(buffer, PPL_CONN_DIR_BUFFER_TO_COMP));
312 comp_writeback(src);
313 comp_writeback(sink);
314 irq_local_enable(flags);
315
316 buffer_free(buffer);
317
318 ret = comp_unbind(src, bu);
319 if (ret < 0)
320 return IPC4_INVALID_RESOURCE_ID;
321
322 ret = comp_unbind(sink, bu);
323 if (ret < 0)
324 return IPC4_INVALID_RESOURCE_ID;
325
326 return 0;
327 }
328
ipc4_get_drv(uint8_t * uuid)329 const struct comp_driver *ipc4_get_drv(uint8_t *uuid)
330 {
331 struct comp_driver_list *drivers = comp_drivers_get();
332 struct list_item *clist;
333 const struct comp_driver *drv = NULL;
334 struct comp_driver_info *info;
335 uint32_t flags;
336
337 irq_local_disable(flags);
338
339 /* search driver list with UUID */
340 list_for_item(clist, &drivers->list) {
341 info = container_of(clist, struct comp_driver_info,
342 list);
343 if (!memcmp(info->drv->uid, uuid, UUID_SIZE)) {
344 tr_dbg(&comp_tr,
345 "found type %d, uuid %pU",
346 info->drv->type,
347 info->drv->tctx->uuid_p);
348 drv = info->drv;
349 goto out;
350 }
351 }
352
353 tr_err(&comp_tr, "get_drv(): the provided UUID (%8x %8x %8x %8x) can't be found!",
354 *(uint32_t *)(&uuid[0]),
355 *(uint32_t *)(&uuid[4]),
356 *(uint32_t *)(&uuid[8]),
357 *(uint32_t *)(&uuid[12]));
358
359 out:
360 irq_local_enable(flags);
361 return drv;
362 }
363
ipc4_get_comp_drv(int module_id)364 const struct comp_driver *ipc4_get_comp_drv(int module_id)
365 {
366 struct sof_man_fw_desc *desc = (struct sof_man_fw_desc *)IMR_BOOT_LDR_MANIFEST_BASE;
367 struct sof_man_module *mod;
368 const struct comp_driver *drv;
369
370 /* skip basefw of module 0 in manifest */
371 mod = (struct sof_man_module *)((char *)desc + SOF_MAN_MODULE_OFFSET(module_id));
372 drv = ipc4_get_drv(mod->uuid);
373
374 return drv;
375 }
376
ipc4_get_comp_dev(uint32_t comp_id)377 struct comp_dev *ipc4_get_comp_dev(uint32_t comp_id)
378 {
379 struct ipc *ipc = ipc_get();
380 struct ipc_comp_dev *icd;
381
382 icd = ipc_get_comp_by_id(ipc, comp_id);
383 if (!icd)
384 return NULL;
385
386 return icd->cd;
387 }
388
ipc4_add_comp_dev(struct comp_dev * dev)389 int ipc4_add_comp_dev(struct comp_dev *dev)
390 {
391 struct ipc *ipc = ipc_get();
392 struct ipc_comp_dev *icd;
393
394 /* allocate the IPC component container */
395 icd = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM,
396 sizeof(struct ipc_comp_dev));
397 if (!icd) {
398 tr_err(&ipc_tr, "ipc_comp_new(): alloc failed");
399 rfree(icd);
400 return IPC4_OUT_OF_MEMORY;
401 }
402
403 icd->cd = dev;
404 icd->type = COMP_TYPE_COMPONENT;
405 icd->core = dev->ipc_config.core;
406 icd->id = dev->ipc_config.id;
407
408 tr_dbg(&ipc_tr, "ipc4_add_comp_dev add comp %x", icd->id);
409 /* add new component to the list */
410 list_item_append(&icd->list, &ipc->comp_list);
411
412 return 0;
413 };
414