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