162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright(c) 2022 Intel Corporation. All rights reserved. 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Authors: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> 662306a36Sopenharmony_ci// Peter Ujfalusi <peter.ujfalusi@linux.intel.com> 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/debugfs.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/list.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/mutex.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <sound/sof/ipc4/header.h> 1662306a36Sopenharmony_ci#include "ops.h" 1762306a36Sopenharmony_ci#include "sof-client.h" 1862306a36Sopenharmony_ci#include "sof-priv.h" 1962306a36Sopenharmony_ci#include "ipc3-priv.h" 2062306a36Sopenharmony_ci#include "ipc4-priv.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/** 2362306a36Sopenharmony_ci * struct sof_ipc_event_entry - IPC client event description 2462306a36Sopenharmony_ci * @ipc_msg_type: IPC msg type of the event the client is interested 2562306a36Sopenharmony_ci * @cdev: sof_client_dev of the requesting client 2662306a36Sopenharmony_ci * @callback: Callback function of the client 2762306a36Sopenharmony_ci * @list: item in SOF core client event list 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistruct sof_ipc_event_entry { 3062306a36Sopenharmony_ci u32 ipc_msg_type; 3162306a36Sopenharmony_ci struct sof_client_dev *cdev; 3262306a36Sopenharmony_ci sof_client_event_callback callback; 3362306a36Sopenharmony_ci struct list_head list; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/** 3762306a36Sopenharmony_ci * struct sof_state_event_entry - DSP panic event subscription entry 3862306a36Sopenharmony_ci * @cdev: sof_client_dev of the requesting client 3962306a36Sopenharmony_ci * @callback: Callback function of the client 4062306a36Sopenharmony_ci * @list: item in SOF core client event list 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_cistruct sof_state_event_entry { 4362306a36Sopenharmony_ci struct sof_client_dev *cdev; 4462306a36Sopenharmony_ci sof_client_fw_state_callback callback; 4562306a36Sopenharmony_ci struct list_head list; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void sof_client_auxdev_release(struct device *dev) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct auxiliary_device *auxdev = to_auxiliary_dev(dev); 5162306a36Sopenharmony_ci struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci kfree(cdev->auxdev.dev.platform_data); 5462306a36Sopenharmony_ci kfree(cdev); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data, 5862306a36Sopenharmony_ci size_t size) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci void *d = NULL; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (data) { 6362306a36Sopenharmony_ci d = kmemdup(data, size, GFP_KERNEL); 6462306a36Sopenharmony_ci if (!d) 6562306a36Sopenharmony_ci return -ENOMEM; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci cdev->auxdev.dev.platform_data = d; 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) 7362306a36Sopenharmony_cistatic int sof_register_ipc_flood_test(struct snd_sof_dev *sdev) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci int ret = 0; 7662306a36Sopenharmony_ci int i; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (sdev->pdata->ipc_type != SOF_IPC) 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) { 8262306a36Sopenharmony_ci ret = sof_client_dev_register(sdev, "ipc_flood", i, NULL, 0); 8362306a36Sopenharmony_ci if (ret < 0) 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (ret) { 8862306a36Sopenharmony_ci for (; i >= 0; --i) 8962306a36Sopenharmony_ci sof_client_dev_unregister(sdev, "ipc_flood", i); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return ret; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci int i; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) 10062306a36Sopenharmony_ci sof_client_dev_unregister(sdev, "ipc_flood", i); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci#else 10362306a36Sopenharmony_cistatic inline int sof_register_ipc_flood_test(struct snd_sof_dev *sdev) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic inline void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev) {} 10962306a36Sopenharmony_ci#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) 11262306a36Sopenharmony_cistatic int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci return sof_client_dev_register(sdev, "msg_injector", 0, NULL, 0); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci sof_client_dev_unregister(sdev, "msg_injector", 0); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci#else 12262306a36Sopenharmony_cistatic inline int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic inline void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) {} 12862306a36Sopenharmony_ci#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR */ 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR) 13162306a36Sopenharmony_cistatic int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci /* Only IPC3 supported right now */ 13462306a36Sopenharmony_ci if (sdev->pdata->ipc_type != SOF_IPC) 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return sof_client_dev_register(sdev, "kernel_injector", 0, NULL, 0); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci sof_client_dev_unregister(sdev, "kernel_injector", 0); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci#else 14562306a36Sopenharmony_cistatic inline int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic inline void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev) {} 15162306a36Sopenharmony_ci#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciint sof_register_clients(struct snd_sof_dev *sdev) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int ret; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (sdev->dspless_mode_selected) 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Register platform independent client devices */ 16162306a36Sopenharmony_ci ret = sof_register_ipc_flood_test(sdev); 16262306a36Sopenharmony_ci if (ret) { 16362306a36Sopenharmony_ci dev_err(sdev->dev, "IPC flood test client registration failed\n"); 16462306a36Sopenharmony_ci return ret; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ret = sof_register_ipc_msg_injector(sdev); 16862306a36Sopenharmony_ci if (ret) { 16962306a36Sopenharmony_ci dev_err(sdev->dev, "IPC message injector client registration failed\n"); 17062306a36Sopenharmony_ci goto err_msg_injector; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ret = sof_register_ipc_kernel_injector(sdev); 17462306a36Sopenharmony_ci if (ret) { 17562306a36Sopenharmony_ci dev_err(sdev->dev, "IPC kernel injector client registration failed\n"); 17662306a36Sopenharmony_ci goto err_kernel_injector; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Platform depndent client device registration */ 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients) 18262306a36Sopenharmony_ci ret = sof_ops(sdev)->register_ipc_clients(sdev); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (!ret) 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci sof_unregister_ipc_kernel_injector(sdev); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cierr_kernel_injector: 19062306a36Sopenharmony_ci sof_unregister_ipc_msg_injector(sdev); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cierr_msg_injector: 19362306a36Sopenharmony_ci sof_unregister_ipc_flood_test(sdev); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return ret; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_civoid sof_unregister_clients(struct snd_sof_dev *sdev) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients) 20162306a36Sopenharmony_ci sof_ops(sdev)->unregister_ipc_clients(sdev); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci sof_unregister_ipc_kernel_injector(sdev); 20462306a36Sopenharmony_ci sof_unregister_ipc_msg_injector(sdev); 20562306a36Sopenharmony_ci sof_unregister_ipc_flood_test(sdev); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciint sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, 20962306a36Sopenharmony_ci const void *data, size_t size) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct auxiliary_device *auxdev; 21262306a36Sopenharmony_ci struct sof_client_dev *cdev; 21362306a36Sopenharmony_ci int ret; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); 21662306a36Sopenharmony_ci if (!cdev) 21762306a36Sopenharmony_ci return -ENOMEM; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci cdev->sdev = sdev; 22062306a36Sopenharmony_ci auxdev = &cdev->auxdev; 22162306a36Sopenharmony_ci auxdev->name = name; 22262306a36Sopenharmony_ci auxdev->dev.parent = sdev->dev; 22362306a36Sopenharmony_ci auxdev->dev.release = sof_client_auxdev_release; 22462306a36Sopenharmony_ci auxdev->id = id; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci ret = sof_client_dev_add_data(cdev, data, size); 22762306a36Sopenharmony_ci if (ret < 0) 22862306a36Sopenharmony_ci goto err_dev_add_data; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci ret = auxiliary_device_init(auxdev); 23162306a36Sopenharmony_ci if (ret < 0) { 23262306a36Sopenharmony_ci dev_err(sdev->dev, "failed to initialize client dev %s.%d\n", name, id); 23362306a36Sopenharmony_ci goto err_dev_init; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ret = auxiliary_device_add(&cdev->auxdev); 23762306a36Sopenharmony_ci if (ret < 0) { 23862306a36Sopenharmony_ci dev_err(sdev->dev, "failed to add client dev %s.%d\n", name, id); 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * sof_client_auxdev_release() will be invoked to free up memory 24162306a36Sopenharmony_ci * allocations through put_device() 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci auxiliary_device_uninit(&cdev->auxdev); 24462306a36Sopenharmony_ci return ret; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* add to list of SOF client devices */ 24862306a36Sopenharmony_ci mutex_lock(&sdev->ipc_client_mutex); 24962306a36Sopenharmony_ci list_add(&cdev->list, &sdev->ipc_client_list); 25062306a36Sopenharmony_ci mutex_unlock(&sdev->ipc_client_mutex); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cierr_dev_init: 25562306a36Sopenharmony_ci kfree(cdev->auxdev.dev.platform_data); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cierr_dev_add_data: 25862306a36Sopenharmony_ci kfree(cdev); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_dev_register, SND_SOC_SOF_CLIENT); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_civoid sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct sof_client_dev *cdev; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci mutex_lock(&sdev->ipc_client_mutex); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* 27162306a36Sopenharmony_ci * sof_client_auxdev_release() will be invoked to free up memory 27262306a36Sopenharmony_ci * allocations through put_device() 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ci list_for_each_entry(cdev, &sdev->ipc_client_list, list) { 27562306a36Sopenharmony_ci if (!strcmp(cdev->auxdev.name, name) && cdev->auxdev.id == id) { 27662306a36Sopenharmony_ci list_del(&cdev->list); 27762306a36Sopenharmony_ci auxiliary_device_delete(&cdev->auxdev); 27862306a36Sopenharmony_ci auxiliary_device_uninit(&cdev->auxdev); 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci mutex_unlock(&sdev->ipc_client_mutex); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, SND_SOC_SOF_CLIENT); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ciint sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, 28862306a36Sopenharmony_ci void *reply_data, size_t reply_bytes) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci if (cdev->sdev->pdata->ipc_type == SOF_IPC) { 29162306a36Sopenharmony_ci struct sof_ipc_cmd_hdr *hdr = ipc_msg; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, hdr->size, 29462306a36Sopenharmony_ci reply_data, reply_bytes); 29562306a36Sopenharmony_ci } else if (cdev->sdev->pdata->ipc_type == SOF_INTEL_IPC4) { 29662306a36Sopenharmony_ci struct sof_ipc4_msg *msg = ipc_msg; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, msg->data_size, 29962306a36Sopenharmony_ci reply_data, reply_bytes); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return -EINVAL; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ciint sof_client_ipc_rx_message(struct sof_client_dev *cdev, void *ipc_msg, void *msg_buf) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci if (cdev->sdev->pdata->ipc_type == SOF_IPC) { 30962306a36Sopenharmony_ci struct sof_ipc_cmd_hdr *hdr = ipc_msg; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (hdr->size < sizeof(hdr)) { 31262306a36Sopenharmony_ci dev_err(cdev->sdev->dev, "The received message size is invalid\n"); 31362306a36Sopenharmony_ci return -EINVAL; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci sof_ipc3_do_rx_work(cdev->sdev, ipc_msg, msg_buf); 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return -EOPNOTSUPP; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_ipc_rx_message, SND_SOC_SOF_CLIENT); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ciint sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg, 32562306a36Sopenharmony_ci bool set) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci if (cdev->sdev->pdata->ipc_type == SOF_IPC) { 32862306a36Sopenharmony_ci struct sof_ipc_cmd_hdr *hdr = ipc_msg; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return sof_ipc_set_get_data(cdev->sdev->ipc, ipc_msg, hdr->size, 33162306a36Sopenharmony_ci set); 33262306a36Sopenharmony_ci } else if (cdev->sdev->pdata->ipc_type == SOF_INTEL_IPC4) { 33362306a36Sopenharmony_ci struct sof_ipc4_msg *msg = ipc_msg; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return sof_ipc_set_get_data(cdev->sdev->ipc, ipc_msg, 33662306a36Sopenharmony_ci msg->data_size, set); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return -EINVAL; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_ipc_set_get_data, SND_SOC_SOF_CLIENT); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci#ifdef CONFIG_SND_SOC_SOF_INTEL_IPC4 34462306a36Sopenharmony_cistruct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c, const guid_t *uuid) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct snd_sof_dev *sdev = c->sdev; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) 34962306a36Sopenharmony_ci return sof_ipc4_find_module_by_uuid(sdev, uuid); 35062306a36Sopenharmony_ci dev_err(sdev->dev, "Only supported with IPC4\n"); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return NULL; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_module, SND_SOC_SOF_CLIENT); 35562306a36Sopenharmony_ci#endif 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ciint sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct auxiliary_driver *adrv; 36062306a36Sopenharmony_ci struct sof_client_dev *cdev; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci mutex_lock(&sdev->ipc_client_mutex); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci list_for_each_entry(cdev, &sdev->ipc_client_list, list) { 36562306a36Sopenharmony_ci /* Skip devices without loaded driver */ 36662306a36Sopenharmony_ci if (!cdev->auxdev.dev.driver) 36762306a36Sopenharmony_ci continue; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci adrv = to_auxiliary_drv(cdev->auxdev.dev.driver); 37062306a36Sopenharmony_ci if (adrv->suspend) 37162306a36Sopenharmony_ci adrv->suspend(&cdev->auxdev, state); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci mutex_unlock(&sdev->ipc_client_mutex); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_suspend_clients, SND_SOC_SOF_CLIENT); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ciint sof_resume_clients(struct snd_sof_dev *sdev) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct auxiliary_driver *adrv; 38362306a36Sopenharmony_ci struct sof_client_dev *cdev; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci mutex_lock(&sdev->ipc_client_mutex); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci list_for_each_entry(cdev, &sdev->ipc_client_list, list) { 38862306a36Sopenharmony_ci /* Skip devices without loaded driver */ 38962306a36Sopenharmony_ci if (!cdev->auxdev.dev.driver) 39062306a36Sopenharmony_ci continue; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci adrv = to_auxiliary_drv(cdev->auxdev.dev.driver); 39362306a36Sopenharmony_ci if (adrv->resume) 39462306a36Sopenharmony_ci adrv->resume(&cdev->auxdev); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci mutex_unlock(&sdev->ipc_client_mutex); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_resume_clients, SND_SOC_SOF_CLIENT); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistruct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci return cdev->sdev->debugfs_root; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_get_debugfs_root, SND_SOC_SOF_CLIENT); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci/* DMA buffer allocation in client drivers must use the core SOF device */ 41062306a36Sopenharmony_cistruct device *sof_client_get_dma_dev(struct sof_client_dev *cdev) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci return cdev->sdev->dev; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, SND_SOC_SOF_CLIENT); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ciconst struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return &sdev->fw_ready.version; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, SND_SOC_SOF_CLIENT); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cisize_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return sdev->ipc->max_payload_size; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_max_payload_size, SND_SOC_SOF_CLIENT); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cienum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci return sdev->pdata->ipc_type; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_type, SND_SOC_SOF_CLIENT); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/* module refcount management of SOF core */ 44162306a36Sopenharmony_ciint sof_client_core_module_get(struct sof_client_dev *cdev) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (!try_module_get(sdev->dev->driver->owner)) 44662306a36Sopenharmony_ci return -ENODEV; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_core_module_get, SND_SOC_SOF_CLIENT); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_civoid sof_client_core_module_put(struct sof_client_dev *cdev) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci module_put(sdev->dev->driver->owner); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, SND_SOC_SOF_CLIENT); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci/* IPC event handling */ 46162306a36Sopenharmony_civoid sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct sof_ipc_event_entry *event; 46462306a36Sopenharmony_ci u32 msg_type; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (sdev->pdata->ipc_type == SOF_IPC) { 46762306a36Sopenharmony_ci struct sof_ipc_cmd_hdr *hdr = msg_buf; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci msg_type = hdr->cmd & SOF_GLB_TYPE_MASK; 47062306a36Sopenharmony_ci } else if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) { 47162306a36Sopenharmony_ci struct sof_ipc4_msg *msg = msg_buf; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci msg_type = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary); 47462306a36Sopenharmony_ci } else { 47562306a36Sopenharmony_ci dev_dbg_once(sdev->dev, "Not supported IPC version: %d\n", 47662306a36Sopenharmony_ci sdev->pdata->ipc_type); 47762306a36Sopenharmony_ci return; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci mutex_lock(&sdev->client_event_handler_mutex); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { 48362306a36Sopenharmony_ci if (event->ipc_msg_type == msg_type) 48462306a36Sopenharmony_ci event->callback(event->cdev, msg_buf); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci mutex_unlock(&sdev->client_event_handler_mutex); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ciint sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, 49162306a36Sopenharmony_ci u32 ipc_msg_type, 49262306a36Sopenharmony_ci sof_client_event_callback callback) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 49562306a36Sopenharmony_ci struct sof_ipc_event_entry *event; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (!callback) 49862306a36Sopenharmony_ci return -EINVAL; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (cdev->sdev->pdata->ipc_type == SOF_IPC) { 50162306a36Sopenharmony_ci if (!(ipc_msg_type & SOF_GLB_TYPE_MASK)) 50262306a36Sopenharmony_ci return -EINVAL; 50362306a36Sopenharmony_ci } else if (cdev->sdev->pdata->ipc_type == SOF_INTEL_IPC4) { 50462306a36Sopenharmony_ci if (!(ipc_msg_type & SOF_IPC4_NOTIFICATION_TYPE_MASK)) 50562306a36Sopenharmony_ci return -EINVAL; 50662306a36Sopenharmony_ci } else { 50762306a36Sopenharmony_ci dev_warn(sdev->dev, "%s: Not supported IPC version: %d\n", 50862306a36Sopenharmony_ci __func__, sdev->pdata->ipc_type); 50962306a36Sopenharmony_ci return -EINVAL; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci event = kmalloc(sizeof(*event), GFP_KERNEL); 51362306a36Sopenharmony_ci if (!event) 51462306a36Sopenharmony_ci return -ENOMEM; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci event->ipc_msg_type = ipc_msg_type; 51762306a36Sopenharmony_ci event->cdev = cdev; 51862306a36Sopenharmony_ci event->callback = callback; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* add to list of SOF client devices */ 52162306a36Sopenharmony_ci mutex_lock(&sdev->client_event_handler_mutex); 52262306a36Sopenharmony_ci list_add(&event->list, &sdev->ipc_rx_handler_list); 52362306a36Sopenharmony_ci mutex_unlock(&sdev->client_event_handler_mutex); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_register_ipc_rx_handler, SND_SOC_SOF_CLIENT); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_civoid sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, 53062306a36Sopenharmony_ci u32 ipc_msg_type) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 53362306a36Sopenharmony_ci struct sof_ipc_event_entry *event; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci mutex_lock(&sdev->client_event_handler_mutex); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { 53862306a36Sopenharmony_ci if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) { 53962306a36Sopenharmony_ci list_del(&event->list); 54062306a36Sopenharmony_ci kfree(event); 54162306a36Sopenharmony_ci break; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci mutex_unlock(&sdev->client_event_handler_mutex); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, SND_SOC_SOF_CLIENT); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci/*DSP state notification and query */ 55062306a36Sopenharmony_civoid sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct sof_state_event_entry *event; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci mutex_lock(&sdev->client_event_handler_mutex); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci list_for_each_entry(event, &sdev->fw_state_handler_list, list) 55762306a36Sopenharmony_ci event->callback(event->cdev, sdev->fw_state); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci mutex_unlock(&sdev->client_event_handler_mutex); 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ciint sof_client_register_fw_state_handler(struct sof_client_dev *cdev, 56362306a36Sopenharmony_ci sof_client_fw_state_callback callback) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 56662306a36Sopenharmony_ci struct sof_state_event_entry *event; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (!callback) 56962306a36Sopenharmony_ci return -EINVAL; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci event = kmalloc(sizeof(*event), GFP_KERNEL); 57262306a36Sopenharmony_ci if (!event) 57362306a36Sopenharmony_ci return -ENOMEM; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci event->cdev = cdev; 57662306a36Sopenharmony_ci event->callback = callback; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* add to list of SOF client devices */ 57962306a36Sopenharmony_ci mutex_lock(&sdev->client_event_handler_mutex); 58062306a36Sopenharmony_ci list_add(&event->list, &sdev->fw_state_handler_list); 58162306a36Sopenharmony_ci mutex_unlock(&sdev->client_event_handler_mutex); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_register_fw_state_handler, SND_SOC_SOF_CLIENT); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_civoid sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 59062306a36Sopenharmony_ci struct sof_state_event_entry *event; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci mutex_lock(&sdev->client_event_handler_mutex); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci list_for_each_entry(event, &sdev->fw_state_handler_list, list) { 59562306a36Sopenharmony_ci if (event->cdev == cdev) { 59662306a36Sopenharmony_ci list_del(&event->list); 59762306a36Sopenharmony_ci kfree(event); 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci mutex_unlock(&sdev->client_event_handler_mutex); 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, SND_SOC_SOF_CLIENT); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cienum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return sdev->fw_state; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(sof_client_get_fw_state, SND_SOC_SOF_CLIENT); 613