1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2016 Intel Corporation. All rights reserved.
4 //
5 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
6 //         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/lib/memory.h>
12 #include <sof/lib/mm_heap.h>
13 #include <sof/list.h>
14 #include <sof/spinlock.h>
15 #include <ipc/stream.h>
16 #include <ipc/topology.h>
17 #include <errno.h>
18 #include <stdbool.h>
19 #include <stddef.h>
20 #include <stdint.h>
21 
pipeline_comp_params_neg(struct comp_dev * current,struct comp_buffer * calling_buf,struct pipeline_walk_context * ctx,int dir)22 static int pipeline_comp_params_neg(struct comp_dev *current,
23 				    struct comp_buffer *calling_buf,
24 				    struct pipeline_walk_context *ctx,
25 				    int dir)
26 {
27 	struct pipeline_data *ppl_data = ctx->comp_data;
28 	uint32_t flags = 0;
29 	int err = 0;
30 
31 	pipe_dbg(current->pipeline, "pipeline_comp_params_neg(), current->comp.id = %u, dir = %u",
32 		 dev_comp_id(current), dir);
33 
34 	/* check if 'current' is already configured */
35 	if (current->state != COMP_STATE_INIT &&
36 	    current->state != COMP_STATE_READY) {
37 		/* return 0 if params matches */
38 		if (buffer_params_match(calling_buf,
39 					&ppl_data->params->params,
40 					BUFF_PARAMS_FRAME_FMT |
41 					BUFF_PARAMS_RATE))
42 			return 0;
43 		/*
44 		 * the param is conflict with an active pipeline,
45 		 * drop an error and reject the .params() command.
46 		 */
47 		pipe_err(current->pipeline, "pipeline_comp_params_neg(): params conflict with existed active pipeline!");
48 		return -EINVAL;
49 	}
50 
51 	/*
52 	 * Negotiation only happen when the current component has > 1
53 	 * source or sink, we are propagating the params to branched
54 	 * buffers, and the subsequent component's .params() or .prepare()
55 	 * should be responsible for calibrating if needed. For example,
56 	 * a component who has different channels input/output buffers
57 	 * should explicitly configure the channels of the branched buffers.
58 	 */
59 	if (calling_buf) {
60 		buffer_lock(calling_buf, &flags);
61 		err = buffer_set_params(calling_buf,
62 					&ppl_data->params->params,
63 					BUFFER_UPDATE_FORCE);
64 		buffer_unlock(calling_buf, flags);
65 	}
66 
67 	return err;
68 }
69 
pipeline_comp_params(struct comp_dev * current,struct comp_buffer * calling_buf,struct pipeline_walk_context * ctx,int dir)70 static int pipeline_comp_params(struct comp_dev *current,
71 				struct comp_buffer *calling_buf,
72 				struct pipeline_walk_context *ctx, int dir)
73 {
74 	struct pipeline_data *ppl_data = ctx->comp_data;
75 	struct pipeline_walk_context param_neg_ctx = {
76 		.comp_func = pipeline_comp_params_neg,
77 		.comp_data = ppl_data,
78 		.skip_incomplete = true,
79 	};
80 	int stream_direction = ppl_data->params->params.direction;
81 	int end_type;
82 	int err;
83 
84 	pipe_dbg(current->pipeline, "pipeline_comp_params(), current->comp.id = %u, dir = %u",
85 		 dev_comp_id(current), dir);
86 
87 	if (!comp_is_single_pipeline(current, ppl_data->start)) {
88 		/* If pipeline connected to the starting one is in improper
89 		 * direction (CAPTURE towards DAI, PLAYBACK towards HOST),
90 		 * stop propagation of parameters not to override their config.
91 		 * Direction param of the pipeline can not be trusted at this
92 		 * point, as it might not be configured yet, hence checking
93 		 * for endpoint component type.
94 		 */
95 		end_type = comp_get_endpoint_type(current->pipeline->sink_comp);
96 		if (stream_direction == SOF_IPC_STREAM_PLAYBACK) {
97 			if (end_type == COMP_ENDPOINT_HOST ||
98 			    end_type == COMP_ENDPOINT_NODE)
99 				return 0;
100 		}
101 
102 		if (stream_direction == SOF_IPC_STREAM_CAPTURE) {
103 			if (end_type == COMP_ENDPOINT_DAI ||
104 			    end_type == COMP_ENDPOINT_NODE)
105 				return 0;
106 		}
107 	}
108 
109 	/* don't do any params if current is running */
110 	if (current->state == COMP_STATE_ACTIVE)
111 		return 0;
112 
113 	/* do params negotiation with other branches(opposite direction) */
114 	err = pipeline_for_each_comp(current, &param_neg_ctx, !dir);
115 	if (err < 0 || err == PPL_STATUS_PATH_STOP)
116 		return err;
117 
118 	/* set comp direction */
119 	current->direction = ppl_data->params->params.direction;
120 
121 	err = comp_params(current, &ppl_data->params->params);
122 	if (err < 0 || err == PPL_STATUS_PATH_STOP)
123 		return err;
124 
125 	return pipeline_for_each_comp(current, ctx, dir);
126 }
127 
128 /* save params changes made by component */
pipeline_update_buffer_pcm_params(struct comp_buffer * buffer,void * data)129 static void pipeline_update_buffer_pcm_params(struct comp_buffer *buffer,
130 					      void *data)
131 {
132 	struct sof_ipc_stream_params *params = data;
133 	int i;
134 
135 	params->buffer_fmt = buffer->buffer_fmt;
136 	params->frame_fmt = buffer->stream.frame_fmt;
137 	params->rate = buffer->stream.rate;
138 	params->channels = buffer->stream.channels;
139 	for (i = 0; i < SOF_IPC_MAX_CHANNELS; i++)
140 		params->chmap[i] = buffer->chmap[i];
141 }
142 
143 /* fetch hardware stream parameters from DAI and propagate them to the remaining
144  * buffers in pipeline.
145  */
pipeline_comp_hw_params(struct comp_dev * current,struct comp_buffer * calling_buf,struct pipeline_walk_context * ctx,int dir)146 static int pipeline_comp_hw_params(struct comp_dev *current,
147 				   struct comp_buffer *calling_buf,
148 				   struct pipeline_walk_context *ctx, int dir)
149 {
150 	struct pipeline_data *ppl_data = ctx->comp_data;
151 	uint32_t flags = 0;
152 	int ret;
153 
154 	pipe_dbg(current->pipeline, "pipeline_comp_hw_params(), current->comp.id = %u, dir = %u",
155 		 dev_comp_id(current), dir);
156 
157 	pipeline_for_each_comp(current, ctx, dir);
158 
159 	/* Fetch hardware stream parameters from DAI component */
160 	if (dev_comp_type(current) == SOF_COMP_DAI) {
161 		ret = comp_dai_get_hw_params(current,
162 					     &ppl_data->params->params, dir);
163 		if (ret < 0) {
164 			pipe_err(current->pipeline, "pipeline_find_dai_comp(): comp_dai_get_hw_params() error.");
165 			return ret;
166 		}
167 	}
168 
169 	/* set buffer parameters */
170 	if (calling_buf) {
171 		buffer_lock(calling_buf, &flags);
172 		buffer_set_params(calling_buf, &ppl_data->params->params,
173 				  BUFFER_UPDATE_IF_UNSET);
174 		buffer_unlock(calling_buf, flags);
175 	}
176 
177 	return 0;
178 }
179 
180 /* Send pipeline component params from host to endpoints.
181  * Params always start at host (PCM) and go downstream for playback and
182  * upstream for capture.
183  *
184  * Playback params can be re-written by upstream components. e.g. upstream SRC
185  * can change sample rate for all downstream components regardless of sample
186  * rate from host.
187  *
188  * Capture params can be re-written by downstream components.
189  *
190  * Params are always modified in the direction of host PCM to DAI.
191  */
pipeline_params(struct pipeline * p,struct comp_dev * host,struct sof_ipc_pcm_params * params)192 int pipeline_params(struct pipeline *p, struct comp_dev *host,
193 		    struct sof_ipc_pcm_params *params)
194 {
195 	struct sof_ipc_pcm_params hw_params;
196 	struct pipeline_data data = {
197 		.start = host,
198 		.params = &hw_params,
199 	};
200 	struct pipeline_walk_context hw_param_ctx = {
201 		.comp_func = pipeline_comp_hw_params,
202 		.comp_data = &data,
203 		.skip_incomplete = true,
204 	};
205 	struct pipeline_walk_context param_ctx = {
206 		.comp_func = pipeline_comp_params,
207 		.comp_data = &data,
208 		.buff_func = pipeline_update_buffer_pcm_params,
209 		.buff_data = &params->params,
210 		.skip_incomplete = true,
211 	};
212 	int dir = params->params.direction;
213 	int ret;
214 
215 	pipe_info(p, "pipe params dir %d frame_fmt %d buffer_fmt %d rate %d",
216 		  params->params.direction, params->params.frame_fmt,
217 		  params->params.buffer_fmt, params->params.rate);
218 	pipe_info(p, "pipe params stream_tag %d channels %d sample_valid_bytes %d sample_container_bytes %d",
219 		  params->params.stream_tag, params->params.channels,
220 		  params->params.sample_valid_bytes,
221 		  params->params.sample_container_bytes);
222 
223 	ret = hw_param_ctx.comp_func(host, NULL, &hw_param_ctx, dir);
224 	if (ret < 0) {
225 		pipe_err(p, "pipeline_params(): ret = %d, dev->comp.id = %u",
226 			 ret, dev_comp_id(host));
227 		return ret;
228 	}
229 
230 	/* setting pcm params */
231 	data.params = params;
232 	data.start = host;
233 
234 	ret = param_ctx.comp_func(host, NULL, &param_ctx, dir);
235 	if (ret < 0) {
236 		pipe_err(p, "pipeline_params(): ret = %d, host->comp.id = %u",
237 			 ret, dev_comp_id(host));
238 	}
239 
240 #if CONFIG_DEBUG_HEAP
241 	/* show heap status update with this pipeline run */
242 	heap_trace_all(0);
243 #endif
244 	return ret;
245 }
246 
pipeline_comp_prepare(struct comp_dev * current,struct comp_buffer * calling_buf,struct pipeline_walk_context * ctx,int dir)247 static int pipeline_comp_prepare(struct comp_dev *current,
248 				 struct comp_buffer *calling_buf,
249 				 struct pipeline_walk_context *ctx, int dir)
250 {
251 	struct pipeline_data *ppl_data = ctx->comp_data;
252 	int stream_direction = dir;
253 	int end_type;
254 	int err;
255 
256 	pipe_dbg(current->pipeline, "pipeline_comp_prepare(), current->comp.id = %u, dir = %u",
257 		 dev_comp_id(current), dir);
258 
259 	if (!comp_is_single_pipeline(current, ppl_data->start)) {
260 		/* If pipeline connected to the starting one is in improper
261 		 * direction (CAPTURE towards DAI, PLAYBACK towards HOST),
262 		 * stop propagation. Direction param of the pipeline can not be
263 		 * trusted at this point, as it might not be configured yet,
264 		 * hence checking for endpoint component type.
265 		 */
266 		end_type = comp_get_endpoint_type(current->pipeline->sink_comp);
267 		if (stream_direction == SOF_IPC_STREAM_PLAYBACK) {
268 			if (end_type == COMP_ENDPOINT_HOST ||
269 			    end_type == COMP_ENDPOINT_NODE)
270 				return 0;
271 		}
272 
273 		if (stream_direction == SOF_IPC_STREAM_CAPTURE) {
274 			if (end_type == COMP_ENDPOINT_DAI ||
275 			    end_type == COMP_ENDPOINT_NODE)
276 				return 0;
277 		}
278 	}
279 
280 	err = pipeline_comp_task_init(current->pipeline);
281 	if (err < 0)
282 		return err;
283 
284 	err = comp_prepare(current);
285 	if (err < 0 || err == PPL_STATUS_PATH_STOP)
286 		return err;
287 
288 	return pipeline_for_each_comp(current, ctx, dir);
289 }
290 
291 /* prepare the pipeline for usage */
pipeline_prepare(struct pipeline * p,struct comp_dev * dev)292 int pipeline_prepare(struct pipeline *p, struct comp_dev *dev)
293 {
294 	struct pipeline_data ppl_data;
295 	struct pipeline_walk_context walk_ctx = {
296 		.comp_func = pipeline_comp_prepare,
297 		.comp_data = &ppl_data,
298 		.buff_func = buffer_reset_pos,
299 		.skip_incomplete = true,
300 	};
301 	int ret;
302 
303 	pipe_info(p, "pipe prepare");
304 
305 	ppl_data.start = dev;
306 
307 	ret = walk_ctx.comp_func(dev, NULL, &walk_ctx, dev->direction);
308 	if (ret < 0) {
309 		pipe_err(p, "pipeline_prepare(): ret = %d, dev->comp.id = %u",
310 			 ret, dev_comp_id(dev));
311 		return ret;
312 	}
313 
314 	p->status = COMP_STATE_PREPARE;
315 
316 	return ret;
317 }
318