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