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