Commit 54f5bae0 authored by Mark Brown's avatar Mark Brown

ASoC: SOF: IPC client infrastructure

Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>:

The Linux SOF implementation is historically monolithic in a sense that all
features accessible in the firmware can be used via the snd_sof_dev struct in
one way or another.

Support for features can not be added or removed runtime and with the current
way of things it is hard if not impossible to implement support for dynamic
feature support when based on the firmware manifest we can easily enable/access
independent modules with the SOF.

In order to be able to support such modularity this series introduces a small
framework within SOF for client support using the Auxiliary bus.

Client drivers can be removed runtime and later re-loaded if needed without
affecting the core's behaviour, but it is the core's and the platform's duty
to create the Auxiliary devices usable in the platform and via the firmware.

There is still a need for SOF manifest update to convey information about
features to really make the full dynamic client device creation.

The series will introduce the core SOF client support and converts the generic
ipc flood test, ipc message injector and the probes (Intel HDA only) to a client
driver.
parents a61faea1 3dc0d709
......@@ -39,6 +39,14 @@ enum sof_fw_state {
SOF_FW_CRASHED,
};
/* DSP power states */
enum sof_dsp_power_states {
SOF_DSP_PM_D0,
SOF_DSP_PM_D1,
SOF_DSP_PM_D2,
SOF_DSP_PM_D3,
};
/*
* SOF Platform data.
*/
......
......@@ -53,13 +53,21 @@ config SND_SOC_SOF_COMPRESS
select SND_SOC_COMPRESS
config SND_SOC_SOF_DEBUG_PROBES
bool "SOF enable data probing"
tristate
select SND_SOC_SOF_CLIENT
select SND_SOC_COMPRESS
help
This option enables the data probing feature that can be used to
gather data directly from specific points of the audio pipeline.
Say Y if you want to enable probes.
If unsure, select "N".
This option is not user-selectable but automagically handled by
'select' statements at a higher level.
config SND_SOC_SOF_CLIENT
tristate
select AUXILIARY_BUS
help
This option is not user-selectable but automagically handled by
'select' statements at a higher level.
config SND_SOC_SOF_DEVELOPER_SUPPORT
bool "SOF developer options support"
......@@ -187,15 +195,26 @@ config SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE
If unsure, select "N".
config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST
bool "SOF enable IPC flood test"
tristate "SOF enable IPC flood test"
select SND_SOC_SOF_CLIENT
help
This option enables the IPC flood test which can be used to flood
the DSP with test IPCs and gather stats about response times.
This option enables a separate client device for IPC flood test
which can be used to flood the DSP with test IPCs and gather stats
about response times.
Say Y if you want to enable IPC flood test.
If unsure, select "N".
config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM
int "Number of IPC flood test clients"
range 1 32
default 2
depends on SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST
help
Select the number of IPC flood test clients to be created.
config SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR
bool "SOF enable IPC message injector"
tristate "SOF enable IPC message injector"
select SND_SOC_SOF_CLIENT
help
This option enables the IPC message injector which can be used to send
crafted IPC messages to the DSP to test its robustness.
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\
control.o trace.o utils.o sof-audio.o stream-ipc.o
control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o
snd-sof-$(CONFIG_SND_SOC_SOF_CLIENT) += sof-client.o
snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += sof-probes.o
snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += compress.o
snd-sof-pci-objs := sof-pci-dev.o
snd-sof-acpi-objs := sof-acpi-dev.o
snd-sof-of-objs := sof-of-dev.o
snd-sof-ipc-flood-test-objs := sof-client-ipc-flood-test.o
snd-sof-ipc-msg-injector-objs := sof-client-ipc-msg-injector.o
snd-sof-probes-objs := sof-client-probes.o
snd-sof-nocodec-objs := nocodec.o
snd-sof-utils-objs := sof-utils.o
obj-$(CONFIG_SND_SOC_SOF) += snd-sof.o
obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o
obj-$(CONFIG_SND_SOC_SOF) += snd-sof-utils.o
obj-$(CONFIG_SND_SOC_SOF_ACPI_DEV) += snd-sof-acpi.o
obj-$(CONFIG_SND_SOC_SOF_OF_DEV) += snd-sof-of.o
obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o
obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) += snd-sof-ipc-flood-test.o
obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) += snd-sof-ipc-msg-injector.o
obj-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += snd-sof-probes.o
obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/
obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/
obj-$(CONFIG_SND_SOC_SOF_AMD_TOPLEVEL) += amd/
......
......@@ -9,6 +9,7 @@
#include <sound/compress_driver.h>
#include "sof-audio.h"
#include "sof-priv.h"
#include "sof-utils.h"
static void sof_set_transferred_bytes(struct snd_compr_tstamp *tstamp,
u64 host_pos, u64 buffer_size)
......
......@@ -14,9 +14,6 @@
#include <sound/sof.h>
#include "sof-priv.h"
#include "ops.h"
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
#include "sof-probes.h"
#endif
/* see SOF_DBG_ flags */
static int sof_core_debug = IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE);
......@@ -122,6 +119,27 @@ void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level,
}
EXPORT_SYMBOL(sof_print_oops_and_stack);
/* Helper to manage DSP state */
void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state)
{
if (sdev->fw_state == new_state)
return;
dev_dbg(sdev->dev, "fw_state change: %d -> %d\n", sdev->fw_state, new_state);
sdev->fw_state = new_state;
switch (new_state) {
case SOF_FW_BOOT_NOT_STARTED:
case SOF_FW_BOOT_COMPLETE:
case SOF_FW_CRASHED:
sof_client_fw_state_dispatcher(sdev);
fallthrough;
default:
break;
}
}
EXPORT_SYMBOL(sof_set_fw_state);
/*
* FW Boot State Transition Diagram
*
......@@ -266,6 +284,12 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
goto fw_trace_err;
}
ret = sof_register_clients(sdev);
if (ret < 0) {
dev_err(sdev->dev, "failed to register clients %d\n", ret);
goto sof_machine_err;
}
/*
* Some platforms in SOF, ex: BYT, may not have their platform PM
* callbacks set. Increment the usage count so as to
......@@ -281,6 +305,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
return 0;
sof_machine_err:
snd_sof_machine_unregister(sdev, plat_data);
fw_trace_err:
snd_sof_free_trace(sdev);
fw_run_err:
......@@ -329,10 +355,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
sdev->pdata = plat_data;
sdev->first_boot = true;
sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID;
#endif
dev_set_drvdata(dev, sdev);
/* check all mandatory ops */
......@@ -350,9 +372,14 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
INIT_LIST_HEAD(&sdev->widget_list);
INIT_LIST_HEAD(&sdev->dai_list);
INIT_LIST_HEAD(&sdev->route_list);
INIT_LIST_HEAD(&sdev->ipc_client_list);
INIT_LIST_HEAD(&sdev->ipc_rx_handler_list);
INIT_LIST_HEAD(&sdev->fw_state_handler_list);
spin_lock_init(&sdev->ipc_lock);
spin_lock_init(&sdev->hw_lock);
mutex_init(&sdev->power_state_access);
mutex_init(&sdev->ipc_client_mutex);
mutex_init(&sdev->client_event_handler_mutex);
/* set default timeouts if none provided */
if (plat_data->desc->ipc_timeout == 0)
......@@ -364,6 +391,8 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
else
sdev->boot_timeout = plat_data->desc->boot_timeout;
sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) {
INIT_WORK(&sdev->probe_work, sof_probe_work);
schedule_work(&sdev->probe_work);
......@@ -391,6 +420,12 @@ int snd_sof_device_remove(struct device *dev)
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
cancel_work_sync(&sdev->probe_work);
/*
* Unregister any registered client device first before IPC and debugfs
* to allow client drivers to be removed cleanly
*/
sof_unregister_clients(sdev);
/*
* Unregister machine driver. This will unbind the snd_card which
* will remove the component driver and unload the topology
......@@ -443,3 +478,4 @@ MODULE_AUTHOR("Liam Girdwood");
MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("platform:sof-audio");
MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
This diff is collapsed.
......@@ -215,6 +215,7 @@ config SND_SOC_SOF_HDA_COMMON
select SND_SOC_SOF_PCI_DEV
select SND_INTEL_DSP_CONFIG
select SND_SOC_SOF_HDA_LINK_BASELINE
select SND_SOC_SOF_HDA_PROBES
help
This option is not user-selectable but automagically handled by
'select' statements at a higher level.
......@@ -240,15 +241,6 @@ config SND_SOC_SOF_HDA_AUDIO_CODEC
Say Y if you want to enable HDAudio codecs with SOF.
If unsure select "N".
config SND_SOC_SOF_HDA_PROBES
bool "SOF enable probes over HDA"
depends on SND_SOC_SOF_DEBUG_PROBES
help
This option enables the data probing for Intel(R)
Skylake and newer platforms.
Say Y if you want to enable probes.
If unsure, select "N".
endif ## SND_SOC_SOF_HDA_COMMON
config SND_SOC_SOF_HDA_LINK_BASELINE
......@@ -266,6 +258,15 @@ config SND_SOC_SOF_HDA
This option is not user-selectable but automagically handled by
'select' statements at a higher level.
config SND_SOC_SOF_HDA_PROBES
bool
select SND_SOC_SOF_DEBUG_PROBES
help
The option enables the data probing for Intel(R) Skylake and newer
(HDA) platforms.
This option is not user-selectable but automagically handled by
'select' statements at a higher level.
config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
tristate
select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE
......
......@@ -80,15 +80,6 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
.pcm_pointer = hda_dsp_pcm_pointer,
.pcm_ack = hda_dsp_pcm_ack,
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
/* probe callbacks */
.probe_assign = hda_probe_compr_assign,
.probe_free = hda_probe_compr_free,
.probe_set_params = hda_probe_compr_set_params,
.probe_trigger = hda_probe_compr_trigger,
.probe_pointer = hda_probe_compr_pointer,
#endif
/* firmware loading */
.load_firmware = snd_sof_load_firmware_raw,
......@@ -110,6 +101,10 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
.trace_release = hda_dsp_trace_release,
.trace_trigger = hda_dsp_trace_trigger,
/* client ops */
.register_ipc_clients = hda_register_clients,
.unregister_ipc_clients = hda_unregister_clients,
/* DAI drivers */
.drv = skl_dai,
.num_drv = SOF_SKL_NUM_DAIS,
......
......@@ -298,15 +298,6 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
.pcm_pointer = hda_dsp_pcm_pointer,
.pcm_ack = hda_dsp_pcm_ack,
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
/* probe callbacks */
.probe_assign = hda_probe_compr_assign,
.probe_free = hda_probe_compr_free,
.probe_set_params = hda_probe_compr_set_params,
.probe_trigger = hda_probe_compr_trigger,
.probe_pointer = hda_probe_compr_pointer,
#endif
/* firmware loading */
.load_firmware = snd_sof_load_firmware_raw,
......@@ -328,6 +319,10 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
.trace_release = hda_dsp_trace_release,
.trace_trigger = hda_dsp_trace_trigger,
/* client ops */
.register_ipc_clients = hda_register_clients,
.unregister_ipc_clients = hda_unregister_clients,
/* DAI drivers */
.drv = skl_dai,
.num_drv = SOF_SKL_NUM_DAIS,
......
......@@ -16,10 +16,6 @@
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
#include "../sof-probes.h"
#endif
struct hda_pipe_params {
u32 ch;
u32 s_freq;
......@@ -737,20 +733,5 @@ struct snd_soc_dai_driver skl_dai[] = {
.channels_max = 16,
},
},
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
{
.name = "Probe Extraction CPU DAI",
.compress_new = snd_soc_new_compress,
.cops = &sof_probe_compr_ops,
.capture = {
.stream_name = "Probe Extraction",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
},
},
#endif
#endif
};
......@@ -498,15 +498,9 @@ static void hda_dsp_state_log(struct snd_sof_dev *sdev)
case SOF_DSP_PM_D2:
dev_dbg(sdev->dev, "Current DSP power state: D2\n");
break;
case SOF_DSP_PM_D3_HOT:
dev_dbg(sdev->dev, "Current DSP power state: D3_HOT\n");
break;
case SOF_DSP_PM_D3:
dev_dbg(sdev->dev, "Current DSP power state: D3\n");
break;
case SOF_DSP_PM_D3_COLD:
dev_dbg(sdev->dev, "Current DSP power state: D3_COLD\n");
break;
default:
dev_dbg(sdev->dev, "Unknown DSP power state: %d\n",
sdev->dsp_power_state.state);
......
......@@ -3,14 +3,20 @@
// This file is provided under a dual BSD/GPLv2 license. When using or
// redistributing this file, you may do so under either license.
//
// Copyright(c) 2019-2020 Intel Corporation. All rights reserved.
// Copyright(c) 2019-2021 Intel Corporation. All rights reserved.
//
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
// Converted to SOF client:
// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
// Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
//
#include <linux/module.h>
#include <sound/hdaudio_ext.h>
#include <sound/soc.h>
#include "../sof-priv.h"
#include "../sof-client-probes.h"
#include "../sof-client.h"
#include "hda.h"
static inline struct hdac_ext_stream *
......@@ -19,10 +25,11 @@ hda_compr_get_stream(struct snd_compr_stream *cstream)
return cstream->runtime->private_data;
}
int hda_probe_compr_assign(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_soc_dai *dai)
static int hda_probes_compr_assign(struct sof_client_dev *cdev,
struct snd_compr_stream *cstream,
struct snd_soc_dai *dai, u32 *stream_id)
{
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
struct hdac_ext_stream *hext_stream;
hext_stream = hda_dsp_stream_get(sdev, cstream->direction, 0);
......@@ -33,14 +40,17 @@ int hda_probe_compr_assign(struct snd_sof_dev *sdev,
hdac_stream(hext_stream)->cstream = cstream;
cstream->runtime->private_data = hext_stream;
return hdac_stream(hext_stream)->stream_tag;
*stream_id = hdac_stream(hext_stream)->stream_tag;
return 0;
}
int hda_probe_compr_free(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_soc_dai *dai)
static int hda_probes_compr_free(struct sof_client_dev *cdev,
struct snd_compr_stream *cstream,
struct snd_soc_dai *dai)
{
struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream);
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
int ret;
ret = hda_dsp_stream_put(sdev, cstream->direction,
......@@ -56,12 +66,13 @@ int hda_probe_compr_free(struct snd_sof_dev *sdev,
return 0;
}
int hda_probe_compr_set_params(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_compr_params *params,
struct snd_soc_dai *dai)
static int hda_probes_compr_set_params(struct sof_client_dev *cdev,
struct snd_compr_stream *cstream,
struct snd_compr_params *params,
struct snd_soc_dai *dai)
{
struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream);
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
struct hdac_stream *hstream = hdac_stream(hext_stream);
struct snd_dma_buffer *dmab;
u32 bits, rate;
......@@ -89,19 +100,20 @@ int hda_probe_compr_set_params(struct snd_sof_dev *sdev,
return 0;
}
int hda_probe_compr_trigger(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream, int cmd,
struct snd_soc_dai *dai)
static int hda_probes_compr_trigger(struct sof_client_dev *cdev,
struct snd_compr_stream *cstream,
int cmd, struct snd_soc_dai *dai)
{
struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream);
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
return hda_dsp_stream_trigger(sdev, hext_stream, cmd);
}
int hda_probe_compr_pointer(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_compr_tstamp *tstamp,
struct snd_soc_dai *dai)
static int hda_probes_compr_pointer(struct sof_client_dev *cdev,
struct snd_compr_stream *cstream,
struct snd_compr_tstamp *tstamp,
struct snd_soc_dai *dai)
{
struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream);
struct snd_soc_pcm_stream *pstream;
......@@ -112,3 +124,25 @@ int hda_probe_compr_pointer(struct snd_sof_dev *sdev,
return 0;
}
/* SOF client implementation */
static const struct sof_probes_host_ops hda_probes_ops = {
.assign = hda_probes_compr_assign,
.free = hda_probes_compr_free,
.set_params = hda_probes_compr_set_params,
.trigger = hda_probes_compr_trigger,
.pointer = hda_probes_compr_pointer,
};
int hda_probes_register(struct snd_sof_dev *sdev)
{
return sof_client_dev_register(sdev, "hda-probes", 0, &hda_probes_ops,
sizeof(hda_probes_ops));
}
void hda_probes_unregister(struct snd_sof_dev *sdev)
{
sof_client_dev_unregister(sdev, "hda-probes", 0);
}
MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
......@@ -1423,6 +1423,16 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
}
EXPORT_SYMBOL_NS(hda_pci_intel_probe, SND_SOC_SOF_INTEL_HDA_COMMON);
int hda_register_clients(struct snd_sof_dev *sdev)
{
return hda_probes_register(sdev);
}
void hda_unregister_clients(struct snd_sof_dev *sdev)
{
hda_probes_unregister(sdev);
}
MODULE_LICENSE("Dual BSD/GPL");
MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV);
MODULE_IMPORT_NS(SND_SOC_SOF_HDA_AUDIO_CODEC);
......
......@@ -16,6 +16,7 @@
#include <sound/compress_driver.h>
#include <sound/hda_codec.h>
#include <sound/hdaudio_ext.h>
#include "../sof-client-probes.h"
#include "shim.h"
/* PCI registers */
......@@ -351,13 +352,7 @@
/* Number of DAIs */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
#define SOF_SKL_NUM_DAIS 16
#else
#define SOF_SKL_NUM_DAIS 15
#endif
#else
#define SOF_SKL_NUM_DAIS 8
#endif
......@@ -575,29 +570,6 @@ int hda_ipc_pcm_params(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
const struct sof_ipc_pcm_params_reply *reply);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
/*
* Probe Compress Operations.
*/
int hda_probe_compr_assign(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_soc_dai *dai);
int hda_probe_compr_free(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_soc_dai *dai);
int hda_probe_compr_set_params(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_compr_params *params,
struct snd_soc_dai *dai);
int hda_probe_compr_trigger(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream, int cmd,
struct snd_soc_dai *dai);
int hda_probe_compr_pointer(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_compr_tstamp *tstamp,
struct snd_soc_dai *dai);
#endif
/*
* DSP IPC Operations.
*/
......@@ -729,6 +701,25 @@ extern const struct sof_intel_dsp_desc ehl_chip_info;
extern const struct sof_intel_dsp_desc jsl_chip_info;
extern const struct sof_intel_dsp_desc adls_chip_info;
/* Probes support */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
int hda_probes_register(struct snd_sof_dev *sdev);
void hda_probes_unregister(struct snd_sof_dev *sdev);
#else
static inline int hda_probes_register(struct snd_sof_dev *sdev)
{
return 0;
}
static inline void hda_probes_unregister(struct snd_sof_dev *sdev)
{
}
#endif /* CONFIG_SND_SOC_SOF_HDA_PROBES */
/* SOF client registration for HDA platforms */
int hda_register_clients(struct snd_sof_dev *sdev);
void hda_unregister_clients(struct snd_sof_dev *sdev);
/* machine driver select */
struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev);
void hda_set_mach_params(struct snd_soc_acpi_mach *mach,
......
......@@ -142,15 +142,6 @@ const struct snd_sof_dsp_ops sof_icl_ops = {
.pcm_pointer = hda_dsp_pcm_pointer,
.pcm_ack = hda_dsp_pcm_ack,
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
/* probe callbacks */
.probe_assign = hda_probe_compr_assign,
.probe_free = hda_probe_compr_free,
.probe_set_params = hda_probe_compr_set_params,
.probe_trigger = hda_probe_compr_trigger,
.probe_pointer = hda_probe_compr_pointer,
#endif
/* firmware loading */
.load_firmware = snd_sof_load_firmware_raw,
......@@ -173,6 +164,10 @@ const struct snd_sof_dsp_ops sof_icl_ops = {
.trace_release = hda_dsp_trace_release,
.trace_trigger = hda_dsp_trace_trigger,
/* client ops */
.register_ipc_clients = hda_register_clients,
.unregister_ipc_clients = hda_unregister_clients,
/* DAI drivers */
.drv = skl_dai,
.num_drv = SOF_SKL_NUM_DAIS,
......
......@@ -115,15 +115,6 @@ const struct snd_sof_dsp_ops sof_tgl_ops = {
.pcm_pointer = hda_dsp_pcm_pointer,
.pcm_ack = hda_dsp_pcm_ack,
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
/* probe callbacks */
.probe_assign = hda_probe_compr_assign,
.probe_free = hda_probe_compr_free,
.probe_set_params = hda_probe_compr_set_params,
.probe_trigger = hda_probe_compr_trigger,
.probe_pointer = hda_probe_compr_pointer,
#endif
/* firmware loading */
.load_firmware = snd_sof_load_firmware_raw,
......@@ -146,6 +137,10 @@ const struct snd_sof_dsp_ops sof_tgl_ops = {
.trace_release = hda_dsp_trace_release,
.trace_trigger = hda_dsp_trace_trigger,
/* client ops */
.register_ipc_clients = hda_register_clients,
.unregister_ipc_clients = hda_unregister_clients,
/* DAI drivers */
.drv = skl_dai,
.num_drv = SOF_SKL_NUM_DAIS,
......
......@@ -3,7 +3,7 @@
// This file is provided under a dual BSD/GPLv2 license. When using or
// redistributing this file, you may do so under either license.
//
// Copyright(c) 2018 Intel Corporation. All rights reserved.
// Copyright(c) 2018-2022 Intel Corporation. All rights reserved.
//
// Author: Keyon Jie <yang.jie@linux.intel.com>
//
......@@ -125,62 +125,3 @@ int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
return 0;
}
EXPORT_SYMBOL(sof_block_read);
/*
* Generic buffer page table creation.
* Take the each physical page address and drop the least significant unused
* bits from each (based on PAGE_SIZE). Then pack valid page address bits
* into compressed page table.
*/
int snd_sof_create_page_table(struct device *dev,
struct snd_dma_buffer *dmab,
unsigned char *page_table, size_t size)
{
int i, pages;
pages = snd_sgbuf_aligned_pages(size);
dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n",
dmab->area, size, pages);
for (i = 0; i < pages; i++) {
/*
* The number of valid address bits for each page is 20.
* idx determines the byte position within page_table
* where the current page's address is stored
* in the compressed page_table.
* This can be calculated by multiplying the page number by 2.5.
*/
u32 idx = (5 * i) >> 1;
u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
u8 *pg_table;
dev_vdbg(dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
pg_table = (u8 *)(page_table + idx);
/*
* pagetable compression:
* byte 0 byte 1 byte 2 byte 3 byte 4 byte 5
* ___________pfn 0__________ __________pfn 1___________ _pfn 2...
* .... .... .... .... .... .... .... .... .... .... ....
* It is created by:
* 1. set current location to 0, PFN index i to 0
* 2. put pfn[i] at current location in Little Endian byte order
* 3. calculate an intermediate value as
* x = (pfn[i+1] << 4) | (pfn[i] & 0xf)
* 4. put x at offset (current location + 2) in LE byte order
* 5. increment current location by 5 bytes, increment i by 2
* 6. continue to (2)
*/
if (i & 1)
put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4,
pg_table);
else
put_unaligned_le32(pfn, pg_table);
}
return pages;
}
EXPORT_SYMBOL(snd_sof_create_page_table);
......@@ -18,8 +18,10 @@
#include "sof-audio.h"
#include "ops.h"
static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type);
static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd);
typedef void (*ipc_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf);
static void ipc_trace_message(struct snd_sof_dev *sdev, void *msg_buf);
static void ipc_stream_message(struct snd_sof_dev *sdev, void *msg_buf);
/*
* IPC message Tx/Rx message handling.
......@@ -477,44 +479,30 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
}
EXPORT_SYMBOL(snd_sof_ipc_reply);
static void ipc_comp_notification(struct snd_sof_dev *sdev,
struct sof_ipc_cmd_hdr *hdr)
static void ipc_comp_notification(struct snd_sof_dev *sdev, void *msg_buf)
{
struct sof_ipc_cmd_hdr *hdr = msg_buf;
u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
struct sof_ipc_ctrl_data *cdata;
int ret;
switch (msg_type) {
case SOF_IPC_COMP_GET_VALUE:
case SOF_IPC_COMP_GET_DATA:
cdata = kmalloc(hdr->size, GFP_KERNEL);
if (!cdata)
return;
/* read back full message */
ret = snd_sof_ipc_msg_data(sdev, NULL, cdata, hdr->size);
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to read component event: %d\n", ret);
goto err;
}
break;
default:
dev_err(sdev->dev, "error: unhandled component message %#x\n", msg_type);
return;
}
snd_sof_control_notify(sdev, cdata);
err:
kfree(cdata);
snd_sof_control_notify(sdev, msg_buf);
}
/* DSP firmware has sent host a message */
void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
{
ipc_rx_callback rx_callback = NULL;
struct sof_ipc_cmd_hdr hdr;
u32 cmd, type;
void *msg_buf;
u32 cmd;
int err;
/* read back header */
......@@ -523,10 +511,15 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
dev_warn(sdev->dev, "failed to read IPC header: %d\n", err);
return;
}
if (hdr.size < sizeof(hdr)) {
dev_err(sdev->dev, "The received message size is invalid\n");
return;
}
ipc_log_header(sdev->dev, "ipc rx", hdr.cmd);
cmd = hdr.cmd & SOF_GLB_TYPE_MASK;
type = hdr.cmd & SOF_CMD_TYPE_MASK;
/* check message type */
switch (cmd) {
......@@ -551,20 +544,38 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
case SOF_IPC_GLB_PM_MSG:
break;
case SOF_IPC_GLB_COMP_MSG:
ipc_comp_notification(sdev, &hdr);
rx_callback = ipc_comp_notification;
break;
case SOF_IPC_GLB_STREAM_MSG:
/* need to pass msg id into the function */
ipc_stream_message(sdev, hdr.cmd);
rx_callback = ipc_stream_message;
break;
case SOF_IPC_GLB_TRACE_MSG:
ipc_trace_message(sdev, type);
rx_callback = ipc_trace_message;
break;
default:
dev_err(sdev->dev, "error: unknown DSP message 0x%x\n", cmd);
dev_err(sdev->dev, "%s: Unknown DSP message: 0x%x\n", __func__, cmd);
break;
}
/* read the full message */
msg_buf = kmalloc(hdr.size, GFP_KERNEL);
if (!msg_buf)
return;
err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size);
if (err < 0) {
dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err);
} else {
/* Call local handler for the message */
if (rx_callback)
rx_callback(sdev, msg_buf);
/* Notify registered clients */
sof_client_ipc_rx_dispatcher(sdev, msg_buf);
}
kfree(msg_buf);
ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd);
}
EXPORT_SYMBOL(snd_sof_ipc_msgs_rx);
......@@ -573,19 +584,14 @@ EXPORT_SYMBOL(snd_sof_ipc_msgs_rx);
* IPC trace mechanism.
*/
static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type)
static void ipc_trace_message(struct snd_sof_dev *sdev, void *msg_buf)
{
struct sof_ipc_dma_trace_posn posn;
int ret;
struct sof_ipc_cmd_hdr *hdr = msg_buf;
u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
switch (msg_type) {
case SOF_IPC_TRACE_DMA_POSITION:
/* read back full message */
ret = snd_sof_ipc_msg_data(sdev, NULL, &posn, sizeof(posn));
if (ret < 0)
dev_warn(sdev->dev, "failed to read trace position: %d\n", ret);
else
snd_sof_trace_update_pos(sdev, &posn);
snd_sof_trace_update_pos(sdev, msg_buf);
break;
default:
dev_err(sdev->dev, "error: unhandled trace message %#x\n", msg_type);
......@@ -667,11 +673,11 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id)
}
/* stream notifications from DSP FW */
static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd)
static void ipc_stream_message(struct snd_sof_dev *sdev, void *msg_buf)
{
/* get msg cmd type and msd id */
u32 msg_type = msg_cmd & SOF_CMD_TYPE_MASK;
u32 msg_id = SOF_IPC_MESSAGE_ID(msg_cmd);
struct sof_ipc_cmd_hdr *hdr = msg_buf;
u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd);
switch (msg_type) {
case SOF_IPC_STREAM_POSITION:
......
......@@ -497,49 +497,6 @@ static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev,
return 0;
}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
static inline int
snd_sof_probe_compr_assign(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
{
return sof_ops(sdev)->probe_assign(sdev, cstream, dai);
}
static inline int
snd_sof_probe_compr_free(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
{
return sof_ops(sdev)->probe_free(sdev, cstream, dai);
}
static inline int
snd_sof_probe_compr_set_params(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_compr_params *params, struct snd_soc_dai *dai)
{
return sof_ops(sdev)->probe_set_params(sdev, cstream, params, dai);
}
static inline int
snd_sof_probe_compr_trigger(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream, int cmd,
struct snd_soc_dai *dai)
{
return sof_ops(sdev)->probe_trigger(sdev, cstream, cmd, dai);
}
static inline int
snd_sof_probe_compr_pointer(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai)
{
if (sof_ops(sdev) && sof_ops(sdev)->probe_pointer)
return sof_ops(sdev)->probe_pointer(sdev, cstream, tstamp, dai);
return 0;
}
#endif
/* machine driver */
static inline int
snd_sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
......
......@@ -15,10 +15,8 @@
#include <sound/sof.h>
#include "sof-priv.h"
#include "sof-audio.h"
#include "sof-utils.h"
#include "ops.h"
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
#include "sof-probes.h"
#endif
/* Create DMA buffer page table for DSP */
static int create_page_table(struct snd_soc_component *component,
......@@ -924,9 +922,6 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
pd->pointer = sof_pcm_pointer;
pd->ack = sof_pcm_ack;
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
pd->compress_ops = &sof_probe_compressed_ops;
#endif
pd->pcm_construct = sof_pcm_new;
pd->ignore_machine = drv_name;
pd->be_hw_params_fixup = sof_pcm_dai_link_fixup;
......
......@@ -167,6 +167,9 @@ static int sof_resume(struct device *dev, bool runtime_resume)
return ret;
}
/* Notify clients not managed by pm framework about core resume */
sof_resume_clients(sdev);
/* notify DSP of system resume */
ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE);
if (ret < 0)
......@@ -180,6 +183,7 @@ static int sof_resume(struct device *dev, bool runtime_resume)
static int sof_suspend(struct device *dev, bool runtime_suspend)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
pm_message_t pm_state;
u32 target_state = 0;
int ret;
......@@ -205,16 +209,23 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
}
target_state = snd_sof_dsp_power_target(sdev);
pm_state.event = target_state;
/* Skip to platform-specific suspend if DSP is entering D0 */
if (target_state == SOF_DSP_PM_D0)
if (target_state == SOF_DSP_PM_D0) {
/* Notify clients not managed by pm framework about core suspend */
sof_suspend_clients(sdev, pm_state);
goto suspend;
}
sof_tear_down_pipelines(sdev, false);
/* release trace */
snd_sof_release_trace(sdev);
/* Notify clients not managed by pm framework about core suspend */
sof_suspend_clients(sdev, pm_state);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
/* cache debugfs contents during runtime suspend */
if (runtime_suspend)
......
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2022 Intel Corporation. All rights reserved.
//
// Author: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
//
#include <linux/auxiliary_bus.h>
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/ktime.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <sound/sof/header.h>
#include "sof-client.h"
#define SOF_IPC_CLIENT_SUSPEND_DELAY_MS 3000
struct sof_msg_inject_priv {
struct dentry *dfs_file;
void *tx_buffer;
void *rx_buffer;
};
static int sof_msg_inject_dfs_open(struct inode *inode, struct file *file)
{
struct sof_client_dev *cdev = inode->i_private;
int ret;
if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED)
return -ENODEV;
ret = debugfs_file_get(file->f_path.dentry);
if (unlikely(ret))
return ret;
ret = simple_open(inode, file);
if (ret)
debugfs_file_put(file->f_path.dentry);
return ret;
}
static ssize_t sof_msg_inject_dfs_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct sof_client_dev *cdev = file->private_data;
struct sof_msg_inject_priv *priv = cdev->data;
struct sof_ipc_reply *rhdr = priv->rx_buffer;
if (!rhdr->hdr.size || !count || *ppos)
return 0;
if (count > rhdr->hdr.size)
count = rhdr->hdr.size;
if (copy_to_user(buffer, priv->rx_buffer, count))
return -EFAULT;
*ppos += count;
return count;
}
static ssize_t sof_msg_inject_dfs_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct sof_client_dev *cdev = file->private_data;
struct sof_msg_inject_priv *priv = cdev->data;
struct device *dev = &cdev->auxdev.dev;
int ret, err;
size_t size;
if (*ppos)
return 0;
size = simple_write_to_buffer(priv->tx_buffer, SOF_IPC_MSG_MAX_SIZE,
ppos, buffer, count);
if (size != count)
return size > 0 ? -EFAULT : size;
ret = pm_runtime_get_sync(dev);
if (ret < 0 && ret != -EACCES) {
dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
pm_runtime_put_noidle(dev);
return ret;
}
/* send the message */
memset(priv->rx_buffer, 0, SOF_IPC_MSG_MAX_SIZE);
ret = sof_client_ipc_tx_message(cdev, priv->tx_buffer, priv->rx_buffer,
SOF_IPC_MSG_MAX_SIZE);
pm_runtime_mark_last_busy(dev);
err = pm_runtime_put_autosuspend(dev);
if (err < 0)
dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
/* return size if test is successful */
if (ret >= 0)
ret = size;
return ret;
};
static int sof_msg_inject_dfs_release(struct inode *inode, struct file *file)
{
debugfs_file_put(file->f_path.dentry);
return 0;
}
static const struct file_operations sof_msg_inject_fops = {
.open = sof_msg_inject_dfs_open,
.read = sof_msg_inject_dfs_read,
.write = sof_msg_inject_dfs_write,
.llseek = default_llseek,
.release = sof_msg_inject_dfs_release,
.owner = THIS_MODULE,
};
static int sof_msg_inject_probe(struct auxiliary_device *auxdev,
const struct auxiliary_device_id *id)
{
struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev);
struct device *dev = &auxdev->dev;
struct sof_msg_inject_priv *priv;
/* allocate memory for client data */
priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->tx_buffer = devm_kmalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
priv->rx_buffer = devm_kmalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
if (!priv->tx_buffer || !priv->rx_buffer)
return -ENOMEM;
cdev->data = priv;
priv->dfs_file = debugfs_create_file("ipc_msg_inject", 0644, debugfs_root,
cdev, &sof_msg_inject_fops);
/* enable runtime PM */
pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
pm_runtime_enable(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_idle(dev);
return 0;
}
static void sof_msg_inject_remove(struct auxiliary_device *auxdev)
{
struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
struct sof_msg_inject_priv *priv = cdev->data;
pm_runtime_disable(&auxdev->dev);
debugfs_remove(priv->dfs_file);
}
static const struct auxiliary_device_id sof_msg_inject_client_id_table[] = {
{ .name = "snd_sof.msg_injector" },
{},
};
MODULE_DEVICE_TABLE(auxiliary, sof_msg_inject_client_id_table);
/*
* No need for driver pm_ops as the generic pm callbacks in the auxiliary bus
* type are enough to ensure that the parent SOF device resumes to bring the DSP
* back to D0.
* Driver name will be set based on KBUILD_MODNAME.
*/
static struct auxiliary_driver sof_msg_inject_client_drv = {
.probe = sof_msg_inject_probe,
.remove = sof_msg_inject_remove,
.id_table = sof_msg_inject_client_id_table,
};
module_auxiliary_driver(sof_msg_inject_client_drv);
MODULE_DESCRIPTION("SOF IPC Message Injector Client Driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __SOF_CLIENT_PROBES_H
#define __SOF_CLIENT_PROBES_H
struct snd_compr_stream;
struct snd_compr_tstamp;
struct snd_compr_params;
struct sof_client_dev;
struct snd_soc_dai;
/*
* Callbacks used on platforms where the control for audio is split between
* DSP and host, like HDA.
*/
struct sof_probes_host_ops {
int (*assign)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream,
struct snd_soc_dai *dai, u32 *stream_id);
int (*free)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream,
struct snd_soc_dai *dai);
int (*set_params)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream,
struct snd_compr_params *params,
struct snd_soc_dai *dai);
int (*trigger)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream,
int cmd, struct snd_soc_dai *dai);
int (*pointer)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream,
struct snd_compr_tstamp *tstamp,
struct snd_soc_dai *dai);
};
#endif
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __SOC_SOF_CLIENT_H
#define __SOC_SOF_CLIENT_H
#include <linux/auxiliary_bus.h>
#include <linux/device.h>
#include <linux/list.h>
#include <sound/sof.h>
struct sof_ipc_fw_version;
struct sof_ipc_cmd_hdr;
struct snd_sof_dev;
struct dentry;
/**
* struct sof_client_dev - SOF client device
* @auxdev: auxiliary device
* @sdev: pointer to SOF core device struct
* @list: item in SOF core client dev list
* @data: device specific data
*/
struct sof_client_dev {
struct auxiliary_device auxdev;
struct snd_sof_dev *sdev;
struct list_head list;
void *data;
};
#define sof_client_dev_to_sof_dev(cdev) ((cdev)->sdev)
#define auxiliary_dev_to_sof_client_dev(auxiliary_dev) \
container_of(auxiliary_dev, struct sof_client_dev, auxdev)
#define dev_to_sof_client_dev(dev) \
container_of(to_auxiliary_dev(dev), struct sof_client_dev, auxdev)
int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
void *reply_data, size_t reply_bytes);
struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev);
struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev);
const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev);
/* module refcount management of SOF core */
int sof_client_core_module_get(struct sof_client_dev *cdev);
void sof_client_core_module_put(struct sof_client_dev *cdev);
/* IPC notification */
typedef void (*sof_client_event_callback)(struct sof_client_dev *cdev, void *msg_buf);
int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
u32 ipc_msg_type,
sof_client_event_callback callback);
void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev,
u32 ipc_msg_type);
/* DSP state notification and query */
typedef void (*sof_client_fw_state_callback)(struct sof_client_dev *cdev,
enum sof_fw_state state);
int sof_client_register_fw_state_handler(struct sof_client_dev *cdev,
sof_client_fw_state_callback callback);
void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev);
enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev);
#endif /* __SOC_SOF_CLIENT_H */
......@@ -69,26 +69,12 @@ bool sof_debug_check_flag(int mask);
#define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT)
#define ENABLE_DEBUGFS_CACHEBUF \
(IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) || \
IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST))
/* So far the primary core on all DSPs has ID 0 */
#define SOF_DSP_PRIMARY_CORE 0
/* max number of DSP cores */
#define SOF_MAX_DSP_NUM_CORES 8
/* DSP power state */
enum sof_dsp_power_states {
SOF_DSP_PM_D0,
SOF_DSP_PM_D1,
SOF_DSP_PM_D2,
SOF_DSP_PM_D3_HOT,
SOF_DSP_PM_D3,
SOF_DSP_PM_D3_COLD,
};
struct sof_dsp_power_state {
u32 state;
u32 substate; /* platform-specific */
......@@ -215,27 +201,6 @@ struct snd_sof_dsp_ops {
/* pcm ack */
int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
/* Except for probe_pointer, all probe ops are mandatory */
int (*probe_assign)(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_soc_dai *dai); /* mandatory */
int (*probe_free)(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_soc_dai *dai); /* mandatory */
int (*probe_set_params)(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_compr_params *params,
struct snd_soc_dai *dai); /* mandatory */
int (*probe_trigger)(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream, int cmd,
struct snd_soc_dai *dai); /* mandatory */
int (*probe_pointer)(struct snd_sof_dev *sdev,
struct snd_compr_stream *cstream,
struct snd_compr_tstamp *tstamp,
struct snd_soc_dai *dai); /* optional */
#endif
/* host read DSP stream data */
int (*ipc_msg_data)(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
......@@ -302,6 +267,10 @@ struct snd_sof_dsp_ops {
void (*set_mach_params)(struct snd_soc_acpi_mach *mach,
struct snd_sof_dev *sdev); /* optional */
/* IPC client ops */
int (*register_ipc_clients)(struct snd_sof_dev *sdev); /* optional */
void (*unregister_ipc_clients)(struct snd_sof_dev *sdev); /* optional */
/* DAI ops */
struct snd_soc_dai_driver *drv;
int num_drv;
......@@ -332,12 +301,8 @@ struct snd_sof_dfsentry {
* or if it is accessible only when the DSP is in D0.
*/
enum sof_debugfs_access_type access_type;
#if ENABLE_DEBUGFS_CACHEBUF
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
char *cache_buf; /* buffer to cache the contents of debugfs memory */
#endif
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR)
void *msg_inject_tx;
void *msg_inject_rx;
#endif
struct snd_sof_dev *sdev;
struct list_head list; /* list in sdev dfsentry list */
......@@ -460,10 +425,6 @@ struct snd_sof_dev {
int ipc_timeout;
int boot_timeout;
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
unsigned int extractor_stream_tag;
#endif
/* DMA for Trace */
struct snd_dma_buffer dmatb;
struct snd_dma_buffer dmatp;
......@@ -489,6 +450,30 @@ struct snd_sof_dev {
*/
int dsp_core_ref_count[SOF_MAX_DSP_NUM_CORES];
/*
* Used to keep track of registered IPC client devices so that they can
* be removed when the parent SOF module is removed.
*/
struct list_head ipc_client_list;
/* mutex to protect client list */
struct mutex ipc_client_mutex;
/*
* Used for tracking the IPC client's RX registration for DSP initiated
* message handling.
*/
struct list_head ipc_rx_handler_list;
/*
* Used for tracking the IPC client's registration for DSP state change
* notification
*/
struct list_head fw_state_handler_list;
/* to protect the ipc_rx_handler_list and dsp_state_handler_list list */
struct mutex client_event_handler_mutex;
void *private; /* core does not touch this */
};
......@@ -512,10 +497,6 @@ void snd_sof_complete(struct device *dev);
void snd_sof_new_platform_drv(struct snd_sof_dev *sdev);
int snd_sof_create_page_table(struct device *dev,
struct snd_dma_buffer *dmab,
unsigned char *page_table, size_t size);
/*
* Firmware loading.
*/
......@@ -596,15 +577,7 @@ extern const struct dsp_arch_ops sof_xtensa_arch_ops;
/*
* Firmware state tracking
*/
static inline void sof_set_fw_state(struct snd_sof_dev *sdev,
enum sof_fw_state new_state)
{
if (sdev->fw_state == new_state)
return;
dev_dbg(sdev->dev, "fw_state change: %d -> %d\n", sdev->fw_state, new_state);
sdev->fw_state = new_state;
}
void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state);
/*
* Utilities
......@@ -637,4 +610,56 @@ int sof_stream_pcm_close(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream);
int sof_machine_check(struct snd_sof_dev *sdev);
/* SOF client support */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_CLIENT)
int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
const void *data, size_t size);
void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id);
int sof_register_clients(struct snd_sof_dev *sdev);
void sof_unregister_clients(struct snd_sof_dev *sdev);
void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf);
void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev);
int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state);
int sof_resume_clients(struct snd_sof_dev *sdev);
#else /* CONFIG_SND_SOC_SOF_CLIENT */
static inline int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name,
u32 id, const void *data, size_t size)
{
return 0;
}
static inline void sof_client_dev_unregister(struct snd_sof_dev *sdev,
const char *name, u32 id)
{
}
static inline int sof_register_clients(struct snd_sof_dev *sdev)
{
return 0;
}
static inline void sof_unregister_clients(struct snd_sof_dev *sdev)
{
}
static inline void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf)
{
}
static inline void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev)
{
}
static inline int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state)
{
return 0;
}
static inline int sof_resume_clients(struct snd_sof_dev *sdev)
{
return 0;
}
#endif /* CONFIG_SND_SOC_SOF_CLIENT */
#endif
This diff is collapsed.
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* Copyright(c) 2019-2021 Intel Corporation. All rights reserved.
* Author: Cezary Rojewski <cezary.rojewski@intel.com>
*/
#ifndef __SOF_PROBES_H
#define __SOF_PROBES_H
#include <sound/compress_driver.h>
#include <sound/sof/header.h>
struct snd_sof_dev;
#define SOF_PROBE_INVALID_NODE_ID UINT_MAX
struct sof_probe_point_desc {
unsigned int buffer_id;
unsigned int purpose;
unsigned int stream_tag;
} __packed;
int sof_ipc_probe_points_info(struct snd_sof_dev *sdev,
struct sof_probe_point_desc **desc,
size_t *num_desc);
int sof_ipc_probe_points_add(struct snd_sof_dev *sdev,
struct sof_probe_point_desc *desc,
size_t num_desc);
int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev,
unsigned int *buffer_id, size_t num_buffer_id);
extern const struct snd_soc_cdai_ops sof_probe_compr_ops;
extern const struct snd_compress_ops sof_probe_compressed_ops;
#endif
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
//
// This file is provided under a dual BSD/GPLv2 license. When using or
// redistributing this file, you may do so under either license.
//
// Copyright(c) 2018-2022 Intel Corporation. All rights reserved.
//
// Author: Keyon Jie <yang.jie@linux.intel.com>
//
#include <asm/unaligned.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/device.h>
#include <sound/memalloc.h>
#include <linux/module.h>
#include "sof-utils.h"
/*
* Generic buffer page table creation.
* Take the each physical page address and drop the least significant unused
* bits from each (based on PAGE_SIZE). Then pack valid page address bits
* into compressed page table.
*/
int snd_sof_create_page_table(struct device *dev,
struct snd_dma_buffer *dmab,
unsigned char *page_table, size_t size)
{
int i, pages;
pages = snd_sgbuf_aligned_pages(size);
dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n",
dmab->area, size, pages);
for (i = 0; i < pages; i++) {
/*
* The number of valid address bits for each page is 20.
* idx determines the byte position within page_table
* where the current page's address is stored
* in the compressed page_table.
* This can be calculated by multiplying the page number by 2.5.
*/
u32 idx = (5 * i) >> 1;
u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
u8 *pg_table;
dev_vdbg(dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
pg_table = (u8 *)(page_table + idx);
/*
* pagetable compression:
* byte 0 byte 1 byte 2 byte 3 byte 4 byte 5
* ___________pfn 0__________ __________pfn 1___________ _pfn 2...
* .... .... .... .... .... .... .... .... .... .... ....
* It is created by:
* 1. set current location to 0, PFN index i to 0
* 2. put pfn[i] at current location in Little Endian byte order
* 3. calculate an intermediate value as
* x = (pfn[i+1] << 4) | (pfn[i] & 0xf)
* 4. put x at offset (current location + 2) in LE byte order
* 5. increment current location by 5 bytes, increment i by 2
* 6. continue to (2)
*/
if (i & 1)
put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4,
pg_table);
else
put_unaligned_le32(pfn, pg_table);
}
return pages;
}
EXPORT_SYMBOL(snd_sof_create_page_table);
MODULE_LICENSE("Dual BSD/GPL");
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* Copyright(c) 2022 Intel Corporation. All rights reserved.
*/
#ifndef __SOC_SOF_UTILS_H
#define __SOC_SOF_UTILS_H
struct snd_dma_buffer;
struct device;
int snd_sof_create_page_table(struct device *dev,
struct snd_dma_buffer *dmab,
unsigned char *page_table, size_t size);
#endif
......@@ -12,6 +12,7 @@
#include <linux/sched/signal.h>
#include "sof-priv.h"
#include "ops.h"
#include "sof-utils.h"
#define TRACE_FILTER_ELEMENTS_PER_ENTRY 4
#define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment