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