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