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