0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/debugfs.h>
0010 #include <linux/errno.h>
0011 #include <linux/list.h>
0012 #include <linux/module.h>
0013 #include <linux/mutex.h>
0014 #include <linux/slab.h>
0015 #include <sound/sof/ipc4/header.h>
0016 #include "ops.h"
0017 #include "sof-client.h"
0018 #include "sof-priv.h"
0019
0020
0021
0022
0023
0024
0025
0026
0027 struct sof_ipc_event_entry {
0028 u32 ipc_msg_type;
0029 struct sof_client_dev *cdev;
0030 sof_client_event_callback callback;
0031 struct list_head list;
0032 };
0033
0034
0035
0036
0037
0038
0039
0040 struct sof_state_event_entry {
0041 struct sof_client_dev *cdev;
0042 sof_client_fw_state_callback callback;
0043 struct list_head list;
0044 };
0045
0046 static void sof_client_auxdev_release(struct device *dev)
0047 {
0048 struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
0049 struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
0050
0051 kfree(cdev->auxdev.dev.platform_data);
0052 kfree(cdev);
0053 }
0054
0055 static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data,
0056 size_t size)
0057 {
0058 void *d = NULL;
0059
0060 if (data) {
0061 d = kmemdup(data, size, GFP_KERNEL);
0062 if (!d)
0063 return -ENOMEM;
0064 }
0065
0066 cdev->auxdev.dev.platform_data = d;
0067 return 0;
0068 }
0069
0070 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
0071 static int sof_register_ipc_flood_test(struct snd_sof_dev *sdev)
0072 {
0073 int ret = 0;
0074 int i;
0075
0076 if (sdev->pdata->ipc_type != SOF_IPC)
0077 return 0;
0078
0079 for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) {
0080 ret = sof_client_dev_register(sdev, "ipc_flood", i, NULL, 0);
0081 if (ret < 0)
0082 break;
0083 }
0084
0085 if (ret) {
0086 for (; i >= 0; --i)
0087 sof_client_dev_unregister(sdev, "ipc_flood", i);
0088 }
0089
0090 return ret;
0091 }
0092
0093 static void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev)
0094 {
0095 int i;
0096
0097 for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++)
0098 sof_client_dev_unregister(sdev, "ipc_flood", i);
0099 }
0100 #else
0101 static inline int sof_register_ipc_flood_test(struct snd_sof_dev *sdev)
0102 {
0103 return 0;
0104 }
0105
0106 static inline void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev) {}
0107 #endif
0108
0109 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR)
0110 static int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev)
0111 {
0112 return sof_client_dev_register(sdev, "msg_injector", 0, NULL, 0);
0113 }
0114
0115 static void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev)
0116 {
0117 sof_client_dev_unregister(sdev, "msg_injector", 0);
0118 }
0119 #else
0120 static inline int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev)
0121 {
0122 return 0;
0123 }
0124
0125 static inline void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) {}
0126 #endif
0127
0128 int sof_register_clients(struct snd_sof_dev *sdev)
0129 {
0130 int ret;
0131
0132
0133 ret = sof_register_ipc_flood_test(sdev);
0134 if (ret) {
0135 dev_err(sdev->dev, "IPC flood test client registration failed\n");
0136 return ret;
0137 }
0138
0139 ret = sof_register_ipc_msg_injector(sdev);
0140 if (ret) {
0141 dev_err(sdev->dev, "IPC message injector client registration failed\n");
0142 goto err_msg_injector;
0143 }
0144
0145
0146
0147 if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients)
0148 ret = sof_ops(sdev)->register_ipc_clients(sdev);
0149
0150 if (!ret)
0151 return 0;
0152
0153 sof_unregister_ipc_msg_injector(sdev);
0154
0155 err_msg_injector:
0156 sof_unregister_ipc_flood_test(sdev);
0157
0158 return ret;
0159 }
0160
0161 void sof_unregister_clients(struct snd_sof_dev *sdev)
0162 {
0163 if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients)
0164 sof_ops(sdev)->unregister_ipc_clients(sdev);
0165
0166 sof_unregister_ipc_msg_injector(sdev);
0167 sof_unregister_ipc_flood_test(sdev);
0168 }
0169
0170 int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
0171 const void *data, size_t size)
0172 {
0173 struct auxiliary_device *auxdev;
0174 struct sof_client_dev *cdev;
0175 int ret;
0176
0177 cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
0178 if (!cdev)
0179 return -ENOMEM;
0180
0181 cdev->sdev = sdev;
0182 auxdev = &cdev->auxdev;
0183 auxdev->name = name;
0184 auxdev->dev.parent = sdev->dev;
0185 auxdev->dev.release = sof_client_auxdev_release;
0186 auxdev->id = id;
0187
0188 ret = sof_client_dev_add_data(cdev, data, size);
0189 if (ret < 0)
0190 goto err_dev_add_data;
0191
0192 ret = auxiliary_device_init(auxdev);
0193 if (ret < 0) {
0194 dev_err(sdev->dev, "failed to initialize client dev %s.%d\n", name, id);
0195 goto err_dev_init;
0196 }
0197
0198 ret = auxiliary_device_add(&cdev->auxdev);
0199 if (ret < 0) {
0200 dev_err(sdev->dev, "failed to add client dev %s.%d\n", name, id);
0201
0202
0203
0204
0205 auxiliary_device_uninit(&cdev->auxdev);
0206 return ret;
0207 }
0208
0209
0210 mutex_lock(&sdev->ipc_client_mutex);
0211 list_add(&cdev->list, &sdev->ipc_client_list);
0212 mutex_unlock(&sdev->ipc_client_mutex);
0213
0214 return 0;
0215
0216 err_dev_init:
0217 kfree(cdev->auxdev.dev.platform_data);
0218
0219 err_dev_add_data:
0220 kfree(cdev);
0221
0222 return ret;
0223 }
0224 EXPORT_SYMBOL_NS_GPL(sof_client_dev_register, SND_SOC_SOF_CLIENT);
0225
0226 void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id)
0227 {
0228 struct sof_client_dev *cdev;
0229
0230 mutex_lock(&sdev->ipc_client_mutex);
0231
0232
0233
0234
0235
0236 list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
0237 if (!strcmp(cdev->auxdev.name, name) && cdev->auxdev.id == id) {
0238 list_del(&cdev->list);
0239 auxiliary_device_delete(&cdev->auxdev);
0240 auxiliary_device_uninit(&cdev->auxdev);
0241 break;
0242 }
0243 }
0244
0245 mutex_unlock(&sdev->ipc_client_mutex);
0246 }
0247 EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, SND_SOC_SOF_CLIENT);
0248
0249 int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
0250 void *reply_data, size_t reply_bytes)
0251 {
0252 if (cdev->sdev->pdata->ipc_type == SOF_IPC) {
0253 struct sof_ipc_cmd_hdr *hdr = ipc_msg;
0254
0255 return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, hdr->size,
0256 reply_data, reply_bytes);
0257 } else if (cdev->sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
0258 struct sof_ipc4_msg *msg = ipc_msg;
0259
0260 return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, msg->data_size,
0261 reply_data, reply_bytes);
0262 }
0263
0264 return -EINVAL;
0265 }
0266 EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT);
0267
0268 int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state)
0269 {
0270 struct auxiliary_driver *adrv;
0271 struct sof_client_dev *cdev;
0272
0273 mutex_lock(&sdev->ipc_client_mutex);
0274
0275 list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
0276
0277 if (!cdev->auxdev.dev.driver)
0278 continue;
0279
0280 adrv = to_auxiliary_drv(cdev->auxdev.dev.driver);
0281 if (adrv->suspend)
0282 adrv->suspend(&cdev->auxdev, state);
0283 }
0284
0285 mutex_unlock(&sdev->ipc_client_mutex);
0286
0287 return 0;
0288 }
0289 EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, SND_SOC_SOF_CLIENT);
0290
0291 int sof_resume_clients(struct snd_sof_dev *sdev)
0292 {
0293 struct auxiliary_driver *adrv;
0294 struct sof_client_dev *cdev;
0295
0296 mutex_lock(&sdev->ipc_client_mutex);
0297
0298 list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
0299
0300 if (!cdev->auxdev.dev.driver)
0301 continue;
0302
0303 adrv = to_auxiliary_drv(cdev->auxdev.dev.driver);
0304 if (adrv->resume)
0305 adrv->resume(&cdev->auxdev);
0306 }
0307
0308 mutex_unlock(&sdev->ipc_client_mutex);
0309
0310 return 0;
0311 }
0312 EXPORT_SYMBOL_NS_GPL(sof_resume_clients, SND_SOC_SOF_CLIENT);
0313
0314 struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev)
0315 {
0316 return cdev->sdev->debugfs_root;
0317 }
0318 EXPORT_SYMBOL_NS_GPL(sof_client_get_debugfs_root, SND_SOC_SOF_CLIENT);
0319
0320
0321 struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev)
0322 {
0323 return cdev->sdev->dev;
0324 }
0325 EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, SND_SOC_SOF_CLIENT);
0326
0327 const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev)
0328 {
0329 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
0330
0331 return &sdev->fw_ready.version;
0332 }
0333 EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, SND_SOC_SOF_CLIENT);
0334
0335 size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev)
0336 {
0337 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
0338
0339 return sdev->ipc->max_payload_size;
0340 }
0341 EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_max_payload_size, SND_SOC_SOF_CLIENT);
0342
0343 enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev)
0344 {
0345 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
0346
0347 return sdev->pdata->ipc_type;
0348 }
0349 EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_type, SND_SOC_SOF_CLIENT);
0350
0351
0352 int sof_client_core_module_get(struct sof_client_dev *cdev)
0353 {
0354 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
0355
0356 if (!try_module_get(sdev->dev->driver->owner))
0357 return -ENODEV;
0358
0359 return 0;
0360 }
0361 EXPORT_SYMBOL_NS_GPL(sof_client_core_module_get, SND_SOC_SOF_CLIENT);
0362
0363 void sof_client_core_module_put(struct sof_client_dev *cdev)
0364 {
0365 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
0366
0367 module_put(sdev->dev->driver->owner);
0368 }
0369 EXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, SND_SOC_SOF_CLIENT);
0370
0371
0372 void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf)
0373 {
0374 struct sof_ipc_event_entry *event;
0375 u32 msg_type;
0376
0377 if (sdev->pdata->ipc_type == SOF_IPC) {
0378 struct sof_ipc_cmd_hdr *hdr = msg_buf;
0379
0380 msg_type = hdr->cmd & SOF_GLB_TYPE_MASK;
0381 } else if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
0382 struct sof_ipc4_msg *msg = msg_buf;
0383
0384 msg_type = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary);
0385 } else {
0386 dev_dbg_once(sdev->dev, "Not supported IPC version: %d\n",
0387 sdev->pdata->ipc_type);
0388 return;
0389 }
0390
0391 mutex_lock(&sdev->client_event_handler_mutex);
0392
0393 list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) {
0394 if (event->ipc_msg_type == msg_type)
0395 event->callback(event->cdev, msg_buf);
0396 }
0397
0398 mutex_unlock(&sdev->client_event_handler_mutex);
0399 }
0400
0401 int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
0402 u32 ipc_msg_type,
0403 sof_client_event_callback callback)
0404 {
0405 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
0406 struct sof_ipc_event_entry *event;
0407
0408 if (!callback)
0409 return -EINVAL;
0410
0411 if (cdev->sdev->pdata->ipc_type == SOF_IPC) {
0412 if (!(ipc_msg_type & SOF_GLB_TYPE_MASK))
0413 return -EINVAL;
0414 } else if (cdev->sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
0415 if (!(ipc_msg_type & SOF_IPC4_NOTIFICATION_TYPE_MASK))
0416 return -EINVAL;
0417 } else {
0418 dev_warn(sdev->dev, "%s: Not supported IPC version: %d\n",
0419 __func__, sdev->pdata->ipc_type);
0420 return -EINVAL;
0421 }
0422
0423 event = kmalloc(sizeof(*event), GFP_KERNEL);
0424 if (!event)
0425 return -ENOMEM;
0426
0427 event->ipc_msg_type = ipc_msg_type;
0428 event->cdev = cdev;
0429 event->callback = callback;
0430
0431
0432 mutex_lock(&sdev->client_event_handler_mutex);
0433 list_add(&event->list, &sdev->ipc_rx_handler_list);
0434 mutex_unlock(&sdev->client_event_handler_mutex);
0435
0436 return 0;
0437 }
0438 EXPORT_SYMBOL_NS_GPL(sof_client_register_ipc_rx_handler, SND_SOC_SOF_CLIENT);
0439
0440 void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev,
0441 u32 ipc_msg_type)
0442 {
0443 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
0444 struct sof_ipc_event_entry *event;
0445
0446 mutex_lock(&sdev->client_event_handler_mutex);
0447
0448 list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) {
0449 if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) {
0450 list_del(&event->list);
0451 kfree(event);
0452 break;
0453 }
0454 }
0455
0456 mutex_unlock(&sdev->client_event_handler_mutex);
0457 }
0458 EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, SND_SOC_SOF_CLIENT);
0459
0460
0461 void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev)
0462 {
0463 struct sof_state_event_entry *event;
0464
0465 mutex_lock(&sdev->client_event_handler_mutex);
0466
0467 list_for_each_entry(event, &sdev->fw_state_handler_list, list)
0468 event->callback(event->cdev, sdev->fw_state);
0469
0470 mutex_unlock(&sdev->client_event_handler_mutex);
0471 }
0472
0473 int sof_client_register_fw_state_handler(struct sof_client_dev *cdev,
0474 sof_client_fw_state_callback callback)
0475 {
0476 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
0477 struct sof_state_event_entry *event;
0478
0479 if (!callback)
0480 return -EINVAL;
0481
0482 event = kmalloc(sizeof(*event), GFP_KERNEL);
0483 if (!event)
0484 return -ENOMEM;
0485
0486 event->cdev = cdev;
0487 event->callback = callback;
0488
0489
0490 mutex_lock(&sdev->client_event_handler_mutex);
0491 list_add(&event->list, &sdev->fw_state_handler_list);
0492 mutex_unlock(&sdev->client_event_handler_mutex);
0493
0494 return 0;
0495 }
0496 EXPORT_SYMBOL_NS_GPL(sof_client_register_fw_state_handler, SND_SOC_SOF_CLIENT);
0497
0498 void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev)
0499 {
0500 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
0501 struct sof_state_event_entry *event;
0502
0503 mutex_lock(&sdev->client_event_handler_mutex);
0504
0505 list_for_each_entry(event, &sdev->fw_state_handler_list, list) {
0506 if (event->cdev == cdev) {
0507 list_del(&event->list);
0508 kfree(event);
0509 break;
0510 }
0511 }
0512
0513 mutex_unlock(&sdev->client_event_handler_mutex);
0514 }
0515 EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, SND_SOC_SOF_CLIENT);
0516
0517 enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev)
0518 {
0519 struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
0520
0521 return sdev->fw_state;
0522 }
0523 EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_state, SND_SOC_SOF_CLIENT);