1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2019 Intel Corporation. All rights reserved.
4 //
5 // Author: Artur Kloniecki <arturx.kloniecki@linux.intel.com>
6
7 #if CONFIG_COMP_MUX
8
9 #include <sof/audio/buffer.h>
10 #include <sof/audio/component.h>
11 #include <sof/audio/format.h>
12 #include <sof/audio/mux.h>
13 #include <sof/bit.h>
14 #include <sof/common.h>
15 #include <ipc/stream.h>
16 #include <stddef.h>
17 #include <stdint.h>
18
mux_check_for_wrap(struct audio_stream * sink,const struct audio_stream ** sources,struct mux_look_up * lookup)19 static void mux_check_for_wrap(struct audio_stream *sink,
20 const struct audio_stream **sources,
21 struct mux_look_up *lookup)
22 {
23 const struct audio_stream *source;
24 uint32_t elem;
25
26 /* check sources and destinations for wrap */
27 for (elem = 0; elem < lookup->num_elems; elem++) {
28 source = sources[lookup->copy_elem[elem].stream_id];
29 lookup->copy_elem[elem].dest =
30 audio_stream_wrap(sink, lookup->copy_elem[elem].dest);
31 lookup->copy_elem[elem].src =
32 audio_stream_wrap(source, lookup->copy_elem[elem].src);
33 }
34 }
35
demux_check_for_wrap(struct audio_stream * sink,const struct audio_stream * source,struct mux_look_up * lookup)36 static void demux_check_for_wrap(struct audio_stream *sink,
37 const struct audio_stream *source,
38 struct mux_look_up *lookup)
39 {
40 uint32_t elem;
41
42 /* check sources and destinations for wrap */
43 for (elem = 0; elem < lookup->num_elems; elem++) {
44 lookup->copy_elem[elem].dest =
45 audio_stream_wrap(sink, lookup->copy_elem[elem].dest);
46 lookup->copy_elem[elem].src =
47 audio_stream_wrap(source, lookup->copy_elem[elem].src);
48 }
49 }
50
51 #if CONFIG_FORMAT_S16LE
52
demux_calc_frames_without_wrap_s16(struct comp_dev * dev,struct audio_stream * sink,const struct audio_stream * source,struct mux_look_up * lookup)53 static uint32_t demux_calc_frames_without_wrap_s16(struct comp_dev *dev,
54 struct audio_stream *sink,
55 const struct audio_stream
56 *source,
57 struct mux_look_up *lookup)
58 {
59 uint32_t frames;
60 uint32_t min_frames;
61 void *ptr;
62
63 /* for demux we process each source buffer separately - dest/src for
64 * each copy_elem refers to the same sink/source buffer, so min_frames
65 * calculation based only on lookup table first element is sufficient.
66 */
67 ptr = (int16_t *)lookup->copy_elem[0].dest -
68 lookup->copy_elem[0].out_ch;
69 min_frames = audio_stream_frames_without_wrap(sink, ptr);
70
71 ptr = (int16_t *)lookup->copy_elem[0].src -
72 lookup->copy_elem[0].in_ch;
73 frames = audio_stream_frames_without_wrap(source, ptr);
74
75 min_frames = (frames < min_frames) ? frames : min_frames;
76
77 return min_frames;
78 }
79
mux_calc_frames_without_wrap_s16(struct comp_dev * dev,struct audio_stream * sink,const struct audio_stream ** sources,struct mux_look_up * lookup)80 static uint32_t mux_calc_frames_without_wrap_s16(struct comp_dev *dev,
81 struct audio_stream *sink,
82 const struct audio_stream
83 **sources,
84 struct mux_look_up *lookup)
85 {
86 const struct audio_stream *source;
87 uint32_t frames;
88 uint32_t min_frames;
89 uint32_t elem;
90 void *ptr;
91
92 /* dest pointer for all copy_elems in lookup refers to the same
93 * sink buffer (mux has one sink buffer), so dest min_frames
94 * calculation based only on lookup table first element is sufficient.
95 */
96 ptr = (int16_t *)lookup->copy_elem[0].dest -
97 lookup->copy_elem[0].out_ch;
98 min_frames = audio_stream_frames_without_wrap(sink, ptr);
99
100 for (elem = 0; elem < lookup->num_elems; elem++) {
101 source = sources[lookup->copy_elem[elem].stream_id];
102
103 ptr = (int16_t *)lookup->copy_elem[elem].src -
104 lookup->copy_elem[elem].in_ch;
105 frames = audio_stream_frames_without_wrap(source, ptr);
106
107 min_frames = (frames < min_frames) ? frames : min_frames;
108 }
109
110 return min_frames;
111 }
112
mux_init_look_up_pointers_s16(struct comp_dev * dev,struct audio_stream * sink,const struct audio_stream ** sources,struct mux_look_up * lookup)113 static void mux_init_look_up_pointers_s16(struct comp_dev *dev,
114 struct audio_stream *sink,
115 const struct audio_stream **sources,
116 struct mux_look_up *lookup)
117 {
118 const struct audio_stream *source;
119 uint32_t elem;
120
121 /* init pointers */
122 for (elem = 0; elem < lookup->num_elems; elem++) {
123 source = sources[lookup->copy_elem[elem].stream_id];
124
125 lookup->copy_elem[elem].src = (int16_t *)source->r_ptr +
126 lookup->copy_elem[elem].in_ch;
127 lookup->copy_elem[elem].src_inc = source->channels;
128
129 lookup->copy_elem[elem].dest = (int16_t *)sink->w_ptr +
130 lookup->copy_elem[elem].out_ch;
131 lookup->copy_elem[elem].dest_inc = sink->channels;
132 }
133 }
134
demux_init_look_up_pointers_s16(struct comp_dev * dev,struct audio_stream * sink,const struct audio_stream * source,struct mux_look_up * lookup)135 static void demux_init_look_up_pointers_s16(struct comp_dev *dev,
136 struct audio_stream *sink,
137 const struct audio_stream *source,
138 struct mux_look_up *lookup)
139 {
140 uint32_t elem;
141
142 /* init pointers */
143 for (elem = 0; elem < lookup->num_elems; elem++) {
144 lookup->copy_elem[elem].src = (int16_t *)source->r_ptr +
145 lookup->copy_elem[elem].in_ch;
146 lookup->copy_elem[elem].src_inc = source->channels;
147
148 lookup->copy_elem[elem].dest = (int16_t *)sink->w_ptr +
149 lookup->copy_elem[elem].out_ch;
150 lookup->copy_elem[elem].dest_inc = sink->channels;
151 }
152 }
153
154 /**
155 * Source stream are routed to sinks with regard to look up table based on
156 * routing bitmasks from mux_stream_data structures array. Each sink channel
157 * has it's own lookup[].copy_elem describing source and sink fragment of
158 * memory featured in copying.
159 *
160 * @param[in] dev Component device
161 * @param[in,out] sink Destination buffer.
162 * @param[in,out] sources Array of source buffers.
163 * @param[in] frames Number of frames to process.
164 * @param[in] lookup mux look up table.
165 */
demux_s16le(struct comp_dev * dev,struct audio_stream * sink,const struct audio_stream * source,uint32_t frames,struct mux_look_up * lookup)166 static void demux_s16le(struct comp_dev *dev, struct audio_stream *sink,
167 const struct audio_stream *source, uint32_t frames,
168 struct mux_look_up *lookup)
169 {
170 uint8_t i;
171 int16_t *src;
172 int16_t *dst;
173 uint32_t elem;
174 uint32_t frames_without_wrap;
175
176 comp_dbg(dev, "demux_s16le()");
177
178 if (!lookup || !lookup->num_elems)
179 return;
180
181 demux_init_look_up_pointers_s16(dev, sink, source, lookup);
182
183 while (frames) {
184 frames_without_wrap =
185 demux_calc_frames_without_wrap_s16(dev, sink, source,
186 lookup);
187
188 frames_without_wrap = MIN(frames, frames_without_wrap);
189
190 for (i = 0; i < frames_without_wrap; i++) {
191 for (elem = 0; elem < lookup->num_elems; elem++) {
192 src = (int16_t *)lookup->copy_elem[elem].src;
193 dst = (int16_t *)lookup->copy_elem[elem].dest;
194 *dst = *src;
195 lookup->copy_elem[elem].src = src +
196 lookup->copy_elem[elem].src_inc;
197 lookup->copy_elem[elem].dest = dst +
198 lookup->copy_elem[elem].dest_inc;
199 }
200 }
201
202 demux_check_for_wrap(sink, source, lookup);
203
204 frames -= frames_without_wrap;
205 }
206 }
207
208 /**
209 * Source streams are routed to sink with regard to look up table based on
210 * routing bitmasks from mux_stream_data structures array. Each sink channel
211 * has it's own lookup[].copy_elem describing source and sink fragment of
212 * memory featured in copying.
213 *
214 * @param[in] dev Component device
215 * @param[in,out] sink Destination buffer.
216 * @param[in,out] sources Array of source buffers.
217 * @param[in] frames Number of frames to process.
218 * @param[in] lookup mux look up table.
219 */
mux_s16le(struct comp_dev * dev,struct audio_stream * sink,const struct audio_stream ** sources,uint32_t frames,struct mux_look_up * lookup)220 static void mux_s16le(struct comp_dev *dev, struct audio_stream *sink,
221 const struct audio_stream **sources, uint32_t frames,
222 struct mux_look_up *lookup)
223 {
224 uint8_t i;
225 int16_t *src;
226 int16_t *dst;
227 uint32_t elem;
228 uint32_t frames_without_wrap;
229
230 comp_dbg(dev, "mux_s16le()");
231
232 if (!lookup || !lookup->num_elems)
233 return;
234
235 mux_init_look_up_pointers_s16(dev, sink, sources, lookup);
236
237 while (frames) {
238 frames_without_wrap =
239 mux_calc_frames_without_wrap_s16(dev, sink, sources,
240 lookup);
241
242 frames_without_wrap = MIN(frames, frames_without_wrap);
243
244 for (i = 0; i < frames_without_wrap; i++) {
245 for (elem = 0; elem < lookup->num_elems; elem++) {
246 src = (int16_t *)lookup->copy_elem[elem].src;
247 dst = (int16_t *)lookup->copy_elem[elem].dest;
248 *dst = *src;
249 lookup->copy_elem[elem].src = src +
250 lookup->copy_elem[elem].src_inc;
251 lookup->copy_elem[elem].dest = dst +
252 lookup->copy_elem[elem].dest_inc;
253 }
254 }
255
256 mux_check_for_wrap(sink, sources, lookup);
257
258 frames -= frames_without_wrap;
259 }
260 }
261 #endif /* CONFIG_FORMAT_S16LE */
262
263 #if CONFIG_FORMAT_S24LE || CONFIG_FORMAT_S32LE
264
mux_calc_frames_without_wrap_s32(struct comp_dev * dev,struct audio_stream * sink,const struct audio_stream ** sources,struct mux_look_up * lookup)265 static uint32_t mux_calc_frames_without_wrap_s32(struct comp_dev *dev,
266 struct audio_stream *sink,
267 const struct audio_stream
268 **sources,
269 struct mux_look_up *lookup)
270 {
271 const struct audio_stream *source;
272 uint32_t frames;
273 uint32_t min_frames;
274 uint32_t elem;
275 void *ptr;
276
277 /* dest pointer for all copy_elems in lookup refers to the same
278 * sink buffer (mux has one sink buffer), so dest min_frames
279 * calculation based only on lookup table first element is sufficient.
280 */
281 ptr = (int32_t *)lookup->copy_elem[0].dest -
282 lookup->copy_elem[0].out_ch;
283 min_frames = audio_stream_frames_without_wrap(sink, ptr);
284
285 for (elem = 0; elem < lookup->num_elems; elem++) {
286 source = sources[lookup->copy_elem[elem].stream_id];
287
288 ptr = (int32_t *)lookup->copy_elem[elem].src -
289 lookup->copy_elem[elem].in_ch;
290 frames = audio_stream_frames_without_wrap(source, ptr);
291
292 min_frames = (frames < min_frames) ? frames : min_frames;
293 }
294
295 return min_frames;
296 }
297
demux_calc_frames_without_wrap_s32(struct comp_dev * dev,struct audio_stream * sink,const struct audio_stream * source,struct mux_look_up * lookup)298 static uint32_t demux_calc_frames_without_wrap_s32(struct comp_dev *dev,
299 struct audio_stream *sink,
300 const struct audio_stream
301 *source,
302 struct mux_look_up *lookup)
303 {
304 uint32_t frames;
305 uint32_t min_frames;
306 void *ptr;
307
308 /* for demux we process each source buffer separately - dest/src for
309 * each copy_elem refers to the same sink/source buffer, so min_frames
310 * calculation based only on lookup table first element is sufficient.
311 */
312 ptr = (int32_t *)lookup->copy_elem[0].dest -
313 lookup->copy_elem[0].out_ch;
314 min_frames = audio_stream_frames_without_wrap(sink, ptr);
315
316 ptr = (int32_t *)lookup->copy_elem[0].src -
317 lookup->copy_elem[0].in_ch;
318 frames = audio_stream_frames_without_wrap(source, ptr);
319
320 min_frames = (frames < min_frames) ? frames : min_frames;
321
322 return min_frames;
323 }
324
mux_init_look_up_pointers_s32(struct comp_dev * dev,struct audio_stream * sink,const struct audio_stream ** sources,struct mux_look_up * lookup)325 static void mux_init_look_up_pointers_s32(struct comp_dev *dev,
326 struct audio_stream *sink,
327 const struct audio_stream **sources,
328 struct mux_look_up *lookup)
329 {
330 const struct audio_stream *source;
331 uint32_t elem;
332
333 /* init pointers */
334 for (elem = 0; elem < lookup->num_elems; elem++) {
335 source = sources[lookup->copy_elem[elem].stream_id];
336
337 lookup->copy_elem[elem].src = (int32_t *)source->r_ptr +
338 lookup->copy_elem[elem].in_ch;
339 lookup->copy_elem[elem].src_inc = source->channels;
340
341 lookup->copy_elem[elem].dest = (int32_t *)sink->w_ptr +
342 lookup->copy_elem[elem].out_ch;
343 lookup->copy_elem[elem].dest_inc = sink->channels;
344 }
345 }
346
demux_init_look_up_pointers_s32(struct comp_dev * dev,struct audio_stream * sink,const struct audio_stream * source,struct mux_look_up * lookup)347 static void demux_init_look_up_pointers_s32(struct comp_dev *dev,
348 struct audio_stream *sink,
349 const struct audio_stream *source,
350 struct mux_look_up *lookup)
351 {
352 uint32_t elem;
353
354 /* init pointers */
355 for (elem = 0; elem < lookup->num_elems; elem++) {
356 lookup->copy_elem[elem].src = (int32_t *)source->r_ptr +
357 lookup->copy_elem[elem].in_ch;
358 lookup->copy_elem[elem].src_inc = source->channels;
359
360 lookup->copy_elem[elem].dest = (int32_t *)sink->w_ptr +
361 lookup->copy_elem[elem].out_ch;
362 lookup->copy_elem[elem].dest_inc = sink->channels;
363 }
364 }
365
366 /**
367 * Source stream are routed to sinks with regard to look up table based on
368 * routing bitmasks from mux_stream_data structures array. Each sink channel
369 * has it's own lookup[].copy_elem describing source and sink fragment of
370 * memory featured in copying.
371 *
372 * @param[in] dev Component device
373 * @param[in,out] sink Destination buffer.
374 * @param[in,out] sources Array of source buffers.
375 * @param[in] frames Number of frames to process.
376 * @param[in] lookup mux look up table.
377 */
demux_s32le(struct comp_dev * dev,struct audio_stream * sink,const struct audio_stream * source,uint32_t frames,struct mux_look_up * lookup)378 static void demux_s32le(struct comp_dev *dev, struct audio_stream *sink,
379 const struct audio_stream *source, uint32_t frames,
380 struct mux_look_up *lookup)
381 {
382 uint8_t i;
383 int32_t *src;
384 int32_t *dst;
385 uint32_t elem;
386 uint32_t frames_without_wrap;
387
388 comp_dbg(dev, "demux_s32le");
389
390 if (!lookup || !lookup->num_elems)
391 return;
392
393 demux_init_look_up_pointers_s32(dev, sink, source, lookup);
394
395 while (frames) {
396 frames_without_wrap =
397 demux_calc_frames_without_wrap_s32(dev, sink, source,
398 lookup);
399
400 frames_without_wrap = MIN(frames, frames_without_wrap);
401
402 for (i = 0; i < frames_without_wrap; i++) {
403 for (elem = 0; elem < lookup->num_elems; elem++) {
404 src = (int32_t *)lookup->copy_elem[elem].src;
405 dst = (int32_t *)lookup->copy_elem[elem].dest;
406 *dst = *src;
407 lookup->copy_elem[elem].src = src +
408 lookup->copy_elem[elem].src_inc;
409 lookup->copy_elem[elem].dest = dst +
410 lookup->copy_elem[elem].dest_inc;
411 }
412 }
413
414 demux_check_for_wrap(sink, source, lookup);
415
416 frames -= frames_without_wrap;
417 }
418 }
419
420 /**
421 * Source streams are routed to sink with regard to look up table based on
422 * routing bitmasks from mux_stream_data structures array. Each sink channel
423 * has it's own lookup[].copy_elem describing source and sink fragment of
424 * memory featured in copying.
425 *
426 * @param[in] dev Component device
427 * @param[in,out] sink Destination buffer.
428 * @param[in,out] sources Array of source buffers.
429 * @param[in] frames Number of frames to process.
430 * @param[in] lookup mux look up table.
431 */
mux_s32le(struct comp_dev * dev,struct audio_stream * sink,const struct audio_stream ** sources,uint32_t frames,struct mux_look_up * lookup)432 static void mux_s32le(struct comp_dev *dev, struct audio_stream *sink,
433 const struct audio_stream **sources, uint32_t frames,
434 struct mux_look_up *lookup)
435 {
436 uint8_t i;
437 int32_t *src;
438 int32_t *dst;
439 uint32_t elem;
440 uint32_t frames_without_wrap;
441
442 comp_dbg(dev, "mux_s32le()");
443
444 if (!lookup || !lookup->num_elems)
445 return;
446
447 mux_init_look_up_pointers_s32(dev, sink, sources, lookup);
448
449 while (frames) {
450 frames_without_wrap =
451 mux_calc_frames_without_wrap_s32(dev, sink, sources,
452 lookup);
453
454 frames_without_wrap = MIN(frames, frames_without_wrap);
455
456 for (i = 0; i < frames_without_wrap; i++) {
457 for (elem = 0; elem < lookup->num_elems; elem++) {
458 src = (int32_t *)lookup->copy_elem[elem].src;
459 dst = (int32_t *)lookup->copy_elem[elem].dest;
460 *dst = *src;
461 lookup->copy_elem[elem].src = src +
462 lookup->copy_elem[elem].src_inc;
463 lookup->copy_elem[elem].dest = dst +
464 lookup->copy_elem[elem].dest_inc;
465 }
466 }
467
468 mux_check_for_wrap(sink, sources, lookup);
469
470 frames -= frames_without_wrap;
471 }
472 }
473
474 #endif /* CONFIG_FORMAT_S24LE CONFIG_FORMAT_S32LE */
475
476 const struct comp_func_map mux_func_map[] = {
477 #if CONFIG_FORMAT_S16LE
478 { SOF_IPC_FRAME_S16_LE, &mux_s16le, &demux_s16le },
479 #endif
480 #if CONFIG_FORMAT_S24LE
481 { SOF_IPC_FRAME_S24_4LE, &mux_s32le, &demux_s32le },
482 #endif
483 #if CONFIG_FORMAT_S32LE
484 { SOF_IPC_FRAME_S32_LE, &mux_s32le, &demux_s32le },
485 #endif
486 };
487
mux_prepare_look_up_table(struct comp_dev * dev)488 void mux_prepare_look_up_table(struct comp_dev *dev)
489 {
490 struct comp_data *cd = comp_get_drvdata(dev);
491 uint8_t i;
492 uint8_t j;
493 uint8_t k;
494 uint8_t idx = 0;
495
496 /* Prepare look up table */
497 for (i = 0; i < cd->config.num_streams; i++) {
498 for (j = 0; j < PLATFORM_MAX_CHANNELS; j++) {
499 for (k = 0; k < PLATFORM_MAX_CHANNELS; k++) {
500 if (cd->config.streams[i].mask[j] & BIT(k)) {
501 /* MUX component has only one sink */
502 cd->lookup[0].copy_elem[idx].in_ch = j;
503 cd->lookup[0].copy_elem[idx].out_ch = k;
504 cd->lookup[0].copy_elem[idx].stream_id =
505 i;
506 cd->lookup[0].num_elems = ++idx;
507 }
508 }
509 }
510 }
511 }
512
demux_prepare_look_up_table(struct comp_dev * dev)513 void demux_prepare_look_up_table(struct comp_dev *dev)
514 {
515 struct comp_data *cd = comp_get_drvdata(dev);
516 uint8_t i;
517 uint8_t j;
518 uint8_t k;
519 uint8_t idx;
520
521 /* Prepare look up table */
522 for (i = 0; i < cd->config.num_streams; i++) {
523 idx = 0;
524 for (j = 0; j < PLATFORM_MAX_CHANNELS; j++) {
525 for (k = 0; k < PLATFORM_MAX_CHANNELS; k++) {
526 if (cd->config.streams[i].mask[j] & BIT(k)) {
527 /* DEMUX component has only one source */
528 cd->lookup[i].copy_elem[idx].in_ch = k;
529 cd->lookup[i].copy_elem[idx].out_ch = j;
530 cd->lookup[i].copy_elem[idx].stream_id =
531 i;
532 cd->lookup[i].num_elems = ++idx;
533 }
534 }
535 }
536 }
537 }
538
mux_get_processing_function(struct comp_dev * dev)539 mux_func mux_get_processing_function(struct comp_dev *dev)
540 {
541 struct comp_buffer *sinkb;
542 uint8_t i;
543
544 if (list_is_empty(&dev->bsink_list))
545 return NULL;
546
547 sinkb = list_first_item(&dev->bsink_list, struct comp_buffer,
548 source_list);
549
550 for (i = 0; i < ARRAY_SIZE(mux_func_map); i++) {
551 if (sinkb->stream.frame_fmt == mux_func_map[i].frame_format)
552 return mux_func_map[i].mux_proc_func;
553 }
554
555 return NULL;
556 }
557
demux_get_processing_function(struct comp_dev * dev)558 demux_func demux_get_processing_function(struct comp_dev *dev)
559 {
560 struct comp_buffer *sourceb;
561 uint8_t i;
562
563 if (list_is_empty(&dev->bsource_list))
564 return NULL;
565
566 sourceb = list_first_item(&dev->bsource_list, struct comp_buffer,
567 sink_list);
568
569 for (i = 0; i < ARRAY_SIZE(mux_func_map); i++) {
570 if (sourceb->stream.frame_fmt == mux_func_map[i].frame_format)
571 return mux_func_map[i].demux_proc_func;
572 }
573
574 return NULL;
575 }
576
577 #endif /* CONFIG_COMP_MUX */
578