1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // |
3 | // Copyright(c) 2022 Intel Corporation. All rights reserved. |
4 | // |
5 | // Authors: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> |
6 | // Peter Ujfalusi <peter.ujfalusi@linux.intel.com> |
7 | // |
8 | |
9 | #include <linux/debugfs.h> |
10 | #include <linux/errno.h> |
11 | #include <linux/list.h> |
12 | #include <linux/module.h> |
13 | #include <linux/mutex.h> |
14 | #include <linux/slab.h> |
15 | #include <sound/sof/ipc4/header.h> |
16 | #include "ops.h" |
17 | #include "sof-client.h" |
18 | #include "sof-priv.h" |
19 | #include "ipc3-priv.h" |
20 | #include "ipc4-priv.h" |
21 | |
22 | /** |
23 | * struct sof_ipc_event_entry - IPC client event description |
24 | * @ipc_msg_type: IPC msg type of the event the client is interested |
25 | * @cdev: sof_client_dev of the requesting client |
26 | * @callback: Callback function of the client |
27 | * @list: item in SOF core client event list |
28 | */ |
29 | struct sof_ipc_event_entry { |
30 | u32 ipc_msg_type; |
31 | struct sof_client_dev *cdev; |
32 | sof_client_event_callback callback; |
33 | struct list_head list; |
34 | }; |
35 | |
36 | /** |
37 | * struct sof_state_event_entry - DSP panic event subscription entry |
38 | * @cdev: sof_client_dev of the requesting client |
39 | * @callback: Callback function of the client |
40 | * @list: item in SOF core client event list |
41 | */ |
42 | struct sof_state_event_entry { |
43 | struct sof_client_dev *cdev; |
44 | sof_client_fw_state_callback callback; |
45 | struct list_head list; |
46 | }; |
47 | |
48 | static void sof_client_auxdev_release(struct device *dev) |
49 | { |
50 | struct auxiliary_device *auxdev = to_auxiliary_dev(dev); |
51 | struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); |
52 | |
53 | kfree(objp: cdev->auxdev.dev.platform_data); |
54 | kfree(objp: cdev); |
55 | } |
56 | |
57 | static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data, |
58 | size_t size) |
59 | { |
60 | void *d = NULL; |
61 | |
62 | if (data) { |
63 | d = kmemdup(p: data, size, GFP_KERNEL); |
64 | if (!d) |
65 | return -ENOMEM; |
66 | } |
67 | |
68 | cdev->auxdev.dev.platform_data = d; |
69 | return 0; |
70 | } |
71 | |
72 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) |
73 | static int sof_register_ipc_flood_test(struct snd_sof_dev *sdev) |
74 | { |
75 | int ret = 0; |
76 | int i; |
77 | |
78 | if (sdev->pdata->ipc_type != SOF_IPC_TYPE_3) |
79 | return 0; |
80 | |
81 | for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) { |
82 | ret = sof_client_dev_register(sdev, name: "ipc_flood" , id: i, NULL, size: 0); |
83 | if (ret < 0) |
84 | break; |
85 | } |
86 | |
87 | if (ret) { |
88 | for (; i >= 0; --i) |
89 | sof_client_dev_unregister(sdev, name: "ipc_flood" , id: i); |
90 | } |
91 | |
92 | return ret; |
93 | } |
94 | |
95 | static void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev) |
96 | { |
97 | int i; |
98 | |
99 | for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) |
100 | sof_client_dev_unregister(sdev, name: "ipc_flood" , id: i); |
101 | } |
102 | #else |
103 | static inline int sof_register_ipc_flood_test(struct snd_sof_dev *sdev) |
104 | { |
105 | return 0; |
106 | } |
107 | |
108 | static inline void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev) {} |
109 | #endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST */ |
110 | |
111 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) |
112 | static int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev) |
113 | { |
114 | return sof_client_dev_register(sdev, name: "msg_injector" , id: 0, NULL, size: 0); |
115 | } |
116 | |
117 | static void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) |
118 | { |
119 | sof_client_dev_unregister(sdev, name: "msg_injector" , id: 0); |
120 | } |
121 | #else |
122 | static inline int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev) |
123 | { |
124 | return 0; |
125 | } |
126 | |
127 | static inline void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) {} |
128 | #endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR */ |
129 | |
130 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR) |
131 | static int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev) |
132 | { |
133 | /* Only IPC3 supported right now */ |
134 | if (sdev->pdata->ipc_type != SOF_IPC_TYPE_3) |
135 | return 0; |
136 | |
137 | return sof_client_dev_register(sdev, name: "kernel_injector" , id: 0, NULL, size: 0); |
138 | } |
139 | |
140 | static void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev) |
141 | { |
142 | sof_client_dev_unregister(sdev, name: "kernel_injector" , id: 0); |
143 | } |
144 | #else |
145 | static inline int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev) |
146 | { |
147 | return 0; |
148 | } |
149 | |
150 | static inline void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev) {} |
151 | #endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR */ |
152 | |
153 | int sof_register_clients(struct snd_sof_dev *sdev) |
154 | { |
155 | int ret; |
156 | |
157 | if (sdev->dspless_mode_selected) |
158 | return 0; |
159 | |
160 | /* Register platform independent client devices */ |
161 | ret = sof_register_ipc_flood_test(sdev); |
162 | if (ret) { |
163 | dev_err(sdev->dev, "IPC flood test client registration failed\n" ); |
164 | return ret; |
165 | } |
166 | |
167 | ret = sof_register_ipc_msg_injector(sdev); |
168 | if (ret) { |
169 | dev_err(sdev->dev, "IPC message injector client registration failed\n" ); |
170 | goto err_msg_injector; |
171 | } |
172 | |
173 | ret = sof_register_ipc_kernel_injector(sdev); |
174 | if (ret) { |
175 | dev_err(sdev->dev, "IPC kernel injector client registration failed\n" ); |
176 | goto err_kernel_injector; |
177 | } |
178 | |
179 | /* Platform dependent client device registration */ |
180 | |
181 | if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients) |
182 | ret = sof_ops(sdev)->register_ipc_clients(sdev); |
183 | |
184 | if (!ret) |
185 | return 0; |
186 | |
187 | sof_unregister_ipc_kernel_injector(sdev); |
188 | |
189 | err_kernel_injector: |
190 | sof_unregister_ipc_msg_injector(sdev); |
191 | |
192 | err_msg_injector: |
193 | sof_unregister_ipc_flood_test(sdev); |
194 | |
195 | return ret; |
196 | } |
197 | |
198 | void sof_unregister_clients(struct snd_sof_dev *sdev) |
199 | { |
200 | if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients) |
201 | sof_ops(sdev)->unregister_ipc_clients(sdev); |
202 | |
203 | sof_unregister_ipc_kernel_injector(sdev); |
204 | sof_unregister_ipc_msg_injector(sdev); |
205 | sof_unregister_ipc_flood_test(sdev); |
206 | } |
207 | |
208 | int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, |
209 | const void *data, size_t size) |
210 | { |
211 | struct auxiliary_device *auxdev; |
212 | struct sof_client_dev *cdev; |
213 | int ret; |
214 | |
215 | cdev = kzalloc(size: sizeof(*cdev), GFP_KERNEL); |
216 | if (!cdev) |
217 | return -ENOMEM; |
218 | |
219 | cdev->sdev = sdev; |
220 | auxdev = &cdev->auxdev; |
221 | auxdev->name = name; |
222 | auxdev->dev.parent = sdev->dev; |
223 | auxdev->dev.release = sof_client_auxdev_release; |
224 | auxdev->id = id; |
225 | |
226 | ret = sof_client_dev_add_data(cdev, data, size); |
227 | if (ret < 0) |
228 | goto err_dev_add_data; |
229 | |
230 | ret = auxiliary_device_init(auxdev); |
231 | if (ret < 0) { |
232 | dev_err(sdev->dev, "failed to initialize client dev %s.%d\n" , name, id); |
233 | goto err_dev_init; |
234 | } |
235 | |
236 | ret = auxiliary_device_add(&cdev->auxdev); |
237 | if (ret < 0) { |
238 | dev_err(sdev->dev, "failed to add client dev %s.%d\n" , name, id); |
239 | /* |
240 | * sof_client_auxdev_release() will be invoked to free up memory |
241 | * allocations through put_device() |
242 | */ |
243 | auxiliary_device_uninit(auxdev: &cdev->auxdev); |
244 | return ret; |
245 | } |
246 | |
247 | /* add to list of SOF client devices */ |
248 | mutex_lock(&sdev->ipc_client_mutex); |
249 | list_add(new: &cdev->list, head: &sdev->ipc_client_list); |
250 | mutex_unlock(lock: &sdev->ipc_client_mutex); |
251 | |
252 | return 0; |
253 | |
254 | err_dev_init: |
255 | kfree(objp: cdev->auxdev.dev.platform_data); |
256 | |
257 | err_dev_add_data: |
258 | kfree(objp: cdev); |
259 | |
260 | return ret; |
261 | } |
262 | EXPORT_SYMBOL_NS_GPL(sof_client_dev_register, SND_SOC_SOF_CLIENT); |
263 | |
264 | void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id) |
265 | { |
266 | struct sof_client_dev *cdev; |
267 | |
268 | mutex_lock(&sdev->ipc_client_mutex); |
269 | |
270 | /* |
271 | * sof_client_auxdev_release() will be invoked to free up memory |
272 | * allocations through put_device() |
273 | */ |
274 | list_for_each_entry(cdev, &sdev->ipc_client_list, list) { |
275 | if (!strcmp(cdev->auxdev.name, name) && cdev->auxdev.id == id) { |
276 | list_del(entry: &cdev->list); |
277 | auxiliary_device_delete(auxdev: &cdev->auxdev); |
278 | auxiliary_device_uninit(auxdev: &cdev->auxdev); |
279 | break; |
280 | } |
281 | } |
282 | |
283 | mutex_unlock(lock: &sdev->ipc_client_mutex); |
284 | } |
285 | EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, SND_SOC_SOF_CLIENT); |
286 | |
287 | int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, |
288 | void *reply_data, size_t reply_bytes) |
289 | { |
290 | if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { |
291 | struct sof_ipc_cmd_hdr *hdr = ipc_msg; |
292 | |
293 | return sof_ipc_tx_message(ipc: cdev->sdev->ipc, msg_data: ipc_msg, msg_bytes: hdr->size, |
294 | reply_data, reply_bytes); |
295 | } else if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { |
296 | struct sof_ipc4_msg *msg = ipc_msg; |
297 | |
298 | return sof_ipc_tx_message(ipc: cdev->sdev->ipc, msg_data: ipc_msg, msg_bytes: msg->data_size, |
299 | reply_data, reply_bytes); |
300 | } |
301 | |
302 | return -EINVAL; |
303 | } |
304 | EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT); |
305 | |
306 | int sof_client_ipc_rx_message(struct sof_client_dev *cdev, void *ipc_msg, void *msg_buf) |
307 | { |
308 | if (IS_ENABLED(CONFIG_SND_SOC_SOF_IPC3) && |
309 | cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { |
310 | struct sof_ipc_cmd_hdr *hdr = ipc_msg; |
311 | |
312 | if (hdr->size < sizeof(hdr)) { |
313 | dev_err(cdev->sdev->dev, "The received message size is invalid\n" ); |
314 | return -EINVAL; |
315 | } |
316 | |
317 | sof_ipc3_do_rx_work(sdev: cdev->sdev, hdr: ipc_msg, msg_buf); |
318 | return 0; |
319 | } |
320 | |
321 | return -EOPNOTSUPP; |
322 | } |
323 | EXPORT_SYMBOL_NS_GPL(sof_client_ipc_rx_message, SND_SOC_SOF_CLIENT); |
324 | |
325 | int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg, |
326 | bool set) |
327 | { |
328 | if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { |
329 | struct sof_ipc_cmd_hdr *hdr = ipc_msg; |
330 | |
331 | return sof_ipc_set_get_data(ipc: cdev->sdev->ipc, msg_data: ipc_msg, msg_bytes: hdr->size, |
332 | set); |
333 | } else if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { |
334 | struct sof_ipc4_msg *msg = ipc_msg; |
335 | |
336 | return sof_ipc_set_get_data(ipc: cdev->sdev->ipc, msg_data: ipc_msg, |
337 | msg_bytes: msg->data_size, set); |
338 | } |
339 | |
340 | return -EINVAL; |
341 | } |
342 | EXPORT_SYMBOL_NS_GPL(sof_client_ipc_set_get_data, SND_SOC_SOF_CLIENT); |
343 | |
344 | #ifdef CONFIG_SND_SOC_SOF_IPC4 |
345 | struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c, const guid_t *uuid) |
346 | { |
347 | struct snd_sof_dev *sdev = c->sdev; |
348 | |
349 | if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) |
350 | return sof_ipc4_find_module_by_uuid(sdev, uuid); |
351 | dev_err(sdev->dev, "Only supported with IPC4\n" ); |
352 | |
353 | return NULL; |
354 | } |
355 | EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_module, SND_SOC_SOF_CLIENT); |
356 | #endif |
357 | |
358 | int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) |
359 | { |
360 | struct auxiliary_driver *adrv; |
361 | struct sof_client_dev *cdev; |
362 | |
363 | mutex_lock(&sdev->ipc_client_mutex); |
364 | |
365 | list_for_each_entry(cdev, &sdev->ipc_client_list, list) { |
366 | /* Skip devices without loaded driver */ |
367 | if (!cdev->auxdev.dev.driver) |
368 | continue; |
369 | |
370 | adrv = to_auxiliary_drv(drv: cdev->auxdev.dev.driver); |
371 | if (adrv->suspend) |
372 | adrv->suspend(&cdev->auxdev, state); |
373 | } |
374 | |
375 | mutex_unlock(lock: &sdev->ipc_client_mutex); |
376 | |
377 | return 0; |
378 | } |
379 | EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, SND_SOC_SOF_CLIENT); |
380 | |
381 | int sof_resume_clients(struct snd_sof_dev *sdev) |
382 | { |
383 | struct auxiliary_driver *adrv; |
384 | struct sof_client_dev *cdev; |
385 | |
386 | mutex_lock(&sdev->ipc_client_mutex); |
387 | |
388 | list_for_each_entry(cdev, &sdev->ipc_client_list, list) { |
389 | /* Skip devices without loaded driver */ |
390 | if (!cdev->auxdev.dev.driver) |
391 | continue; |
392 | |
393 | adrv = to_auxiliary_drv(drv: cdev->auxdev.dev.driver); |
394 | if (adrv->resume) |
395 | adrv->resume(&cdev->auxdev); |
396 | } |
397 | |
398 | mutex_unlock(lock: &sdev->ipc_client_mutex); |
399 | |
400 | return 0; |
401 | } |
402 | EXPORT_SYMBOL_NS_GPL(sof_resume_clients, SND_SOC_SOF_CLIENT); |
403 | |
404 | struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev) |
405 | { |
406 | return cdev->sdev->debugfs_root; |
407 | } |
408 | EXPORT_SYMBOL_NS_GPL(sof_client_get_debugfs_root, SND_SOC_SOF_CLIENT); |
409 | |
410 | /* DMA buffer allocation in client drivers must use the core SOF device */ |
411 | struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev) |
412 | { |
413 | return cdev->sdev->dev; |
414 | } |
415 | EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, SND_SOC_SOF_CLIENT); |
416 | |
417 | const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev) |
418 | { |
419 | struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); |
420 | |
421 | return &sdev->fw_ready.version; |
422 | } |
423 | EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, SND_SOC_SOF_CLIENT); |
424 | |
425 | size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev) |
426 | { |
427 | struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); |
428 | |
429 | return sdev->ipc->max_payload_size; |
430 | } |
431 | EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_max_payload_size, SND_SOC_SOF_CLIENT); |
432 | |
433 | enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev) |
434 | { |
435 | struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); |
436 | |
437 | return sdev->pdata->ipc_type; |
438 | } |
439 | EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_type, SND_SOC_SOF_CLIENT); |
440 | |
441 | /* module refcount management of SOF core */ |
442 | int sof_client_core_module_get(struct sof_client_dev *cdev) |
443 | { |
444 | struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); |
445 | |
446 | if (!try_module_get(module: sdev->dev->driver->owner)) |
447 | return -ENODEV; |
448 | |
449 | return 0; |
450 | } |
451 | EXPORT_SYMBOL_NS_GPL(sof_client_core_module_get, SND_SOC_SOF_CLIENT); |
452 | |
453 | void sof_client_core_module_put(struct sof_client_dev *cdev) |
454 | { |
455 | struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); |
456 | |
457 | module_put(module: sdev->dev->driver->owner); |
458 | } |
459 | EXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, SND_SOC_SOF_CLIENT); |
460 | |
461 | /* IPC event handling */ |
462 | void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf) |
463 | { |
464 | struct sof_ipc_event_entry *event; |
465 | u32 msg_type; |
466 | |
467 | if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { |
468 | struct sof_ipc_cmd_hdr *hdr = msg_buf; |
469 | |
470 | msg_type = hdr->cmd & SOF_GLB_TYPE_MASK; |
471 | } else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { |
472 | struct sof_ipc4_msg *msg = msg_buf; |
473 | |
474 | msg_type = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary); |
475 | } else { |
476 | dev_dbg_once(sdev->dev, "Not supported IPC version: %d\n" , |
477 | sdev->pdata->ipc_type); |
478 | return; |
479 | } |
480 | |
481 | mutex_lock(&sdev->client_event_handler_mutex); |
482 | |
483 | list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { |
484 | if (event->ipc_msg_type == msg_type) |
485 | event->callback(event->cdev, msg_buf); |
486 | } |
487 | |
488 | mutex_unlock(lock: &sdev->client_event_handler_mutex); |
489 | } |
490 | |
491 | int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, |
492 | u32 ipc_msg_type, |
493 | sof_client_event_callback callback) |
494 | { |
495 | struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); |
496 | struct sof_ipc_event_entry *event; |
497 | |
498 | if (!callback) |
499 | return -EINVAL; |
500 | |
501 | if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { |
502 | if (!(ipc_msg_type & SOF_GLB_TYPE_MASK)) |
503 | return -EINVAL; |
504 | } else if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { |
505 | if (!(ipc_msg_type & SOF_IPC4_NOTIFICATION_TYPE_MASK)) |
506 | return -EINVAL; |
507 | } else { |
508 | dev_warn(sdev->dev, "%s: Not supported IPC version: %d\n" , |
509 | __func__, sdev->pdata->ipc_type); |
510 | return -EINVAL; |
511 | } |
512 | |
513 | event = kmalloc(size: sizeof(*event), GFP_KERNEL); |
514 | if (!event) |
515 | return -ENOMEM; |
516 | |
517 | event->ipc_msg_type = ipc_msg_type; |
518 | event->cdev = cdev; |
519 | event->callback = callback; |
520 | |
521 | /* add to list of SOF client devices */ |
522 | mutex_lock(&sdev->client_event_handler_mutex); |
523 | list_add(new: &event->list, head: &sdev->ipc_rx_handler_list); |
524 | mutex_unlock(lock: &sdev->client_event_handler_mutex); |
525 | |
526 | return 0; |
527 | } |
528 | EXPORT_SYMBOL_NS_GPL(sof_client_register_ipc_rx_handler, SND_SOC_SOF_CLIENT); |
529 | |
530 | void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, |
531 | u32 ipc_msg_type) |
532 | { |
533 | struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); |
534 | struct sof_ipc_event_entry *event; |
535 | |
536 | mutex_lock(&sdev->client_event_handler_mutex); |
537 | |
538 | list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { |
539 | if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) { |
540 | list_del(entry: &event->list); |
541 | kfree(objp: event); |
542 | break; |
543 | } |
544 | } |
545 | |
546 | mutex_unlock(lock: &sdev->client_event_handler_mutex); |
547 | } |
548 | EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, SND_SOC_SOF_CLIENT); |
549 | |
550 | /*DSP state notification and query */ |
551 | void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) |
552 | { |
553 | struct sof_state_event_entry *event; |
554 | |
555 | mutex_lock(&sdev->client_event_handler_mutex); |
556 | |
557 | list_for_each_entry(event, &sdev->fw_state_handler_list, list) |
558 | event->callback(event->cdev, sdev->fw_state); |
559 | |
560 | mutex_unlock(lock: &sdev->client_event_handler_mutex); |
561 | } |
562 | |
563 | int sof_client_register_fw_state_handler(struct sof_client_dev *cdev, |
564 | sof_client_fw_state_callback callback) |
565 | { |
566 | struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); |
567 | struct sof_state_event_entry *event; |
568 | |
569 | if (!callback) |
570 | return -EINVAL; |
571 | |
572 | event = kmalloc(size: sizeof(*event), GFP_KERNEL); |
573 | if (!event) |
574 | return -ENOMEM; |
575 | |
576 | event->cdev = cdev; |
577 | event->callback = callback; |
578 | |
579 | /* add to list of SOF client devices */ |
580 | mutex_lock(&sdev->client_event_handler_mutex); |
581 | list_add(new: &event->list, head: &sdev->fw_state_handler_list); |
582 | mutex_unlock(lock: &sdev->client_event_handler_mutex); |
583 | |
584 | return 0; |
585 | } |
586 | EXPORT_SYMBOL_NS_GPL(sof_client_register_fw_state_handler, SND_SOC_SOF_CLIENT); |
587 | |
588 | void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev) |
589 | { |
590 | struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); |
591 | struct sof_state_event_entry *event; |
592 | |
593 | mutex_lock(&sdev->client_event_handler_mutex); |
594 | |
595 | list_for_each_entry(event, &sdev->fw_state_handler_list, list) { |
596 | if (event->cdev == cdev) { |
597 | list_del(entry: &event->list); |
598 | kfree(objp: event); |
599 | break; |
600 | } |
601 | } |
602 | |
603 | mutex_unlock(lock: &sdev->client_event_handler_mutex); |
604 | } |
605 | EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, SND_SOC_SOF_CLIENT); |
606 | |
607 | enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev) |
608 | { |
609 | struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); |
610 | |
611 | return sdev->fw_state; |
612 | } |
613 | EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_state, SND_SOC_SOF_CLIENT); |
614 | |