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 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) 106 static int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev) 107 { 108 return sof_client_dev_register(sdev, "msg_injector", 0, NULL, 0); 109 } 110 111 static void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) 112 { 113 sof_client_dev_unregister(sdev, "msg_injector", 0); 114 } 115 #else 116 static inline int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev) 117 { 118 return 0; 119 } 120 121 static inline void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) {} 122 #endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR */ 123 124 int sof_register_clients(struct snd_sof_dev *sdev) 125 { 126 int ret; 127 128 /* Register platform independent client devices */ 129 ret = sof_register_ipc_flood_test(sdev); 130 if (ret) { 131 dev_err(sdev->dev, "IPC flood test client registration failed\n"); 132 return ret; 133 } 134 135 ret = sof_register_ipc_msg_injector(sdev); 136 if (ret) { 137 dev_err(sdev->dev, "IPC message injector client registration failed\n"); 138 goto err_msg_injector; 139 } 140 141 /* Platform depndent client device registration */ 142 143 if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients) 144 ret = sof_ops(sdev)->register_ipc_clients(sdev); 145 146 if (!ret) 147 return 0; 148 149 sof_unregister_ipc_msg_injector(sdev); 150 151 err_msg_injector: 152 sof_unregister_ipc_flood_test(sdev); 153 154 return ret; 155 } 156 157 void sof_unregister_clients(struct snd_sof_dev *sdev) 158 { 159 if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients) 160 sof_ops(sdev)->unregister_ipc_clients(sdev); 161 162 sof_unregister_ipc_msg_injector(sdev); 163 sof_unregister_ipc_flood_test(sdev); 164 } 165 166 int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, 167 const void *data, size_t size) 168 { 169 struct auxiliary_device *auxdev; 170 struct sof_client_dev *cdev; 171 int ret; 172 173 cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); 174 if (!cdev) 175 return -ENOMEM; 176 177 cdev->sdev = sdev; 178 auxdev = &cdev->auxdev; 179 auxdev->name = name; 180 auxdev->dev.parent = sdev->dev; 181 auxdev->dev.release = sof_client_auxdev_release; 182 auxdev->id = id; 183 184 ret = sof_client_dev_add_data(cdev, data, size); 185 if (ret < 0) 186 goto err_dev_add_data; 187 188 ret = auxiliary_device_init(auxdev); 189 if (ret < 0) { 190 dev_err(sdev->dev, "failed to initialize client dev %s.%d\n", name, id); 191 goto err_dev_init; 192 } 193 194 ret = auxiliary_device_add(&cdev->auxdev); 195 if (ret < 0) { 196 dev_err(sdev->dev, "failed to add client dev %s.%d\n", name, id); 197 /* 198 * sof_client_auxdev_release() will be invoked to free up memory 199 * allocations through put_device() 200 */ 201 auxiliary_device_uninit(&cdev->auxdev); 202 return ret; 203 } 204 205 /* add to list of SOF client devices */ 206 mutex_lock(&sdev->ipc_client_mutex); 207 list_add(&cdev->list, &sdev->ipc_client_list); 208 mutex_unlock(&sdev->ipc_client_mutex); 209 210 return 0; 211 212 err_dev_init: 213 kfree(cdev->auxdev.dev.platform_data); 214 215 err_dev_add_data: 216 kfree(cdev); 217 218 return ret; 219 } 220 EXPORT_SYMBOL_NS_GPL(sof_client_dev_register, SND_SOC_SOF_CLIENT); 221 222 void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id) 223 { 224 struct sof_client_dev *cdev; 225 226 mutex_lock(&sdev->ipc_client_mutex); 227 228 /* 229 * sof_client_auxdev_release() will be invoked to free up memory 230 * allocations through put_device() 231 */ 232 list_for_each_entry(cdev, &sdev->ipc_client_list, list) { 233 if (!strcmp(cdev->auxdev.name, name) && cdev->auxdev.id == id) { 234 list_del(&cdev->list); 235 auxiliary_device_delete(&cdev->auxdev); 236 auxiliary_device_uninit(&cdev->auxdev); 237 break; 238 } 239 } 240 241 mutex_unlock(&sdev->ipc_client_mutex); 242 } 243 EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, SND_SOC_SOF_CLIENT); 244 245 int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, 246 void *reply_data, size_t reply_bytes) 247 { 248 struct sof_ipc_cmd_hdr *hdr = ipc_msg; 249 250 return sof_ipc_tx_message(cdev->sdev->ipc, hdr->cmd, ipc_msg, hdr->size, 251 reply_data, reply_bytes); 252 } 253 EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT); 254 255 int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) 256 { 257 struct auxiliary_driver *adrv; 258 struct sof_client_dev *cdev; 259 260 mutex_lock(&sdev->ipc_client_mutex); 261 262 list_for_each_entry(cdev, &sdev->ipc_client_list, list) { 263 /* Skip devices without loaded driver */ 264 if (!cdev->auxdev.dev.driver) 265 continue; 266 267 adrv = to_auxiliary_drv(cdev->auxdev.dev.driver); 268 if (adrv->suspend) 269 adrv->suspend(&cdev->auxdev, state); 270 } 271 272 mutex_unlock(&sdev->ipc_client_mutex); 273 274 return 0; 275 } 276 EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, SND_SOC_SOF_CLIENT); 277 278 int sof_resume_clients(struct snd_sof_dev *sdev) 279 { 280 struct auxiliary_driver *adrv; 281 struct sof_client_dev *cdev; 282 283 mutex_lock(&sdev->ipc_client_mutex); 284 285 list_for_each_entry(cdev, &sdev->ipc_client_list, list) { 286 /* Skip devices without loaded driver */ 287 if (!cdev->auxdev.dev.driver) 288 continue; 289 290 adrv = to_auxiliary_drv(cdev->auxdev.dev.driver); 291 if (adrv->resume) 292 adrv->resume(&cdev->auxdev); 293 } 294 295 mutex_unlock(&sdev->ipc_client_mutex); 296 297 return 0; 298 } 299 EXPORT_SYMBOL_NS_GPL(sof_resume_clients, SND_SOC_SOF_CLIENT); 300 301 struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev) 302 { 303 return cdev->sdev->debugfs_root; 304 } 305 EXPORT_SYMBOL_NS_GPL(sof_client_get_debugfs_root, SND_SOC_SOF_CLIENT); 306 307 /* DMA buffer allocation in client drivers must use the core SOF device */ 308 struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev) 309 { 310 return cdev->sdev->dev; 311 } 312 EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, SND_SOC_SOF_CLIENT); 313 314 const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev) 315 { 316 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 317 318 return &sdev->fw_ready.version; 319 } 320 EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, SND_SOC_SOF_CLIENT); 321 322 /* module refcount management of SOF core */ 323 int sof_client_core_module_get(struct sof_client_dev *cdev) 324 { 325 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 326 327 if (!try_module_get(sdev->dev->driver->owner)) 328 return -ENODEV; 329 330 return 0; 331 } 332 EXPORT_SYMBOL_NS_GPL(sof_client_core_module_get, SND_SOC_SOF_CLIENT); 333 334 void sof_client_core_module_put(struct sof_client_dev *cdev) 335 { 336 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 337 338 module_put(sdev->dev->driver->owner); 339 } 340 EXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, SND_SOC_SOF_CLIENT); 341 342 /* IPC event handling */ 343 void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf) 344 { 345 struct sof_ipc_cmd_hdr *hdr = msg_buf; 346 u32 msg_type = hdr->cmd & SOF_GLB_TYPE_MASK; 347 struct sof_ipc_event_entry *event; 348 349 mutex_lock(&sdev->client_event_handler_mutex); 350 351 list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { 352 if (event->ipc_msg_type == msg_type) 353 event->callback(event->cdev, msg_buf); 354 } 355 356 mutex_unlock(&sdev->client_event_handler_mutex); 357 } 358 359 int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, 360 u32 ipc_msg_type, 361 sof_client_event_callback callback) 362 { 363 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 364 struct sof_ipc_event_entry *event; 365 366 if (!callback || !(ipc_msg_type & SOF_GLB_TYPE_MASK)) 367 return -EINVAL; 368 369 event = kmalloc(sizeof(*event), GFP_KERNEL); 370 if (!event) 371 return -ENOMEM; 372 373 event->ipc_msg_type = ipc_msg_type; 374 event->cdev = cdev; 375 event->callback = callback; 376 377 /* add to list of SOF client devices */ 378 mutex_lock(&sdev->client_event_handler_mutex); 379 list_add(&event->list, &sdev->ipc_rx_handler_list); 380 mutex_unlock(&sdev->client_event_handler_mutex); 381 382 return 0; 383 } 384 EXPORT_SYMBOL_NS_GPL(sof_client_register_ipc_rx_handler, SND_SOC_SOF_CLIENT); 385 386 void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, 387 u32 ipc_msg_type) 388 { 389 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 390 struct sof_ipc_event_entry *event; 391 392 mutex_lock(&sdev->client_event_handler_mutex); 393 394 list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { 395 if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) { 396 list_del(&event->list); 397 kfree(event); 398 break; 399 } 400 } 401 402 mutex_unlock(&sdev->client_event_handler_mutex); 403 } 404 EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, SND_SOC_SOF_CLIENT); 405 406 /*DSP state notification and query */ 407 void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) 408 { 409 struct sof_state_event_entry *event; 410 411 mutex_lock(&sdev->client_event_handler_mutex); 412 413 list_for_each_entry(event, &sdev->fw_state_handler_list, list) 414 event->callback(event->cdev, sdev->fw_state); 415 416 mutex_unlock(&sdev->client_event_handler_mutex); 417 } 418 419 int sof_client_register_fw_state_handler(struct sof_client_dev *cdev, 420 sof_client_fw_state_callback callback) 421 { 422 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 423 struct sof_state_event_entry *event; 424 425 if (!callback) 426 return -EINVAL; 427 428 event = kmalloc(sizeof(*event), GFP_KERNEL); 429 if (!event) 430 return -ENOMEM; 431 432 event->cdev = cdev; 433 event->callback = callback; 434 435 /* add to list of SOF client devices */ 436 mutex_lock(&sdev->client_event_handler_mutex); 437 list_add(&event->list, &sdev->fw_state_handler_list); 438 mutex_unlock(&sdev->client_event_handler_mutex); 439 440 return 0; 441 } 442 EXPORT_SYMBOL_NS_GPL(sof_client_register_fw_state_handler, SND_SOC_SOF_CLIENT); 443 444 void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev) 445 { 446 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 447 struct sof_state_event_entry *event; 448 449 mutex_lock(&sdev->client_event_handler_mutex); 450 451 list_for_each_entry(event, &sdev->fw_state_handler_list, list) { 452 if (event->cdev == cdev) { 453 list_del(&event->list); 454 kfree(event); 455 break; 456 } 457 } 458 459 mutex_unlock(&sdev->client_event_handler_mutex); 460 } 461 EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, SND_SOC_SOF_CLIENT); 462 463 enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev) 464 { 465 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 466 467 return sdev->fw_state; 468 } 469 EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_state, SND_SOC_SOF_CLIENT); 470