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