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