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