16955d951SPeter Ujfalusi // SPDX-License-Identifier: GPL-2.0-only
26955d951SPeter Ujfalusi //
3293ad281SPierre-Louis Bossart // Copyright(c) 2022 Intel Corporation
46955d951SPeter Ujfalusi //
56955d951SPeter Ujfalusi // Authors: Ranjani Sridharan <[email protected]>
66955d951SPeter Ujfalusi // Peter Ujfalusi <[email protected]>
76955d951SPeter Ujfalusi //
86955d951SPeter Ujfalusi
96955d951SPeter Ujfalusi #include <linux/debugfs.h>
106955d951SPeter Ujfalusi #include <linux/errno.h>
116955d951SPeter Ujfalusi #include <linux/list.h>
126955d951SPeter Ujfalusi #include <linux/module.h>
136955d951SPeter Ujfalusi #include <linux/mutex.h>
146955d951SPeter Ujfalusi #include <linux/slab.h>
15100c9374SPeter Ujfalusi #include <sound/sof/ipc4/header.h>
166955d951SPeter Ujfalusi #include "ops.h"
176955d951SPeter Ujfalusi #include "sof-client.h"
186955d951SPeter Ujfalusi #include "sof-priv.h"
1970dad53dSCurtis Malainey #include "ipc3-priv.h"
207f0a3dffSJyri Sarha #include "ipc4-priv.h"
216955d951SPeter Ujfalusi
226955d951SPeter Ujfalusi /**
236955d951SPeter Ujfalusi * struct sof_ipc_event_entry - IPC client event description
246955d951SPeter Ujfalusi * @ipc_msg_type: IPC msg type of the event the client is interested
256955d951SPeter Ujfalusi * @cdev: sof_client_dev of the requesting client
266955d951SPeter Ujfalusi * @callback: Callback function of the client
276955d951SPeter Ujfalusi * @list: item in SOF core client event list
286955d951SPeter Ujfalusi */
296955d951SPeter Ujfalusi struct sof_ipc_event_entry {
306955d951SPeter Ujfalusi u32 ipc_msg_type;
316955d951SPeter Ujfalusi struct sof_client_dev *cdev;
326955d951SPeter Ujfalusi sof_client_event_callback callback;
336955d951SPeter Ujfalusi struct list_head list;
346955d951SPeter Ujfalusi };
356955d951SPeter Ujfalusi
366955d951SPeter Ujfalusi /**
376955d951SPeter Ujfalusi * struct sof_state_event_entry - DSP panic event subscription entry
386955d951SPeter Ujfalusi * @cdev: sof_client_dev of the requesting client
396955d951SPeter Ujfalusi * @callback: Callback function of the client
406955d951SPeter Ujfalusi * @list: item in SOF core client event list
416955d951SPeter Ujfalusi */
426955d951SPeter Ujfalusi struct sof_state_event_entry {
436955d951SPeter Ujfalusi struct sof_client_dev *cdev;
446955d951SPeter Ujfalusi sof_client_fw_state_callback callback;
456955d951SPeter Ujfalusi struct list_head list;
466955d951SPeter Ujfalusi };
476955d951SPeter Ujfalusi
sof_client_auxdev_release(struct device * dev)486955d951SPeter Ujfalusi static void sof_client_auxdev_release(struct device *dev)
496955d951SPeter Ujfalusi {
506955d951SPeter Ujfalusi struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
516955d951SPeter Ujfalusi struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
526955d951SPeter Ujfalusi
536955d951SPeter Ujfalusi kfree(cdev->auxdev.dev.platform_data);
546955d951SPeter Ujfalusi kfree(cdev);
556955d951SPeter Ujfalusi }
566955d951SPeter Ujfalusi
sof_client_dev_add_data(struct sof_client_dev * cdev,const void * data,size_t size)576955d951SPeter Ujfalusi static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data,
586955d951SPeter Ujfalusi size_t size)
596955d951SPeter Ujfalusi {
606955d951SPeter Ujfalusi void *d = NULL;
616955d951SPeter Ujfalusi
626955d951SPeter Ujfalusi if (data) {
636955d951SPeter Ujfalusi d = kmemdup(data, size, GFP_KERNEL);
646955d951SPeter Ujfalusi if (!d)
656955d951SPeter Ujfalusi return -ENOMEM;
666955d951SPeter Ujfalusi }
676955d951SPeter Ujfalusi
686955d951SPeter Ujfalusi cdev->auxdev.dev.platform_data = d;
696955d951SPeter Ujfalusi return 0;
706955d951SPeter Ujfalusi }
716955d951SPeter Ujfalusi
726e9548cdSRanjani Sridharan #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
sof_register_ipc_flood_test(struct snd_sof_dev * sdev)736e9548cdSRanjani Sridharan static int sof_register_ipc_flood_test(struct snd_sof_dev *sdev)
746e9548cdSRanjani Sridharan {
756e9548cdSRanjani Sridharan int ret = 0;
766e9548cdSRanjani Sridharan int i;
776e9548cdSRanjani Sridharan
78ebe18b15SPeter Ujfalusi if (sdev->pdata->ipc_type != SOF_IPC_TYPE_3)
795889ccddSPeter Ujfalusi return 0;
805889ccddSPeter Ujfalusi
816e9548cdSRanjani Sridharan for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) {
826e9548cdSRanjani Sridharan ret = sof_client_dev_register(sdev, "ipc_flood", i, NULL, 0);
836e9548cdSRanjani Sridharan if (ret < 0)
846e9548cdSRanjani Sridharan break;
856e9548cdSRanjani Sridharan }
866e9548cdSRanjani Sridharan
876e9548cdSRanjani Sridharan if (ret) {
886e9548cdSRanjani Sridharan for (; i >= 0; --i)
896e9548cdSRanjani Sridharan sof_client_dev_unregister(sdev, "ipc_flood", i);
906e9548cdSRanjani Sridharan }
916e9548cdSRanjani Sridharan
926e9548cdSRanjani Sridharan return ret;
936e9548cdSRanjani Sridharan }
946e9548cdSRanjani Sridharan
sof_unregister_ipc_flood_test(struct snd_sof_dev * sdev)956e9548cdSRanjani Sridharan static void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev)
966e9548cdSRanjani Sridharan {
976e9548cdSRanjani Sridharan int i;
986e9548cdSRanjani Sridharan
996e9548cdSRanjani Sridharan for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++)
1006e9548cdSRanjani Sridharan sof_client_dev_unregister(sdev, "ipc_flood", i);
1016e9548cdSRanjani Sridharan }
1026e9548cdSRanjani Sridharan #else
sof_register_ipc_flood_test(struct snd_sof_dev * sdev)1036e9548cdSRanjani Sridharan static inline int sof_register_ipc_flood_test(struct snd_sof_dev *sdev)
1046e9548cdSRanjani Sridharan {
1056e9548cdSRanjani Sridharan return 0;
1066e9548cdSRanjani Sridharan }
1076e9548cdSRanjani Sridharan
sof_unregister_ipc_flood_test(struct snd_sof_dev * sdev)1086e9548cdSRanjani Sridharan static inline void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev) {}
1096e9548cdSRanjani Sridharan #endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST */
1106e9548cdSRanjani Sridharan
111cac0b088SPeter Ujfalusi #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR)
sof_register_ipc_msg_injector(struct snd_sof_dev * sdev)112cac0b088SPeter Ujfalusi static int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev)
113cac0b088SPeter Ujfalusi {
114cac0b088SPeter Ujfalusi return sof_client_dev_register(sdev, "msg_injector", 0, NULL, 0);
115cac0b088SPeter Ujfalusi }
116cac0b088SPeter Ujfalusi
sof_unregister_ipc_msg_injector(struct snd_sof_dev * sdev)117cac0b088SPeter Ujfalusi static void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev)
118cac0b088SPeter Ujfalusi {
119cac0b088SPeter Ujfalusi sof_client_dev_unregister(sdev, "msg_injector", 0);
120cac0b088SPeter Ujfalusi }
121cac0b088SPeter Ujfalusi #else
sof_register_ipc_msg_injector(struct snd_sof_dev * sdev)122cac0b088SPeter Ujfalusi static inline int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev)
123cac0b088SPeter Ujfalusi {
124cac0b088SPeter Ujfalusi return 0;
125cac0b088SPeter Ujfalusi }
126cac0b088SPeter Ujfalusi
sof_unregister_ipc_msg_injector(struct snd_sof_dev * sdev)127cac0b088SPeter Ujfalusi static inline void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) {}
128cac0b088SPeter Ujfalusi #endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR */
129cac0b088SPeter Ujfalusi
13070dad53dSCurtis Malainey #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR)
sof_register_ipc_kernel_injector(struct snd_sof_dev * sdev)13170dad53dSCurtis Malainey static int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev)
13270dad53dSCurtis Malainey {
13370dad53dSCurtis Malainey /* Only IPC3 supported right now */
134ebe18b15SPeter Ujfalusi if (sdev->pdata->ipc_type != SOF_IPC_TYPE_3)
13570dad53dSCurtis Malainey return 0;
13670dad53dSCurtis Malainey
13770dad53dSCurtis Malainey return sof_client_dev_register(sdev, "kernel_injector", 0, NULL, 0);
13870dad53dSCurtis Malainey }
13970dad53dSCurtis Malainey
sof_unregister_ipc_kernel_injector(struct snd_sof_dev * sdev)14070dad53dSCurtis Malainey static void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev)
14170dad53dSCurtis Malainey {
14270dad53dSCurtis Malainey sof_client_dev_unregister(sdev, "kernel_injector", 0);
14370dad53dSCurtis Malainey }
14470dad53dSCurtis Malainey #else
sof_register_ipc_kernel_injector(struct snd_sof_dev * sdev)14570dad53dSCurtis Malainey static inline int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev)
14670dad53dSCurtis Malainey {
14770dad53dSCurtis Malainey return 0;
14870dad53dSCurtis Malainey }
14970dad53dSCurtis Malainey
sof_unregister_ipc_kernel_injector(struct snd_sof_dev * sdev)15070dad53dSCurtis Malainey static inline void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev) {}
15170dad53dSCurtis Malainey #endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR */
15270dad53dSCurtis Malainey
sof_register_clients(struct snd_sof_dev * sdev)1536955d951SPeter Ujfalusi int sof_register_clients(struct snd_sof_dev *sdev)
1546955d951SPeter Ujfalusi {
1556e9548cdSRanjani Sridharan int ret;
1566955d951SPeter Ujfalusi
15728d40e7aSPeter Ujfalusi if (sdev->dspless_mode_selected)
15828d40e7aSPeter Ujfalusi return 0;
15928d40e7aSPeter Ujfalusi
1606e9548cdSRanjani Sridharan /* Register platform independent client devices */
1616e9548cdSRanjani Sridharan ret = sof_register_ipc_flood_test(sdev);
1626e9548cdSRanjani Sridharan if (ret) {
1636e9548cdSRanjani Sridharan dev_err(sdev->dev, "IPC flood test client registration failed\n");
1646e9548cdSRanjani Sridharan return ret;
1656e9548cdSRanjani Sridharan }
1666e9548cdSRanjani Sridharan
167cac0b088SPeter Ujfalusi ret = sof_register_ipc_msg_injector(sdev);
168cac0b088SPeter Ujfalusi if (ret) {
169cac0b088SPeter Ujfalusi dev_err(sdev->dev, "IPC message injector client registration failed\n");
170cac0b088SPeter Ujfalusi goto err_msg_injector;
171cac0b088SPeter Ujfalusi }
172cac0b088SPeter Ujfalusi
17370dad53dSCurtis Malainey ret = sof_register_ipc_kernel_injector(sdev);
17470dad53dSCurtis Malainey if (ret) {
17570dad53dSCurtis Malainey dev_err(sdev->dev, "IPC kernel injector client registration failed\n");
17670dad53dSCurtis Malainey goto err_kernel_injector;
17770dad53dSCurtis Malainey }
17870dad53dSCurtis Malainey
17945f2f28bSEugen Hristev /* Platform dependent client device registration */
1806e9548cdSRanjani Sridharan
1816e9548cdSRanjani Sridharan if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients)
1826e9548cdSRanjani Sridharan ret = sof_ops(sdev)->register_ipc_clients(sdev);
1836e9548cdSRanjani Sridharan
184cac0b088SPeter Ujfalusi if (!ret)
185cac0b088SPeter Ujfalusi return 0;
186cac0b088SPeter Ujfalusi
18770dad53dSCurtis Malainey sof_unregister_ipc_kernel_injector(sdev);
18870dad53dSCurtis Malainey
18970dad53dSCurtis Malainey err_kernel_injector:
190cac0b088SPeter Ujfalusi sof_unregister_ipc_msg_injector(sdev);
191cac0b088SPeter Ujfalusi
192cac0b088SPeter Ujfalusi err_msg_injector:
1936e9548cdSRanjani Sridharan sof_unregister_ipc_flood_test(sdev);
1946e9548cdSRanjani Sridharan
1956e9548cdSRanjani Sridharan return ret;
1966955d951SPeter Ujfalusi }
1976955d951SPeter Ujfalusi
sof_unregister_clients(struct snd_sof_dev * sdev)1986955d951SPeter Ujfalusi void sof_unregister_clients(struct snd_sof_dev *sdev)
1996955d951SPeter Ujfalusi {
2006955d951SPeter Ujfalusi if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients)
2016955d951SPeter Ujfalusi sof_ops(sdev)->unregister_ipc_clients(sdev);
2026e9548cdSRanjani Sridharan
20370dad53dSCurtis Malainey sof_unregister_ipc_kernel_injector(sdev);
204cac0b088SPeter Ujfalusi sof_unregister_ipc_msg_injector(sdev);
2056e9548cdSRanjani Sridharan sof_unregister_ipc_flood_test(sdev);
2066955d951SPeter Ujfalusi }
2076955d951SPeter Ujfalusi
sof_client_dev_register(struct snd_sof_dev * sdev,const char * name,u32 id,const void * data,size_t size)2086955d951SPeter Ujfalusi int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
2096955d951SPeter Ujfalusi const void *data, size_t size)
2106955d951SPeter Ujfalusi {
2116955d951SPeter Ujfalusi struct auxiliary_device *auxdev;
2126955d951SPeter Ujfalusi struct sof_client_dev *cdev;
2136955d951SPeter Ujfalusi int ret;
2146955d951SPeter Ujfalusi
2156955d951SPeter Ujfalusi cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
2166955d951SPeter Ujfalusi if (!cdev)
2176955d951SPeter Ujfalusi return -ENOMEM;
2186955d951SPeter Ujfalusi
2196955d951SPeter Ujfalusi cdev->sdev = sdev;
2206955d951SPeter Ujfalusi auxdev = &cdev->auxdev;
2216955d951SPeter Ujfalusi auxdev->name = name;
2226955d951SPeter Ujfalusi auxdev->dev.parent = sdev->dev;
2236955d951SPeter Ujfalusi auxdev->dev.release = sof_client_auxdev_release;
2246955d951SPeter Ujfalusi auxdev->id = id;
2256955d951SPeter Ujfalusi
2266955d951SPeter Ujfalusi ret = sof_client_dev_add_data(cdev, data, size);
2276955d951SPeter Ujfalusi if (ret < 0)
2286955d951SPeter Ujfalusi goto err_dev_add_data;
2296955d951SPeter Ujfalusi
2306955d951SPeter Ujfalusi ret = auxiliary_device_init(auxdev);
2316955d951SPeter Ujfalusi if (ret < 0) {
2326955d951SPeter Ujfalusi dev_err(sdev->dev, "failed to initialize client dev %s.%d\n", name, id);
2336955d951SPeter Ujfalusi goto err_dev_init;
2346955d951SPeter Ujfalusi }
2356955d951SPeter Ujfalusi
2366955d951SPeter Ujfalusi ret = auxiliary_device_add(&cdev->auxdev);
2376955d951SPeter Ujfalusi if (ret < 0) {
2386955d951SPeter Ujfalusi dev_err(sdev->dev, "failed to add client dev %s.%d\n", name, id);
2396955d951SPeter Ujfalusi /*
2406955d951SPeter Ujfalusi * sof_client_auxdev_release() will be invoked to free up memory
2416955d951SPeter Ujfalusi * allocations through put_device()
2426955d951SPeter Ujfalusi */
2436955d951SPeter Ujfalusi auxiliary_device_uninit(&cdev->auxdev);
2446955d951SPeter Ujfalusi return ret;
2456955d951SPeter Ujfalusi }
2466955d951SPeter Ujfalusi
2476955d951SPeter Ujfalusi /* add to list of SOF client devices */
2486955d951SPeter Ujfalusi mutex_lock(&sdev->ipc_client_mutex);
2496955d951SPeter Ujfalusi list_add(&cdev->list, &sdev->ipc_client_list);
2506955d951SPeter Ujfalusi mutex_unlock(&sdev->ipc_client_mutex);
2516955d951SPeter Ujfalusi
2526955d951SPeter Ujfalusi return 0;
2536955d951SPeter Ujfalusi
2546955d951SPeter Ujfalusi err_dev_init:
2556955d951SPeter Ujfalusi kfree(cdev->auxdev.dev.platform_data);
2566955d951SPeter Ujfalusi
2576955d951SPeter Ujfalusi err_dev_add_data:
2586955d951SPeter Ujfalusi kfree(cdev);
2596955d951SPeter Ujfalusi
2606955d951SPeter Ujfalusi return ret;
2616955d951SPeter Ujfalusi }
262*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_dev_register, "SND_SOC_SOF_CLIENT");
2636955d951SPeter Ujfalusi
sof_client_dev_unregister(struct snd_sof_dev * sdev,const char * name,u32 id)2646955d951SPeter Ujfalusi void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id)
2656955d951SPeter Ujfalusi {
2666955d951SPeter Ujfalusi struct sof_client_dev *cdev;
2676955d951SPeter Ujfalusi
2686955d951SPeter Ujfalusi mutex_lock(&sdev->ipc_client_mutex);
2696955d951SPeter Ujfalusi
2706955d951SPeter Ujfalusi /*
2716955d951SPeter Ujfalusi * sof_client_auxdev_release() will be invoked to free up memory
2726955d951SPeter Ujfalusi * allocations through put_device()
2736955d951SPeter Ujfalusi */
2746955d951SPeter Ujfalusi list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
2756955d951SPeter Ujfalusi if (!strcmp(cdev->auxdev.name, name) && cdev->auxdev.id == id) {
2766955d951SPeter Ujfalusi list_del(&cdev->list);
2776955d951SPeter Ujfalusi auxiliary_device_delete(&cdev->auxdev);
2786955d951SPeter Ujfalusi auxiliary_device_uninit(&cdev->auxdev);
2796955d951SPeter Ujfalusi break;
2806955d951SPeter Ujfalusi }
2816955d951SPeter Ujfalusi }
2826955d951SPeter Ujfalusi
2836955d951SPeter Ujfalusi mutex_unlock(&sdev->ipc_client_mutex);
2846955d951SPeter Ujfalusi }
285*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, "SND_SOC_SOF_CLIENT");
2866955d951SPeter Ujfalusi
sof_client_ipc_tx_message(struct sof_client_dev * cdev,void * ipc_msg,void * reply_data,size_t reply_bytes)2876955d951SPeter Ujfalusi int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
2886955d951SPeter Ujfalusi void *reply_data, size_t reply_bytes)
2896955d951SPeter Ujfalusi {
290ebe18b15SPeter Ujfalusi if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
2916955d951SPeter Ujfalusi struct sof_ipc_cmd_hdr *hdr = ipc_msg;
2926955d951SPeter Ujfalusi
2932a51c0f8SPeter Ujfalusi return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, hdr->size,
2946955d951SPeter Ujfalusi reply_data, reply_bytes);
295ebe18b15SPeter Ujfalusi } else if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
296100c9374SPeter Ujfalusi struct sof_ipc4_msg *msg = ipc_msg;
297100c9374SPeter Ujfalusi
298100c9374SPeter Ujfalusi return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, msg->data_size,
299100c9374SPeter Ujfalusi reply_data, reply_bytes);
300100c9374SPeter Ujfalusi }
301100c9374SPeter Ujfalusi
302100c9374SPeter Ujfalusi return -EINVAL;
3036955d951SPeter Ujfalusi }
304*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, "SND_SOC_SOF_CLIENT");
3056955d951SPeter Ujfalusi
sof_client_ipc_rx_message(struct sof_client_dev * cdev,void * ipc_msg,void * msg_buf)30670dad53dSCurtis Malainey int sof_client_ipc_rx_message(struct sof_client_dev *cdev, void *ipc_msg, void *msg_buf)
30770dad53dSCurtis Malainey {
308d65d4a2cSPierre-Louis Bossart if (IS_ENABLED(CONFIG_SND_SOC_SOF_IPC3) &&
309d65d4a2cSPierre-Louis Bossart cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
31070dad53dSCurtis Malainey struct sof_ipc_cmd_hdr *hdr = ipc_msg;
31170dad53dSCurtis Malainey
31270dad53dSCurtis Malainey if (hdr->size < sizeof(hdr)) {
31370dad53dSCurtis Malainey dev_err(cdev->sdev->dev, "The received message size is invalid\n");
31470dad53dSCurtis Malainey return -EINVAL;
31570dad53dSCurtis Malainey }
31670dad53dSCurtis Malainey
31770dad53dSCurtis Malainey sof_ipc3_do_rx_work(cdev->sdev, ipc_msg, msg_buf);
31870dad53dSCurtis Malainey return 0;
31970dad53dSCurtis Malainey }
32070dad53dSCurtis Malainey
32170dad53dSCurtis Malainey return -EOPNOTSUPP;
32270dad53dSCurtis Malainey }
323*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_ipc_rx_message, "SND_SOC_SOF_CLIENT");
32470dad53dSCurtis Malainey
sof_client_ipc_set_get_data(struct sof_client_dev * cdev,void * ipc_msg,bool set)325d8bc54a5SJyri Sarha int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg,
326d8bc54a5SJyri Sarha bool set)
327d8bc54a5SJyri Sarha {
328ebe18b15SPeter Ujfalusi if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
329d8bc54a5SJyri Sarha struct sof_ipc_cmd_hdr *hdr = ipc_msg;
330d8bc54a5SJyri Sarha
331d8bc54a5SJyri Sarha return sof_ipc_set_get_data(cdev->sdev->ipc, ipc_msg, hdr->size,
332d8bc54a5SJyri Sarha set);
333ebe18b15SPeter Ujfalusi } else if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
334d8bc54a5SJyri Sarha struct sof_ipc4_msg *msg = ipc_msg;
335d8bc54a5SJyri Sarha
336d8bc54a5SJyri Sarha return sof_ipc_set_get_data(cdev->sdev->ipc, ipc_msg,
337d8bc54a5SJyri Sarha msg->data_size, set);
338d8bc54a5SJyri Sarha }
339d8bc54a5SJyri Sarha
340d8bc54a5SJyri Sarha return -EINVAL;
341d8bc54a5SJyri Sarha }
342*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_ipc_set_get_data, "SND_SOC_SOF_CLIENT");
343d8bc54a5SJyri Sarha
34482f4b383SPeter Ujfalusi #ifdef CONFIG_SND_SOC_SOF_IPC4
sof_client_ipc4_find_module(struct sof_client_dev * c,const guid_t * uuid)3457f0a3dffSJyri Sarha struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c, const guid_t *uuid)
3467f0a3dffSJyri Sarha {
3477f0a3dffSJyri Sarha struct snd_sof_dev *sdev = c->sdev;
3487f0a3dffSJyri Sarha
349ebe18b15SPeter Ujfalusi if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4)
3507f0a3dffSJyri Sarha return sof_ipc4_find_module_by_uuid(sdev, uuid);
3517f0a3dffSJyri Sarha dev_err(sdev->dev, "Only supported with IPC4\n");
3527f0a3dffSJyri Sarha
3537f0a3dffSJyri Sarha return NULL;
3547f0a3dffSJyri Sarha }
355*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_module, "SND_SOC_SOF_CLIENT");
3567f0a3dffSJyri Sarha #endif
3577f0a3dffSJyri Sarha
sof_suspend_clients(struct snd_sof_dev * sdev,pm_message_t state)3581069967aSPeter Ujfalusi int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state)
3591069967aSPeter Ujfalusi {
360ff985c75SGreg Kroah-Hartman const struct auxiliary_driver *adrv;
3611069967aSPeter Ujfalusi struct sof_client_dev *cdev;
3621069967aSPeter Ujfalusi
3631069967aSPeter Ujfalusi mutex_lock(&sdev->ipc_client_mutex);
3641069967aSPeter Ujfalusi
3651069967aSPeter Ujfalusi list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
3661069967aSPeter Ujfalusi /* Skip devices without loaded driver */
3671069967aSPeter Ujfalusi if (!cdev->auxdev.dev.driver)
3681069967aSPeter Ujfalusi continue;
3691069967aSPeter Ujfalusi
3701069967aSPeter Ujfalusi adrv = to_auxiliary_drv(cdev->auxdev.dev.driver);
3711069967aSPeter Ujfalusi if (adrv->suspend)
3721069967aSPeter Ujfalusi adrv->suspend(&cdev->auxdev, state);
3731069967aSPeter Ujfalusi }
3741069967aSPeter Ujfalusi
3751069967aSPeter Ujfalusi mutex_unlock(&sdev->ipc_client_mutex);
3761069967aSPeter Ujfalusi
3771069967aSPeter Ujfalusi return 0;
3781069967aSPeter Ujfalusi }
379*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, "SND_SOC_SOF_CLIENT");
3801069967aSPeter Ujfalusi
sof_resume_clients(struct snd_sof_dev * sdev)3811069967aSPeter Ujfalusi int sof_resume_clients(struct snd_sof_dev *sdev)
3821069967aSPeter Ujfalusi {
383ff985c75SGreg Kroah-Hartman const struct auxiliary_driver *adrv;
3841069967aSPeter Ujfalusi struct sof_client_dev *cdev;
3851069967aSPeter Ujfalusi
3861069967aSPeter Ujfalusi mutex_lock(&sdev->ipc_client_mutex);
3871069967aSPeter Ujfalusi
3881069967aSPeter Ujfalusi list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
3891069967aSPeter Ujfalusi /* Skip devices without loaded driver */
3901069967aSPeter Ujfalusi if (!cdev->auxdev.dev.driver)
3911069967aSPeter Ujfalusi continue;
3921069967aSPeter Ujfalusi
3931069967aSPeter Ujfalusi adrv = to_auxiliary_drv(cdev->auxdev.dev.driver);
3941069967aSPeter Ujfalusi if (adrv->resume)
3951069967aSPeter Ujfalusi adrv->resume(&cdev->auxdev);
3961069967aSPeter Ujfalusi }
3971069967aSPeter Ujfalusi
3981069967aSPeter Ujfalusi mutex_unlock(&sdev->ipc_client_mutex);
3991069967aSPeter Ujfalusi
4001069967aSPeter Ujfalusi return 0;
4011069967aSPeter Ujfalusi }
402*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_resume_clients, "SND_SOC_SOF_CLIENT");
4031069967aSPeter Ujfalusi
sof_client_get_debugfs_root(struct sof_client_dev * cdev)4046955d951SPeter Ujfalusi struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev)
4056955d951SPeter Ujfalusi {
4066955d951SPeter Ujfalusi return cdev->sdev->debugfs_root;
4076955d951SPeter Ujfalusi }
408*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_get_debugfs_root, "SND_SOC_SOF_CLIENT");
4096955d951SPeter Ujfalusi
4106955d951SPeter Ujfalusi /* DMA buffer allocation in client drivers must use the core SOF device */
sof_client_get_dma_dev(struct sof_client_dev * cdev)4116955d951SPeter Ujfalusi struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev)
4126955d951SPeter Ujfalusi {
4136955d951SPeter Ujfalusi return cdev->sdev->dev;
4146955d951SPeter Ujfalusi }
415*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, "SND_SOC_SOF_CLIENT");
4166955d951SPeter Ujfalusi
sof_client_get_fw_version(struct sof_client_dev * cdev)4176955d951SPeter Ujfalusi const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev)
4186955d951SPeter Ujfalusi {
4196955d951SPeter Ujfalusi struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
4206955d951SPeter Ujfalusi
4216955d951SPeter Ujfalusi return &sdev->fw_ready.version;
4226955d951SPeter Ujfalusi }
423*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, "SND_SOC_SOF_CLIENT");
4246955d951SPeter Ujfalusi
sof_client_get_ipc_max_payload_size(struct sof_client_dev * cdev)425a669ec5fSPeter Ujfalusi size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev)
426a669ec5fSPeter Ujfalusi {
427a669ec5fSPeter Ujfalusi struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
428a669ec5fSPeter Ujfalusi
429a669ec5fSPeter Ujfalusi return sdev->ipc->max_payload_size;
430a669ec5fSPeter Ujfalusi }
431*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_max_payload_size, "SND_SOC_SOF_CLIENT");
432a669ec5fSPeter Ujfalusi
sof_client_get_ipc_type(struct sof_client_dev * cdev)433cdf8233dSPeter Ujfalusi enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev)
434cdf8233dSPeter Ujfalusi {
435cdf8233dSPeter Ujfalusi struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
436cdf8233dSPeter Ujfalusi
437cdf8233dSPeter Ujfalusi return sdev->pdata->ipc_type;
438cdf8233dSPeter Ujfalusi }
439*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_type, "SND_SOC_SOF_CLIENT");
440cdf8233dSPeter Ujfalusi
4416955d951SPeter Ujfalusi /* module refcount management of SOF core */
sof_client_core_module_get(struct sof_client_dev * cdev)4426955d951SPeter Ujfalusi int sof_client_core_module_get(struct sof_client_dev *cdev)
4436955d951SPeter Ujfalusi {
4446955d951SPeter Ujfalusi struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
4456955d951SPeter Ujfalusi
4466955d951SPeter Ujfalusi if (!try_module_get(sdev->dev->driver->owner))
4476955d951SPeter Ujfalusi return -ENODEV;
4486955d951SPeter Ujfalusi
4496955d951SPeter Ujfalusi return 0;
4506955d951SPeter Ujfalusi }
451*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_core_module_get, "SND_SOC_SOF_CLIENT");
4526955d951SPeter Ujfalusi
sof_client_core_module_put(struct sof_client_dev * cdev)4536955d951SPeter Ujfalusi void sof_client_core_module_put(struct sof_client_dev *cdev)
4546955d951SPeter Ujfalusi {
4556955d951SPeter Ujfalusi struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
4566955d951SPeter Ujfalusi
4576955d951SPeter Ujfalusi module_put(sdev->dev->driver->owner);
4586955d951SPeter Ujfalusi }
459*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, "SND_SOC_SOF_CLIENT");
4606955d951SPeter Ujfalusi
4616955d951SPeter Ujfalusi /* IPC event handling */
sof_client_ipc_rx_dispatcher(struct snd_sof_dev * sdev,void * msg_buf)4626955d951SPeter Ujfalusi void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf)
4636955d951SPeter Ujfalusi {
4646955d951SPeter Ujfalusi struct sof_ipc_event_entry *event;
465100c9374SPeter Ujfalusi u32 msg_type;
466100c9374SPeter Ujfalusi
467ebe18b15SPeter Ujfalusi if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
468100c9374SPeter Ujfalusi struct sof_ipc_cmd_hdr *hdr = msg_buf;
469100c9374SPeter Ujfalusi
470100c9374SPeter Ujfalusi msg_type = hdr->cmd & SOF_GLB_TYPE_MASK;
471ebe18b15SPeter Ujfalusi } else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
472100c9374SPeter Ujfalusi struct sof_ipc4_msg *msg = msg_buf;
473100c9374SPeter Ujfalusi
474100c9374SPeter Ujfalusi msg_type = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary);
475100c9374SPeter Ujfalusi } else {
476298e3abaSPierre-Louis Bossart dev_dbg_once(sdev->dev, "Not supported IPC version: %d\n",
477298e3abaSPierre-Louis Bossart sdev->pdata->ipc_type);
478100c9374SPeter Ujfalusi return;
479100c9374SPeter Ujfalusi }
4806955d951SPeter Ujfalusi
4816955d951SPeter Ujfalusi mutex_lock(&sdev->client_event_handler_mutex);
4826955d951SPeter Ujfalusi
4836955d951SPeter Ujfalusi list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) {
4846955d951SPeter Ujfalusi if (event->ipc_msg_type == msg_type)
4856955d951SPeter Ujfalusi event->callback(event->cdev, msg_buf);
4866955d951SPeter Ujfalusi }
4876955d951SPeter Ujfalusi
4886955d951SPeter Ujfalusi mutex_unlock(&sdev->client_event_handler_mutex);
4896955d951SPeter Ujfalusi }
4906955d951SPeter Ujfalusi
sof_client_register_ipc_rx_handler(struct sof_client_dev * cdev,u32 ipc_msg_type,sof_client_event_callback callback)4916955d951SPeter Ujfalusi int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
4926955d951SPeter Ujfalusi u32 ipc_msg_type,
4936955d951SPeter Ujfalusi sof_client_event_callback callback)
4946955d951SPeter Ujfalusi {
4956955d951SPeter Ujfalusi struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
4966955d951SPeter Ujfalusi struct sof_ipc_event_entry *event;
4976955d951SPeter Ujfalusi
498100c9374SPeter Ujfalusi if (!callback)
4996955d951SPeter Ujfalusi return -EINVAL;
5006955d951SPeter Ujfalusi
501ebe18b15SPeter Ujfalusi if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
502100c9374SPeter Ujfalusi if (!(ipc_msg_type & SOF_GLB_TYPE_MASK))
503100c9374SPeter Ujfalusi return -EINVAL;
504ebe18b15SPeter Ujfalusi } else if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
505100c9374SPeter Ujfalusi if (!(ipc_msg_type & SOF_IPC4_NOTIFICATION_TYPE_MASK))
506100c9374SPeter Ujfalusi return -EINVAL;
507100c9374SPeter Ujfalusi } else {
508100c9374SPeter Ujfalusi dev_warn(sdev->dev, "%s: Not supported IPC version: %d\n",
509100c9374SPeter Ujfalusi __func__, sdev->pdata->ipc_type);
510100c9374SPeter Ujfalusi return -EINVAL;
511100c9374SPeter Ujfalusi }
512100c9374SPeter Ujfalusi
5136955d951SPeter Ujfalusi event = kmalloc(sizeof(*event), GFP_KERNEL);
5146955d951SPeter Ujfalusi if (!event)
5156955d951SPeter Ujfalusi return -ENOMEM;
5166955d951SPeter Ujfalusi
5176955d951SPeter Ujfalusi event->ipc_msg_type = ipc_msg_type;
5186955d951SPeter Ujfalusi event->cdev = cdev;
5196955d951SPeter Ujfalusi event->callback = callback;
5206955d951SPeter Ujfalusi
5216955d951SPeter Ujfalusi /* add to list of SOF client devices */
5226955d951SPeter Ujfalusi mutex_lock(&sdev->client_event_handler_mutex);
5236955d951SPeter Ujfalusi list_add(&event->list, &sdev->ipc_rx_handler_list);
5246955d951SPeter Ujfalusi mutex_unlock(&sdev->client_event_handler_mutex);
5256955d951SPeter Ujfalusi
5266955d951SPeter Ujfalusi return 0;
5276955d951SPeter Ujfalusi }
528*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_register_ipc_rx_handler, "SND_SOC_SOF_CLIENT");
5296955d951SPeter Ujfalusi
sof_client_unregister_ipc_rx_handler(struct sof_client_dev * cdev,u32 ipc_msg_type)5306955d951SPeter Ujfalusi void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev,
5316955d951SPeter Ujfalusi u32 ipc_msg_type)
5326955d951SPeter Ujfalusi {
5336955d951SPeter Ujfalusi struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
5346955d951SPeter Ujfalusi struct sof_ipc_event_entry *event;
5356955d951SPeter Ujfalusi
5366955d951SPeter Ujfalusi mutex_lock(&sdev->client_event_handler_mutex);
5376955d951SPeter Ujfalusi
5386955d951SPeter Ujfalusi list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) {
5396955d951SPeter Ujfalusi if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) {
5406955d951SPeter Ujfalusi list_del(&event->list);
5416955d951SPeter Ujfalusi kfree(event);
5426955d951SPeter Ujfalusi break;
5436955d951SPeter Ujfalusi }
5446955d951SPeter Ujfalusi }
5456955d951SPeter Ujfalusi
5466955d951SPeter Ujfalusi mutex_unlock(&sdev->client_event_handler_mutex);
5476955d951SPeter Ujfalusi }
548*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, "SND_SOC_SOF_CLIENT");
5496955d951SPeter Ujfalusi
5506955d951SPeter Ujfalusi /*DSP state notification and query */
sof_client_fw_state_dispatcher(struct snd_sof_dev * sdev)5516955d951SPeter Ujfalusi void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev)
5526955d951SPeter Ujfalusi {
5536955d951SPeter Ujfalusi struct sof_state_event_entry *event;
5546955d951SPeter Ujfalusi
5556955d951SPeter Ujfalusi mutex_lock(&sdev->client_event_handler_mutex);
5566955d951SPeter Ujfalusi
5576955d951SPeter Ujfalusi list_for_each_entry(event, &sdev->fw_state_handler_list, list)
5586955d951SPeter Ujfalusi event->callback(event->cdev, sdev->fw_state);
5596955d951SPeter Ujfalusi
5606955d951SPeter Ujfalusi mutex_unlock(&sdev->client_event_handler_mutex);
5616955d951SPeter Ujfalusi }
5626955d951SPeter Ujfalusi
sof_client_register_fw_state_handler(struct sof_client_dev * cdev,sof_client_fw_state_callback callback)5636955d951SPeter Ujfalusi int sof_client_register_fw_state_handler(struct sof_client_dev *cdev,
5646955d951SPeter Ujfalusi sof_client_fw_state_callback callback)
5656955d951SPeter Ujfalusi {
5666955d951SPeter Ujfalusi struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
5676955d951SPeter Ujfalusi struct sof_state_event_entry *event;
5686955d951SPeter Ujfalusi
5696955d951SPeter Ujfalusi if (!callback)
5706955d951SPeter Ujfalusi return -EINVAL;
5716955d951SPeter Ujfalusi
5726955d951SPeter Ujfalusi event = kmalloc(sizeof(*event), GFP_KERNEL);
5736955d951SPeter Ujfalusi if (!event)
5746955d951SPeter Ujfalusi return -ENOMEM;
5756955d951SPeter Ujfalusi
5766955d951SPeter Ujfalusi event->cdev = cdev;
5776955d951SPeter Ujfalusi event->callback = callback;
5786955d951SPeter Ujfalusi
5796955d951SPeter Ujfalusi /* add to list of SOF client devices */
5806955d951SPeter Ujfalusi mutex_lock(&sdev->client_event_handler_mutex);
5816955d951SPeter Ujfalusi list_add(&event->list, &sdev->fw_state_handler_list);
5826955d951SPeter Ujfalusi mutex_unlock(&sdev->client_event_handler_mutex);
5836955d951SPeter Ujfalusi
5846955d951SPeter Ujfalusi return 0;
5856955d951SPeter Ujfalusi }
586*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_register_fw_state_handler, "SND_SOC_SOF_CLIENT");
5876955d951SPeter Ujfalusi
sof_client_unregister_fw_state_handler(struct sof_client_dev * cdev)5886955d951SPeter Ujfalusi void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev)
5896955d951SPeter Ujfalusi {
5906955d951SPeter Ujfalusi struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
5916955d951SPeter Ujfalusi struct sof_state_event_entry *event;
5926955d951SPeter Ujfalusi
5936955d951SPeter Ujfalusi mutex_lock(&sdev->client_event_handler_mutex);
5946955d951SPeter Ujfalusi
5956955d951SPeter Ujfalusi list_for_each_entry(event, &sdev->fw_state_handler_list, list) {
5966955d951SPeter Ujfalusi if (event->cdev == cdev) {
5976955d951SPeter Ujfalusi list_del(&event->list);
5986955d951SPeter Ujfalusi kfree(event);
5996955d951SPeter Ujfalusi break;
6006955d951SPeter Ujfalusi }
6016955d951SPeter Ujfalusi }
6026955d951SPeter Ujfalusi
6036955d951SPeter Ujfalusi mutex_unlock(&sdev->client_event_handler_mutex);
6046955d951SPeter Ujfalusi }
605*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, "SND_SOC_SOF_CLIENT");
6066955d951SPeter Ujfalusi
sof_client_get_fw_state(struct sof_client_dev * cdev)6076955d951SPeter Ujfalusi enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev)
6086955d951SPeter Ujfalusi {
6096955d951SPeter Ujfalusi struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
6106955d951SPeter Ujfalusi
6116955d951SPeter Ujfalusi return sdev->fw_state;
6126955d951SPeter Ujfalusi }
613*cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_state, "SND_SOC_SOF_CLIENT");
614