1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2019 Intel Corporation. All rights reserved.
4 //
5 // Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com>
6 
7 #include <sof/audio/buffer.h>
8 #include <sof/audio/component.h>
9 #include <sof/audio/data_blob.h>
10 #include <sof/audio/format.h>
11 #include <sof/audio/ipc-config.h>
12 #include <sof/audio/kpb.h>
13 #include <sof/common.h>
14 #include <sof/compiler_attributes.h>
15 #include <rtos/panic.h>
16 #include <sof/ipc/msg.h>
17 #include <rtos/alloc.h>
18 #include <rtos/init.h>
19 #include <sof/lib/memory.h>
20 #include <sof/lib/notifier.h>
21 #include <rtos/wait.h>
22 #include <sof/lib/uuid.h>
23 #include <sof/list.h>
24 #include <sof/math/numbers.h>
25 #include <rtos/string.h>
26 #include <sof/ut.h>
27 #include <sof/trace/trace.h>
28 #include <ipc/control.h>
29 #include <ipc/stream.h>
30 #include <ipc/topology.h>
31 #if CONFIG_IPC_MAJOR_4
32 #include <ipc4/detect_test.h>
33 #include <ipc4/notification.h>
34 #endif
35 #include <kernel/abi.h>
36 #include <sof/samples/audio/detect_test.h>
37 #include <user/trace.h>
38 #include <errno.h>
39 #include <stdbool.h>
40 #include <stddef.h>
41 #include <stdint.h>
42 #include <stdlib.h>
43 #include <sof/samples/audio/kwd_nn_detect_test.h>
44 
45 #define ACTIVATION_DEFAULT_SHIFT 3
46 #define ACTIVATION_DEFAULT_COEF 0.05
47 
48 #define ACTIVATION_DEFAULT_THRESHOLD_S16 \
49 	Q_CONVERT_FLOAT(ACTIVATION_DEFAULT_COEF, 15) /* Q1.15 */
50 #define ACTIVATION_DEFAULT_THRESHOLD_S24 \
51 		Q_CONVERT_FLOAT(ACTIVATION_DEFAULT_COEF, 23) /* Q1.23 */
52 #define ACTIVATION_DEFAULT_THRESHOLD_S32 \
53 		Q_CONVERT_FLOAT(ACTIVATION_DEFAULT_COEF, 31) /* Q1.31 */
54 
55 #define INITIAL_MODEL_DATA_SIZE 64
56 
57 /* default number of samples before detection is activated  */
58 #define KEYPHRASE_DEFAULT_PREAMBLE_LENGTH 0
59 
60 #define KWD_NN_BUFF_ALIGN	64
61 
62 static const struct comp_driver comp_keyword;
63 
64 LOG_MODULE_REGISTER(kd_test, CONFIG_SOF_LOG_LEVEL);
65 
66 /* eba8d51f-7827-47b5-82ee-de6e7743af67 */
67 DECLARE_SOF_RT_UUID("kd-test", keyword_uuid, 0xeba8d51f, 0x7827, 0x47b5,
68 		 0x82, 0xee, 0xde, 0x6e, 0x77, 0x43, 0xaf, 0x67);
69 
70 DECLARE_TR_CTX(keyword_tr, SOF_UUID(keyword_uuid), LOG_LEVEL_INFO);
71 
72 struct comp_data {
73 #ifdef CONFIG_IPC_MAJOR_4
74 	struct ipc4_base_module_cfg base_cfg;
75 #endif
76 	struct sof_detect_test_config config;
77 	struct comp_data_blob_handler *model_handler;
78 	void *data_blob;
79 	size_t data_blob_size;
80 	uint32_t data_blob_crc;
81 
82 	int32_t activation;
83 	uint32_t detected;
84 	uint32_t detect_preamble; /**< current keyphrase preamble length */
85 	uint32_t keyphrase_samples; /**< keyphrase length in samples */
86 	uint32_t drain_req; /** defines draining size in bytes. */
87 	uint16_t sample_valid_bytes;
88 	struct kpb_event_data event_data;
89 	struct kpb_client client_data;
90 
91 #if CONFIG_KWD_NN_SAMPLE_KEYPHRASE
92 	int16_t *input;
93 	size_t input_size;
94 #endif
95 
96 	struct ipc_msg *msg;	/**< host notification */
97 
98 	void (*detect_func)(struct comp_dev *dev,
99 			    const struct audio_stream __sparse_cache *source, uint32_t frames);
100 	struct sof_ipc_comp_event event;
101 };
102 
detector_is_sample_width_supported(enum sof_ipc_frame sf)103 static inline bool detector_is_sample_width_supported(enum sof_ipc_frame sf)
104 {
105 	bool ret;
106 
107 	switch (sf) {
108 #if CONFIG_FORMAT_S16LE
109 	case SOF_IPC_FRAME_S16_LE:
110 		/* FALLTHRU */
111 #endif /* CONFIG_FORMAT_S16LE */
112 #if CONFIG_FORMAT_S24LE
113 	case SOF_IPC_FRAME_S24_4LE:
114 		/* FALLTHRU */
115 #endif /* CONFIG_FORMAT_S24LE */
116 #if CONFIG_FORMAT_S32LE
117 	case SOF_IPC_FRAME_S32_LE:
118 		/* FALLTHRU */
119 #endif /* CONFIG_FORMAT_S32LE */
120 		ret = true;
121 		break;
122 	default:
123 		ret = false;
124 		break;
125 	}
126 
127 	return ret;
128 }
129 
notify_host(const struct comp_dev * dev)130 static void notify_host(const struct comp_dev *dev)
131 {
132 	struct comp_data *cd = comp_get_drvdata(dev);
133 
134 	comp_info(dev, "notify_host()");
135 
136 #if CONFIG_IPC_MAJOR_4
137 	ipc_msg_send(cd->msg, NULL, true);
138 #else
139 	ipc_msg_send(cd->msg, &cd->event, true);
140 #endif /* CONFIG_IPC_MAJOR_4 */
141 }
142 
notify_kpb(const struct comp_dev * dev)143 static void notify_kpb(const struct comp_dev *dev)
144 {
145 	struct comp_data *cd = comp_get_drvdata(dev);
146 
147 	comp_info(dev, "notify_kpb(), preamble: %u", cd->detect_preamble);
148 
149 	cd->client_data.r_ptr = NULL;
150 	cd->client_data.sink = NULL;
151 	cd->client_data.id = 0; /**< TODO: acquire proper id from kpb */
152 	/* time in milliseconds */
153 	cd->client_data.drain_req = (cd->drain_req != 0) ?
154 					 cd->drain_req :
155 					 cd->config.drain_req;
156 	cd->event_data.event_id = KPB_EVENT_BEGIN_DRAINING;
157 	cd->event_data.client_data = &cd->client_data;
158 
159 	notifier_event(dev, NOTIFIER_ID_KPB_CLIENT_EVT,
160 		       NOTIFIER_TARGET_CORE_ALL_MASK, &cd->event_data,
161 		       sizeof(cd->event_data));
162 }
163 
detect_test_notify(const struct comp_dev * dev)164 void detect_test_notify(const struct comp_dev *dev)
165 {
166 	notify_host(dev);
167 	notify_kpb(dev);
168 }
169 
default_detect_test(struct comp_dev * dev,const struct audio_stream __sparse_cache * source,uint32_t frames)170 static void default_detect_test(struct comp_dev *dev,
171 				const struct audio_stream __sparse_cache *source,
172 				uint32_t frames)
173 {
174 	struct comp_data *cd = comp_get_drvdata(dev);
175 	void *src;
176 	int32_t diff;
177 	uint32_t count = frames; /**< Assuming single channel */
178 	uint32_t sample;
179 	uint16_t valid_bits = cd->sample_valid_bytes * 8;
180 	const int32_t activation_threshold = cd->config.activation_threshold;
181 	uint32_t cycles_per_frame; /**< Clock cycles required per frame */
182 
183 	/* synthetic load */
184 	if (cd->config.load_mips) {
185 		/* assuming count is a processing frame size in samples */
186 		cycles_per_frame = (cd->config.load_mips * 1000000 * count)
187 				   / source->rate;
188 		idelay(cycles_per_frame);
189 	}
190 
191 	/* perform detection within current period */
192 	for (sample = 0; sample < count && !cd->detected; ++sample) {
193 		src = (valid_bits == 16U) ?
194 		      audio_stream_read_frag_s16(source, sample) :
195 		      audio_stream_read_frag_s32(source, sample);
196 		if (valid_bits > 16U) {
197 			diff = abs(*(int32_t *)src) - abs(cd->activation);
198 		} else {
199 			diff = abs(*(int16_t *)src) -
200 			       abs((int16_t)cd->activation);
201 		}
202 
203 		diff >>= cd->config.activation_shift;
204 		cd->activation += diff;
205 
206 		if (cd->detect_preamble >= cd->keyphrase_samples) {
207 			if (cd->activation >= activation_threshold) {
208 				/* The algorithm shall use cd->drain_req
209 				 * to specify its draining size request.
210 				 * Zero value means default config value
211 				 * will be used.
212 				 */
213 				cd->drain_req = 0;
214 				detect_test_notify(dev);
215 				cd->detected = 1;
216 			}
217 		} else {
218 			++cd->detect_preamble;
219 		}
220 	}
221 }
222 
test_keyword_get_threshold(struct comp_dev * dev,int sample_width)223 static int test_keyword_get_threshold(struct comp_dev *dev, int sample_width)
224 {
225 	switch (sample_width) {
226 #if CONFIG_FORMAT_S16LE
227 	case 16:
228 		return ACTIVATION_DEFAULT_THRESHOLD_S16;
229 #endif /* CONFIG_FORMAT_S16LE */
230 #if CONFIG_FORMAT_S24LE
231 	case 24:
232 		return ACTIVATION_DEFAULT_THRESHOLD_S24;
233 #endif /* CONFIG_FORMAT_S24LE */
234 #if CONFIG_FORMAT_S32LE
235 	case 32:
236 		return ACTIVATION_DEFAULT_THRESHOLD_S32;
237 #endif /* CONFIG_FORMAT_S32LE */
238 	default:
239 		comp_err(dev, "test_keyword_get_threshold(), unsupported sample width: %d",
240 			 sample_width);
241 		return -EINVAL;
242 	}
243 }
244 
test_keyword_apply_config(struct comp_dev * dev,const struct sof_detect_test_config * cfg)245 static int test_keyword_apply_config(struct comp_dev *dev,
246 				     const struct sof_detect_test_config *cfg)
247 {
248 	struct comp_data *cd = comp_get_drvdata(dev);
249 	uint16_t sample_width;
250 	int ret;
251 
252 	ret = memcpy_s(&cd->config, sizeof(cd->config), cfg,
253 		       sizeof(struct sof_detect_test_config));
254 	assert(!ret);
255 
256 #if CONFIG_IPC_MAJOR_4
257 	sample_width = cd->base_cfg.audio_fmt.depth;
258 #else
259 	sample_width = cd->config.sample_width;
260 #endif /* CONFIG_IPC_MAJOR_4 */
261 
262 	if (!cd->config.activation_shift)
263 		cd->config.activation_shift = ACTIVATION_DEFAULT_SHIFT;
264 
265 	if (!cd->config.activation_threshold) {
266 		ret = test_keyword_get_threshold(dev, sample_width);
267 		if (ret < 0) {
268 			comp_err(dev, "test_keyword_apply_config(): unsupported sample width %u",
269 				 sample_width);
270 			return ret;
271 		}
272 
273 		cd->config.activation_threshold = ret;
274 	}
275 
276 	return 0;
277 }
278 
279 #if CONFIG_IPC_MAJOR_4
280 #define NOTIFICATION_DEFAULT_WORD_ID 1
281 #define NOTIFICATION_DEFAULT_SCORE 100
282 
test_keyword_set_params(struct comp_dev * dev,struct sof_ipc_stream_params * params)283 static void test_keyword_set_params(struct comp_dev *dev,
284 				    struct sof_ipc_stream_params *params)
285 {
286 	struct comp_data *cd = comp_get_drvdata(dev);
287 	uint32_t __sparse_cache valid_fmt, frame_fmt;
288 
289 	comp_info(dev, "test_keyword_set_params()");
290 
291 	memset(params, 0, sizeof(*params));
292 	params->channels = cd->base_cfg.audio_fmt.channels_count;
293 	params->rate = cd->base_cfg.audio_fmt.sampling_frequency;
294 	params->sample_container_bytes = cd->base_cfg.audio_fmt.depth / 8;
295 	params->sample_valid_bytes =
296 		cd->base_cfg.audio_fmt.valid_bit_depth / 8;
297 	params->buffer_fmt = cd->base_cfg.audio_fmt.interleaving_style;
298 	params->buffer.size = cd->base_cfg.ibs;
299 
300 	audio_stream_fmt_conversion(cd->base_cfg.audio_fmt.depth,
301 				    cd->base_cfg.audio_fmt.valid_bit_depth,
302 				    &frame_fmt, &valid_fmt,
303 				    cd->base_cfg.audio_fmt.s_type);
304 
305 	params->frame_fmt = frame_fmt;
306 }
307 
test_keyword_set_config(struct comp_dev * dev,const char * data,uint32_t data_size)308 static int test_keyword_set_config(struct comp_dev *dev, const char *data,
309 				   uint32_t data_size)
310 {
311 	const struct sof_detect_test_config *cfg;
312 	size_t cfg_size;
313 
314 	/* Copy new config */
315 	cfg = (const struct sof_detect_test_config *)data;
316 	cfg_size = data_size;
317 
318 	comp_info(dev, "test_keyword_set_config(): config size = %u",
319 		  cfg_size);
320 
321 	if (cfg_size != sizeof(struct sof_detect_test_config)) {
322 		comp_err(dev, "test_keyword_set_config(): invalid config size");
323 		return -EINVAL;
324 	}
325 
326 	return test_keyword_apply_config(dev, cfg);
327 }
328 
test_keyword_get_config(struct comp_dev * dev,char * data,uint32_t * data_size)329 static int test_keyword_get_config(struct comp_dev *dev, char *data,
330 				   uint32_t *data_size)
331 {
332 	struct comp_data *cd = comp_get_drvdata(dev);
333 	size_t cfg_size;
334 	int ret;
335 
336 	comp_info(dev, "test_keyword_get_config()");
337 
338 	cfg_size = sizeof(struct sof_detect_test_config);
339 
340 	if (cfg_size > *data_size) {
341 		comp_err(dev, "test_keyword_get_config(): wrong config size: %d",
342 			 *data_size);
343 		return -EINVAL;
344 	}
345 
346 	*data_size = cfg_size;
347 
348 	/* Copy back to user space */
349 	ret = memcpy_s(data, cfg_size, &cd->config, cfg_size);
350 	assert(!ret);
351 
352 	return 0;
353 }
354 
test_keyword_set_large_config(struct comp_dev * dev,uint32_t param_id,bool first_block,bool last_block,uint32_t data_offset,const char * data)355 static int test_keyword_set_large_config(struct comp_dev *dev,
356 					 uint32_t param_id,
357 					 bool first_block,
358 					 bool last_block,
359 					 uint32_t data_offset,
360 					 const char *data)
361 {
362 	comp_dbg(dev, "test_keyword_set_large_config()");
363 	struct comp_data *cd = comp_get_drvdata(dev);
364 
365 	switch (param_id) {
366 	case IPC4_DETECT_TEST_SET_MODEL_BLOB:
367 		return ipc4_comp_data_blob_set(cd->model_handler,
368 					       first_block,
369 					       last_block,
370 					       data_offset,
371 					       data);
372 	case IPC4_DETECT_TEST_SET_CONFIG:
373 		return test_keyword_set_config(dev, data, data_offset);
374 	default:
375 		return -EINVAL;
376 	}
377 }
378 
test_keyword_get_large_config(struct comp_dev * dev,uint32_t param_id,bool first_block,bool last_block,uint32_t * data_offset,char * data)379 static int test_keyword_get_large_config(struct comp_dev *dev,
380 					 uint32_t param_id,
381 					 bool first_block,
382 					 bool last_block,
383 					 uint32_t *data_offset,
384 					 char *data)
385 {
386 	comp_dbg(dev, "test_keyword_get_large_config()");
387 
388 	switch (param_id) {
389 	case IPC4_DETECT_TEST_GET_CONFIG:
390 		return test_keyword_get_config(dev, data, data_offset);
391 	default:
392 		return -EINVAL;
393 	}
394 }
395 
test_keyword_get_attribute(struct comp_dev * dev,uint32_t type,void * value)396 static int test_keyword_get_attribute(struct comp_dev *dev,
397 				      uint32_t type,
398 				      void *value)
399 {
400 	struct comp_data *cd = comp_get_drvdata(dev);
401 
402 	switch (type) {
403 	case COMP_ATTR_BASE_CONFIG:
404 		*(struct ipc4_base_module_cfg *)value = cd->base_cfg;
405 		break;
406 	default:
407 		return -EINVAL;
408 	}
409 
410 	return 0;
411 }
412 
ipc4_kd_notification_init(uint32_t word_id,uint32_t score)413 static struct ipc_msg *ipc4_kd_notification_init(uint32_t word_id,
414 						 uint32_t score)
415 {
416 	struct ipc4_voice_cmd_notification notif;
417 	struct ipc_msg *msg;
418 
419 	memset_s(&notif, sizeof(notif), 0, sizeof(notif));
420 
421 	notif.primary.r.word_id = word_id;
422 	notif.primary.r.notif_type = SOF_IPC4_NOTIFY_PHRASE_DETECTED;
423 	notif.primary.r.type = SOF_IPC4_GLB_NOTIFICATION;
424 	notif.primary.r.rsp = SOF_IPC4_MESSAGE_DIR_MSG_REQUEST;
425 	notif.primary.r.msg_tgt = SOF_IPC4_MESSAGE_TARGET_FW_GEN_MSG;
426 
427 	notif.extension.r.sv_score = score;
428 
429 	msg = ipc_msg_w_ext_init(notif.primary.dat,
430 				 notif.extension.dat,
431 				 0);
432 	if (!msg)
433 		return NULL;
434 
435 	return msg;
436 }
437 
438 #else /* CONFIG_IPC_MAJOR_4 */
test_keyword_set_config(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata)439 static int test_keyword_set_config(struct comp_dev *dev,
440 				   struct sof_ipc_ctrl_data *cdata)
441 {
442 	struct sof_detect_test_config *cfg;
443 	size_t bs;
444 
445 	/* Copy new config, find size from header */
446 	cfg = (struct sof_detect_test_config *)cdata->data->data;
447 	bs = cfg->size;
448 
449 	comp_info(dev, "test_keyword_set_config(), blob size = %u", bs);
450 
451 	if (bs != sizeof(struct sof_detect_test_config)) {
452 		comp_err(dev, "test_keyword_set_config(): invalid blob size");
453 		return -EINVAL;
454 	}
455 
456 	return test_keyword_apply_config(dev, cfg);
457 }
458 
test_keyword_set_params(struct comp_dev * dev,struct sof_ipc_stream_params * params)459 static void test_keyword_set_params(struct comp_dev *dev,
460 				    struct sof_ipc_stream_params *params)
461 {}
462 
test_keyword_ctrl_set_bin_data(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata)463 static int test_keyword_ctrl_set_bin_data(struct comp_dev *dev,
464 					  struct sof_ipc_ctrl_data *cdata)
465 {
466 	struct comp_data *cd = comp_get_drvdata(dev);
467 	int ret = 0;
468 
469 	if (dev->state != COMP_STATE_READY) {
470 		/* It is a valid request but currently this is not
471 		 * supported during playback/capture. The driver will
472 		 * re-send data in next resume when idle and the new
473 		 * configuration will be used when playback/capture
474 		 * starts.
475 		 */
476 		comp_err(dev, "keyword_ctrl_set_bin_data(): driver is busy");
477 		return -EBUSY;
478 	}
479 
480 	switch (cdata->data->type) {
481 	case SOF_DETECT_TEST_CONFIG:
482 		ret = test_keyword_set_config(dev, cdata);
483 		break;
484 	case SOF_DETECT_TEST_MODEL:
485 		ret = comp_data_blob_set_cmd(cd->model_handler, cdata);
486 		break;
487 	default:
488 		comp_err(dev, "keyword_ctrl_set_bin_data(): unknown binary data type");
489 		break;
490 	}
491 
492 	return ret;
493 }
494 
test_keyword_ctrl_set_data(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata)495 static int test_keyword_ctrl_set_data(struct comp_dev *dev,
496 				      struct sof_ipc_ctrl_data *cdata)
497 {
498 	int ret = 0;
499 
500 	/* Check version from ABI header */
501 	if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) {
502 		comp_err(dev, "test_keyword_cmd_set_data(): invalid version");
503 		return -EINVAL;
504 	}
505 
506 	switch (cdata->cmd) {
507 	case SOF_CTRL_CMD_ENUM:
508 		comp_info(dev, "test_keyword_cmd_set_data(), SOF_CTRL_CMD_ENUM");
509 		break;
510 	case SOF_CTRL_CMD_BINARY:
511 		comp_info(dev, "test_keyword_cmd_set_data(), SOF_CTRL_CMD_BINARY");
512 		ret = test_keyword_ctrl_set_bin_data(dev, cdata);
513 		break;
514 	default:
515 		comp_err(dev, "test_keyword_cmd_set_data(): invalid cdata->cmd");
516 		ret = -EINVAL;
517 		break;
518 	}
519 
520 	return ret;
521 }
522 
test_keyword_get_config(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata,int size)523 static int test_keyword_get_config(struct comp_dev *dev,
524 				   struct sof_ipc_ctrl_data *cdata, int size)
525 {
526 	struct comp_data *cd = comp_get_drvdata(dev);
527 	size_t bs;
528 	int ret = 0;
529 
530 	comp_info(dev, "test_keyword_get_config()");
531 
532 	/* Copy back to user space */
533 	bs = cd->config.size;
534 	comp_info(dev, "value of block size: %u", bs);
535 
536 	if (bs == 0 || bs > size)
537 		return -EINVAL;
538 
539 	ret = memcpy_s(cdata->data->data, size, &cd->config, bs);
540 	assert(!ret);
541 
542 	cdata->data->abi = SOF_ABI_VERSION;
543 	cdata->data->size = bs;
544 
545 	return ret;
546 }
547 
test_keyword_ctrl_get_bin_data(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata,int size)548 static int test_keyword_ctrl_get_bin_data(struct comp_dev *dev,
549 					  struct sof_ipc_ctrl_data *cdata,
550 					  int size)
551 {
552 	struct comp_data *cd = comp_get_drvdata(dev);
553 	int ret = 0;
554 
555 	switch (cdata->data->type) {
556 	case SOF_DETECT_TEST_CONFIG:
557 		ret = test_keyword_get_config(dev, cdata, size);
558 		break;
559 	case SOF_DETECT_TEST_MODEL:
560 		ret = comp_data_blob_get_cmd(cd->model_handler, cdata, size);
561 		break;
562 	default:
563 		comp_err(dev, "test_keyword_ctrl_get_bin_data(): unknown binary data type");
564 		break;
565 	}
566 
567 	return ret;
568 }
569 
test_keyword_ctrl_get_data(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata,int size)570 static int test_keyword_ctrl_get_data(struct comp_dev *dev,
571 				      struct sof_ipc_ctrl_data *cdata, int size)
572 {
573 	int ret = 0;
574 
575 	comp_info(dev, "test_keyword_ctrl_get_data() size: %d", size);
576 
577 	switch (cdata->cmd) {
578 	case SOF_CTRL_CMD_BINARY:
579 		ret = test_keyword_ctrl_get_bin_data(dev, cdata, size);
580 		break;
581 	default:
582 		comp_err(dev, "test_keyword_ctrl_get_data(): invalid cdata->cmd");
583 		return -EINVAL;
584 	}
585 
586 	return ret;
587 }
588 
589 /* used to pass standard and bespoke commands (with data) to component */
test_keyword_cmd(struct comp_dev * dev,int cmd,void * data,int max_data_size)590 static int test_keyword_cmd(struct comp_dev *dev, int cmd, void *data,
591 			    int max_data_size)
592 {
593 	struct sof_ipc_ctrl_data *cdata = ASSUME_ALIGNED(data, 4);
594 
595 	comp_info(dev, "test_keyword_cmd()");
596 
597 	switch (cmd) {
598 	case COMP_CMD_SET_DATA:
599 		return test_keyword_ctrl_set_data(dev, cdata);
600 	case COMP_CMD_GET_DATA:
601 		return test_keyword_ctrl_get_data(dev, cdata, max_data_size);
602 	default:
603 		return -EINVAL;
604 	}
605 }
606 #endif /* CONFIG_IPC_MAJOR_4 */
607 
test_keyword_new(const struct comp_driver * drv,const struct comp_ipc_config * config,const void * spec)608 static struct comp_dev *test_keyword_new(const struct comp_driver *drv,
609 					 const struct comp_ipc_config *config,
610 					 const void *spec)
611 {
612 	struct comp_dev *dev = NULL;
613 #if CONFIG_IPC_MAJOR_4
614 	const struct ipc4_base_module_cfg *base_cfg = spec;
615 #else
616 	const struct ipc_config_process *ipc_keyword = spec;
617 	const struct sof_detect_test_config *cfg;
618 	size_t bs;
619 #endif /* CONFIG_IPC_MAJOR_4 */
620 	struct comp_data *cd = NULL;
621 	int ret = 0;
622 
623 	comp_cl_info(&comp_keyword, "test_keyword_new()");
624 
625 	dev = comp_alloc(drv, sizeof(*dev));
626 	if (!dev)
627 		return NULL;
628 	dev->ipc_config = *config;
629 
630 	cd = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*cd));
631 
632 	if (!cd)
633 		goto fail;
634 
635 #if CONFIG_KWD_NN_SAMPLE_KEYPHRASE
636 	cd->detect_func = kwd_nn_detect_test;
637 #else
638 	/* using default processing function */
639 	cd->detect_func = default_detect_test;
640 #endif
641 
642 	comp_set_drvdata(dev, cd);
643 
644 	/* component model data handler */
645 	cd->model_handler = comp_data_blob_handler_new(dev);
646 
647 #if CONFIG_IPC_MAJOR_4
648 	/* For IPC4 we only receive the base_cfg, make a copy of it */
649 	memcpy_s(&cd->base_cfg, sizeof(cd->base_cfg), base_cfg, sizeof(*base_cfg));
650 #else
651 	cfg = (const struct sof_detect_test_config *)ipc_keyword->data;
652 	bs = ipc_keyword->size;
653 
654 	if (bs > 0) {
655 		if (bs < sizeof(struct sof_detect_test_config)) {
656 			comp_err(dev, "test_keyword_new(): invalid data size");
657 			goto cd_fail;
658 		}
659 
660 		if (test_keyword_apply_config(dev, cfg)) {
661 			comp_err(dev, "test_keyword_new(): failed to apply config");
662 			goto cd_fail;
663 		}
664 	}
665 #endif /* CONFIG_IPC_MAJOR_4 */
666 
667 	ret = comp_init_data_blob(cd->model_handler, INITIAL_MODEL_DATA_SIZE,
668 				  NULL);
669 	if (ret < 0) {
670 		comp_err(dev, "test_keyword_new(): model data initial failed");
671 		goto cd_fail;
672 	}
673 
674 	/* build component event */
675 	ipc_build_comp_event(&cd->event, dev->ipc_config.type, dev->ipc_config.id);
676 	cd->event.event_type = SOF_CTRL_EVENT_KD;
677 	cd->event.num_elems = 0;
678 
679 #if CONFIG_IPC_MAJOR_4
680 	cd->msg = ipc4_kd_notification_init(NOTIFICATION_DEFAULT_WORD_ID,
681 					    NOTIFICATION_DEFAULT_SCORE);
682 #else
683 	cd->msg = ipc_msg_init(cd->event.rhdr.hdr.cmd, sizeof(cd->event));
684 #endif /* CONFIG_IPC_MAJOR_4 */
685 
686 	if (!cd->msg) {
687 		comp_err(dev, "test_keyword_new(): ipc notification init failed");
688 		goto cd_fail;
689 	}
690 
691 #if CONFIG_KWD_NN_SAMPLE_KEYPHRASE
692 	/* global buffer to accumulate data for processing */
693 	cd->input = rballoc_align(0, SOF_MEM_CAPS_RAM,
694 				  sizeof(int16_t) * KWD_NN_IN_BUFF_SIZE, 64);
695 	if (!cd->input) {
696 		comp_err(dev, "test_keyword_new(): input alloc failed");
697 		goto cd_fail;
698 	}
699 	bzero(cd->input, sizeof(int16_t) * KWD_NN_IN_BUFF_SIZE);
700 	cd->input_size = 0;
701 #endif
702 
703 	dev->direction = SOF_IPC_STREAM_CAPTURE;
704 	dev->direction_set = true;
705 	dev->state = COMP_STATE_READY;
706 	return dev;
707 
708 cd_fail:
709 	comp_data_blob_handler_free(cd->model_handler);
710 	rfree(cd);
711 fail:
712 	rfree(dev);
713 	return NULL;
714 }
715 
test_keyword_free(struct comp_dev * dev)716 static void test_keyword_free(struct comp_dev *dev)
717 {
718 	struct comp_data *cd = comp_get_drvdata(dev);
719 
720 	comp_info(dev, "test_keyword_free()");
721 
722 	ipc_msg_free(cd->msg);
723 	comp_data_blob_handler_free(cd->model_handler);
724 	rfree(cd);
725 	rfree(dev);
726 }
727 
test_keyword_verify_params(struct comp_dev * dev,struct sof_ipc_stream_params * params)728 static int test_keyword_verify_params(struct comp_dev *dev,
729 				      struct sof_ipc_stream_params *params)
730 {
731 	int ret;
732 
733 	comp_dbg(dev, "test_keyword_verify_params()");
734 
735 	ret = comp_verify_params(dev, 0, params);
736 	if (ret < 0) {
737 		comp_err(dev, "test_keyword_verify_params(): verification failed!");
738 		return ret;
739 	}
740 
741 	return 0;
742 }
743 
744 /* set component audio stream parameters */
test_keyword_params(struct comp_dev * dev,struct sof_ipc_stream_params * params)745 static int test_keyword_params(struct comp_dev *dev,
746 			       struct sof_ipc_stream_params *params)
747 {
748 	struct comp_data *cd = comp_get_drvdata(dev);
749 	struct comp_buffer *sourceb;
750 	struct comp_buffer __sparse_cache *source_c;
751 	unsigned int channels, rate;
752 	enum sof_ipc_frame frame_fmt;
753 	int err;
754 
755 	test_keyword_set_params(dev, params);
756 
757 	err = test_keyword_verify_params(dev, params);
758 	if (err < 0) {
759 		comp_err(dev, "test_keyword_params(): pcm params verification failed.");
760 		return err;
761 	}
762 
763 	cd->sample_valid_bytes = params->sample_valid_bytes;
764 
765 	/* keyword components will only ever have 1 source */
766 	sourceb = list_first_item(&dev->bsource_list, struct comp_buffer,
767 				  sink_list);
768 	source_c = buffer_acquire(sourceb);
769 	channels = source_c->stream.channels;
770 	frame_fmt = source_c->stream.frame_fmt;
771 	rate = source_c->stream.rate;
772 	buffer_release(source_c);
773 
774 	if (channels != 1) {
775 		comp_err(dev, "test_keyword_params(): only single-channel supported");
776 		return -EINVAL;
777 	}
778 
779 	if (!detector_is_sample_width_supported(frame_fmt)) {
780 		comp_err(dev, "test_keyword_params(): only 16-bit format supported");
781 		return -EINVAL;
782 	}
783 
784 	/* calculate the length of the preamble */
785 	if (cd->config.preamble_time) {
786 		cd->keyphrase_samples = cd->config.preamble_time *
787 					(rate / 1000);
788 	} else {
789 		cd->keyphrase_samples = KEYPHRASE_DEFAULT_PREAMBLE_LENGTH;
790 	}
791 
792 	err = test_keyword_get_threshold(dev, params->sample_valid_bytes * 8);
793 	if (err < 0) {
794 		comp_err(dev, "test_keyword_params(): unsupported sample width %u",
795 			 params->sample_valid_bytes * 8);
796 		return err;
797 	}
798 
799 	cd->config.activation_threshold = err;
800 
801 	return 0;
802 }
803 
test_keyword_trigger(struct comp_dev * dev,int cmd)804 static int test_keyword_trigger(struct comp_dev *dev, int cmd)
805 {
806 	int ret;
807 	struct comp_data *cd = comp_get_drvdata(dev);
808 
809 	comp_info(dev, "test_keyword_trigger()");
810 
811 	ret = comp_set_state(dev, cmd);
812 	if (ret)
813 		return ret;
814 
815 	if (cmd == COMP_TRIGGER_START ||
816 	    cmd == COMP_TRIGGER_RELEASE) {
817 		cd->detect_preamble = 0;
818 		cd->detected = 0;
819 		cd->activation = 0;
820 	}
821 
822 	return 0;
823 }
824 
825 /*  process stream data from source buffer */
test_keyword_copy(struct comp_dev * dev)826 static int test_keyword_copy(struct comp_dev *dev)
827 {
828 	struct comp_data *cd = comp_get_drvdata(dev);
829 	struct comp_buffer *source;
830 	struct comp_buffer __sparse_cache *source_c;
831 	uint32_t frames;
832 
833 	comp_dbg(dev, "test_keyword_copy()");
834 
835 	/* keyword components will only ever have 1 source */
836 	source = list_first_item(&dev->bsource_list,
837 				 struct comp_buffer, sink_list);
838 	source_c = buffer_acquire(source);
839 
840 	if (!source_c->stream.avail) {
841 		buffer_release(source_c);
842 		return PPL_STATUS_PATH_STOP;
843 	}
844 
845 	frames = audio_stream_get_avail_frames(&source_c->stream);
846 
847 	/* copy and perform detection */
848 	buffer_stream_invalidate(source_c, audio_stream_get_avail_bytes(&source_c->stream));
849 	cd->detect_func(dev, &source_c->stream, frames);
850 
851 	/* calc new available */
852 	comp_update_buffer_consume(source_c, audio_stream_get_avail_bytes(&source_c->stream));
853 
854 	buffer_release(source_c);
855 
856 	return 0;
857 }
858 
test_keyword_reset(struct comp_dev * dev)859 static int test_keyword_reset(struct comp_dev *dev)
860 {
861 	struct comp_data *cd = comp_get_drvdata(dev);
862 
863 	comp_info(dev, "test_keyword_reset()");
864 
865 	cd->activation = 0;
866 	cd->detect_preamble = 0;
867 	cd->detected = 0;
868 
869 	return comp_set_state(dev, COMP_TRIGGER_RESET);
870 }
871 
test_keyword_prepare(struct comp_dev * dev)872 static int test_keyword_prepare(struct comp_dev *dev)
873 {
874 	struct comp_data *cd = comp_get_drvdata(dev);
875 	uint16_t valid_bits = cd->sample_valid_bytes * 8;
876 	uint16_t sample_width;
877 
878 #if CONFIG_IPC_MAJOR_4
879 	sample_width = cd->base_cfg.audio_fmt.depth;
880 #else
881 	sample_width = cd->config.sample_width;
882 #endif /* CONFIG_IPC_MAJOR_4 */
883 
884 	comp_info(dev, "test_keyword_prepare()");
885 
886 	if (valid_bits != sample_width) {
887 		/* Default threshold value has to be changed
888 		 * according to host new format.
889 		 */
890 		int ret = test_keyword_get_threshold(dev, valid_bits);
891 
892 		if (ret < 0) {
893 			comp_err(dev, "test_keyword_prepare(): unsupported sample width %u",
894 				 valid_bits);
895 			return ret;
896 		}
897 
898 		cd->config.activation_threshold = ret;
899 	}
900 
901 	cd->data_blob = comp_get_data_blob(cd->model_handler,
902 					   &cd->data_blob_size,
903 					   &cd->data_blob_crc);
904 
905 	return comp_set_state(dev, COMP_TRIGGER_PREPARE);
906 }
907 
test_keyword_get_sample_valid_bytes(struct comp_dev * dev)908 uint16_t test_keyword_get_sample_valid_bytes(struct comp_dev *dev)
909 {
910 	struct comp_data *cd = comp_get_drvdata(dev);
911 
912 	return cd->sample_valid_bytes;
913 }
914 
test_keyword_get_detected(struct comp_dev * dev)915 uint32_t test_keyword_get_detected(struct comp_dev *dev)
916 {
917 	struct comp_data *cd = comp_get_drvdata(dev);
918 
919 	return cd->detected;
920 }
921 
test_keyword_set_detected(struct comp_dev * dev,uint32_t detected)922 void test_keyword_set_detected(struct comp_dev *dev, uint32_t detected)
923 {
924 	struct comp_data *cd = comp_get_drvdata(dev);
925 
926 	cd->detected = detected;
927 }
928 
929 #if CONFIG_KWD_NN_SAMPLE_KEYPHRASE
test_keyword_get_input(struct comp_dev * dev)930 const int16_t *test_keyword_get_input(struct comp_dev *dev)
931 {
932 	struct comp_data *cd = comp_get_drvdata(dev);
933 
934 	return cd->input;
935 }
936 
test_keyword_get_input_byte(struct comp_dev * dev,uint32_t index)937 int16_t test_keyword_get_input_byte(struct comp_dev *dev, uint32_t index)
938 {
939 	struct comp_data *cd = comp_get_drvdata(dev);
940 
941 	if (index >= KWD_NN_IN_BUFF_SIZE * sizeof(int16_t))
942 		return -EINVAL;
943 
944 	return *((unsigned char *)cd->input + index);
945 }
946 
test_keyword_get_input_elem(struct comp_dev * dev,uint32_t index)947 int16_t test_keyword_get_input_elem(struct comp_dev *dev, uint32_t index)
948 {
949 	struct comp_data *cd = comp_get_drvdata(dev);
950 
951 	if (index >= KWD_NN_IN_BUFF_SIZE)
952 		return -EINVAL;
953 	return cd->input[index];
954 }
955 
test_keyword_set_input_elem(struct comp_dev * dev,uint32_t index,int16_t val)956 int test_keyword_set_input_elem(struct comp_dev *dev, uint32_t index, int16_t val)
957 {
958 	struct comp_data *cd = comp_get_drvdata(dev);
959 
960 	if (index >= KWD_NN_IN_BUFF_SIZE)
961 		return -EINVAL;
962 
963 	cd->input[index] = val;
964 
965 	return 0;
966 }
967 
test_keyword_get_input_size(struct comp_dev * dev)968 size_t test_keyword_get_input_size(struct comp_dev *dev)
969 {
970 	struct comp_data *cd = comp_get_drvdata(dev);
971 
972 	return cd->input_size;
973 }
974 
test_keyword_set_input_size(struct comp_dev * dev,size_t input_size)975 void test_keyword_set_input_size(struct comp_dev *dev, size_t input_size)
976 {
977 	struct comp_data *cd = comp_get_drvdata(dev);
978 
979 	cd->input_size = input_size;
980 }
981 #endif
982 
test_keyword_get_drain_req(struct comp_dev * dev)983 uint32_t test_keyword_get_drain_req(struct comp_dev *dev)
984 {
985 	struct comp_data *cd = comp_get_drvdata(dev);
986 
987 	return cd->drain_req;
988 }
989 
test_keyword_set_drain_req(struct comp_dev * dev,uint32_t drain_req)990 void test_keyword_set_drain_req(struct comp_dev *dev, uint32_t drain_req)
991 {
992 	struct comp_data *cd = comp_get_drvdata(dev);
993 
994 	cd->drain_req = drain_req;
995 }
996 
997 static const struct comp_driver comp_keyword = {
998 	.type	= SOF_COMP_KEYWORD_DETECT,
999 	.uid	= SOF_RT_UUID(keyword_uuid),
1000 	.tctx	= &keyword_tr,
1001 	.ops	= {
1002 		.create			= test_keyword_new,
1003 		.free			= test_keyword_free,
1004 		.params			= test_keyword_params,
1005 #if CONFIG_IPC_MAJOR_4
1006 		.set_large_config	= test_keyword_set_large_config,
1007 		.get_large_config	= test_keyword_get_large_config,
1008 		.get_attribute		= test_keyword_get_attribute,
1009 #else
1010 		.cmd			= test_keyword_cmd,
1011 #endif /* CONFIG_IPC_MAJOR_4 */
1012 		.trigger		= test_keyword_trigger,
1013 		.copy			= test_keyword_copy,
1014 		.prepare		= test_keyword_prepare,
1015 		.reset			= test_keyword_reset,
1016 	},
1017 };
1018 
1019 static SHARED_DATA struct comp_driver_info comp_keyword_info = {
1020 	.drv = &comp_keyword,
1021 };
1022 
sys_comp_keyword_init(void)1023 UT_STATIC void sys_comp_keyword_init(void)
1024 {
1025 	comp_register(platform_shared_get(&comp_keyword_info,
1026 					  sizeof(comp_keyword_info)));
1027 }
1028 
1029 DECLARE_MODULE(sys_comp_keyword_init);
1030 SOF_MODULE_INIT(keyword, sys_comp_keyword_init);
1031