1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2020 Google LLC. All rights reserved.
4 //
5 // Author: Ben Zhang <benzh@chromium.org>
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/kpb.h>
12 #include <sof/audio/ipc-config.h>
13 #include <sof/common.h>
14 #include <rtos/panic.h>
15 #include <sof/ipc/msg.h>
16 #include <rtos/alloc.h>
17 #include <rtos/init.h>
18 #include <sof/lib/memory.h>
19 #include <sof/lib/notifier.h>
20 #include <rtos/wait.h>
21 #include <sof/lib/uuid.h>
22 #include <sof/list.h>
23 #include <sof/math/numbers.h>
24 #include <rtos/string.h>
25 #include <sof/trace/trace.h>
26 #include <sof/ut.h>
27 #include <ipc/control.h>
28 #include <ipc/stream.h>
29 #include <ipc/topology.h>
30 #include <kernel/abi.h>
31 #include <user/trace.h>
32 #include <errno.h>
33 #include <stdbool.h>
34 #include <stddef.h>
35 #include <stdint.h>
36 #include <stdlib.h>
37 
38 #include <hotword_dsp_api.h>
39 
40 /* IPC blob types */
41 #define GOOGLE_HOTWORD_DETECT_MODEL	0
42 
43 static const struct comp_driver ghd_driver;
44 
45 LOG_MODULE_REGISTER(google_hotword_detect, CONFIG_SOF_LOG_LEVEL);
46 
47 /* c3c74249-058e-414f-8240-4da5f3fc2389 */
48 DECLARE_SOF_RT_UUID("google-hotword-detect", ghd_uuid,
49 		    0xc3c74249, 0x058e, 0x414f,
50 		    0x82, 0x40, 0x4d, 0xa5, 0xf3, 0xfc, 0x23, 0x89);
51 DECLARE_TR_CTX(ghd_tr, SOF_UUID(ghd_uuid), LOG_LEVEL_INFO);
52 
53 struct comp_data {
54 	struct comp_data_blob_handler *model_handler;
55 	struct kpb_event_data event_data;
56 	struct kpb_client client_data;
57 
58 	struct ipc_msg *msg;
59 
60 	int detected;
61 	size_t history_bytes;
62 	struct sof_ipc_comp_event event;
63 };
64 
notify_host(const struct comp_dev * dev)65 static void notify_host(const struct comp_dev *dev)
66 {
67 	struct comp_data *cd = comp_get_drvdata(dev);
68 
69 	comp_dbg(dev, "notify_host()");
70 
71 	ipc_msg_send(cd->msg, &cd->event, true);
72 }
73 
notify_kpb(const struct comp_dev * dev)74 static void notify_kpb(const struct comp_dev *dev)
75 {
76 	struct comp_data *cd = comp_get_drvdata(dev);
77 
78 	comp_dbg(dev, "notify_kpb()");
79 
80 	cd->client_data.r_ptr = NULL;
81 	cd->client_data.sink = NULL;
82 	cd->client_data.id = 0;
83 	cd->event_data.event_id = KPB_EVENT_BEGIN_DRAINING;
84 	cd->event_data.client_data = &cd->client_data;
85 
86 	notifier_event(dev, NOTIFIER_ID_KPB_CLIENT_EVT,
87 		       NOTIFIER_TARGET_CORE_ALL_MASK, &cd->event_data,
88 		       sizeof(cd->event_data));
89 }
90 
ghd_create(const struct comp_driver * drv,const struct comp_ipc_config * config,const void * spec)91 static struct comp_dev *ghd_create(const struct comp_driver *drv,
92 				   const struct comp_ipc_config *config,
93 				   const void *spec)
94 {
95 	struct comp_dev *dev;
96 	struct comp_data *cd;
97 
98 	comp_cl_info(drv, "ghd_create()");
99 
100 	/* Create component device with an effect processing component */
101 	dev = comp_alloc(drv, sizeof(*dev));
102 	if (!dev)
103 		return NULL;
104 
105 	dev->drv = drv;
106 	dev->ipc_config = *config;
107 
108 	/* Create private component data */
109 	cd = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM,
110 		     sizeof(*cd));
111 	if (!cd)
112 		goto fail;
113 	comp_set_drvdata(dev, cd);
114 
115 	/* Build component event */
116 	ipc_build_comp_event(&cd->event, dev->ipc_config.type, dev->ipc_config.id);
117 	cd->event.event_type = SOF_CTRL_EVENT_KD;
118 	cd->event.num_elems = 0;
119 
120 	cd->msg = ipc_msg_init(cd->event.rhdr.hdr.cmd, cd->event.rhdr.hdr.size);
121 	if (!cd->msg) {
122 		comp_err(dev, "ghd_create(): ipc_msg_init failed");
123 		goto cd_fail;
124 	}
125 
126 	/* Create component model data handler */
127 	cd->model_handler = comp_data_blob_handler_new(dev);
128 	if (!cd->model_handler) {
129 		comp_err(dev, "ghd_create(): comp_data_blob_handler_new failed");
130 		goto cd_fail;
131 	}
132 
133 	dev->state = COMP_STATE_READY;
134 	comp_dbg(dev, "ghd_create(): Ready");
135 	return dev;
136 
137 cd_fail:
138 	comp_data_blob_handler_free(cd->model_handler);
139 	ipc_msg_free(cd->msg);
140 	rfree(cd);
141 fail:
142 	rfree(dev);
143 	return NULL;
144 }
145 
ghd_free(struct comp_dev * dev)146 static void ghd_free(struct comp_dev *dev)
147 {
148 	struct comp_data *cd = comp_get_drvdata(dev);
149 
150 	comp_dbg(dev, "ghd_free()");
151 
152 	comp_data_blob_handler_free(cd->model_handler);
153 	ipc_msg_free(cd->msg);
154 	rfree(cd);
155 	rfree(dev);
156 }
157 
ghd_params(struct comp_dev * dev,struct sof_ipc_stream_params * params)158 static int ghd_params(struct comp_dev *dev,
159 		      struct sof_ipc_stream_params *params)
160 {
161 	struct comp_buffer *sourceb;
162 	struct comp_buffer __sparse_cache *source_c;
163 	int ret;
164 
165 	/* Detector is used only in KPB topology. It always requires channels
166 	 * parameter set to 1.
167 	 */
168 	params->channels = 1;
169 
170 	ret = comp_verify_params(dev, 0, params);
171 	if (ret < 0) {
172 		comp_err(dev, "ghd_params(): comp_verify_params failed.");
173 		return -EINVAL;
174 	}
175 
176 	/* This detector component will only ever have 1 source */
177 	sourceb = list_first_item(&dev->bsource_list, struct comp_buffer,
178 				  sink_list);
179 	source_c = buffer_acquire(sourceb);
180 
181 	if (source_c->stream.channels != 1) {
182 		comp_err(dev, "ghd_params(): Only single-channel supported");
183 		ret = -EINVAL;
184 	} else if (source_c->stream.frame_fmt != SOF_IPC_FRAME_S16_LE) {
185 		comp_err(dev, "ghd_params(): Only S16_LE supported");
186 		ret = -EINVAL;
187 	} else if (source_c->stream.rate != KPB_SAMPLNG_FREQUENCY) {
188 		comp_err(dev, "ghd_params(): Only 16KHz supported");
189 		ret = -EINVAL;
190 	}
191 
192 	buffer_release(source_c);
193 
194 	return ret;
195 }
196 
ghd_setup_model(struct comp_dev * dev)197 static int ghd_setup_model(struct comp_dev *dev)
198 {
199 	struct comp_data *cd = comp_get_drvdata(dev);
200 	void *model;
201 	size_t size;
202 	int ret;
203 
204 	/* Avoid the CRC calculation since it takes too long and causes XRUN.
205 	 *
206 	 * TODO: Add it back when there is support for running it in a low
207 	 * priority background task.
208 	 */
209 	model = comp_get_data_blob(cd->model_handler, &size, NULL);
210 	if (!model || !size) {
211 		comp_err(dev, "Model not set");
212 		return -EINVAL;
213 	}
214 	comp_info(dev, "Model: data=0x%08x, size=%zu",
215 		  (uint32_t)model, size);
216 
217 	comp_info(dev, "GoogleHotwordVersion %d",
218 		  GoogleHotwordVersion());
219 
220 	ret = GoogleHotwordDspInit(model);
221 	cd->detected = 0;
222 	cd->history_bytes = 0;
223 	if (ret != 1) {
224 		comp_err(dev, "GoogleHotwordDSPInit failed: %d", ret);
225 		return -EINVAL;
226 	}
227 
228 	return 0;
229 }
230 
ghd_ctrl_set_bin_data(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata)231 static int ghd_ctrl_set_bin_data(struct comp_dev *dev,
232 				 struct sof_ipc_ctrl_data *cdata)
233 {
234 	struct comp_data *cd = comp_get_drvdata(dev);
235 	int ret;
236 
237 	switch (cdata->data->type) {
238 	case GOOGLE_HOTWORD_DETECT_MODEL:
239 		ret = comp_data_blob_set_cmd(cd->model_handler, cdata);
240 		comp_dbg(dev, "ghd_ctrl_set_bin_data(): comp_data_blob_set_cmd=%d",
241 			 ret);
242 		return ret;
243 	default:
244 		comp_err(dev, "ghd_ctrl_set_bin_data(): Unknown cdata->data->type %d",
245 			 cdata->data->type);
246 		return -EINVAL;
247 	}
248 }
249 
ghd_ctrl_set_data(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata)250 static int ghd_ctrl_set_data(struct comp_dev *dev,
251 			     struct sof_ipc_ctrl_data *cdata)
252 {
253 	switch (cdata->cmd) {
254 	case SOF_CTRL_CMD_BINARY:
255 		return ghd_ctrl_set_bin_data(dev, cdata);
256 	default:
257 		comp_err(dev, "ghd_ctrl_set_data(): Only binary controls supported %d",
258 			 cdata->cmd);
259 		return -EINVAL;
260 	}
261 }
262 
ghd_ctrl_get_bin_data(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata,int max_data_size)263 static int ghd_ctrl_get_bin_data(struct comp_dev *dev,
264 				 struct sof_ipc_ctrl_data *cdata,
265 				 int max_data_size)
266 {
267 	struct comp_data *cd = comp_get_drvdata(dev);
268 	int ret;
269 
270 	switch (cdata->data->type) {
271 	case GOOGLE_HOTWORD_DETECT_MODEL:
272 		ret = comp_data_blob_get_cmd(cd->model_handler,
273 					     cdata,
274 					     max_data_size);
275 		comp_dbg(dev, "ghd_ctrl_get_bin_data(): comp_data_blob_get_cmd=%d, size=%d",
276 			 ret, max_data_size);
277 		return ret;
278 	default:
279 		comp_err(dev, "ghd_ctrl_get_bin_data(): Unknown cdata->data->type %d",
280 			 cdata->data->type);
281 		return -EINVAL;
282 	}
283 }
284 
ghd_ctrl_get_data(struct comp_dev * dev,struct sof_ipc_ctrl_data * cdata,int max_data_size)285 static int ghd_ctrl_get_data(struct comp_dev *dev,
286 			     struct sof_ipc_ctrl_data *cdata,
287 			     int max_data_size)
288 {
289 	switch (cdata->cmd) {
290 	case SOF_CTRL_CMD_BINARY:
291 		return ghd_ctrl_get_bin_data(dev, cdata, max_data_size);
292 	default:
293 		comp_err(dev, "ghd_ctrl_get_data(): Only binary controls supported %d",
294 			 cdata->cmd);
295 		return -EINVAL;
296 	}
297 }
298 
ghd_cmd(struct comp_dev * dev,int cmd,void * data,int max_data_size)299 static int ghd_cmd(struct comp_dev *dev, int cmd, void *data,
300 		   int max_data_size)
301 {
302 	struct sof_ipc_ctrl_data *cdata = data;
303 
304 	comp_dbg(dev, "ghd_cmd(): %d", cmd);
305 
306 	switch (cmd) {
307 	case COMP_CMD_SET_DATA:
308 		return ghd_ctrl_set_data(dev, cdata);
309 	case COMP_CMD_GET_DATA:
310 		return ghd_ctrl_get_data(dev, cdata, max_data_size);
311 	default:
312 		comp_err(dev, "ghd_cmd(): Unknown cmd %d", cmd);
313 		return -EINVAL;
314 	}
315 }
316 
ghd_trigger(struct comp_dev * dev,int cmd)317 static int ghd_trigger(struct comp_dev *dev, int cmd)
318 {
319 	struct comp_data *cd = comp_get_drvdata(dev);
320 
321 	comp_dbg(dev, "ghd_trigger(): %d", cmd);
322 
323 	if (cmd == COMP_TRIGGER_START || cmd == COMP_TRIGGER_RELEASE) {
324 		cd->detected = 0;
325 		cd->history_bytes = 0;
326 		GoogleHotwordDspReset();
327 	}
328 
329 	return comp_set_state(dev, cmd);
330 }
331 
ghd_detect(struct comp_dev * dev,struct audio_stream * stream,const void * samples,uint32_t bytes)332 static void ghd_detect(struct comp_dev *dev,
333 		       struct audio_stream *stream,
334 		       const void *samples,
335 		       uint32_t bytes)
336 {
337 	struct comp_data *cd = comp_get_drvdata(dev);
338 	int preamble_length_ms = 0;
339 	uint32_t sample_bytes;
340 	int ret;
341 
342 	if (cd->detected)
343 		return;
344 
345 	/* Assuming 1 channel, verified in ghd_params.
346 	 *
347 	 * TODO Make the logic multi channel safe when new hotword library can
348 	 * utilize multi channel data for detection.
349 	 */
350 	sample_bytes = audio_stream_sample_bytes(stream);
351 
352 	if (cd->history_bytes <
353 	    KPB_MAX_BUFF_TIME * KPB_SAMPLES_PER_MS * sample_bytes) {
354 		cd->history_bytes += bytes;
355 	}
356 
357 	comp_dbg(dev, "GoogleHotwordDspProcess(0x%x, %u)",
358 		 (uint32_t)samples, bytes / sample_bytes);
359 	ret = GoogleHotwordDspProcess(samples, bytes / sample_bytes,
360 				      &preamble_length_ms);
361 	if (ret == 1) {
362 		cd->detected = 1;
363 
364 		/* The current version of GoogleHotwordDspProcess always
365 		 * reports 2000ms preamble. Clamp this by the actual history
366 		 * length so KPB doesn't complain not enough data to drain when
367 		 * the hotword is detected right after pcm device open.
368 		 */
369 		cd->client_data.drain_req =
370 			MIN((size_t)preamble_length_ms,
371 			    (cd->history_bytes / sample_bytes) /
372 			     KPB_SAMPLES_PER_MS);
373 
374 		/* drain_req is actually in ms. See kpb_init_draining. */
375 		comp_info(dev, "Hotword detected %dms",
376 			  cd->client_data.drain_req);
377 		notify_host(dev);
378 		notify_kpb(dev);
379 	}
380 }
381 
ghd_copy(struct comp_dev * dev)382 static int ghd_copy(struct comp_dev *dev)
383 {
384 	struct comp_data *cd = comp_get_drvdata(dev);
385 	struct comp_buffer *source;
386 	struct comp_buffer __sparse_cache *source_c;
387 	struct audio_stream __sparse_cache *stream;
388 	uint32_t bytes, tail_bytes, head_bytes = 0;
389 	int ret;
390 
391 	/* Check for new model */
392 	if (comp_is_new_data_blob_available(cd->model_handler)) {
393 		comp_dbg(dev, "ghd_copy(): Switch to new model");
394 		ret = ghd_setup_model(dev);
395 		if (ret)
396 			return ret;
397 	}
398 
399 	/* keyword components will only ever have 1 source */
400 	source = list_first_item(&dev->bsource_list,
401 				 struct comp_buffer, sink_list);
402 	source_c = buffer_acquire(sourceb);
403 	stream = &source_c->stream;
404 
405 	bytes = audio_stream_get_avail_bytes(stream);
406 
407 	comp_dbg(dev, "ghd_copy() avail_bytes %u", bytes);
408 	comp_dbg(dev, "buffer begin/r_ptr/end [0x%x 0x%x 0x%x]",
409 		 (uint32_t)stream->addr,
410 		 (uint32_t)stream->r_ptr,
411 		 (uint32_t)stream->end_addr);
412 
413 	/* copy and perform detection */
414 	buffer_stream_invalidate(source_c, bytes);
415 
416 	tail_bytes = (char *)stream->end_addr - (char *)stream->r_ptr;
417 	if (bytes <= tail_bytes)
418 		tail_bytes = bytes;
419 	else
420 		head_bytes = bytes - tail_bytes;
421 
422 	if (tail_bytes)
423 		ghd_detect(dev, stream, stream->r_ptr, tail_bytes);
424 	if (head_bytes)
425 		ghd_detect(dev, stream, stream->addr, head_bytes);
426 
427 	/* calc new available */
428 	comp_update_buffer_consume(source_c, bytes);
429 
430 	buffer_release(source_c);
431 
432 	return 0;
433 }
434 
ghd_reset(struct comp_dev * dev)435 static int ghd_reset(struct comp_dev *dev)
436 {
437 	struct comp_data *cd = comp_get_drvdata(dev);
438 
439 	comp_dbg(dev, "ghd_reset()");
440 
441 	cd->detected = 0;
442 	cd->history_bytes = 0;
443 	GoogleHotwordDspReset();
444 
445 	return comp_set_state(dev, COMP_TRIGGER_RESET);
446 }
447 
ghd_prepare(struct comp_dev * dev)448 static int ghd_prepare(struct comp_dev *dev)
449 {
450 	int ret;
451 
452 	comp_dbg(dev, "ghd_prepare()");
453 
454 	ret = ghd_setup_model(dev);
455 	if (ret)
456 		return ret;
457 
458 	return comp_set_state(dev, COMP_TRIGGER_PREPARE);
459 }
460 
461 static const struct comp_driver ghd_driver = {
462 	.type	= SOF_COMP_KEYWORD_DETECT,
463 	.uid	= SOF_RT_UUID(ghd_uuid),
464 	.tctx	= &ghd_tr,
465 	.ops	= {
466 		.create		= ghd_create,
467 		.free		= ghd_free,
468 		.params		= ghd_params,
469 		.cmd		= ghd_cmd,
470 		.trigger	= ghd_trigger,
471 		.copy		= ghd_copy,
472 		.prepare	= ghd_prepare,
473 		.reset		= ghd_reset,
474 	},
475 };
476 
477 static SHARED_DATA struct comp_driver_info ghd_driver_info = {
478 	.drv = &ghd_driver,
479 };
480 
sys_comp_ghd_init(void)481 UT_STATIC void sys_comp_ghd_init(void)
482 {
483 	comp_register(platform_shared_get(&ghd_driver_info,
484 					  sizeof(ghd_driver_info)));
485 }
486 
487 DECLARE_MODULE(sys_comp_ghd_init);
488 SOF_MODULE_INIT(ghd, sys_comp_ghd_init);
489