1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2020 Intel Corporation. All rights reserved.
4 //
5 // Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
6
7 #include <ipc/control.h>
8 #include <ipc/stream.h>
9 #include <ipc/topology.h>
10 #include <user/tdfb.h>
11 #include <user/trace.h>
12 #include <sof/common.h>
13 #include <sof/debug/panic.h>
14 #include <sof/ipc/msg.h>
15 #include <sof/lib/alloc.h>
16 #include <sof/lib/memory.h>
17 #include <sof/lib/uuid.h>
18 #include <sof/list.h>
19 #include <sof/platform.h>
20 #include <sof/string.h>
21 #include <sof/audio/buffer.h>
22 #include <sof/audio/component.h>
23 #include <sof/audio/pipeline.h>
24 #include <sof/audio/ipc-config.h>
25 #include <sof/audio/tdfb/tdfb_comp.h>
26 #include <sof/math/fir_generic.h>
27 #include <sof/math/fir_hifi2ep.h>
28 #include <sof/math/fir_hifi3.h>
29 #include <sof/trace/trace.h>
30 #include <sof/ut.h>
31 #include <errno.h>
32 #include <stddef.h>
33 #include <stdint.h>
34
35 /* The driver assigns running numbers for control index. If there's single control of
36 * type switch, enum, binary they all have index 0.
37 */
38 #define CTRL_INDEX_PROCESS 0 /* switch */
39 #define CTRL_INDEX_DIRECTION 1 /* switch */
40 #define CTRL_INDEX_AZIMUTH 0 /* enum */
41 #define CTRL_INDEX_AZIMUTH_ESTIMATE 1 /* enum */
42 #define CTRL_INDEX_FILTERBANK 0 /* bytes */
43
44 static const struct comp_driver comp_tdfb;
45
46 /* dd511749-d9fa-455c-b3a7-13585693f1af */
47 DECLARE_SOF_RT_UUID("tdfb", tdfb_uuid, 0xdd511749, 0xd9fa, 0x455c, 0xb3, 0xa7,
48 0x13, 0x58, 0x56, 0x93, 0xf1, 0xaf);
49
50 DECLARE_TR_CTX(tdfb_tr, SOF_UUID(tdfb_uuid), LOG_LEVEL_INFO);
51
52 /* IPC */
53
init_get_ctl_ipc(struct comp_dev * dev)54 static int init_get_ctl_ipc(struct comp_dev *dev)
55 {
56 struct tdfb_comp_data *cd = comp_get_drvdata(dev);
57 int comp_id = dev_comp_id(dev);
58
59 cd->ctrl_data = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, TDFB_GET_CTRL_DATA_SIZE);
60 if (!cd->ctrl_data)
61 return -ENOMEM;
62
63 cd->ctrl_data->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | SOF_IPC_COMP_GET_VALUE | comp_id;
64 cd->ctrl_data->rhdr.hdr.size = TDFB_GET_CTRL_DATA_SIZE;
65 cd->msg = ipc_msg_init(cd->ctrl_data->rhdr.hdr.cmd, cd->ctrl_data->rhdr.hdr.size);
66
67 cd->ctrl_data->comp_id = comp_id;
68 cd->ctrl_data->type = SOF_CTRL_TYPE_VALUE_COMP_GET;
69 cd->ctrl_data->cmd = SOF_CTRL_CMD_ENUM;
70 cd->ctrl_data->index = CTRL_INDEX_AZIMUTH_ESTIMATE;
71 cd->ctrl_data->num_elems = 0;
72 return 0;
73 }
74
send_get_ctl_ipc(struct comp_dev * dev)75 static void send_get_ctl_ipc(struct comp_dev *dev)
76 {
77 struct tdfb_comp_data *cd = comp_get_drvdata(dev);
78
79 #if TDFB_ADD_DIRECTION_TO_GET_CMD
80 cd->ctrl_data->chanv[0].channel = 0;
81 cd->ctrl_data->chanv[0].value = cd->az_value_estimate;
82 cd->ctrl_data->num_elems = 1;
83 #endif
84
85 ipc_msg_send(cd->msg, cd->ctrl_data, false);
86 }
87
88 /*
89 * The optimized FIR functions variants need to be updated into function
90 * set_func.
91 */
92
93 #if CONFIG_FORMAT_S16LE
set_s16_fir(struct tdfb_comp_data * cd)94 static inline void set_s16_fir(struct tdfb_comp_data *cd)
95 {
96 cd->tdfb_func = tdfb_fir_s16;
97 }
98 #endif /* CONFIG_FORMAT_S16LE */
99 #if CONFIG_FORMAT_S24LE
set_s24_fir(struct tdfb_comp_data * cd)100 static inline void set_s24_fir(struct tdfb_comp_data *cd)
101 {
102 cd->tdfb_func = tdfb_fir_s24;
103 }
104 #endif /* CONFIG_FORMAT_S24LE */
105 #if CONFIG_FORMAT_S32LE
set_s32_fir(struct tdfb_comp_data * cd)106 static inline void set_s32_fir(struct tdfb_comp_data *cd)
107 {
108 cd->tdfb_func = tdfb_fir_s32;
109 }
110 #endif /* CONFIG_FORMAT_S32LE */
111
set_func(struct comp_dev * dev)112 static inline int set_func(struct comp_dev *dev)
113 {
114 struct tdfb_comp_data *cd = comp_get_drvdata(dev);
115 struct comp_buffer *sourceb;
116
117 sourceb = list_first_item(&dev->bsource_list, struct comp_buffer,
118 sink_list);
119
120 switch (sourceb->stream.frame_fmt) {
121 #if CONFIG_FORMAT_S16LE
122 case SOF_IPC_FRAME_S16_LE:
123 comp_info(dev, "set_func(), SOF_IPC_FRAME_S16_LE");
124 set_s16_fir(cd);
125 break;
126 #endif /* CONFIG_FORMAT_S16LE */
127 #if CONFIG_FORMAT_S24LE
128 case SOF_IPC_FRAME_S24_4LE:
129 comp_info(dev, "set_func(), SOF_IPC_FRAME_S24_4LE");
130 set_s24_fir(cd);
131 break;
132 #endif /* CONFIG_FORMAT_S24LE */
133 #if CONFIG_FORMAT_S32LE
134 case SOF_IPC_FRAME_S32_LE:
135 comp_info(dev, "set_func(), SOF_IPC_FRAME_S32_LE");
136 set_s32_fir(cd);
137 break;
138 #endif /* CONFIG_FORMAT_S32LE */
139 default:
140 comp_err(dev, "set_func(), invalid frame_fmt");
141 return -EINVAL;
142 }
143 return 0;
144 }
145
146 /*
147 * Control code functions next. The processing is in fir_ C modules.
148 */
149
tdfb_free_delaylines(struct tdfb_comp_data * cd)150 static void tdfb_free_delaylines(struct tdfb_comp_data *cd)
151 {
152 struct fir_state_32x16 *fir = cd->fir;
153 int i = 0;
154
155 /* Free the common buffer for all EQs and point then
156 * each FIR channel delay line to NULL.
157 */
158 rfree(cd->fir_delay);
159 cd->fir_delay = NULL;
160 cd->fir_delay_size = 0;
161 for (i = 0; i < PLATFORM_MAX_CHANNELS; i++)
162 fir[i].delay = NULL;
163 }
164
tdfb_filter_seek(struct sof_tdfb_config * config,int num_filters)165 static int16_t *tdfb_filter_seek(struct sof_tdfb_config *config, int num_filters)
166 {
167 struct sof_fir_coef_data *coef_data;
168 int i;
169 int16_t *coefp = ASSUME_ALIGNED(&config->data[0], 2); /* 2 for 16 bits data */
170
171 /* Note: FIR coefficients are int16_t. An uint_8 type pointer coefp
172 * is used for jumping in the flexible array member structs coef_data.
173 */
174 for (i = 0; i < num_filters; i++) {
175 coef_data = (struct sof_fir_coef_data *)coefp;
176 coefp = coef_data->coef + coef_data->length;
177 }
178
179 return coefp;
180 }
181
tdfb_init_coef(struct tdfb_comp_data * cd,int source_nch,int sink_nch)182 static int tdfb_init_coef(struct tdfb_comp_data *cd, int source_nch,
183 int sink_nch)
184 {
185 struct sof_fir_coef_data *coef_data;
186 struct sof_tdfb_config *config = cd->config;
187 int16_t *output_channel_mix_beam_off = NULL;
188 int16_t *coefp;
189 int size_sum = 0;
190 int min_delta_idx; /* Index to beam angle with smallest delta vs. target */
191 int min_delta; /* Smallest angle difference found in degrees */
192 int max_ch;
193 int num_filters;
194 int target_az; /* Target azimuth angle in degrees */
195 int delta; /* Target minus found angle in degrees absolute value */
196 int idx;
197 int s;
198 int i;
199
200 /* Sanity checks */
201 if (config->num_output_channels > PLATFORM_MAX_CHANNELS ||
202 !config->num_output_channels) {
203 comp_cl_err(&comp_tdfb, "tdfb_init_coef(), invalid num_output_channels %d",
204 config->num_output_channels);
205 return -EINVAL;
206 }
207
208 if (config->num_output_channels != sink_nch) {
209 comp_cl_err(&comp_tdfb, "tdfb_init_coef(), stream output channels count %d does not match configuration %d",
210 sink_nch, config->num_output_channels);
211 return -EINVAL;
212 }
213
214 if (config->num_filters > SOF_TDFB_FIR_MAX_COUNT) {
215 comp_cl_err(&comp_tdfb, "tdfb_init_coef(), invalid num_filters %d",
216 config->num_filters);
217 return -EINVAL;
218 }
219
220 if (config->num_angles > SOF_TDFB_MAX_ANGLES) {
221 comp_cl_err(&comp_tdfb, "tdfb_init_coef(), invalid num_angles %d",
222 config->num_angles);
223 return -EINVAL;
224 }
225
226 if (config->beam_off_defined > 1) {
227 comp_cl_err(&comp_tdfb, "tdfb_init_coef(), invalid beam_off_defined %d",
228 config->beam_off_defined);
229 return -EINVAL;
230 }
231
232 if (config->num_mic_locations > SOF_TDFB_MAX_MICROPHONES) {
233 comp_cl_err(&comp_tdfb, "tdfb_init_coef(), invalid num_mic_locations %d",
234 config->num_mic_locations);
235 return -EINVAL;
236 }
237
238 /* In SOF v1.6 - 1.8 based beamformer topologies the multiple angles, mic locations,
239 * and beam on/off switch were not defined. Return error if such configuration is seen.
240 * A most basic blob has num_angles equals 1. Mic locations data is optional.
241 */
242 if (config->num_angles == 0 && config->num_mic_locations == 0) {
243 comp_cl_err(&comp_tdfb, "tdfb_init_coef(), ABI version less than 3.19.1 is not supported.");
244 return -EINVAL;
245 }
246
247 /* Skip filter coefficients */
248 num_filters = config->num_filters * (config->num_angles + config->beam_off_defined);
249 coefp = tdfb_filter_seek(config, num_filters);
250
251 /* Get shortcuts to input and output configuration */
252 cd->input_channel_select = coefp;
253 coefp += config->num_filters;
254 cd->output_channel_mix = coefp;
255 coefp += config->num_filters;
256 cd->output_stream_mix = coefp;
257 coefp += config->num_filters;
258
259 /* Check if there's beam-off configured, then get pointers to beam angles data
260 * and microphone locations. Finally check that size matches.
261 */
262 if (config->beam_off_defined) {
263 output_channel_mix_beam_off = coefp;
264 coefp += config->num_filters;
265 }
266 cd->filter_angles = (struct sof_tdfb_angle *)coefp;
267 cd->mic_locations = (struct sof_tdfb_mic_location *)
268 (&cd->filter_angles[config->num_angles]);
269 if ((uint8_t *)&cd->mic_locations[config->num_mic_locations] !=
270 (uint8_t *)config + config->size) {
271 comp_cl_err(&comp_tdfb, "tdfb_init_coef(), invalid config size");
272 return -EINVAL;
273 }
274
275 /* Skip to requested coefficient set */
276 min_delta = 360;
277 min_delta_idx = 0;
278 target_az = cd->az_value * config->angle_enum_mult + config->angle_enum_offs;
279 if (target_az > 180)
280 target_az -= 360;
281
282 if (target_az < -180)
283 target_az += 360;
284
285 for (i = 0; i < config->num_angles; i++) {
286 delta = ABS(target_az - cd->filter_angles[i].azimuth);
287 if (delta < min_delta) {
288 min_delta = delta;
289 min_delta_idx = i;
290 }
291 }
292
293 idx = cd->filter_angles[min_delta_idx].filter_index;
294 if (cd->beam_on) {
295 comp_cl_info(&comp_tdfb, "tdfb_init_coef(), angle request %d, found %d, idx %d",
296 target_az, cd->filter_angles[min_delta_idx].azimuth, idx);
297 } else if (config->beam_off_defined) {
298 cd->output_channel_mix = output_channel_mix_beam_off;
299 idx = config->num_filters * config->num_angles;
300 comp_cl_info(&comp_tdfb, "tdfb_init_coef(), configure beam off");
301 } else {
302 comp_cl_info(&comp_tdfb, "tdfb_init_coef(), beam off is not defined, using filter %d, idx %d",
303 cd->filter_angles[min_delta_idx].azimuth, idx);
304 }
305
306 /* Seek to proper filter for requested angle or beam off configuration */
307 coefp = tdfb_filter_seek(config, idx);
308
309 /* Initialize filter bank */
310 for (i = 0; i < config->num_filters; i++) {
311 /* Get delay line size */
312 coef_data = (struct sof_fir_coef_data *)coefp;
313 s = fir_delay_size(coef_data);
314 if (s > 0) {
315 size_sum += s;
316 } else {
317 comp_cl_info(&comp_tdfb, "tdfb_init_coef(), FIR length %d is invalid",
318 coef_data->length);
319 return -EINVAL;
320 }
321
322 /* Initialize coefficients for FIR filter and find next
323 * filter.
324 */
325 fir_init_coef(&cd->fir[i], coef_data);
326 coefp = coef_data->coef + coef_data->length;
327 }
328
329 /* Find max used input channel */
330 max_ch = 0;
331 for (i = 0; i < config->num_filters; i++) {
332 if (cd->input_channel_select[i] > max_ch)
333 max_ch = cd->input_channel_select[i];
334 }
335
336 /* The stream must contain at least the number of channels that is
337 * used for filters input.
338 */
339 if (max_ch + 1 > source_nch) {
340 comp_cl_err(&comp_tdfb, "tdfb_init_coef(), stream input channels count %d is not sufficient for configuration %d",
341 source_nch, max_ch + 1);
342 return -EINVAL;
343 }
344
345 return size_sum;
346 }
347
tdfb_init_delay(struct tdfb_comp_data * cd)348 static void tdfb_init_delay(struct tdfb_comp_data *cd)
349 {
350 int32_t *fir_delay = cd->fir_delay;
351 int i;
352
353 /* Initialize second phase to set delay lines pointers */
354 for (i = 0; i < cd->config->num_filters; i++) {
355 if (cd->fir[i].length > 0)
356 fir_init_delay(&cd->fir[i], &fir_delay);
357 }
358 }
359
tdfb_setup(struct tdfb_comp_data * cd,int source_nch,int sink_nch)360 static int tdfb_setup(struct tdfb_comp_data *cd, int source_nch, int sink_nch)
361 {
362 int delay_size;
363
364 /* Set coefficients for each channel from coefficient blob */
365 delay_size = tdfb_init_coef(cd, source_nch, sink_nch);
366 if (delay_size < 0)
367 return delay_size; /* Contains error code */
368
369 /* If all channels were set to bypass there's no need to
370 * allocate delay. Just return with success.
371 */
372 if (!delay_size)
373 return 0;
374
375 if (delay_size > cd->fir_delay_size) {
376 /* Free existing FIR channels data if it was allocated */
377 tdfb_free_delaylines(cd);
378
379 /* Allocate all FIR channels data in a big chunk and clear it */
380 cd->fir_delay = rballoc(0, SOF_MEM_CAPS_RAM, delay_size);
381 if (!cd->fir_delay) {
382 comp_cl_err(&comp_tdfb, "tdfb_setup(), delay allocation failed for size %d",
383 delay_size);
384 return -ENOMEM;
385 }
386
387 memset(cd->fir_delay, 0, delay_size);
388 cd->fir_delay_size = delay_size;
389 }
390
391 /* Assign delay line to all channel filters */
392 tdfb_init_delay(cd);
393
394 return 0;
395 }
396
397 /*
398 * End of algorithm code. Next the standard component methods.
399 */
400
tdfb_new(const struct comp_driver * drv,struct comp_ipc_config * config,void * spec)401 static struct comp_dev *tdfb_new(const struct comp_driver *drv,
402 struct comp_ipc_config *config,
403 void *spec)
404 {
405 struct ipc_config_process *ipc_tdfb = spec;
406 struct comp_dev *dev = NULL;
407 struct tdfb_comp_data *cd = NULL;
408 size_t bs = ipc_tdfb->size;
409 int ret;
410 int i;
411
412 comp_cl_info(&comp_tdfb, "tdfb_new()");
413
414 /* Check first that configuration blob size is sane */
415 if (bs > SOF_TDFB_MAX_SIZE) {
416 comp_cl_err(&comp_tdfb, "tdfb_new() error: configuration blob size = %u > %d",
417 bs, SOF_TDFB_MAX_SIZE);
418 return NULL;
419 }
420
421 dev = comp_alloc(drv, sizeof(*dev));
422 if (!dev)
423 return NULL;
424 dev->ipc_config = *config;
425
426 cd = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*cd));
427 if (!cd)
428 goto fail;
429
430 comp_set_drvdata(dev, cd);
431
432 /* Defaults for processing function pointer tdfb_func, fir_delay
433 * pointer, are NULL. Fir_delay_size is zero from rzalloc().
434 */
435
436 /* Defaults for enum controls are zeros from rzalloc()
437 * az_value is zero, beam off is false, and update is false.
438 */
439
440 /* Initialize IPC for direction of arrival estimate update */
441 ret = init_get_ctl_ipc(dev);
442 if (ret)
443 goto cd_fail;
444
445 /* Handler for configuration data */
446 cd->model_handler = comp_data_blob_handler_new(dev);
447 if (!cd->model_handler) {
448 comp_cl_err(&comp_tdfb, "tdfb_new(): comp_data_blob_handler_new() failed.");
449 goto cd_fail;
450 }
451
452 /* Get configuration data and reset FIR filters */
453 ret = comp_init_data_blob(cd->model_handler, bs, ipc_tdfb->data);
454 if (ret < 0) {
455 comp_cl_err(&comp_tdfb, "tdfb_new(): comp_init_data_blob() failed.");
456 goto cd_fail;
457 }
458
459 for (i = 0; i < PLATFORM_MAX_CHANNELS; i++)
460 fir_reset(&cd->fir[i]);
461
462 dev->state = COMP_STATE_READY;
463 return dev;
464
465 cd_fail:
466 comp_data_blob_handler_free(cd->model_handler); /* works for non-initialized also */
467 rfree(cd);
468 fail:
469 rfree(dev);
470 return NULL;
471 }
472
tdfb_free(struct comp_dev * dev)473 static void tdfb_free(struct comp_dev *dev)
474 {
475 struct tdfb_comp_data *cd = comp_get_drvdata(dev);
476
477 comp_info(dev, "tdfb_free()");
478
479 ipc_msg_free(cd->msg);
480 tdfb_free_delaylines(cd);
481 comp_data_blob_handler_free(cd->model_handler);
482
483 rfree(cd->ctrl_data);
484 rfree(cd);
485 rfree(dev);
486 }
487
tdfb_cmd_get_data(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata,int max_size)488 static int tdfb_cmd_get_data(struct comp_dev *dev,
489 struct sof_ipc_ctrl_data *cdata, int max_size)
490 {
491 struct tdfb_comp_data *cd = comp_get_drvdata(dev);
492
493 if (cdata->cmd == SOF_CTRL_CMD_BINARY) {
494 comp_dbg(dev, "tdfb_cmd_get_data(), SOF_CTRL_CMD_BINARY");
495 return comp_data_blob_get_cmd(cd->model_handler, cdata, max_size);
496 }
497
498 comp_err(dev, "tdfb_cmd_get_data() error: invalid cdata->cmd");
499 return -EINVAL;
500 }
501
tdfb_cmd_switch_get(struct sof_ipc_ctrl_data * cdata,struct tdfb_comp_data * cd)502 static int tdfb_cmd_switch_get(struct sof_ipc_ctrl_data *cdata, struct tdfb_comp_data *cd)
503 {
504 int j;
505
506 /* Fail if wrong index in control, needed if several in same type */
507 if (cdata->index != CTRL_INDEX_PROCESS)
508 return -EINVAL;
509
510 for (j = 0; j < cdata->num_elems; j++)
511 cdata->chanv[j].value = cd->beam_on;
512
513 return 0;
514 }
515
tdfb_cmd_enum_get(struct sof_ipc_ctrl_data * cdata,struct tdfb_comp_data * cd)516 static int tdfb_cmd_enum_get(struct sof_ipc_ctrl_data *cdata, struct tdfb_comp_data *cd)
517 {
518 int j;
519
520 switch (cdata->index) {
521 case CTRL_INDEX_AZIMUTH:
522 for (j = 0; j < cdata->num_elems; j++)
523 cdata->chanv[j].value = cd->az_value;
524
525 break;
526 case CTRL_INDEX_AZIMUTH_ESTIMATE:
527 for (j = 0; j < cdata->num_elems; j++)
528 cdata->chanv[j].value = cd->az_value_estimate;
529
530 break;
531 default:
532 return -EINVAL;
533 }
534
535 return 0;
536 }
537
tdfb_cmd_get_value(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata)538 static int tdfb_cmd_get_value(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata)
539 {
540 struct tdfb_comp_data *cd = comp_get_drvdata(dev);
541
542 switch (cdata->cmd) {
543 case SOF_CTRL_CMD_ENUM:
544 comp_dbg(dev, "tdfb_cmd_get_value(), SOF_CTRL_CMD_ENUM index=%d", cdata->index);
545 return tdfb_cmd_enum_get(cdata, cd);
546 case SOF_CTRL_CMD_SWITCH:
547 comp_dbg(dev, "tdfb_cmd_get_value(), SOF_CTRL_CMD_SWITCH index=%d", cdata->index);
548 return tdfb_cmd_switch_get(cdata, cd);
549 }
550
551 comp_err(dev, "tdfb_cmd_get_value() error: invalid cdata->cmd");
552 return -EINVAL;
553 }
554
tdfb_cmd_set_data(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata)555 static int tdfb_cmd_set_data(struct comp_dev *dev,
556 struct sof_ipc_ctrl_data *cdata)
557 {
558 struct tdfb_comp_data *cd = comp_get_drvdata(dev);
559
560 if (cdata->cmd == SOF_CTRL_CMD_BINARY) {
561 comp_dbg(dev, "tdfb_cmd_set_data(), SOF_CTRL_CMD_BINARY");
562 return comp_data_blob_set_cmd(cd->model_handler, cdata);
563 }
564
565 comp_err(dev, "tdfb_cmd_set_data() error: invalid cdata->cmd");
566 return -EINVAL;
567 }
568
tdfb_cmd_enum_set(struct sof_ipc_ctrl_data * cdata,struct tdfb_comp_data * cd)569 static int tdfb_cmd_enum_set(struct sof_ipc_ctrl_data *cdata, struct tdfb_comp_data *cd)
570 {
571 if (cdata->num_elems != 1)
572 return -EINVAL;
573
574 if (cdata->chanv[0].value > SOF_TDFB_MAX_ANGLES)
575 return -EINVAL;
576
577 switch (cdata->index) {
578 case CTRL_INDEX_AZIMUTH:
579 cd->az_value = cdata->chanv[0].value;
580 cd->update = true;
581 break;
582 case CTRL_INDEX_AZIMUTH_ESTIMATE:
583 cd->az_value_estimate = cdata->chanv[0].value;
584 break;
585 default:
586 return -EINVAL;
587 }
588
589 return 0;
590 }
591
tdfb_cmd_switch_set(struct sof_ipc_ctrl_data * cdata,struct tdfb_comp_data * cd)592 static int tdfb_cmd_switch_set(struct sof_ipc_ctrl_data *cdata, struct tdfb_comp_data *cd)
593 {
594 if (cdata->num_elems != 1)
595 return -EINVAL;
596
597 switch (cdata->index) {
598 case CTRL_INDEX_PROCESS:
599 cd->beam_on = cdata->chanv[0].value;
600 cd->update = true;
601 break;
602 case CTRL_INDEX_DIRECTION:
603 cd->direction_updates = cdata->chanv[0].value;
604 break;
605 default:
606 return -EINVAL;
607 }
608
609 return 0;
610 }
611
tdfb_cmd_set_value(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata)612 static int tdfb_cmd_set_value(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata)
613 {
614 struct tdfb_comp_data *cd = comp_get_drvdata(dev);
615
616 switch (cdata->cmd) {
617 case SOF_CTRL_CMD_ENUM:
618 comp_dbg(dev, "tdfb_cmd_set_value(), SOF_CTRL_CMD_ENUM index=%d", cdata->index);
619 return tdfb_cmd_enum_set(cdata, cd);
620 case SOF_CTRL_CMD_SWITCH:
621 comp_dbg(dev, "tdfb_cmd_set_value(), SOF_CTRL_CMD_SWITCH index=%d", cdata->index);
622 return tdfb_cmd_switch_set(cdata, cd);
623 }
624
625 comp_err(dev, "tdfb_cmd_set_value() error: invalid cdata->cmd");
626 return -EINVAL;
627 }
628
629 /* used to pass standard and bespoke commands (with data) to component */
tdfb_cmd(struct comp_dev * dev,int cmd,void * data,int max_data_size)630 static int tdfb_cmd(struct comp_dev *dev, int cmd, void *data,
631 int max_data_size)
632 {
633 struct sof_ipc_ctrl_data *cdata = ASSUME_ALIGNED(data, 4);
634
635 comp_info(dev, "tdfb_cmd()");
636
637 switch (cmd) {
638 case COMP_CMD_SET_DATA:
639 comp_dbg(dev, "tdfb_cmd(): COMP_CMD_SET_DATA");
640 return tdfb_cmd_set_data(dev, cdata);
641 case COMP_CMD_GET_DATA:
642 comp_dbg(dev, "tdfb_cmd(): COMP_CMD_GET_DATA");
643 return tdfb_cmd_get_data(dev, cdata, max_data_size);
644 case COMP_CMD_SET_VALUE:
645 comp_dbg(dev, "tdfb_cmd(): COMP_CMD_SET_VALUE");
646 return tdfb_cmd_set_value(dev, cdata);
647 case COMP_CMD_GET_VALUE:
648 comp_dbg(dev, "tdfb_cmd(): COMP_CMD_GET_VALUE");
649 return tdfb_cmd_get_value(dev, cdata);
650 }
651
652 comp_err(dev, "tdfb_cmd() error: invalid command");
653 return -EINVAL;
654 }
655
656 /* Placeholder function for sound direction estimate */
update_direction_of_arrival(struct tdfb_comp_data * cd)657 static void update_direction_of_arrival(struct tdfb_comp_data *cd)
658 {
659 }
660
tdfb_process(struct comp_dev * dev,struct comp_buffer * source,struct comp_buffer * sink,int frames,uint32_t source_bytes,uint32_t sink_bytes)661 static void tdfb_process(struct comp_dev *dev, struct comp_buffer *source,
662 struct comp_buffer *sink, int frames,
663 uint32_t source_bytes, uint32_t sink_bytes)
664 {
665 struct tdfb_comp_data *cd = comp_get_drvdata(dev);
666
667 buffer_invalidate(source, source_bytes);
668
669 cd->tdfb_func(cd, &source->stream, &sink->stream, frames);
670
671 buffer_writeback(sink, sink_bytes);
672
673 /* calc new free and available */
674 comp_update_buffer_consume(source, source_bytes);
675 comp_update_buffer_produce(sink, sink_bytes);
676 }
677
678 /* copy and process stream data from source to sink buffers */
tdfb_copy(struct comp_dev * dev)679 static int tdfb_copy(struct comp_dev *dev)
680 {
681 struct comp_copy_limits cl;
682 struct comp_buffer *sourceb;
683 struct comp_buffer *sinkb;
684 struct tdfb_comp_data *cd = comp_get_drvdata(dev);
685 int ret;
686 int n;
687
688 comp_dbg(dev, "tdfb_copy()");
689
690 sourceb = list_first_item(&dev->bsource_list, struct comp_buffer,
691 sink_list);
692 sinkb = list_first_item(&dev->bsink_list, struct comp_buffer,
693 source_list);
694
695 /* Check for changed configuration */
696 if (comp_is_new_data_blob_available(cd->model_handler)) {
697 cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL);
698 ret = tdfb_setup(cd, sourceb->stream.channels, sinkb->stream.channels);
699 if (ret < 0) {
700 comp_err(dev, "tdfb_copy(), failed FIR setup");
701 return ret;
702 }
703 }
704
705 /* Handle enum controls */
706 if (cd->update) {
707 cd->update = false;
708 ret = tdfb_setup(cd, sourceb->stream.channels, sinkb->stream.channels);
709 if (ret < 0) {
710 comp_err(dev, "tdfb_copy(), failed FIR setup");
711 return ret;
712 }
713 }
714
715 /* Get source, sink, number of frames etc. to process. */
716 comp_get_copy_limits(sourceb, sinkb, &cl);
717
718 /*
719 * Process only even number of frames with the FIR function. The
720 * optimized filter function loads the successive input samples from
721 * internal delay line with a 64 bit load operation.
722 */
723 if (cl.frames >= 2) {
724 n = (cl.frames >> 1) << 1;
725
726 /* Run the process function */
727 tdfb_process(dev, sourceb, sinkb, n,
728 n * cl.source_frame_bytes,
729 n * cl.sink_frame_bytes);
730 }
731
732 /* TODO: Update direction of arrival estimate */
733 update_direction_of_arrival(cd);
734 if (cd->direction_updates && cd->direction_change)
735 send_get_ctl_ipc(dev);
736
737 return 0;
738 }
739
tdfb_prepare(struct comp_dev * dev)740 static int tdfb_prepare(struct comp_dev *dev)
741 {
742 struct tdfb_comp_data *cd = comp_get_drvdata(dev);
743 struct comp_buffer *sourceb;
744 struct comp_buffer *sinkb;
745 int ret;
746
747 comp_info(dev, "tdfb_prepare()");
748
749 ret = comp_set_state(dev, COMP_TRIGGER_PREPARE);
750 if (ret < 0)
751 return ret;
752
753 if (ret == COMP_STATUS_STATE_ALREADY_SET)
754 return PPL_STATUS_PATH_STOP;
755
756 /* Find source and sink buffers */
757 sourceb = list_first_item(&dev->bsource_list,
758 struct comp_buffer, sink_list);
759 sinkb = list_first_item(&dev->bsink_list,
760 struct comp_buffer, source_list);
761
762 /* Initialize filter */
763 cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL);
764 if (cd->config) {
765 ret = tdfb_setup(cd, sourceb->stream.channels, sinkb->stream.channels);
766 if (ret < 0) {
767 comp_err(dev, "tdfb_prepare() error: tdfb_setup failed.");
768 goto err;
769 }
770
771 /* Clear in/out buffers */
772 memset(cd->in, 0, TDFB_IN_BUF_LENGTH * sizeof(int32_t));
773 memset(cd->out, 0, TDFB_IN_BUF_LENGTH * sizeof(int32_t));
774
775 ret = set_func(dev);
776 return ret;
777 }
778
779 err:
780 comp_set_state(dev, COMP_TRIGGER_RESET);
781 return ret;
782 }
783
tdfb_reset(struct comp_dev * dev)784 static int tdfb_reset(struct comp_dev *dev)
785 {
786 int i;
787 struct tdfb_comp_data *cd = comp_get_drvdata(dev);
788
789 comp_info(dev, "tdfb_reset()");
790
791 tdfb_free_delaylines(cd);
792
793 cd->tdfb_func = NULL;
794 for (i = 0; i < PLATFORM_MAX_CHANNELS; i++)
795 fir_reset(&cd->fir[i]);
796
797 /* Clear in/out buffers */
798 memset(cd->in, 0, TDFB_IN_BUF_LENGTH * sizeof(int32_t));
799 memset(cd->out, 0, TDFB_IN_BUF_LENGTH * sizeof(int32_t));
800
801 comp_set_state(dev, COMP_TRIGGER_RESET);
802 return 0;
803 }
804
tdfb_trigger(struct comp_dev * dev,int cmd)805 static int tdfb_trigger(struct comp_dev *dev, int cmd)
806 {
807 int ret = 0;
808
809 comp_info(dev, "tdfb_trigger(), command = %u", cmd);
810
811 ret = comp_set_state(dev, cmd);
812 if (ret == COMP_STATUS_STATE_ALREADY_SET)
813 ret = PPL_STATUS_PATH_STOP;
814
815 return ret;
816 }
817
818 static const struct comp_driver comp_tdfb = {
819 .uid = SOF_RT_UUID(tdfb_uuid),
820 .tctx = &tdfb_tr,
821 .ops = {
822 .create = tdfb_new,
823 .free = tdfb_free,
824 .cmd = tdfb_cmd,
825 .copy = tdfb_copy,
826 .prepare = tdfb_prepare,
827 .reset = tdfb_reset,
828 .trigger = tdfb_trigger,
829 },
830 };
831
832 static SHARED_DATA struct comp_driver_info comp_tdfb_info = {
833 .drv = &comp_tdfb,
834 };
835
sys_comp_tdfb_init(void)836 UT_STATIC void sys_comp_tdfb_init(void)
837 {
838 comp_register(platform_shared_get(&comp_tdfb_info,
839 sizeof(comp_tdfb_info)));
840 }
841
842 DECLARE_MODULE(sys_comp_tdfb_init);
843