1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2020 Google LLC. All rights reserved.
4 //
5 // Author: Sebastiano Carlucci <scarlucci@google.com>
6 
7 #include <sof/audio/buffer.h>
8 #include <sof/audio/component.h>
9 #include <sof/audio/format.h>
10 #include <sof/audio/pipeline.h>
11 #include <sof/audio/ipc-config.h>
12 #include <sof/audio/crossover/crossover.h>
13 #include <sof/audio/crossover/crossover_algorithm.h>
14 #include <sof/audio/eq_iir/iir.h>
15 #include <sof/common.h>
16 #include <sof/debug/panic.h>
17 #include <sof/ipc/msg.h>
18 #include <sof/lib/alloc.h>
19 #include <sof/lib/memory.h>
20 #include <sof/lib/uuid.h>
21 #include <sof/list.h>
22 #include <sof/platform.h>
23 #include <sof/string.h>
24 #include <sof/trace/trace.h>
25 #include <sof/ut.h>
26 #include <ipc/control.h>
27 #include <ipc/stream.h>
28 #include <ipc/topology.h>
29 #include <user/trace.h>
30 #include <user/crossover.h>
31 #include <user/eq.h>
32 #include <errno.h>
33 #include <stddef.h>
34 #include <stdint.h>
35 #include <limits.h>
36 
37 static const struct comp_driver comp_crossover;
38 
39 /* 948c9ad1-806a-4131-ad6c-b2bda9e35a9f */
40 DECLARE_SOF_RT_UUID("crossover", crossover_uuid, 0x948c9ad1, 0x806a, 0x4131,
41 		    0xad, 0x6c, 0xb2, 0xbd, 0xa9, 0xe3, 0x5a, 0x9f);
42 
43 DECLARE_TR_CTX(crossover_tr, SOF_UUID(crossover_uuid), LOG_LEVEL_INFO);
44 
crossover_free_config(struct sof_crossover_config ** config)45 static inline void crossover_free_config(struct sof_crossover_config **config)
46 {
47 	rfree(*config);
48 	*config = NULL;
49 }
50 
51 /**
52  * \brief Reset the state of an LR4 filter.
53  */
crossover_reset_state_lr4(struct iir_state_df2t * lr4)54 static inline void crossover_reset_state_lr4(struct iir_state_df2t *lr4)
55 {
56 	rfree(lr4->coef);
57 	rfree(lr4->delay);
58 
59 	lr4->coef = NULL;
60 	lr4->delay = NULL;
61 }
62 
63 /**
64  * \brief Reset the state (coefficients and delay) of the crossover filter
65  *	  of a single channel.
66  */
crossover_reset_state_ch(struct crossover_state * ch_state)67 inline void crossover_reset_state_ch(struct crossover_state *ch_state)
68 {
69 	int i;
70 
71 	for (i = 0; i < CROSSOVER_MAX_LR4; i++) {
72 		crossover_reset_state_lr4(&ch_state->lowpass[i]);
73 		crossover_reset_state_lr4(&ch_state->highpass[i]);
74 	}
75 }
76 
77 /**
78  * \brief Reset the state (coefficients and delay) of the crossover filter
79  *	  across all channels
80  */
crossover_reset_state(struct comp_data * cd)81 static inline void crossover_reset_state(struct comp_data *cd)
82 {
83 	int i;
84 
85 	for (i = 0; i < PLATFORM_MAX_CHANNELS; i++)
86 		crossover_reset_state_ch(&cd->state[i]);
87 }
88 
89 /**
90  * \brief Returns the index i such that assign_sink[i] = pipe_id.
91  *
92  * The assign_sink array in the configuration maps to the pipeline ids.
93  *
94  * \return the position at which pipe_id is found in config->assign_sink.
95  *	   -EINVAL if not found.
96  */
crossover_get_stream_index(struct sof_crossover_config * config,uint32_t pipe_id)97 static uint8_t crossover_get_stream_index(struct sof_crossover_config *config,
98 					  uint32_t pipe_id)
99 {
100 	int i;
101 	uint32_t *assign_sink = config->assign_sink;
102 
103 	for (i = 0; i < config->num_sinks; i++)
104 		if (assign_sink[i] == pipe_id)
105 			return i;
106 
107 	comp_cl_err(&comp_crossover, "crossover_get_stream_index() error: couldn't find any assignment for connected pipeline %u",
108 		    pipe_id);
109 
110 	return -EINVAL;
111 }
112 
113 /*
114  * \brief Aligns the sinks with their respective assignments
115  *	  in the configuration.
116  *
117  * Refer to sof/src/include/sof/crossover.h for more information on assigning
118  * sinks to an output.
119  *
120  * \param[out] sinks array where the sinks are assigned
121  * \return number of sinks assigned. This number should be equal to
122  *	   config->num_sinks if no errors were found.
123  */
crossover_assign_sinks(struct comp_dev * dev,struct sof_crossover_config * config,struct comp_buffer ** sinks)124 static int crossover_assign_sinks(struct comp_dev *dev,
125 				  struct sof_crossover_config *config,
126 				  struct comp_buffer **sinks)
127 {
128 	struct comp_buffer *sink;
129 	struct list_item *sink_list;
130 	int num_sinks = 0;
131 	int i;
132 
133 	/* Align sink streams with their respective configurations */
134 	list_for_item(sink_list, &dev->bsink_list) {
135 		sink = container_of(sink_list, struct comp_buffer, source_list);
136 		if (sink->sink->state == dev->state) {
137 			/* If no config is set, then assign the sinks in order */
138 			if (!config) {
139 				sinks[num_sinks++] = sink;
140 				continue;
141 			}
142 
143 			i = crossover_get_stream_index(config,
144 						       sink->pipeline_id);
145 
146 			/* If this sink buffer is not assigned
147 			 * in the configuration.
148 			 */
149 			if (i < 0) {
150 				comp_err(dev, "crossover_assign_sinks(), could not find sink %d in config",
151 					 sink->pipeline_id);
152 				break;
153 			}
154 
155 			if (sinks[i]) {
156 				comp_err(dev, "crossover_assign_sinks(), multiple sinks from pipeline %d are assigned",
157 					 sink->pipeline_id);
158 				break;
159 			}
160 
161 			sinks[i] = sink;
162 			num_sinks++;
163 		}
164 	}
165 
166 	return num_sinks;
167 }
168 
169 /**
170  * \brief Sets the state of a single LR4 filter.
171  *
172  * An LR4 filter is built by cascading two biquads in series.
173  *
174  * \param coef struct containing the coefficients of a butterworth
175  *	       high/low pass filter.
176  * \param[out] lr4 initialized struct
177  */
crossover_init_coef_lr4(struct sof_eq_iir_biquad_df2t * coef,struct iir_state_df2t * lr4)178 static int crossover_init_coef_lr4(struct sof_eq_iir_biquad_df2t *coef,
179 				   struct iir_state_df2t *lr4)
180 {
181 	int ret;
182 
183 	/* Only one set of coefficients is stored in config for both biquads
184 	 * in series due to identity. To maintain the structure of
185 	 * iir_state_df2t, it requires two copies of coefficients in a row.
186 	 */
187 	lr4->coef = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM,
188 			    sizeof(struct sof_eq_iir_biquad_df2t) * 2);
189 	if (!lr4->coef)
190 		return -ENOMEM;
191 
192 	/* coefficients of the first biquad */
193 	ret = memcpy_s(lr4->coef, sizeof(struct sof_eq_iir_biquad_df2t),
194 		       coef, sizeof(struct sof_eq_iir_biquad_df2t));
195 	assert(!ret);
196 
197 	/* coefficients of the second biquad */
198 	ret = memcpy_s(lr4->coef + SOF_EQ_IIR_NBIQUAD_DF2T,
199 		       sizeof(struct sof_eq_iir_biquad_df2t),
200 		       coef, sizeof(struct sof_eq_iir_biquad_df2t));
201 	assert(!ret);
202 
203 	/* LR4 filters are two 2nd order filters, so only need 4 delay slots
204 	 * delay[0..1] -> state for first biquad
205 	 * delay[2..3] -> state for second biquad
206 	 */
207 	lr4->delay = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM,
208 			     sizeof(uint64_t) * CROSSOVER_NUM_DELAYS_LR4);
209 	if (!lr4->delay)
210 		return -ENOMEM;
211 
212 	lr4->biquads = 2;
213 	lr4->biquads_in_series = 2;
214 
215 	return 0;
216 }
217 
218 /**
219  * \brief Initializes the crossover coefficients for one channel
220  */
crossover_init_coef_ch(struct sof_eq_iir_biquad_df2t * coef,struct crossover_state * ch_state,int32_t num_sinks)221 int crossover_init_coef_ch(struct sof_eq_iir_biquad_df2t *coef,
222 			   struct crossover_state *ch_state,
223 			   int32_t num_sinks)
224 {
225 	int32_t i;
226 	int32_t j = 0;
227 	int32_t num_lr4s = num_sinks == CROSSOVER_2WAY_NUM_SINKS ? 1 : 3;
228 	int err;
229 
230 	for (i = 0; i < num_lr4s; i++) {
231 		/* Get the low pass coefficients */
232 		err = crossover_init_coef_lr4(&coef[j],
233 					      &ch_state->lowpass[i]);
234 		if (err < 0)
235 			return -EINVAL;
236 		/* Get the high pass coefficients */
237 		err = crossover_init_coef_lr4(&coef[j + 1],
238 					      &ch_state->highpass[i]);
239 		if (err < 0)
240 			return -EINVAL;
241 		j += 2;
242 	}
243 
244 	return 0;
245 }
246 
247 /**
248  * \brief Initializes the coefficients of the crossover filter
249  *	  and assign them to the first nch channels.
250  *
251  * \param nch number of channels in the audio stream.
252  */
crossover_init_coef(struct comp_data * cd,int nch)253 static int crossover_init_coef(struct comp_data *cd, int nch)
254 {
255 	struct sof_eq_iir_biquad_df2t *crossover;
256 	struct sof_crossover_config *config = cd->config;
257 	int ch, err;
258 
259 	if (!config) {
260 		comp_cl_err(&comp_crossover, "crossover_init_coef(), no config is set");
261 		return -EINVAL;
262 	}
263 
264 	/* Sanity checks */
265 	if (nch > PLATFORM_MAX_CHANNELS) {
266 		comp_cl_err(&comp_crossover, "crossover_init_coef(), invalid channels count (%i)",
267 			    nch);
268 		return -EINVAL;
269 	}
270 
271 	comp_cl_info(&comp_crossover, "crossover_init_coef(), initiliazing %i-way crossover",
272 		     config->num_sinks);
273 
274 	/* Collect the coef array and assign it to every channel */
275 	crossover = config->coef;
276 	for (ch = 0; ch < nch; ch++) {
277 		err = crossover_init_coef_ch(crossover, &cd->state[ch],
278 					     config->num_sinks);
279 		/* Free all previously allocated blocks in case of an error */
280 		if (err < 0) {
281 			comp_cl_err(&comp_crossover, "crossover_init_coef(), could not assign coefficients to ch %d",
282 				    ch);
283 			crossover_reset_state(cd);
284 			return err;
285 		}
286 	}
287 
288 	return 0;
289 }
290 
291 /**
292  * \brief Setup the state, coefficients and processing functions for crossover.
293  */
crossover_setup(struct comp_data * cd,int nch)294 static int crossover_setup(struct comp_data *cd, int nch)
295 {
296 	int ret = 0;
297 
298 	/* Reset any previous state */
299 	crossover_reset_state(cd);
300 
301 	/* Assign LR4 coefficients from config */
302 	ret = crossover_init_coef(cd, nch);
303 
304 	return ret;
305 }
306 
307 /**
308  * \brief Creates a Crossover Filter component.
309  * \return Pointer to Crossover Filter component device.
310  */
crossover_new(const struct comp_driver * drv,struct comp_ipc_config * config,void * spec)311 static struct comp_dev *crossover_new(const struct comp_driver *drv,
312 				      struct comp_ipc_config *config,
313 				      void *spec)
314 {
315 	struct comp_dev *dev;
316 	struct comp_data *cd;
317 	struct ipc_config_process *ipc_crossover = spec;;
318 	size_t bs = ipc_crossover->size;
319 	int ret;
320 
321 	comp_cl_info(&comp_crossover, "crossover_new()");
322 
323 	/* Check that the coefficients blob size is sane */
324 	if (bs > SOF_CROSSOVER_MAX_SIZE) {
325 		comp_cl_err(&comp_crossover, "crossover_new(), blob size (%d) exceeds maximum allowed size (%i)",
326 			    bs, SOF_CROSSOVER_MAX_SIZE);
327 		return NULL;
328 	}
329 
330 	dev = comp_alloc(drv, sizeof(*dev));
331 	if (!dev)
332 		return NULL;
333 	dev->ipc_config = *config;
334 
335 	cd = rzalloc(SOF_MEM_ZONE_RUNTIME, 0,
336 		     SOF_MEM_CAPS_RAM, sizeof(*cd));
337 	if (!cd) {
338 		rfree(dev);
339 		return NULL;
340 	}
341 
342 	comp_set_drvdata(dev, cd);
343 
344 	cd->crossover_process = NULL;
345 	cd->crossover_split = NULL;
346 	cd->config = NULL;
347 	cd->config_new = NULL;
348 
349 	if (bs) {
350 		cd->config = rzalloc(SOF_MEM_ZONE_RUNTIME, 0,
351 				     SOF_MEM_CAPS_RAM, bs);
352 		if (!cd->config) {
353 			rfree(dev);
354 			rfree(cd);
355 			return NULL;
356 		}
357 
358 		ret = memcpy_s(cd->config, bs, ipc_crossover->data, bs);
359 		assert(!ret);
360 	}
361 
362 	dev->state = COMP_STATE_READY;
363 	return dev;
364 }
365 
366 /**
367  * \brief Frees Crossover Filter component.
368  */
crossover_free(struct comp_dev * dev)369 static void crossover_free(struct comp_dev *dev)
370 {
371 	struct comp_data *cd = comp_get_drvdata(dev);
372 
373 	comp_info(dev, "crossover_free()");
374 
375 	crossover_free_config(&cd->config);
376 	crossover_free_config(&cd->config_new);
377 
378 	crossover_reset_state(cd);
379 
380 	rfree(cd);
381 	rfree(dev);
382 }
383 
384 /**
385  * \brief Verifies that the config is formatted correctly.
386  *
387  * The function can only be called after the buffers have been initialized.
388  */
crossover_validate_config(struct comp_dev * dev,struct sof_crossover_config * config)389 static int crossover_validate_config(struct comp_dev *dev,
390 				     struct sof_crossover_config *config)
391 {
392 	struct comp_buffer *sink;
393 	struct list_item *sink_list;
394 	uint32_t size = config->size;
395 	int32_t num_assigned_sinks = 0;
396 	uint8_t assigned_sinks[SOF_CROSSOVER_MAX_STREAMS] = {0};
397 	int i;
398 
399 	if (size > SOF_CROSSOVER_MAX_SIZE || !size) {
400 		comp_err(dev, "crossover_validate_config(), size %d is invalid",
401 			 size);
402 		return -EINVAL;
403 	}
404 
405 	if (config->num_sinks > SOF_CROSSOVER_MAX_STREAMS ||
406 	    config->num_sinks < 2) {
407 		comp_err(dev, "crossover_validate_config(), invalid num_sinks %i, expected number between 2 and %i",
408 			 config->num_sinks, SOF_CROSSOVER_MAX_STREAMS);
409 		return -EINVAL;
410 	}
411 
412 	/* Align the crossover's sinks, to their respective configuation in
413 	 * the config.
414 	 */
415 	list_for_item(sink_list, &dev->bsink_list) {
416 		sink = container_of(sink_list, struct comp_buffer, source_list);
417 		i = crossover_get_stream_index(config, sink->pipeline_id);
418 		if (i < 0) {
419 			comp_warn(dev, "crossover_validate_config(), could not assign sink %d",
420 				  sink->pipeline_id);
421 			break;
422 		}
423 
424 		if (assigned_sinks[i]) {
425 			comp_warn(dev, "crossover_validate_config(), multiple sinks from pipeline %d are assigned",
426 				  sink->pipeline_id);
427 			break;
428 		}
429 
430 		assigned_sinks[i] = true;
431 		num_assigned_sinks++;
432 	}
433 
434 	/* Config is invalid if the number of assigned sinks
435 	 * is different than what is configured.
436 	 */
437 	if (num_assigned_sinks != config->num_sinks) {
438 		comp_err(dev, "crossover_validate_config(), number of assigned sinks %d, expected from config %d",
439 			 num_assigned_sinks, config->num_sinks);
440 		return -EINVAL;
441 	}
442 
443 	return 0;
444 }
445 
crossover_verify_params(struct comp_dev * dev,struct sof_ipc_stream_params * params)446 static int crossover_verify_params(struct comp_dev *dev,
447 				   struct sof_ipc_stream_params *params)
448 {
449 	int ret;
450 
451 	comp_dbg(dev, "crossover_verify_params()");
452 
453 	ret = comp_verify_params(dev, 0, params);
454 	if (ret < 0) {
455 		comp_err(dev, "crossover_verify_params() error: comp_verify_params() failed.");
456 		return ret;
457 	}
458 
459 	return 0;
460 }
461 
462 /**
463  * \brief Sets Crossover Filter component audio stream parameters.
464  * \param[in,out] dev Crossover Filter base component device.
465  * \return Error code.
466  */
crossover_params(struct comp_dev * dev,struct sof_ipc_stream_params * params)467 static int crossover_params(struct comp_dev *dev,
468 			    struct sof_ipc_stream_params *params)
469 {
470 	int err = 0;
471 
472 	comp_dbg(dev, "crossover_params()");
473 
474 	err = crossover_verify_params(dev, params);
475 	if (err < 0)
476 		comp_err(dev, "crossover_params(): pcm params verification failed");
477 
478 	return err;
479 }
480 
crossover_cmd_set_data(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata)481 static int crossover_cmd_set_data(struct comp_dev *dev,
482 				  struct sof_ipc_ctrl_data *cdata)
483 {
484 	struct comp_data *cd = comp_get_drvdata(dev);
485 	struct sof_crossover_config *request;
486 	uint32_t bs;
487 	int ret = 0;
488 
489 	switch (cdata->cmd) {
490 	case SOF_CTRL_CMD_BINARY:
491 		comp_info(dev, "crossover_cmd_set_data(), SOF_CTRL_CMD_BINARY");
492 
493 		/* Find size from header */
494 		request = (struct sof_crossover_config *)ASSUME_ALIGNED(cdata->data->data, 4);
495 		bs = request->size;
496 
497 		/* Check that there is no work-in-progress previous request */
498 		if (cd->config_new) {
499 			comp_err(dev, "crossover_cmd_set_data(), busy with previous");
500 			return -EBUSY;
501 		}
502 
503 		/* Allocate and make a copy of the blob */
504 		cd->config_new = rzalloc(SOF_MEM_ZONE_RUNTIME, 0,
505 					 SOF_MEM_CAPS_RAM, bs);
506 		if (!cd->config_new) {
507 			comp_err(dev, "crossover_cmd_set_data(), alloc fail");
508 			return -EINVAL;
509 		}
510 
511 		/* Copy the configuration. If the component state is ready
512 		 * the Crossover will initialize in prepare().
513 		 */
514 		ret = memcpy_s(cd->config_new, bs, request, bs);
515 		assert(!ret);
516 
517 		/* If component state is READY we can omit old configuration
518 		 * immediately. When in playback/capture the new configuration
519 		 * presence is checked in copy().
520 		 */
521 		if (dev->state == COMP_STATE_READY)
522 			crossover_free_config(&cd->config);
523 
524 		/* If there is no existing configuration the received can
525 		 * be set to current immediately. It will be applied in
526 		 * prepare() when streaming starts.
527 		 */
528 		if (!cd->config) {
529 			cd->config = cd->config_new;
530 			cd->config_new = NULL;
531 		}
532 
533 		break;
534 	default:
535 		comp_err(dev, "crossover_cmd_set_data(), invalid command");
536 		ret = -EINVAL;
537 		break;
538 	}
539 
540 	return ret;
541 }
542 
crossover_cmd_get_data(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata,int max_size)543 static int crossover_cmd_get_data(struct comp_dev *dev,
544 				  struct sof_ipc_ctrl_data *cdata, int max_size)
545 {
546 	struct comp_data *cd = comp_get_drvdata(dev);
547 	uint32_t bs;
548 	int ret = 0;
549 
550 	switch (cdata->cmd) {
551 	case SOF_CTRL_CMD_BINARY:
552 		comp_info(dev, "crossover_cmd_get_data(), SOF_CTRL_CMD_BINARY");
553 
554 		/* Copy back to user space */
555 		if (cd->config) {
556 			bs = cd->config->size;
557 			comp_info(dev, "crossover_cmd_get_data(), size %u",
558 				  bs);
559 			if (bs > SOF_CROSSOVER_MAX_SIZE || bs == 0 ||
560 			    bs > max_size)
561 				return -EINVAL;
562 			ret = memcpy_s(cdata->data->data,
563 				       cdata->data->size,
564 				       cd->config, bs);
565 			assert(!ret);
566 
567 			cdata->data->abi = SOF_ABI_VERSION;
568 			cdata->data->size = bs;
569 		} else {
570 			comp_err(dev, "crossover_cmd_get_data(), no config");
571 			ret = -EINVAL;
572 		}
573 		break;
574 	default:
575 		comp_err(dev, "crossover_cmd_get_data(), invalid command");
576 		ret = -EINVAL;
577 		break;
578 	}
579 	return ret;
580 }
581 
582 /**
583  * \brief Handles incoming IPC commands for Crossover component.
584  */
crossover_cmd(struct comp_dev * dev,int cmd,void * data,int max_data_size)585 static int crossover_cmd(struct comp_dev *dev, int cmd, void *data,
586 			 int max_data_size)
587 {
588 	struct sof_ipc_ctrl_data *cdata = ASSUME_ALIGNED(data, 4);
589 	int ret = 0;
590 
591 	comp_info(dev, "crossover_cmd()");
592 
593 	switch (cmd) {
594 	case COMP_CMD_SET_DATA:
595 		ret = crossover_cmd_set_data(dev, cdata);
596 		break;
597 	case COMP_CMD_GET_DATA:
598 		ret = crossover_cmd_get_data(dev, cdata, max_data_size);
599 		break;
600 	default:
601 		comp_err(dev, "crossover_cmd(), invalid command");
602 		ret = -EINVAL;
603 	}
604 
605 	return ret;
606 }
607 
608 /**
609  * \brief Sets Crossover Filter component state.
610  * \param[in,out] dev Crossover Filter base component device.
611  * \param[in] cmd Command type.
612  * \return Error code.
613  */
crossover_trigger(struct comp_dev * dev,int cmd)614 static int crossover_trigger(struct comp_dev *dev, int cmd)
615 {
616 	comp_info(dev, "crossover_trigger()");
617 
618 	return comp_set_state(dev, cmd);
619 }
620 
621 /**
622  * \brief Copies and processes stream data.
623  * \param[in,out] dev Crossover Filter base component device.
624  * \return Error code.
625  */
crossover_copy(struct comp_dev * dev)626 static int crossover_copy(struct comp_dev *dev)
627 {
628 	struct comp_data *cd = comp_get_drvdata(dev);
629 	struct comp_buffer *source;
630 	struct comp_buffer *sinks[SOF_CROSSOVER_MAX_STREAMS] = { NULL };
631 	int i, ret;
632 	uint32_t num_sinks;
633 	uint32_t num_assigned_sinks = 0;
634 	uint32_t frames = UINT_MAX;
635 	uint32_t source_bytes, avail;
636 	uint32_t flags = 0;
637 	uint32_t sinks_bytes[SOF_CROSSOVER_MAX_STREAMS] = { 0 };
638 
639 	comp_dbg(dev, "crossover_copy()");
640 
641 	source = list_first_item(&dev->bsource_list, struct comp_buffer,
642 				 sink_list);
643 
644 	/* Check for changed configuration */
645 	if (cd->config_new) {
646 		crossover_free_config(&cd->config);
647 		cd->config = cd->config_new;
648 		cd->config_new = NULL;
649 		ret = crossover_setup(cd, source->stream.channels);
650 		if (ret < 0) {
651 			comp_err(dev, "crossover_copy(), setup failed");
652 			return -EINVAL;
653 		}
654 	}
655 
656 	/* Use the assign_sink array from the config to route
657 	 * the output to the corresponding sinks.
658 	 * It is possible for an assigned sink to be in a different
659 	 * state than the component. Therefore not all sinks are guaranteed
660 	 * to be assigned: sink[i] can be NULL, 0 <= i <= config->num_sinks
661 	 */
662 	num_assigned_sinks = crossover_assign_sinks(dev, cd->config, sinks);
663 	if (cd->config && num_assigned_sinks != cd->config->num_sinks)
664 		comp_dbg(dev, "crossover_copy(), number of assigned sinks (%i) does not match number of sinks in config (%i).",
665 			 num_assigned_sinks, cd->config->num_sinks);
666 
667 	/* If no config is set then assign the number of sinks to the number
668 	 * of sinks that were assigned.
669 	 */
670 	if (cd->config)
671 		num_sinks = cd->config->num_sinks;
672 	else
673 		num_sinks = num_assigned_sinks;
674 
675 	buffer_lock(source, &flags);
676 
677 	/* Check if source is active */
678 	if (source->source->state != dev->state) {
679 		buffer_unlock(source, flags);
680 		return -EINVAL;
681 	}
682 
683 	/* Find the number of frames to copy over */
684 	for (i = 0; i < num_sinks; i++) {
685 		if (!sinks[i])
686 			continue;
687 		buffer_lock(sinks[i], &flags);
688 		avail = audio_stream_avail_frames(&source->stream,
689 						  &sinks[i]->stream);
690 		frames = MIN(frames, avail);
691 		buffer_unlock(sinks[i], flags);
692 	}
693 
694 	buffer_unlock(source, flags);
695 
696 	source_bytes = frames * audio_stream_frame_bytes(&source->stream);
697 
698 	for (i = 0; i < num_sinks; i++) {
699 		if (!sinks[i])
700 			continue;
701 		sinks_bytes[i] = frames *
702 				 audio_stream_frame_bytes(&sinks[i]->stream);
703 	}
704 
705 	/* Process crossover */
706 	buffer_invalidate(source, source_bytes);
707 	cd->crossover_process(dev, source, sinks, num_sinks, frames);
708 
709 	for (i = 0; i < num_sinks; i++) {
710 		if (!sinks[i])
711 			continue;
712 		buffer_writeback(sinks[i], sinks_bytes[i]);
713 		comp_update_buffer_produce(sinks[i], sinks_bytes[i]);
714 	}
715 	comp_update_buffer_consume(source, source_bytes);
716 
717 	return 0;
718 }
719 
720 /**
721  * \brief Prepares Crossover Filter component for processing.
722  * \param[in,out] dev Crossover Filter base component device.
723  * \return Error code.
724  */
crossover_prepare(struct comp_dev * dev)725 static int crossover_prepare(struct comp_dev *dev)
726 {
727 	struct comp_data *cd = comp_get_drvdata(dev);
728 	struct comp_buffer *source, *sink;
729 	struct list_item *sink_list;
730 	int32_t sink_period_bytes;
731 	int ret;
732 
733 	comp_info(dev, "crossover_prepare()");
734 
735 	ret = comp_set_state(dev, COMP_TRIGGER_PREPARE);
736 	if (ret < 0)
737 		return ret;
738 
739 	if (ret == COMP_STATUS_STATE_ALREADY_SET)
740 		return PPL_STATUS_PATH_STOP;
741 
742 	/* Crossover has a variable number of sinks */
743 	source = list_first_item(&dev->bsource_list,
744 				 struct comp_buffer, sink_list);
745 
746 	/* Get source data format */
747 	cd->source_format = source->stream.frame_fmt;
748 
749 	/* Validate frame format and buffer size of sinks */
750 	list_for_item(sink_list, &dev->bsink_list) {
751 		sink = container_of(sink_list, struct comp_buffer, source_list);
752 		if (cd->source_format != sink->stream.frame_fmt) {
753 			comp_err(dev, "crossover_prepare(): Source fmt %d and sink fmt %d are different for sink %d.",
754 				 cd->source_format, sink->stream.frame_fmt,
755 				 sink->pipeline_id);
756 			ret = -EINVAL;
757 			goto err;
758 		}
759 
760 		sink_period_bytes = audio_stream_period_bytes(&sink->stream,
761 							      dev->frames);
762 		if (sink->stream.size < sink_period_bytes) {
763 			comp_err(dev, "crossover_prepare(), sink %d buffer size %d is insufficient",
764 				 sink->pipeline_id, sink->stream.size);
765 			ret = -ENOMEM;
766 			goto err;
767 		}
768 	}
769 
770 	comp_info(dev, "crossover_prepare(), source_format=%d, sink_formats=%d, nch=%d",
771 		  cd->source_format, cd->source_format,
772 		  source->stream.channels);
773 
774 	/* Initialize Crossover */
775 	if (cd->config && crossover_validate_config(dev, cd->config) < 0) {
776 		/* If config is invalid then delete it */
777 		comp_err(dev, "crossover_prepare(), invalid binary config format");
778 		crossover_free_config(&cd->config);
779 	}
780 
781 	if (cd->config) {
782 		ret = crossover_setup(cd, source->stream.channels);
783 		if (ret < 0) {
784 			comp_err(dev, "crossover_prepare(), setup failed");
785 			goto err;
786 		}
787 
788 		cd->crossover_process =
789 			crossover_find_proc_func(cd->source_format);
790 		if (!cd->crossover_process) {
791 			comp_err(dev, "crossover_prepare(), No processing function matching frame_fmt %i",
792 				 cd->source_format);
793 			ret = -EINVAL;
794 			goto err;
795 		}
796 
797 		cd->crossover_split =
798 			crossover_find_split_func(cd->config->num_sinks);
799 		if (!cd->crossover_split) {
800 			comp_err(dev, "crossover_prepare(), No split function matching num_sinks %i",
801 				 cd->config->num_sinks);
802 			ret = -EINVAL;
803 			goto err;
804 		}
805 	} else {
806 		comp_info(dev, "crossover_prepare(), setting crossover to passthrough mode");
807 
808 		cd->crossover_process =
809 			crossover_find_proc_func_pass(cd->source_format);
810 
811 		if (!cd->crossover_process) {
812 			comp_err(dev, "crossover_prepare(), No passthrough function matching frame_fmt %i",
813 				 cd->source_format);
814 			ret = -EINVAL;
815 			goto err;
816 		}
817 	}
818 
819 	return 0;
820 
821 err:
822 	comp_set_state(dev, COMP_TRIGGER_RESET);
823 	return ret;
824 }
825 
826 /**
827  * \brief Resets Crossover Filter component.
828  * \param[in,out] dev Crossover Filter base component device.
829  * \return Error code.
830  */
crossover_reset(struct comp_dev * dev)831 static int crossover_reset(struct comp_dev *dev)
832 {
833 	struct comp_data *cd = comp_get_drvdata(dev);
834 
835 	comp_info(dev, "crossover_reset()");
836 
837 	crossover_reset_state(cd);
838 
839 	comp_set_state(dev, COMP_TRIGGER_RESET);
840 
841 	return 0;
842 }
843 
844 /** \brief Crossover Filter component definition. */
845 static const struct comp_driver comp_crossover = {
846 	.uid	= SOF_RT_UUID(crossover_uuid),
847 	.tctx	= &crossover_tr,
848 	.ops	= {
849 		.create		= crossover_new,
850 		.free		= crossover_free,
851 		.params		= crossover_params,
852 		.cmd		= crossover_cmd,
853 		.trigger	= crossover_trigger,
854 		.copy		= crossover_copy,
855 		.prepare	= crossover_prepare,
856 		.reset		= crossover_reset,
857 	},
858 };
859 
860 static SHARED_DATA struct comp_driver_info comp_crossover_info = {
861 	.drv = &comp_crossover,
862 };
863 
sys_comp_crossover_init(void)864 UT_STATIC void sys_comp_crossover_init(void)
865 {
866 	comp_register(platform_shared_get(&comp_crossover_info,
867 					  sizeof(comp_crossover_info)));
868 }
869 
870 DECLARE_MODULE(sys_comp_crossover_init);
871