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