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, ¶m_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 = ¶ms->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, ¶m_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