xref: /linux/sound/soc/sof/sof-client.c (revision 6e9548cdb30e5d6724236dd7b89a79a270751485)
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 "ops.h"
16 #include "sof-client.h"
17 #include "sof-priv.h"
18 
19 /**
20  * struct sof_ipc_event_entry - IPC client event description
21  * @ipc_msg_type:	IPC msg type of the event the client is interested
22  * @cdev:		sof_client_dev of the requesting client
23  * @callback:		Callback function of the client
24  * @list:		item in SOF core client event list
25  */
26 struct sof_ipc_event_entry {
27 	u32 ipc_msg_type;
28 	struct sof_client_dev *cdev;
29 	sof_client_event_callback callback;
30 	struct list_head list;
31 };
32 
33 /**
34  * struct sof_state_event_entry - DSP panic event subscription entry
35  * @cdev:		sof_client_dev of the requesting client
36  * @callback:		Callback function of the client
37  * @list:		item in SOF core client event list
38  */
39 struct sof_state_event_entry {
40 	struct sof_client_dev *cdev;
41 	sof_client_fw_state_callback callback;
42 	struct list_head list;
43 };
44 
45 static void sof_client_auxdev_release(struct device *dev)
46 {
47 	struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
48 	struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
49 
50 	kfree(cdev->auxdev.dev.platform_data);
51 	kfree(cdev);
52 }
53 
54 static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data,
55 				   size_t size)
56 {
57 	void *d = NULL;
58 
59 	if (data) {
60 		d = kmemdup(data, size, GFP_KERNEL);
61 		if (!d)
62 			return -ENOMEM;
63 	}
64 
65 	cdev->auxdev.dev.platform_data = d;
66 	return 0;
67 }
68 
69 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
70 static int sof_register_ipc_flood_test(struct snd_sof_dev *sdev)
71 {
72 	int ret = 0;
73 	int i;
74 
75 	for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) {
76 		ret = sof_client_dev_register(sdev, "ipc_flood", i, NULL, 0);
77 		if (ret < 0)
78 			break;
79 	}
80 
81 	if (ret) {
82 		for (; i >= 0; --i)
83 			sof_client_dev_unregister(sdev, "ipc_flood", i);
84 	}
85 
86 	return ret;
87 }
88 
89 static void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev)
90 {
91 	int i;
92 
93 	for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++)
94 		sof_client_dev_unregister(sdev, "ipc_flood", i);
95 }
96 #else
97 static inline int sof_register_ipc_flood_test(struct snd_sof_dev *sdev)
98 {
99 	return 0;
100 }
101 
102 static inline void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev) {}
103 #endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST */
104 
105 int sof_register_clients(struct snd_sof_dev *sdev)
106 {
107 	int ret;
108 
109 	/* Register platform independent client devices */
110 	ret = sof_register_ipc_flood_test(sdev);
111 	if (ret) {
112 		dev_err(sdev->dev, "IPC flood test client registration failed\n");
113 		return ret;
114 	}
115 
116 	/* Platform depndent client device registration */
117 
118 	if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients)
119 		ret = sof_ops(sdev)->register_ipc_clients(sdev);
120 
121 	if (ret)
122 		sof_unregister_ipc_flood_test(sdev);
123 
124 	return ret;
125 }
126 
127 void sof_unregister_clients(struct snd_sof_dev *sdev)
128 {
129 	if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients)
130 		sof_ops(sdev)->unregister_ipc_clients(sdev);
131 
132 	sof_unregister_ipc_flood_test(sdev);
133 }
134 
135 int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
136 			    const void *data, size_t size)
137 {
138 	struct auxiliary_device *auxdev;
139 	struct sof_client_dev *cdev;
140 	int ret;
141 
142 	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
143 	if (!cdev)
144 		return -ENOMEM;
145 
146 	cdev->sdev = sdev;
147 	auxdev = &cdev->auxdev;
148 	auxdev->name = name;
149 	auxdev->dev.parent = sdev->dev;
150 	auxdev->dev.release = sof_client_auxdev_release;
151 	auxdev->id = id;
152 
153 	ret = sof_client_dev_add_data(cdev, data, size);
154 	if (ret < 0)
155 		goto err_dev_add_data;
156 
157 	ret = auxiliary_device_init(auxdev);
158 	if (ret < 0) {
159 		dev_err(sdev->dev, "failed to initialize client dev %s.%d\n", name, id);
160 		goto err_dev_init;
161 	}
162 
163 	ret = auxiliary_device_add(&cdev->auxdev);
164 	if (ret < 0) {
165 		dev_err(sdev->dev, "failed to add client dev %s.%d\n", name, id);
166 		/*
167 		 * sof_client_auxdev_release() will be invoked to free up memory
168 		 * allocations through put_device()
169 		 */
170 		auxiliary_device_uninit(&cdev->auxdev);
171 		return ret;
172 	}
173 
174 	/* add to list of SOF client devices */
175 	mutex_lock(&sdev->ipc_client_mutex);
176 	list_add(&cdev->list, &sdev->ipc_client_list);
177 	mutex_unlock(&sdev->ipc_client_mutex);
178 
179 	return 0;
180 
181 err_dev_init:
182 	kfree(cdev->auxdev.dev.platform_data);
183 
184 err_dev_add_data:
185 	kfree(cdev);
186 
187 	return ret;
188 }
189 EXPORT_SYMBOL_NS_GPL(sof_client_dev_register, SND_SOC_SOF_CLIENT);
190 
191 void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id)
192 {
193 	struct sof_client_dev *cdev;
194 
195 	mutex_lock(&sdev->ipc_client_mutex);
196 
197 	/*
198 	 * sof_client_auxdev_release() will be invoked to free up memory
199 	 * allocations through put_device()
200 	 */
201 	list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
202 		if (!strcmp(cdev->auxdev.name, name) && cdev->auxdev.id == id) {
203 			list_del(&cdev->list);
204 			auxiliary_device_delete(&cdev->auxdev);
205 			auxiliary_device_uninit(&cdev->auxdev);
206 			break;
207 		}
208 	}
209 
210 	mutex_unlock(&sdev->ipc_client_mutex);
211 }
212 EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, SND_SOC_SOF_CLIENT);
213 
214 int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
215 			      void *reply_data, size_t reply_bytes)
216 {
217 	struct sof_ipc_cmd_hdr *hdr = ipc_msg;
218 
219 	return sof_ipc_tx_message(cdev->sdev->ipc, hdr->cmd, ipc_msg, hdr->size,
220 				  reply_data, reply_bytes);
221 }
222 EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT);
223 
224 int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state)
225 {
226 	struct auxiliary_driver *adrv;
227 	struct sof_client_dev *cdev;
228 
229 	mutex_lock(&sdev->ipc_client_mutex);
230 
231 	list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
232 		/* Skip devices without loaded driver */
233 		if (!cdev->auxdev.dev.driver)
234 			continue;
235 
236 		adrv = to_auxiliary_drv(cdev->auxdev.dev.driver);
237 		if (adrv->suspend)
238 			adrv->suspend(&cdev->auxdev, state);
239 	}
240 
241 	mutex_unlock(&sdev->ipc_client_mutex);
242 
243 	return 0;
244 }
245 EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, SND_SOC_SOF_CLIENT);
246 
247 int sof_resume_clients(struct snd_sof_dev *sdev)
248 {
249 	struct auxiliary_driver *adrv;
250 	struct sof_client_dev *cdev;
251 
252 	mutex_lock(&sdev->ipc_client_mutex);
253 
254 	list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
255 		/* Skip devices without loaded driver */
256 		if (!cdev->auxdev.dev.driver)
257 			continue;
258 
259 		adrv = to_auxiliary_drv(cdev->auxdev.dev.driver);
260 		if (adrv->resume)
261 			adrv->resume(&cdev->auxdev);
262 	}
263 
264 	mutex_unlock(&sdev->ipc_client_mutex);
265 
266 	return 0;
267 }
268 EXPORT_SYMBOL_NS_GPL(sof_resume_clients, SND_SOC_SOF_CLIENT);
269 
270 struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev)
271 {
272 	return cdev->sdev->debugfs_root;
273 }
274 EXPORT_SYMBOL_NS_GPL(sof_client_get_debugfs_root, SND_SOC_SOF_CLIENT);
275 
276 /* DMA buffer allocation in client drivers must use the core SOF device */
277 struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev)
278 {
279 	return cdev->sdev->dev;
280 }
281 EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, SND_SOC_SOF_CLIENT);
282 
283 const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev)
284 {
285 	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
286 
287 	return &sdev->fw_ready.version;
288 }
289 EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, SND_SOC_SOF_CLIENT);
290 
291 /* module refcount management of SOF core */
292 int sof_client_core_module_get(struct sof_client_dev *cdev)
293 {
294 	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
295 
296 	if (!try_module_get(sdev->dev->driver->owner))
297 		return -ENODEV;
298 
299 	return 0;
300 }
301 EXPORT_SYMBOL_NS_GPL(sof_client_core_module_get, SND_SOC_SOF_CLIENT);
302 
303 void sof_client_core_module_put(struct sof_client_dev *cdev)
304 {
305 	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
306 
307 	module_put(sdev->dev->driver->owner);
308 }
309 EXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, SND_SOC_SOF_CLIENT);
310 
311 /* IPC event handling */
312 void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf)
313 {
314 	struct sof_ipc_cmd_hdr *hdr = msg_buf;
315 	u32 msg_type = hdr->cmd & SOF_GLB_TYPE_MASK;
316 	struct sof_ipc_event_entry *event;
317 
318 	mutex_lock(&sdev->client_event_handler_mutex);
319 
320 	list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) {
321 		if (event->ipc_msg_type == msg_type)
322 			event->callback(event->cdev, msg_buf);
323 	}
324 
325 	mutex_unlock(&sdev->client_event_handler_mutex);
326 }
327 
328 int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
329 				       u32 ipc_msg_type,
330 				       sof_client_event_callback callback)
331 {
332 	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
333 	struct sof_ipc_event_entry *event;
334 
335 	if (!callback || !(ipc_msg_type & SOF_GLB_TYPE_MASK))
336 		return -EINVAL;
337 
338 	event = kmalloc(sizeof(*event), GFP_KERNEL);
339 	if (!event)
340 		return -ENOMEM;
341 
342 	event->ipc_msg_type = ipc_msg_type;
343 	event->cdev = cdev;
344 	event->callback = callback;
345 
346 	/* add to list of SOF client devices */
347 	mutex_lock(&sdev->client_event_handler_mutex);
348 	list_add(&event->list, &sdev->ipc_rx_handler_list);
349 	mutex_unlock(&sdev->client_event_handler_mutex);
350 
351 	return 0;
352 }
353 EXPORT_SYMBOL_NS_GPL(sof_client_register_ipc_rx_handler, SND_SOC_SOF_CLIENT);
354 
355 void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev,
356 					  u32 ipc_msg_type)
357 {
358 	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
359 	struct sof_ipc_event_entry *event;
360 
361 	mutex_lock(&sdev->client_event_handler_mutex);
362 
363 	list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) {
364 		if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) {
365 			list_del(&event->list);
366 			kfree(event);
367 			break;
368 		}
369 	}
370 
371 	mutex_unlock(&sdev->client_event_handler_mutex);
372 }
373 EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, SND_SOC_SOF_CLIENT);
374 
375 /*DSP state notification and query */
376 void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev)
377 {
378 	struct sof_state_event_entry *event;
379 
380 	mutex_lock(&sdev->client_event_handler_mutex);
381 
382 	list_for_each_entry(event, &sdev->fw_state_handler_list, list)
383 		event->callback(event->cdev, sdev->fw_state);
384 
385 	mutex_unlock(&sdev->client_event_handler_mutex);
386 }
387 
388 int sof_client_register_fw_state_handler(struct sof_client_dev *cdev,
389 					 sof_client_fw_state_callback callback)
390 {
391 	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
392 	struct sof_state_event_entry *event;
393 
394 	if (!callback)
395 		return -EINVAL;
396 
397 	event = kmalloc(sizeof(*event), GFP_KERNEL);
398 	if (!event)
399 		return -ENOMEM;
400 
401 	event->cdev = cdev;
402 	event->callback = callback;
403 
404 	/* add to list of SOF client devices */
405 	mutex_lock(&sdev->client_event_handler_mutex);
406 	list_add(&event->list, &sdev->fw_state_handler_list);
407 	mutex_unlock(&sdev->client_event_handler_mutex);
408 
409 	return 0;
410 }
411 EXPORT_SYMBOL_NS_GPL(sof_client_register_fw_state_handler, SND_SOC_SOF_CLIENT);
412 
413 void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev)
414 {
415 	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
416 	struct sof_state_event_entry *event;
417 
418 	mutex_lock(&sdev->client_event_handler_mutex);
419 
420 	list_for_each_entry(event, &sdev->fw_state_handler_list, list) {
421 		if (event->cdev == cdev) {
422 			list_del(&event->list);
423 			kfree(event);
424 			break;
425 		}
426 	}
427 
428 	mutex_unlock(&sdev->client_event_handler_mutex);
429 }
430 EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, SND_SOC_SOF_CLIENT);
431 
432 enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev)
433 {
434 	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
435 
436 	return sdev->fw_state;
437 }
438 EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_state, SND_SOC_SOF_CLIENT);
439