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