summaryrefslogtreecommitdiffstats
path: root/sound/soc/sof
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof')
-rw-r--r--sound/soc/sof/Kconfig9
-rw-r--r--sound/soc/sof/control.c270
-rw-r--r--sound/soc/sof/debug.c247
-rw-r--r--sound/soc/sof/intel/Kconfig33
-rw-r--r--sound/soc/sof/intel/apl.c2
-rw-r--r--sound/soc/sof/intel/byt.c5
-rw-r--r--sound/soc/sof/intel/cnl.c79
-rw-r--r--sound/soc/sof/intel/hda-ctrl.c77
-rw-r--r--sound/soc/sof/intel/hda-dai.c293
-rw-r--r--sound/soc/sof/intel/hda-dsp.c63
-rw-r--r--sound/soc/sof/intel/hda-ipc.c43
-rw-r--r--sound/soc/sof/intel/hda-stream.c157
-rw-r--r--sound/soc/sof/intel/hda.c30
-rw-r--r--sound/soc/sof/intel/hda.h22
-rw-r--r--sound/soc/sof/ipc.c18
-rw-r--r--sound/soc/sof/loader.c10
-rw-r--r--sound/soc/sof/nocodec.c21
-rw-r--r--sound/soc/sof/ops.h24
-rw-r--r--sound/soc/sof/pcm.c36
-rw-r--r--sound/soc/sof/pm.c43
-rw-r--r--sound/soc/sof/sof-acpi-dev.c59
-rw-r--r--sound/soc/sof/sof-pci-dev.c32
-rw-r--r--sound/soc/sof/sof-priv.h23
-rw-r--r--sound/soc/sof/topology.c156
-rw-r--r--sound/soc/sof/trace.c66
-rw-r--r--sound/soc/sof/xtensa/Kconfig1
26 files changed, 1186 insertions, 633 deletions
diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig
index a9a1d502daae..fb01f0ca6027 100644
--- a/sound/soc/sof/Kconfig
+++ b/sound/soc/sof/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_SOC_SOF_TOPLEVEL
bool "Sound Open Firmware Support"
help
@@ -131,6 +132,14 @@ config SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE
Say Y if you want to enable caching the memory windows.
If unsure, select "N".
+config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST
+ bool "SOF enable IPC flood test"
+ 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.
+ Say Y if you want to enable IPC flood test.
+ If unsure, select "N".
+
endif ## SND_SOC_SOF_DEBUG
endif ## SND_SOC_SOF_OPTIONS
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
index 84e2cbfbbcbb..a4983f90ff5b 100644
--- a/sound/soc/sof/control.c
+++ b/sound/soc/sof/control.c
@@ -39,26 +39,8 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *sm =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_sof_control *scontrol = sm->dobj.private;
- struct snd_sof_dev *sdev = scontrol->sdev;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
- int err, ret;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: volume get failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
-
- /* get all the mixer data from DSP */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_GET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
- SOF_CTRL_CMD_VOLUME,
- false);
/* read back each channel */
for (i = 0; i < channels; i++)
@@ -66,12 +48,6 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
ipc_to_mixer(cdata->chanv[i].value,
scontrol->volume_table, sm->max + 1);
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: volume get failed to idle %d\n",
- err);
return 0;
}
@@ -84,16 +60,6 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
struct snd_sof_dev *sdev = scontrol->sdev;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
- int ret, err;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: volume put failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
/* update each channel */
for (i = 0; i < channels; i++) {
@@ -104,18 +70,13 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
}
/* notify DSP of mixer updates */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_SET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
- SOF_CTRL_CMD_VOLUME,
- true);
-
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: volume put failed to idle %d\n",
- err);
+ if (pm_runtime_active(sdev->dev))
+ snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
+ SOF_IPC_COMP_SET_VALUE,
+ SOF_CTRL_TYPE_VALUE_CHAN_GET,
+ SOF_CTRL_CMD_VOLUME,
+ true);
+
return 0;
}
@@ -125,37 +86,13 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *sm =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_sof_control *scontrol = sm->dobj.private;
- struct snd_sof_dev *sdev = scontrol->sdev;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
- int err, ret;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: switch get failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
-
- /* get all the mixer data from DSP */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_GET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
- SOF_CTRL_CMD_SWITCH,
- false);
/* read back each channel */
for (i = 0; i < channels; i++)
ucontrol->value.integer.value[i] = cdata->chanv[i].value;
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: switch get failed to idle %d\n",
- err);
return 0;
}
@@ -168,16 +105,6 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
struct snd_sof_dev *sdev = scontrol->sdev;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
- int ret, err;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: switch put failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
/* update each channel */
for (i = 0; i < channels; i++) {
@@ -186,18 +113,13 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
}
/* notify DSP of mixer updates */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_SET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
- SOF_CTRL_CMD_SWITCH,
- true);
-
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: switch put failed to idle %d\n",
- err);
+ if (pm_runtime_active(sdev->dev))
+ snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
+ SOF_IPC_COMP_SET_VALUE,
+ SOF_CTRL_TYPE_VALUE_CHAN_GET,
+ SOF_CTRL_CMD_SWITCH,
+ true);
+
return 0;
}
@@ -207,37 +129,13 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol,
struct soc_enum *se =
(struct soc_enum *)kcontrol->private_value;
struct snd_sof_control *scontrol = se->dobj.private;
- struct snd_sof_dev *sdev = scontrol->sdev;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
- int err, ret;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: enum get failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
-
- /* get all the enum data from DSP */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_GET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
- SOF_CTRL_CMD_ENUM,
- false);
/* read back each channel */
for (i = 0; i < channels; i++)
ucontrol->value.enumerated.item[i] = cdata->chanv[i].value;
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: enum get failed to idle %d\n",
- err);
return 0;
}
@@ -250,16 +148,6 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol,
struct snd_sof_dev *sdev = scontrol->sdev;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
unsigned int i, channels = scontrol->num_channels;
- int ret, err;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: enum put failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
/* update each channel */
for (i = 0; i < channels; i++) {
@@ -268,18 +156,13 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol,
}
/* notify DSP of enum updates */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_SET_VALUE,
- SOF_CTRL_TYPE_VALUE_CHAN_GET,
- SOF_CTRL_CMD_ENUM,
- true);
-
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: enum put failed to idle %d\n",
- err);
+ if (pm_runtime_active(sdev->dev))
+ snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
+ SOF_IPC_COMP_SET_VALUE,
+ SOF_CTRL_TYPE_VALUE_CHAN_GET,
+ SOF_CTRL_CMD_ENUM,
+ true);
+
return 0;
}
@@ -293,7 +176,7 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
struct sof_abi_hdr *data = cdata->data;
size_t size;
- int ret, err;
+ int ret = 0;
if (be->max > sizeof(ucontrol->value.bytes.data)) {
dev_err_ratelimited(sdev->dev,
@@ -302,22 +185,6 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
return -EINVAL;
}
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: bytes get failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
-
- /* get all the binary data from DSP */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_GET_DATA,
- SOF_CTRL_TYPE_DATA_GET,
- scontrol->cmd,
- false);
-
size = data->size + sizeof(*data);
if (size > be->max) {
dev_err_ratelimited(sdev->dev,
@@ -331,12 +198,6 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
memcpy(ucontrol->value.bytes.data, data, size);
out:
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: bytes get failed to idle %d\n",
- err);
return ret;
}
@@ -350,7 +211,6 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
struct sof_abi_hdr *data = cdata->data;
size_t size = data->size + sizeof(*data);
- int ret, err;
if (be->max > sizeof(ucontrol->value.bytes.data)) {
dev_err_ratelimited(sdev->dev,
@@ -366,32 +226,18 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
}
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: bytes put failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
-
/* copy from kcontrol */
memcpy(data, ucontrol->value.bytes.data, size);
/* notify DSP of byte control updates */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_SET_DATA,
- SOF_CTRL_TYPE_DATA_SET,
- scontrol->cmd,
- true);
-
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: bytes put failed to idle %d\n",
- err);
- return ret;
+ if (pm_runtime_active(sdev->dev))
+ snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
+ SOF_IPC_COMP_SET_DATA,
+ SOF_CTRL_TYPE_DATA_SET,
+ scontrol->cmd,
+ true);
+
+ return 0;
}
int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
@@ -406,8 +252,6 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_tlv header;
const struct snd_ctl_tlv __user *tlvd =
(const struct snd_ctl_tlv __user *)binary_data;
- int ret;
- int err;
/*
* The beginning of bytes data contains a header from where
@@ -453,30 +297,15 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
}
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: bytes_ext put failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
-
/* notify DSP of byte control updates */
- snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_SET_DATA,
- SOF_CTRL_TYPE_DATA_SET,
- scontrol->cmd,
- true);
-
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: bytes_ext put failed to idle %d\n",
- err);
+ if (pm_runtime_active(sdev->dev))
+ snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
+ SOF_IPC_COMP_SET_DATA,
+ SOF_CTRL_TYPE_DATA_SET,
+ scontrol->cmd,
+ true);
- return ret;
+ return 0;
}
int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
@@ -492,17 +321,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_tlv __user *tlvd =
(struct snd_ctl_tlv __user *)binary_data;
int data_size;
- int err;
- int ret;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err_ratelimited(sdev->dev,
- "error: bytes_ext get failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
+ int ret = 0;
/*
* Decrement the limit by ext bytes header size to
@@ -514,13 +333,6 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
cdata->data->magic = SOF_ABI_MAGIC;
cdata->data->abi = SOF_ABI_VERSION;
- /* get all the component data from DSP */
- ret = snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
- SOF_IPC_COMP_GET_DATA,
- SOF_CTRL_TYPE_DATA_GET,
- scontrol->cmd,
- false);
-
/* Prevent read of other kernel data or possibly corrupt response */
data_size = cdata->data->size + sizeof(const struct sof_abi_hdr);
@@ -543,11 +355,5 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
ret = -EFAULT;
out:
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: bytes_ext get failed to idle %d\n",
- err);
return ret;
}
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index 55f1d808dba0..2388477a965e 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -17,6 +17,203 @@
#include "sof-priv.h"
#include "ops.h"
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
+#define MAX_IPC_FLOOD_DURATION_MS 1000
+#define MAX_IPC_FLOOD_COUNT 10000
+#define IPC_FLOOD_TEST_RESULT_LEN 512
+
+static int sof_debug_ipc_flood_test(struct snd_sof_dev *sdev,
+ struct snd_sof_dfsentry *dfse,
+ bool flood_duration_test,
+ unsigned long ipc_duration_ms,
+ unsigned long ipc_count)
+{
+ struct sof_ipc_cmd_hdr hdr;
+ struct sof_ipc_reply reply;
+ u64 min_response_time = U64_MAX;
+ ktime_t start, end, test_end;
+ u64 avg_response_time = 0;
+ u64 max_response_time = 0;
+ u64 ipc_response_time;
+ int i = 0;
+ int ret;
+
+ /* configure test IPC */
+ hdr.cmd = SOF_IPC_GLB_TEST_MSG | SOF_IPC_TEST_IPC_FLOOD;
+ hdr.size = sizeof(hdr);
+
+ /* set test end time for duration flood test */
+ if (flood_duration_test)
+ test_end = ktime_get_ns() + ipc_duration_ms * NSEC_PER_MSEC;
+
+ /* send test IPC's */
+ while (1) {
+ start = ktime_get();
+ ret = sof_ipc_tx_message(sdev->ipc, hdr.cmd, &hdr, hdr.size,
+ &reply, sizeof(reply));
+ end = ktime_get();
+
+ if (ret < 0)
+ break;
+
+ /* compute min and max response times */
+ ipc_response_time = ktime_to_ns(ktime_sub(end, start));
+ min_response_time = min(min_response_time, ipc_response_time);
+ max_response_time = max(max_response_time, ipc_response_time);
+
+ /* sum up response times */
+ avg_response_time += ipc_response_time;
+ i++;
+
+ /* test complete? */
+ if (flood_duration_test) {
+ if (ktime_to_ns(end) >= test_end)
+ break;
+ } else {
+ if (i == ipc_count)
+ break;
+ }
+ }
+
+ if (ret < 0)
+ dev_err(sdev->dev,
+ "error: ipc flood test failed at %d iterations\n", i);
+
+ /* return if the first IPC fails */
+ if (!i)
+ return ret;
+
+ /* compute average response time */
+ do_div(avg_response_time, i);
+
+ /* clear previous test output */
+ memset(dfse->cache_buf, 0, IPC_FLOOD_TEST_RESULT_LEN);
+
+ if (flood_duration_test) {
+ dev_dbg(sdev->dev, "IPC Flood test duration: %lums\n",
+ ipc_duration_ms);
+ snprintf(dfse->cache_buf, IPC_FLOOD_TEST_RESULT_LEN,
+ "IPC Flood test duration: %lums\n", ipc_duration_ms);
+ }
+
+ dev_dbg(sdev->dev,
+ "IPC Flood count: %d, Avg response time: %lluns\n",
+ i, avg_response_time);
+ dev_dbg(sdev->dev, "Max response time: %lluns\n",
+ max_response_time);
+ dev_dbg(sdev->dev, "Min response time: %lluns\n",
+ min_response_time);
+
+ /* format output string */
+ snprintf(dfse->cache_buf + strlen(dfse->cache_buf),
+ IPC_FLOOD_TEST_RESULT_LEN - strlen(dfse->cache_buf),
+ "IPC Flood count: %d\nAvg response time: %lluns\n",
+ i, avg_response_time);
+
+ snprintf(dfse->cache_buf + strlen(dfse->cache_buf),
+ IPC_FLOOD_TEST_RESULT_LEN - strlen(dfse->cache_buf),
+ "Max response time: %lluns\nMin response time: %lluns\n",
+ max_response_time, min_response_time);
+
+ return ret;
+}
+#endif
+
+static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
+ struct snd_sof_dfsentry *dfse = file->private_data;
+ struct snd_sof_dev *sdev = dfse->sdev;
+ unsigned long ipc_duration_ms = 0;
+ bool flood_duration_test = false;
+ unsigned long ipc_count = 0;
+ int err;
+#endif
+ size_t size;
+ char *string;
+ int ret;
+
+ string = kzalloc(count, GFP_KERNEL);
+ if (!string)
+ return -ENOMEM;
+
+ size = simple_write_to_buffer(string, count, ppos, buffer, count);
+ ret = size;
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
+ /*
+ * write op is only supported for ipc_flood_count or
+ * ipc_flood_duration_ms debugfs entries atm.
+ * ipc_flood_count floods the DSP with the number of IPC's specified.
+ * ipc_duration_ms test floods the DSP for the time specified
+ * in the debugfs entry.
+ */
+ if (strcmp(dfse->dfsentry->d_name.name, "ipc_flood_count") &&
+ strcmp(dfse->dfsentry->d_name.name, "ipc_flood_duration_ms"))
+ return -EINVAL;
+
+ if (!strcmp(dfse->dfsentry->d_name.name, "ipc_flood_duration_ms"))
+ flood_duration_test = true;
+
+ /* test completion criterion */
+ if (flood_duration_test)
+ ret = kstrtoul(string, 0, &ipc_duration_ms);
+ else
+ ret = kstrtoul(string, 0, &ipc_count);
+ if (ret < 0)
+ goto out;
+
+ /* limit max duration/ipc count for flood test */
+ if (flood_duration_test) {
+ if (!ipc_duration_ms) {
+ ret = size;
+ goto out;
+ }
+
+ /* find the minimum. min() is not used to avoid warnings */
+ if (ipc_duration_ms > MAX_IPC_FLOOD_DURATION_MS)
+ ipc_duration_ms = MAX_IPC_FLOOD_DURATION_MS;
+ } else {
+ if (!ipc_count) {
+ ret = size;
+ goto out;
+ }
+
+ /* find the minimum. min() is not used to avoid warnings */
+ if (ipc_count > MAX_IPC_FLOOD_COUNT)
+ ipc_count = MAX_IPC_FLOOD_COUNT;
+ }
+
+ ret = pm_runtime_get_sync(sdev->dev);
+ if (ret < 0) {
+ dev_err_ratelimited(sdev->dev,
+ "error: debugfs write failed to resume %d\n",
+ ret);
+ pm_runtime_put_noidle(sdev->dev);
+ goto out;
+ }
+
+ /* flood test */
+ ret = sof_debug_ipc_flood_test(sdev, dfse, flood_duration_test,
+ ipc_duration_ms, ipc_count);
+
+ pm_runtime_mark_last_busy(sdev->dev);
+ err = pm_runtime_put_autosuspend(sdev->dev);
+ if (err < 0)
+ dev_err_ratelimited(sdev->dev,
+ "error: debugfs write failed to idle %d\n",
+ err);
+
+ /* return size if test is successful */
+ if (ret >= 0)
+ ret = size;
+out:
+#endif
+ kfree(string);
+ return ret;
+}
+
static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
@@ -28,6 +225,22 @@ static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer,
int size;
u8 *buf;
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
+ if ((!strcmp(dfse->dfsentry->d_name.name, "ipc_flood_count") ||
+ !strcmp(dfse->dfsentry->d_name.name, "ipc_flood_duration_ms")) &&
+ dfse->cache_buf) {
+ if (*ppos)
+ return 0;
+
+ count = strlen(dfse->cache_buf);
+ size_ret = copy_to_user(buffer, dfse->cache_buf, count);
+ if (size_ret)
+ return -EFAULT;
+
+ *ppos += count;
+ return count;
+ }
+#endif
size = dfse->size;
/* validate position & count */
@@ -107,6 +320,7 @@ static const struct file_operations sof_dfs_fops = {
.open = simple_open,
.read = sof_dfsentry_read,
.llseek = default_llseek,
+ .write = sof_dfsentry_write,
};
/* create FS entry for debug files that can expose DSP memories, registers */
@@ -161,7 +375,7 @@ EXPORT_SYMBOL_GPL(snd_sof_debugfs_io_item);
/* create FS entry for debug files to expose kernel memory */
int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev,
void *base, size_t size,
- const char *name)
+ const char *name, mode_t mode)
{
struct snd_sof_dfsentry *dfse;
@@ -177,7 +391,18 @@ int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev,
dfse->size = size;
dfse->sdev = sdev;
- dfse->dfsentry = debugfs_create_file(name, 0444, sdev->debugfs_root,
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
+ /*
+ * cache_buf is unused for SOF_DFSENTRY_TYPE_BUF debugfs entries.
+ * So, use it to save the results of the last IPC flood test.
+ */
+ dfse->cache_buf = devm_kzalloc(sdev->dev, IPC_FLOOD_TEST_RESULT_LEN,
+ GFP_KERNEL);
+ if (!dfse->cache_buf)
+ return -ENOMEM;
+#endif
+
+ dfse->dfsentry = debugfs_create_file(name, mode, sdev->debugfs_root,
dfse, &sof_dfs_fops);
if (!dfse->dfsentry) {
/* can't rely on debugfs, only log error and keep going */
@@ -221,6 +446,24 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev)
return err;
}
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
+ /* create read-write ipc_flood_count debugfs entry */
+ err = snd_sof_debugfs_buf_item(sdev, NULL, 0,
+ "ipc_flood_count", 0666);
+
+ /* errors are only due to memory allocation, not debugfs */
+ if (err < 0)
+ return err;
+
+ /* create read-write ipc_flood_duration_ms debugfs entry */
+ err = snd_sof_debugfs_buf_item(sdev, NULL, 0,
+ "ipc_flood_duration_ms", 0666);
+
+ /* errors are only due to memory allocation, not debugfs */
+ if (err < 0)
+ return err;
+#endif
+
return 0;
}
EXPORT_SYMBOL_GPL(snd_sof_dbg_init);
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index 603e0db4f012..dd14ce92fe10 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_SOC_SOF_INTEL_TOPLEVEL
bool "SOF support for Intel audio DSPs"
depends on X86 || COMPILE_TEST
@@ -24,6 +25,8 @@ config SND_SOC_SOF_INTEL_PCI
select SND_SOC_SOF_CANNONLAKE if SND_SOC_SOF_CANNONLAKE_SUPPORT
select SND_SOC_SOF_COFFEELAKE if SND_SOC_SOF_COFFEELAKE_SUPPORT
select SND_SOC_SOF_ICELAKE if SND_SOC_SOF_ICELAKE_SUPPORT
+ select SND_SOC_SOF_COMETLAKE_LP if SND_SOC_SOF_COMETLAKE_LP_SUPPORT
+ select SND_SOC_SOF_COMETLAKE_H if SND_SOC_SOF_COMETLAKE_H_SUPPORT
help
This option is not user-selectable but automagically handled by
'select' statements at a higher level
@@ -179,6 +182,36 @@ config SND_SOC_SOF_ICELAKE
This option is not user-selectable but automagically handled by
'select' statements at a higher level
+config SND_SOC_SOF_COMETLAKE_LP
+ tristate
+ select SND_SOC_SOF_HDA_COMMON
+ help
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level
+
+config SND_SOC_SOF_COMETLAKE_LP_SUPPORT
+ bool "SOF support for CometLake-LP"
+ help
+ This adds support for Sound Open Firmware for Intel(R) platforms
+ using the Cometlake-LP processors.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_SOF_COMETLAKE_H
+ tristate
+ select SND_SOC_SOF_HDA_COMMON
+ help
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level
+
+config SND_SOC_SOF_COMETLAKE_H_SUPPORT
+ bool "SOF support for CometLake-H"
+ help
+ This adds support for Sound Open Firmware for Intel(R) platforms
+ using the Cometlake-H processors.
+ Say Y if you have such a device.
+ If unsure select "N".
+
config SND_SOC_SOF_HDA_COMMON
tristate
select SND_SOC_SOF_INTEL_COMMON
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index f215d80dce2c..fd2e26d79796 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -61,6 +61,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
.pcm_open = hda_dsp_pcm_open,
.pcm_close = hda_dsp_pcm_close,
.pcm_hw_params = hda_dsp_pcm_hw_params,
+ .pcm_hw_free = hda_dsp_stream_hw_free,
.pcm_trigger = hda_dsp_pcm_trigger,
.pcm_pointer = hda_dsp_pcm_pointer,
@@ -92,6 +93,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
.resume = hda_dsp_resume,
.runtime_suspend = hda_dsp_runtime_suspend,
.runtime_resume = hda_dsp_runtime_resume,
+ .runtime_idle = hda_dsp_runtime_idle,
.set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
};
EXPORT_SYMBOL(sof_apl_ops);
diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c
index 39d1ae01c45d..107d711efc3f 100644
--- a/sound/soc/sof/intel/byt.c
+++ b/sound/soc/sof/intel/byt.c
@@ -376,13 +376,10 @@ static irqreturn_t byt_irq_thread(int irq, void *context)
static int byt_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
{
- u64 cmd = msg->header;
-
/* send the message */
sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
msg->msg_size);
- snd_sof_dsp_write64(sdev, BYT_DSP_BAR, SHIM_IPCX,
- cmd | SHIM_BYT_IPCX_BUSY);
+ snd_sof_dsp_write64(sdev, BYT_DSP_BAR, SHIM_IPCX, SHIM_BYT_IPCX_BUSY);
return 0;
}
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index b2eba7adcad8..f2b392998f20 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -31,27 +31,20 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
u32 hipci;
- u32 hipcctl;
u32 hipcida;
u32 hipctdr;
u32 hipctdd;
u32 msg;
u32 msg_ext;
- irqreturn_t ret = IRQ_NONE;
+ bool ipc_irq = false;
hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA);
- hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCCTL);
hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR);
-
- /* reenable IPC interrupt */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
- HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC);
+ hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDD);
+ hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR);
/* reply message from DSP */
- if (hipcida & CNL_DSP_REG_HIPCIDA_DONE &&
- hipcctl & CNL_DSP_REG_HIPCCTL_DONE) {
- hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- CNL_DSP_REG_HIPCIDR);
+ if (hipcida & CNL_DSP_REG_HIPCIDA_DONE) {
msg_ext = hipci & CNL_DSP_REG_HIPCIDR_MSG_MASK;
msg = hipcida & CNL_DSP_REG_HIPCIDA_MSG_MASK;
@@ -79,13 +72,11 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
spin_unlock_irq(&sdev->ipc_lock);
- ret = IRQ_HANDLED;
+ ipc_irq = true;
}
/* new message from DSP */
if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) {
- hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- CNL_DSP_REG_HIPCTDD);
msg = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK;
msg_ext = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK;
@@ -101,26 +92,37 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
snd_sof_ipc_msgs_rx(sdev);
}
- /*
- * clear busy interrupt to tell dsp controller this
- * interrupt has been accepted, not trigger it again
- */
- snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR,
- CNL_DSP_REG_HIPCTDR,
- CNL_DSP_REG_HIPCTDR_BUSY,
- CNL_DSP_REG_HIPCTDR_BUSY);
-
cnl_ipc_host_done(sdev);
- ret = IRQ_HANDLED;
+ ipc_irq = true;
+ }
+
+ if (!ipc_irq) {
+ /*
+ * This interrupt is not shared so no need to return IRQ_NONE.
+ */
+ dev_err_ratelimited(sdev->dev,
+ "error: nothing to do in IRQ thread\n");
}
- return ret;
+ /* re-enable IPC interrupt */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
+ HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC);
+
+ return IRQ_HANDLED;
}
static void cnl_ipc_host_done(struct snd_sof_dev *sdev)
{
/*
+ * clear busy interrupt to tell dsp controller this
+ * interrupt has been accepted, not trigger it again
+ */
+ snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR,
+ CNL_DSP_REG_HIPCTDR,
+ CNL_DSP_REG_HIPCTDR_BUSY,
+ CNL_DSP_REG_HIPCTDR_BUSY);
+ /*
* set done bit to ack dsp the msg has been
* processed and send reply msg to dsp
*/
@@ -151,13 +153,11 @@ static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev)
static int cnl_ipc_send_msg(struct snd_sof_dev *sdev,
struct snd_sof_ipc_msg *msg)
{
- u32 cmd = msg->header;
-
/* send the message */
sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
msg->msg_size);
snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR,
- cmd | CNL_DSP_REG_HIPCIDR_BUSY);
+ CNL_DSP_REG_HIPCIDR_BUSY);
return 0;
}
@@ -168,6 +168,8 @@ static void cnl_ipc_dump(struct snd_sof_dev *sdev)
u32 hipcida;
u32 hipctdr;
+ hda_ipc_irq_dump(sdev);
+
/* read IPC status */
hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA);
hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCCTL);
@@ -217,6 +219,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
.pcm_open = hda_dsp_pcm_open,
.pcm_close = hda_dsp_pcm_close,
.pcm_hw_params = hda_dsp_pcm_hw_params,
+ .pcm_hw_free = hda_dsp_stream_hw_free,
.pcm_trigger = hda_dsp_pcm_trigger,
.pcm_pointer = hda_dsp_pcm_pointer,
@@ -248,6 +251,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
.resume = hda_dsp_resume,
.runtime_suspend = hda_dsp_runtime_suspend,
.runtime_resume = hda_dsp_runtime_resume,
+ .runtime_idle = hda_dsp_runtime_idle,
.set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
};
EXPORT_SYMBOL(sof_cnl_ops);
@@ -270,3 +274,22 @@ const struct sof_intel_dsp_desc cnl_chip_info = {
.ssp_base_offset = CNL_SSP_BASE_OFFSET,
};
EXPORT_SYMBOL(cnl_chip_info);
+
+const struct sof_intel_dsp_desc icl_chip_info = {
+ /* Icelake */
+ .cores_num = 4,
+ .init_core_mask = 1,
+ .cores_mask = HDA_DSP_CORE_MASK(0) |
+ HDA_DSP_CORE_MASK(1) |
+ HDA_DSP_CORE_MASK(2) |
+ HDA_DSP_CORE_MASK(3),
+ .ipc_req = CNL_DSP_REG_HIPCIDR,
+ .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY,
+ .ipc_ack = CNL_DSP_REG_HIPCIDA,
+ .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE,
+ .ipc_ctl = CNL_DSP_REG_HIPCCTL,
+ .rom_init_timeout = 300,
+ .ssp_count = ICL_SSP_COUNT,
+ .ssp_base_offset = CNL_SSP_BASE_OFFSET,
+};
+EXPORT_SYMBOL(icl_chip_info);
diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c
index 07bc123112c9..ea63f83a509b 100644
--- a/sound/soc/sof/intel/hda-ctrl.c
+++ b/sound/soc/sof/intel/hda-ctrl.c
@@ -217,17 +217,14 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset)
/* clear stream status */
list_for_each_entry(stream, &bus->stream_list, list) {
sd_offset = SOF_STREAM_SD_OFFSET(stream);
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
- sd_offset +
- SOF_HDA_ADSP_REG_CL_SD_STS,
- SOF_HDA_CL_DMA_SD_INT_MASK,
- SOF_HDA_CL_DMA_SD_INT_MASK);
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS,
+ SOF_HDA_CL_DMA_SD_INT_MASK);
}
/* clear WAKESTS */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS,
- SOF_HDA_WAKESTS_INT_MASK,
- SOF_HDA_WAKESTS_INT_MASK);
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS,
+ SOF_HDA_WAKESTS_INT_MASK);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
/* clear rirb status */
@@ -263,3 +260,67 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset)
return ret;
}
+
+void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ struct hdac_stream *stream;
+ int sd_offset;
+
+ if (!bus->chip_init)
+ return;
+
+ /* disable interrupts in stream descriptor */
+ list_for_each_entry(stream, &bus->stream_list, list) {
+ sd_offset = SOF_STREAM_SD_OFFSET(stream);
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
+ sd_offset +
+ SOF_HDA_ADSP_REG_CL_SD_CTL,
+ SOF_HDA_CL_DMA_SD_INT_MASK,
+ 0);
+ }
+
+ /* disable SIE for all streams */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
+ SOF_HDA_INT_ALL_STREAM, 0);
+
+ /* disable controller CIE and GIE */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
+ SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN,
+ 0);
+
+ /* clear stream status */
+ list_for_each_entry(stream, &bus->stream_list, list) {
+ sd_offset = SOF_STREAM_SD_OFFSET(stream);
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS,
+ SOF_HDA_CL_DMA_SD_INT_MASK);
+ }
+
+ /* clear WAKESTS */
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS,
+ SOF_HDA_WAKESTS_INT_MASK);
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ /* clear rirb status */
+ snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+#endif
+
+ /* clear interrupt status register */
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS,
+ SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM);
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ /* disable CORB/RIRB */
+ snd_hdac_bus_stop_cmd_io(bus);
+#endif
+ /* disable position buffer */
+ if (bus->posbuf.addr) {
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ SOF_HDA_ADSP_DPLBASE, 0);
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ SOF_HDA_ADSP_DPUBASE, 0);
+ }
+
+ bus->chip_init = false;
+}
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index e1decf25aeac..a514f9cf5c9a 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -30,62 +30,90 @@ struct hda_pipe_params {
};
/*
- * Unlike GP dma, there is a set of stream registers in hda controller
- * to control the link dma channels. Each register controls one link
- * dma channel and the relation is fixed. To make sure FW uses correct
- * link dma channels, host allocates stream registers and sends the
- * corresponding link dma channels to FW to allocate link dma channel
- *
- * FIXME: this API is abused in the sense that tx_num and rx_num are
- * passed as arguments, not returned. We need to find a better way to
- * retrieve the stream tag allocated for the link DMA
+ * This function checks if the host dma channel corresponding
+ * to the link DMA stream_tag argument is assigned to one
+ * of the FEs connected to the BE DAI.
*/
-static int hda_link_dma_get_channels(struct snd_soc_dai *dai,
- unsigned int *tx_num,
- unsigned int *tx_slot,
- unsigned int *rx_num,
- unsigned int *rx_slot)
+static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
+ int dir, int stream_tag)
{
- struct hdac_bus *bus;
- struct hdac_ext_stream *stream;
- struct snd_pcm_substream substream;
- struct snd_sof_dev *sdev =
- snd_soc_component_get_drvdata(dai->component);
-
- bus = sof_to_bus(sdev);
-
- memset(&substream, 0, sizeof(substream));
- if (*tx_num == 1) {
- substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
- stream = snd_hdac_ext_stream_assign(bus, &substream,
- HDAC_EXT_STREAM_TYPE_LINK);
- if (!stream) {
- dev_err(bus->dev, "error: failed to find a free hda ext stream for playback");
- return -EBUSY;
- }
+ struct snd_pcm_substream *fe_substream;
+ struct hdac_stream *fe_hstream;
+ struct snd_soc_dpcm *dpcm;
+
+ for_each_dpcm_fe(rtd, dir, dpcm) {
+ fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
+ fe_hstream = fe_substream->runtime->private_data;
+ if (fe_hstream->stream_tag == stream_tag)
+ return true;
+ }
- snd_soc_dai_set_dma_data(dai, &substream, stream);
- *tx_slot = hdac_stream(stream)->stream_tag - 1;
+ return false;
+}
+
+static struct hdac_ext_stream *
+ hda_link_stream_assign(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sof_intel_hda_stream *hda_stream;
+ struct hdac_ext_stream *res = NULL;
+ struct hdac_stream *stream = NULL;
- dev_dbg(bus->dev, "link dma channel %d for playback", *tx_slot);
+ int stream_dir = substream->stream;
+
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "stream type not supported\n");
+ return NULL;
}
- if (*rx_num == 1) {
- substream.stream = SNDRV_PCM_STREAM_CAPTURE;
- stream = snd_hdac_ext_stream_assign(bus, &substream,
- HDAC_EXT_STREAM_TYPE_LINK);
- if (!stream) {
- dev_err(bus->dev, "error: failed to find a free hda ext stream for capture");
- return -EBUSY;
+ list_for_each_entry(stream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hstream =
+ stream_to_hdac_ext_stream(stream);
+ if (stream->direction != substream->stream)
+ continue;
+
+ hda_stream = hstream_to_sof_hda_stream(hstream);
+
+ /* check if link is available */
+ if (!hstream->link_locked) {
+ if (stream->opened) {
+ /*
+ * check if the stream tag matches the stream
+ * tag of one of the connected FEs
+ */
+ if (hda_check_fes(rtd, stream_dir,
+ stream->stream_tag)) {
+ res = hstream;
+ break;
+ }
+ } else {
+ res = hstream;
+
+ /*
+ * This must be a hostless stream.
+ * So reserve the host DMA channel.
+ */
+ hda_stream->host_reserved = 1;
+ break;
+ }
}
+ }
- snd_soc_dai_set_dma_data(dai, &substream, stream);
- *rx_slot = hdac_stream(stream)->stream_tag - 1;
-
- dev_dbg(bus->dev, "link dma channel %d for capture", *rx_slot);
+ if (res) {
+ /*
+ * Decouple host and link DMA. The decoupled flag
+ * is updated in snd_hdac_ext_stream_decouple().
+ */
+ if (!res->decoupled)
+ snd_hdac_ext_stream_decouple(bus, res, true);
+ spin_lock_irq(&bus->reg_lock);
+ res->link_locked = 1;
+ res->link_substream = substream;
+ spin_unlock_irq(&bus->reg_lock);
}
- return 0;
+ return res;
}
static int hda_link_dma_params(struct hdac_ext_stream *stream,
@@ -122,6 +150,51 @@ static int hda_link_dma_params(struct hdac_ext_stream *stream,
return 0;
}
+/* Send DAI_CONFIG IPC to the DAI that matches the dai_name and direction */
+static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream,
+ const char *dai_name, int channel, int dir)
+{
+ struct sof_ipc_dai_config *config;
+ struct snd_sof_dai *sof_dai;
+ struct sof_ipc_reply reply;
+ int ret = 0;
+
+ list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) {
+ if (!sof_dai->cpu_dai_name)
+ continue;
+
+ if (!strcmp(dai_name, sof_dai->cpu_dai_name) &&
+ dir == sof_dai->comp_dai.direction) {
+ config = sof_dai->dai_config;
+
+ if (!config) {
+ dev_err(hda_stream->sdev->dev,
+ "error: no config for DAI %s\n",
+ sof_dai->name);
+ return -EINVAL;
+ }
+
+ /* update config with stream tag */
+ config->hda.link_dma_ch = channel;
+
+ /* send IPC */
+ ret = sof_ipc_tx_message(hda_stream->sdev->ipc,
+ config->hdr.cmd,
+ config,
+ config->hdr.size,
+ &reply, sizeof(reply));
+
+ if (ret < 0)
+ dev_err(hda_stream->sdev->dev,
+ "error: failed to set dai config for %s\n",
+ sof_dai->name);
+ return ret;
+ }
+ }
+
+ return -EINVAL;
+}
+
static int hda_link_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -135,20 +208,31 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
struct hda_pipe_params p_params = {0};
struct hdac_ext_link *link;
int stream_tag;
+ int ret;
- link_dev = snd_soc_dai_get_dma_data(dai, substream);
+ link_dev = hda_link_stream_assign(bus, substream);
+ if (!link_dev)
+ return -EBUSY;
+
+ stream_tag = hdac_stream(link_dev)->stream_tag;
+
+ hda_stream = hstream_to_sof_hda_stream(link_dev);
+
+ /* update the DSP with the new tag */
+ ret = hda_link_config_ipc(hda_stream, dai->name, stream_tag - 1,
+ substream->stream);
+ if (ret < 0)
+ return ret;
+
+ snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
- hda_stream = container_of(link_dev, struct sof_intel_hda_stream,
- hda_stream);
hda_stream->hw_params_upon_resume = 0;
link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
if (!link)
return -EINVAL;
- stream_tag = hdac_stream(link_dev)->stream_tag;
-
- /* set the stream tag in the codec dai dma params */
+ /* set the stream tag in the codec dai dma params */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0);
else
@@ -181,8 +265,7 @@ static int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
int stream = substream->stream;
- hda_stream = container_of(link_dev, struct sof_intel_hda_stream,
- hda_stream);
+ hda_stream = hstream_to_sof_hda_stream(link_dev);
/* setup hw_params again only if resuming from system suspend */
if (!hda_stream->hw_params_upon_resume)
@@ -199,8 +282,24 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
{
struct hdac_ext_stream *link_dev =
snd_soc_dai_get_dma_data(dai, substream);
+ struct sof_intel_hda_stream *hda_stream;
+ struct snd_soc_pcm_runtime *rtd;
+ struct hdac_ext_link *link;
+ struct hdac_stream *hstream;
+ struct hdac_bus *bus;
+ int stream_tag;
int ret;
+ hstream = substream->runtime->private_data;
+ bus = hstream->bus;
+ rtd = snd_pcm_substream_chip(substream);
+
+ link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name);
+ if (!link)
+ return -EINVAL;
+
+ hda_stream = hstream_to_sof_hda_stream(link_dev);
+
dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
@@ -217,8 +316,22 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
snd_hdac_ext_link_stream_start(link_dev);
break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
+ /*
+ * clear and release link DMA channel. It will be assigned when
+ * hw_params is set up again after resume.
+ */
+ ret = hda_link_config_ipc(hda_stream, dai->name,
+ DMA_CHAN_INVALID, substream->stream);
+ if (ret < 0)
+ return ret;
+ stream_tag = hdac_stream(link_dev)->stream_tag;
+ snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+ snd_hdac_ext_stream_release(link_dev,
+ HDAC_EXT_STREAM_TYPE_LINK);
+
+ /* fallthrough */
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_STOP:
snd_hdac_ext_link_stream_clear(link_dev);
break;
@@ -228,62 +341,41 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
return 0;
}
-/*
- * FIXME: This API is also abused since it's used for two purposes.
- * when the substream argument is NULL this function is used for cleanups
- * that aren't necessarily required, and called explicitly by handling
- * ASoC core structures, which is not recommended.
- * This part will be reworked in follow-up patches.
- */
static int hda_link_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- const char *name;
unsigned int stream_tag;
+ struct sof_intel_hda_stream *hda_stream;
struct hdac_bus *bus;
struct hdac_ext_link *link;
struct hdac_stream *hstream;
- struct hdac_ext_stream *stream;
struct snd_soc_pcm_runtime *rtd;
struct hdac_ext_stream *link_dev;
- struct snd_pcm_substream pcm_substream;
-
- memset(&pcm_substream, 0, sizeof(pcm_substream));
- if (substream) {
- hstream = substream->runtime->private_data;
- bus = hstream->bus;
- rtd = snd_pcm_substream_chip(substream);
- link_dev = snd_soc_dai_get_dma_data(dai, substream);
- snd_hdac_ext_stream_decouple(bus, link_dev, false);
- name = rtd->codec_dai->component->name;
- link = snd_hdac_ext_bus_get_link(bus, name);
- if (!link)
- return -EINVAL;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- stream_tag = hdac_stream(link_dev)->stream_tag;
- snd_hdac_ext_link_clear_stream_id(link, stream_tag);
- }
+ int ret;
- link_dev->link_prepared = 0;
- } else {
- /* release all hda streams when dai link is unloaded */
- pcm_substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
- stream = snd_soc_dai_get_dma_data(dai, &pcm_substream);
- if (stream) {
- snd_soc_dai_set_dma_data(dai, &pcm_substream, NULL);
- snd_hdac_ext_stream_release(stream,
- HDAC_EXT_STREAM_TYPE_LINK);
- }
+ hstream = substream->runtime->private_data;
+ bus = hstream->bus;
+ rtd = snd_pcm_substream_chip(substream);
+ link_dev = snd_soc_dai_get_dma_data(dai, substream);
+ hda_stream = hstream_to_sof_hda_stream(link_dev);
- pcm_substream.stream = SNDRV_PCM_STREAM_CAPTURE;
- stream = snd_soc_dai_get_dma_data(dai, &pcm_substream);
- if (stream) {
- snd_soc_dai_set_dma_data(dai, &pcm_substream, NULL);
- snd_hdac_ext_stream_release(stream,
- HDAC_EXT_STREAM_TYPE_LINK);
- }
- }
+ /* free the link DMA channel in the FW */
+ ret = hda_link_config_ipc(hda_stream, dai->name, DMA_CHAN_INVALID,
+ substream->stream);
+ if (ret < 0)
+ return ret;
+
+ link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name);
+ if (!link)
+ return -EINVAL;
+
+ stream_tag = hdac_stream(link_dev)->stream_tag;
+ snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+ snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK);
+ link_dev->link_prepared = 0;
+
+ /* free the host DMA channel reserved by hostless streams */
+ hda_stream->host_reserved = 0;
return 0;
}
@@ -293,7 +385,6 @@ static const struct snd_soc_dai_ops hda_link_dai_ops = {
.hw_free = hda_link_hw_free,
.trigger = hda_link_pcm_trigger,
.prepare = hda_link_pcm_prepare,
- .get_channel_map = hda_link_dma_get_channels,
};
#endif
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index 5b73115a0b78..91de4785b6a3 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -307,23 +307,12 @@ static int hda_suspend(struct snd_sof_dev *sdev, int state)
return ret;
}
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- /* disable ppcap interrupt */
- snd_hdac_ext_bus_ppcap_int_enable(bus, false);
- snd_hdac_ext_bus_ppcap_enable(bus, false);
-
- /* disable hda bus irq and i/o */
- snd_hdac_bus_stop_chip(bus);
-#else
/* disable ppcap interrupt */
hda_dsp_ctrl_ppcap_enable(sdev, false);
hda_dsp_ctrl_ppcap_int_enable(sdev, false);
- /* disable hda bus irq */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
- SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN,
- 0);
-#endif
+ /* disable hda bus irq and streams */
+ hda_dsp_ctrl_stop_chip(sdev);
/* disable LP retention mode */
snd_sof_pci_update_bits(sdev, PCI_PGCTL,
@@ -370,10 +359,6 @@ static int hda_resume(struct snd_sof_dev *sdev)
bus->io_ops->reg_writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
hda_dsp_ctrl_misc_clock_gating(sdev, true);
-
- /* enable ppcap interrupt */
- snd_hdac_ext_bus_ppcap_enable(bus, true);
- snd_hdac_ext_bus_ppcap_int_enable(bus, true);
#else
hda_dsp_ctrl_misc_clock_gating(sdev, false);
@@ -400,11 +385,11 @@ static int hda_resume(struct snd_sof_dev *sdev)
SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN);
hda_dsp_ctrl_misc_clock_gating(sdev, true);
+#endif
/* enable ppcap interrupt */
hda_dsp_ctrl_ppcap_enable(sdev, true);
hda_dsp_ctrl_ppcap_int_enable(sdev, true);
-#endif
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
/* turn off the links that were off before suspend */
@@ -433,6 +418,19 @@ int hda_dsp_runtime_resume(struct snd_sof_dev *sdev)
return hda_resume(sdev);
}
+int hda_dsp_runtime_idle(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *hbus = sof_to_bus(sdev);
+
+ if (hbus->codec_powered) {
+ dev_dbg(sdev->dev, "some codecs still powered (%08X), not idle\n",
+ (unsigned int)hbus->codec_powered);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev, int state)
{
/* stop hda controller and power dsp off */
@@ -454,18 +452,45 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, int state)
return 0;
}
-void hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
+int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
struct sof_intel_hda_stream *hda_stream;
struct hdac_ext_stream *stream;
struct hdac_stream *s;
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ struct snd_soc_pcm_runtime *rtd;
+ struct hdac_ext_link *link;
+ const char *name;
+ int stream_tag;
+#endif
+
/* set internal flag for BE */
list_for_each_entry(s, &bus->stream_list, list) {
stream = stream_to_hdac_ext_stream(s);
hda_stream = container_of(stream, struct sof_intel_hda_stream,
hda_stream);
hda_stream->hw_params_upon_resume = 1;
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ /*
+ * clear and release stream. This should already be taken care
+ * for running streams when the SUSPEND trigger is called.
+ * But paused streams do not get suspended, so this needs to be
+ * done explicitly during suspend.
+ */
+ if (stream->link_substream) {
+ rtd = snd_pcm_substream_chip(stream->link_substream);
+ name = rtd->codec_dai->component->name;
+ link = snd_hdac_ext_bus_get_link(bus, name);
+ if (!link)
+ return -EINVAL;
+ stream_tag = hdac_stream(stream)->stream_tag;
+ snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+ snd_hdac_ext_stream_release(stream,
+ HDAC_EXT_STREAM_TYPE_LINK);
+ }
+#endif
}
+ return 0;
}
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index 51b285103394..50244b82600c 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -56,13 +56,11 @@ static void hda_dsp_ipc_dsp_done(struct snd_sof_dev *sdev)
int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
{
- u32 cmd = msg->header;
-
/* send IPC message to DSP */
sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
msg->msg_size);
snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI,
- cmd | HDA_DSP_REG_HIPCI_BUSY);
+ HDA_DSP_REG_HIPCI_BUSY);
return 0;
}
@@ -133,30 +131,23 @@ static bool hda_dsp_ipc_is_sof(uint32_t msg)
irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
- irqreturn_t ret = IRQ_NONE;
u32 hipci;
u32 hipcie;
u32 hipct;
u32 hipcte;
- u32 hipcctl;
u32 msg;
u32 msg_ext;
+ bool ipc_irq = false;
/* read IPC status */
hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
HDA_DSP_REG_HIPCIE);
hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT);
- hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL);
-
- /* reenable IPC interrupt */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
- HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC);
+ hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI);
+ hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCTE);
/* is this a reply message from the DSP */
- if (hipcie & HDA_DSP_REG_HIPCIE_DONE &&
- hipcctl & HDA_DSP_REG_HIPCCTL_DONE) {
- hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_DSP_REG_HIPCI);
+ if (hipcie & HDA_DSP_REG_HIPCIE_DONE) {
msg = hipci & HDA_DSP_REG_HIPCI_MSG_MASK;
msg_ext = hipcie & HDA_DSP_REG_HIPCIE_MSG_MASK;
@@ -198,15 +189,11 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
spin_unlock_irq(&sdev->ipc_lock);
- ret = IRQ_HANDLED;
+ ipc_irq = true;
}
/* is this a new message from DSP */
- if (hipct & HDA_DSP_REG_HIPCT_BUSY &&
- hipcctl & HDA_DSP_REG_HIPCCTL_BUSY) {
-
- hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_DSP_REG_HIPCTE);
+ if (hipct & HDA_DSP_REG_HIPCT_BUSY) {
msg = hipct & HDA_DSP_REG_HIPCT_MSG_MASK;
msg_ext = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK;
@@ -230,10 +217,22 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
hda_dsp_ipc_host_done(sdev);
- ret = IRQ_HANDLED;
+ ipc_irq = true;
}
- return ret;
+ if (!ipc_irq) {
+ /*
+ * This interrupt is not shared so no need to return IRQ_NONE.
+ */
+ dev_err_ratelimited(sdev->dev,
+ "error: nothing to do in IRQ thread\n");
+ }
+
+ /* re-enable IPC interrupt */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
+ HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC);
+
+ return IRQ_HANDLED;
}
/* is this IRQ for ADSP ? - we only care about IPC here */
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index c92006f89499..ad8d41f22e92 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -155,6 +155,7 @@ struct hdac_ext_stream *
hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction)
{
struct hdac_bus *bus = sof_to_bus(sdev);
+ struct sof_intel_hda_stream *hda_stream;
struct hdac_ext_stream *stream = NULL;
struct hdac_stream *s;
@@ -163,8 +164,15 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction)
/* get an unused stream */
list_for_each_entry(s, &bus->stream_list, list) {
if (s->direction == direction && !s->opened) {
- s->opened = true;
stream = stream_to_hdac_ext_stream(s);
+ hda_stream = container_of(stream,
+ struct sof_intel_hda_stream,
+ hda_stream);
+ /* check if the host DMA channel is reserved */
+ if (hda_stream->host_reserved)
+ continue;
+
+ s->opened = true;
break;
}
}
@@ -209,6 +217,9 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
{
struct hdac_stream *hstream = &stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
+ u32 dma_start = SOF_HDA_SD_CTL_DMA_START;
+ int ret;
+ u32 run;
/* cmd must be for audio stream */
switch (cmd) {
@@ -226,6 +237,16 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
SOF_HDA_SD_CTL_DMA_START |
SOF_HDA_CL_DMA_SD_INT_MASK);
+ ret = snd_sof_dsp_read_poll_timeout(sdev,
+ HDA_DSP_HDA_BAR,
+ sd_offset, run,
+ ((run & dma_start) == dma_start),
+ HDA_DSP_REG_POLL_INTERVAL_US,
+ HDA_DSP_STREAM_RUN_TIMEOUT);
+
+ if (ret)
+ return ret;
+
hstream->running = true;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -236,6 +257,15 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
SOF_HDA_SD_CTL_DMA_START |
SOF_HDA_CL_DMA_SD_INT_MASK, 0x0);
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_HDA_BAR,
+ sd_offset, run,
+ !(run & dma_start),
+ HDA_DSP_REG_POLL_INTERVAL_US,
+ HDA_DSP_STREAM_RUN_TIMEOUT);
+
+ if (ret)
+ return ret;
+
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset +
SOF_HDA_ADSP_REG_CL_SD_STS,
SOF_HDA_CL_DMA_SD_INT_MASK);
@@ -265,7 +295,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
struct hdac_stream *hstream = &stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
+ u32 dma_start = SOF_HDA_SD_CTL_DMA_START;
u32 val, mask;
+ u32 run;
if (!stream) {
dev_err(sdev->dev, "error: no stream available\n");
@@ -286,6 +318,16 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
SOF_HDA_CL_DMA_SD_INT_MASK |
SOF_HDA_SD_CTL_DMA_START, 0);
+
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_HDA_BAR,
+ sd_offset, run,
+ !(run & dma_start),
+ HDA_DSP_REG_POLL_INTERVAL_US,
+ HDA_DSP_STREAM_RUN_TIMEOUT);
+
+ if (ret)
+ return ret;
+
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS,
SOF_HDA_CL_DMA_SD_INT_MASK,
@@ -338,6 +380,16 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
SOF_HDA_CL_DMA_SD_INT_MASK |
SOF_HDA_SD_CTL_DMA_START, 0);
+
+ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_HDA_BAR,
+ sd_offset, run,
+ !(run & dma_start),
+ HDA_DSP_REG_POLL_INTERVAL_US,
+ HDA_DSP_STREAM_RUN_TIMEOUT);
+
+ if (ret)
+ return ret;
+
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS,
SOF_HDA_CL_DMA_SD_INT_MASK,
@@ -430,60 +482,63 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
return ret;
}
+int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *stream = substream->runtime->private_data;
+ struct hdac_ext_stream *link_dev = container_of(stream,
+ struct hdac_ext_stream,
+ hstream);
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ u32 mask = 0x1 << stream->index;
+
+ spin_lock_irq(&bus->reg_lock);
+ /* couple host and link DMA if link DMA channel is idle */
+ if (!link_dev->link_locked)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
+ SOF_HDA_REG_PP_PPCTL, mask, 0);
+ spin_unlock_irq(&bus->reg_lock);
+
+ return 0;
+}
+
irqreturn_t hda_dsp_stream_interrupt(int irq, void *context)
{
struct hdac_bus *bus = context;
- struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus);
- u32 stream_mask;
+ int ret = IRQ_WAKE_THREAD;
u32 status;
- if (!pm_runtime_active(bus->dev))
- return IRQ_NONE;
-
spin_lock(&bus->reg_lock);
status = snd_hdac_chip_readl(bus, INTSTS);
- stream_mask = GENMASK(sof_hda->stream_max - 1, 0) | AZX_INT_CTRL_EN;
+ dev_vdbg(bus->dev, "stream irq, INTSTS status: 0x%x\n", status);
- /* Not stream interrupt or register inaccessible, ignore it.*/
- if (!(status & stream_mask) || status == 0xffffffff) {
- spin_unlock(&bus->reg_lock);
- return IRQ_NONE;
- }
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- /* clear rirb int */
- status = snd_hdac_chip_readb(bus, RIRBSTS);
- if (status & RIRB_INT_MASK) {
- if (status & RIRB_INT_RESPONSE)
- snd_hdac_bus_update_rirb(bus);
- snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
- }
-#endif
+ /* Register inaccessible, ignore it.*/
+ if (status == 0xffffffff)
+ ret = IRQ_NONE;
spin_unlock(&bus->reg_lock);
- return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+ return ret;
}
-irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
+static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status)
{
- struct hdac_bus *bus = context;
struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus);
- u32 status = snd_hdac_chip_readl(bus, INTSTS);
struct hdac_stream *s;
+ bool active = false;
u32 sd_status;
- /* check streams */
list_for_each_entry(s, &bus->stream_list, list) {
- if (status & (1 << s->index) && s->opened) {
+ if (status & BIT(s->index) && s->opened) {
sd_status = snd_hdac_stream_readb(s, SD_STS);
dev_vdbg(bus->dev, "stream %d status 0x%x\n",
s->index, sd_status);
- snd_hdac_stream_writeb(s, SD_STS, SD_INT_MASK);
+ snd_hdac_stream_writeb(s, SD_STS, sd_status);
+ active = true;
if (!s->substream ||
!s->running ||
(sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0)
@@ -492,8 +547,48 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
/* Inform ALSA only in case not do that with IPC */
if (sof_hda->no_ipc_position)
snd_sof_pcm_period_elapsed(s->substream);
+ }
+ }
+
+ return active;
+}
+
+irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
+{
+ struct hdac_bus *bus = context;
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ u32 rirb_status;
+#endif
+ bool active;
+ u32 status;
+ int i;
+
+ /*
+ * Loop 10 times to handle missed interrupts caused by
+ * unsolicited responses from the codec
+ */
+ for (i = 0, active = true; i < 10 && active; i++) {
+ spin_lock_irq(&bus->reg_lock);
+
+ status = snd_hdac_chip_readl(bus, INTSTS);
+ /* check streams */
+ active = hda_dsp_stream_check(bus, status);
+
+ /* check and clear RIRB interrupt */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ if (status & AZX_INT_CTRL_EN) {
+ rirb_status = snd_hdac_chip_readb(bus, RIRBSTS);
+ if (rirb_status & RIRB_INT_MASK) {
+ active = true;
+ if (rirb_status & RIRB_INT_RESPONSE)
+ snd_hdac_bus_update_rirb(bus);
+ snd_hdac_chip_writeb(bus, RIRBSTS,
+ RIRB_INT_MASK);
+ }
}
+#endif
+ spin_unlock_irq(&bus->reg_lock);
}
return IRQ_HANDLED;
@@ -564,6 +659,8 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
if (!hda_stream)
return -ENOMEM;
+ hda_stream->sdev = sdev;
+
stream = &hda_stream->hda_stream;
stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
@@ -617,6 +714,8 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
if (!hda_stream)
return -ENOMEM;
+ hda_stream->sdev = sdev;
+
stream = &hda_stream->hda_stream;
/* we always have DSP support */
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 6811a477d0fb..7f665392618f 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -15,8 +15,10 @@
* Hardware interface for generic Intel audio DSP HDA IP
*/
-#include <linux/module.h>
#include <sound/hdaudio_ext.h>
+#include <sound/hda_register.h>
+
+#include <linux/module.h>
#include <sound/sof.h>
#include <sound/sof/xtensa.h>
#include "../ops.h"
@@ -186,12 +188,38 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
}
}
+void hda_ipc_irq_dump(struct snd_sof_dev *sdev)
+{
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ u32 adspis;
+ u32 intsts;
+ u32 intctl;
+ u32 ppsts;
+ u8 rirbsts;
+
+ /* read key IRQ stats and config registers */
+ adspis = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS);
+ intsts = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS);
+ intctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL);
+ ppsts = snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPSTS);
+ rirbsts = snd_hdac_chip_readb(bus, RIRBSTS);
+
+ dev_err(sdev->dev,
+ "error: hda irq intsts 0x%8.8x intlctl 0x%8.8x rirb %2.2x\n",
+ intsts, intctl, rirbsts);
+ dev_err(sdev->dev,
+ "error: dsp irq ppsts 0x%8.8x adspis 0x%8.8x\n",
+ ppsts, adspis);
+}
+
void hda_ipc_dump(struct snd_sof_dev *sdev)
{
u32 hipcie;
u32 hipct;
u32 hipcctl;
+ hda_ipc_irq_dump(sdev);
+
/* read IPC status */
hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE);
hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT);
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 92d45c43b4b1..d9c17146200b 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -61,6 +61,7 @@
#define SOF_HDA_PP_CAP_ID 0x3
#define SOF_HDA_REG_PP_PPCH 0x10
#define SOF_HDA_REG_PP_PPCTL 0x04
+#define SOF_HDA_REG_PP_PPSTS 0x08
#define SOF_HDA_PPCTL_PIE BIT(31)
#define SOF_HDA_PPCTL_GPROCEN BIT(30)
@@ -158,6 +159,12 @@
#define HDA_DSP_MBOX_UPLINK_OFFSET 0x81000
#define HDA_DSP_STREAM_RESET_TIMEOUT 300
+/*
+ * Timeout in us, for setting the stream RUN bit, during
+ * start/stop the stream. The timeout expires if new RUN bit
+ * value cannot be read back within the specified time.
+ */
+#define HDA_DSP_STREAM_RUN_TIMEOUT 300
#define HDA_DSP_CL_TRIGGER_TIMEOUT 300
#define HDA_DSP_SPIB_ENABLE 1
@@ -348,6 +355,7 @@
/* SSP Count of the Platform */
#define APL_SSP_COUNT 6
#define CNL_SSP_COUNT 3
+#define ICL_SSP_COUNT 6
/* SSP Registers */
#define SSP_SSC1_OFFSET 0x4
@@ -407,11 +415,16 @@ static inline struct hda_bus *sof_to_hbus(struct snd_sof_dev *s)
}
struct sof_intel_hda_stream {
+ struct snd_sof_dev *sdev;
struct hdac_ext_stream hda_stream;
struct sof_intel_stream stream;
int hw_params_upon_resume; /* set up hw_params upon resume */
+ int host_reserved; /* reserve host DMA channel */
};
+#define hstream_to_sof_hda_stream(hstream) \
+ container_of(hstream, struct sof_intel_hda_stream, hda_stream)
+
#define bus_to_sof_hda(bus) \
container_of(bus, struct sof_intel_hda_dev, hbus.core)
@@ -444,10 +457,12 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, int state);
int hda_dsp_resume(struct snd_sof_dev *sdev);
int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev, int state);
int hda_dsp_runtime_resume(struct snd_sof_dev *sdev);
-void hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev);
+int hda_dsp_runtime_idle(struct snd_sof_dev *sdev);
+int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev);
void hda_dsp_dump_skl(struct snd_sof_dev *sdev, u32 flags);
void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags);
void hda_ipc_dump(struct snd_sof_dev *sdev);
+void hda_ipc_irq_dump(struct snd_sof_dev *sdev);
/*
* DSP PCM Operations.
@@ -460,6 +475,8 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct sof_ipc_stream_params *ipc_params);
+int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream);
int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream, int cmd);
snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
@@ -528,7 +545,7 @@ int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev, bool reset);
void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable);
int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable);
int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset);
-
+void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev);
/*
* HDA bus operations.
*/
@@ -579,5 +596,6 @@ extern const struct snd_sof_dsp_ops sof_skl_ops;
extern const struct sof_intel_dsp_desc apl_chip_info;
extern const struct sof_intel_dsp_desc cnl_chip_info;
extern const struct sof_intel_dsp_desc skl_chip_info;
+extern const struct sof_intel_dsp_desc icl_chip_info;
#endif
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c
index 2414640a32d1..20dfca9c93b7 100644
--- a/sound/soc/sof/ipc.c
+++ b/sound/soc/sof/ipc.c
@@ -175,6 +175,15 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
break;
case SOF_IPC_GLB_TRACE_MSG:
str = "GLB_TRACE_MSG"; break;
+ case SOF_IPC_GLB_TEST_MSG:
+ str = "GLB_TEST_MSG";
+ switch (type) {
+ case SOF_IPC_TEST_IPC_FLOOD:
+ str2 = "IPC_FLOOD"; break;
+ default:
+ str2 = "unknown type"; break;
+ }
+ break;
default:
str = "unknown GLB command"; break;
}
@@ -187,7 +196,8 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
#else
static inline void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
{
- dev_dbg(dev, "%s: 0x%x\n", text, cmd);
+ if ((cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_TRACE_MSG)
+ dev_dbg(dev, "%s: 0x%x\n", text, cmd);
}
#endif
@@ -770,11 +780,11 @@ int snd_sof_ipc_valid(struct snd_sof_dev *sdev)
" lock debug: %s\n"
" lock vdebug: %s\n",
v->build, v->date, v->time,
- ready->flags & SOF_IPC_INFO_GDB ?
+ (ready->flags & SOF_IPC_INFO_GDB) ?
"enabled" : "disabled",
- ready->flags & SOF_IPC_INFO_LOCKS ?
+ (ready->flags & SOF_IPC_INFO_LOCKS) ?
"enabled" : "disabled",
- ready->flags & SOF_IPC_INFO_LOCKSV ?
+ (ready->flags & SOF_IPC_INFO_LOCKSV) ?
"enabled" : "disabled");
}
diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c
index 628fae552442..952a19091c58 100644
--- a/sound/soc/sof/loader.c
+++ b/sound/soc/sof/loader.c
@@ -19,15 +19,13 @@ static int get_ext_windows(struct snd_sof_dev *sdev,
{
struct sof_ipc_window *w =
container_of(ext_hdr, struct sof_ipc_window, ext_hdr);
- size_t size;
if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
return -EINVAL;
- size = sizeof(*w) + sizeof(struct sof_ipc_window_elem) * w->num_windows;
-
/* keep a local copy of the data */
- sdev->info_window = kmemdup(w, size, GFP_KERNEL);
+ sdev->info_window = kmemdup(w, struct_size(w, window, w->num_windows),
+ GFP_KERNEL);
if (!sdev->info_window)
return -ENOMEM;
@@ -337,11 +335,11 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
init_waitqueue_head(&sdev->boot_wait);
sdev->boot_complete = false;
- /* create fw_version debugfs to store boot version info */
+ /* create read-only fw_version debugfs to store boot version info */
if (sdev->first_boot) {
ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version,
sizeof(sdev->fw_version),
- "fw_version");
+ "fw_version", 0444);
/* errors are only due to memory allocation, not debugfs */
if (ret < 0) {
dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c
index f84b4344dcc3..3d128e5a132c 100644
--- a/sound/soc/sof/nocodec.c
+++ b/sound/soc/sof/nocodec.c
@@ -21,6 +21,7 @@ static int sof_nocodec_bes_setup(struct device *dev,
struct snd_soc_dai_link *links,
int link_num, struct snd_soc_card *card)
{
+ struct snd_soc_dai_link_component *dlc;
int i;
if (!ops || !links || !card)
@@ -28,17 +29,29 @@ static int sof_nocodec_bes_setup(struct device *dev,
/* set up BE dai_links */
for (i = 0; i < link_num; i++) {
+ dlc = devm_kzalloc(dev, 3 * sizeof(*dlc), GFP_KERNEL);
+ if (!dlc)
+ return -ENOMEM;
+
links[i].name = devm_kasprintf(dev, GFP_KERNEL,
"NoCodec-%d", i);
if (!links[i].name)
return -ENOMEM;
+ links[i].cpus = &dlc[0];
+ links[i].codecs = &dlc[1];
+ links[i].platforms = &dlc[2];
+
+ links[i].num_cpus = 1;
+ links[i].num_codecs = 1;
+ links[i].num_platforms = 1;
+
links[i].id = i;
links[i].no_pcm = 1;
- links[i].cpu_dai_name = ops->drv[i].name;
- links[i].platform_name = dev_name(dev);
- links[i].codec_dai_name = "snd-soc-dummy-dai";
- links[i].codec_name = "snd-soc-dummy";
+ links[i].cpus->dai_name = ops->drv[i].name;
+ links[i].platforms->name = dev_name(dev);
+ links[i].codecs->dai_name = "snd-soc-dummy-dai";
+ links[i].codecs->name = "snd-soc-dummy";
links[i].dpcm_playback = 1;
links[i].dpcm_capture = 1;
}
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index 80fc3b374c2b..b9bdf45889da 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -134,10 +134,19 @@ static inline int snd_sof_dsp_runtime_suspend(struct snd_sof_dev *sdev,
return 0;
}
-static inline void snd_sof_dsp_hw_params_upon_resume(struct snd_sof_dev *sdev)
+static inline int snd_sof_dsp_runtime_idle(struct snd_sof_dev *sdev)
+{
+ if (sof_ops(sdev)->runtime_idle)
+ return sof_ops(sdev)->runtime_idle(sdev);
+
+ return 0;
+}
+
+static inline int snd_sof_dsp_hw_params_upon_resume(struct snd_sof_dev *sdev)
{
if (sof_ops(sdev)->set_hw_params_upon_resume)
- sof_ops(sdev)->set_hw_params_upon_resume(sdev);
+ return sof_ops(sdev)->set_hw_params_upon_resume(sdev);
+ return 0;
}
static inline int snd_sof_dsp_set_clk(struct snd_sof_dev *sdev, u32 freq)
@@ -286,6 +295,17 @@ snd_sof_pcm_platform_hw_params(struct snd_sof_dev *sdev,
return 0;
}
+/* host stream hw free */
+static inline int
+snd_sof_pcm_platform_hw_free(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream)
+{
+ if (sof_ops(sdev) && sof_ops(sdev)->pcm_hw_free)
+ return sof_ops(sdev)->pcm_hw_free(sdev, substream);
+
+ return 0;
+}
+
/* host stream trigger */
static inline int
snd_sof_pcm_platform_trigger(struct snd_sof_dev *sdev,
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index dace6c4cd91e..334e9d59b1ba 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -251,6 +251,13 @@ static int sof_pcm_hw_free(struct snd_pcm_substream *substream)
cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_sof_pcm_platform_hw_free(sdev, substream);
+ if (ret < 0)
+ dev_err(sdev->dev, "error: platform hw free failed\n");
+
return ret;
}
@@ -416,7 +423,6 @@ static int sof_pcm_open(struct snd_pcm_substream *substream)
struct snd_sof_pcm *spcm;
struct snd_soc_tplg_stream_caps *caps;
int ret;
- int err;
/* nothing to do for BE */
if (rtd->dai_link->no_pcm)
@@ -434,14 +440,6 @@ static int sof_pcm_open(struct snd_pcm_substream *substream)
caps = &spcm->pcm.caps[substream->stream];
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0) {
- dev_err(sdev->dev, "error: pcm open failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- return ret;
- }
-
/* set any runtime constraints based on topology */
snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
@@ -485,17 +483,8 @@ static int sof_pcm_open(struct snd_pcm_substream *substream)
spcm->stream[substream->stream].substream = substream;
ret = snd_sof_pcm_platform_open(sdev, substream);
- if (ret < 0) {
- dev_err(sdev->dev, "error: pcm open failed %d\n",
- ret);
-
- pm_runtime_mark_last_busy(sdev->dev);
-
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err(sdev->dev, "error: pcm close failed to idle %d\n",
- err);
- }
+ if (ret < 0)
+ dev_err(sdev->dev, "error: pcm open failed %d\n", ret);
return ret;
}
@@ -530,13 +519,6 @@ static int sof_pcm_close(struct snd_pcm_substream *substream)
*/
}
- pm_runtime_mark_last_busy(sdev->dev);
-
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err(sdev->dev, "error: pcm close failed to idle %d\n",
- err);
-
return 0;
}
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 8ef1d51025d8..278abfd10490 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -153,6 +153,15 @@ static int sof_restore_pipelines(struct snd_sof_dev *sdev)
continue;
}
+ /*
+ * The link DMA channel would be invalidated for running
+ * streams but not for streams that were in the PAUSED
+ * state during suspend. So invalidate it here before setting
+ * the dai config in the DSP.
+ */
+ if (config->type == SOF_DAI_INTEL_HDA)
+ config->hda.link_dma_ch = DMA_CHAN_INVALID;
+
ret = sof_ipc_tx_message(sdev->ipc,
config->hdr.cmd, config,
config->hdr.size,
@@ -204,7 +213,7 @@ static int sof_send_pm_ipc(struct snd_sof_dev *sdev, int cmd)
sizeof(pm_ctx), &reply, sizeof(reply));
}
-static void sof_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
+static int sof_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
{
struct snd_pcm_substream *substream;
struct snd_sof_pcm *spcm;
@@ -229,7 +238,7 @@ static void sof_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
}
/* set internal flag for BE */
- snd_sof_dsp_hw_params_upon_resume(sdev);
+ return snd_sof_dsp_hw_params_upon_resume(sdev);
}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
@@ -333,8 +342,15 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
snd_sof_release_trace(sdev);
/* set restore_stream for all streams during system suspend */
- if (!runtime_suspend)
- sof_set_hw_params_upon_resume(sdev);
+ if (!runtime_suspend) {
+ ret = sof_set_hw_params_upon_resume(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: setting hw_params flag during suspend %d\n",
+ ret);
+ return ret;
+ }
+ }
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
/* cache debugfs contents during runtime suspend */
@@ -343,11 +359,20 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
#endif
/* notify DSP of upcoming power down */
ret = sof_send_pm_ipc(sdev, SOF_IPC_PM_CTX_SAVE);
- if (ret < 0) {
+ if (ret == -EBUSY || ret == -EAGAIN) {
+ /*
+ * runtime PM has logic to handle -EBUSY/-EAGAIN so
+ * pass these errors up
+ */
dev_err(sdev->dev,
"error: ctx_save ipc error during suspend %d\n",
ret);
return ret;
+ } else if (ret < 0) {
+ /* FW in unexpected state, continue to power down */
+ dev_warn(sdev->dev,
+ "ctx_save ipc error %d, proceeding with suspend\n",
+ ret);
}
/* power down all DSP cores */
@@ -369,6 +394,14 @@ int snd_sof_runtime_suspend(struct device *dev)
}
EXPORT_SYMBOL(snd_sof_runtime_suspend);
+int snd_sof_runtime_idle(struct device *dev)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+
+ return snd_sof_dsp_runtime_idle(sdev);
+}
+EXPORT_SYMBOL(snd_sof_runtime_idle);
+
int snd_sof_runtime_resume(struct device *dev)
{
return sof_resume(dev, true);
diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c
index e9cf69874b5b..ea7b8b895412 100644
--- a/sound/soc/sof/sof-acpi-dev.c
+++ b/sound/soc/sof/sof-acpi-dev.c
@@ -15,10 +15,7 @@
#include <sound/soc-acpi.h>
#include <sound/soc-acpi-intel-match.h>
#include <sound/sof.h>
-#ifdef CONFIG_X86
-#include <asm/iosf_mbi.h>
-#endif
-
+#include "../intel/common/soc-intel-quirks.h"
#include "ops.h"
/* platform specific devices */
@@ -99,56 +96,6 @@ static const struct sof_dev_desc sof_acpi_baytrail_desc = {
.arch_ops = &sof_xtensa_arch_ops
};
-#ifdef CONFIG_X86 /* TODO: move this to common helper */
-
-static bool is_byt_cr(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- int status;
-
- if (iosf_mbi_available()) {
- u32 bios_status;
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, /* 0x04 PUNIT */
- MBI_REG_READ, /* 0x10 */
- 0x006, /* BIOS_CONFIG */
- &bios_status);
-
- if (status) {
- dev_err(dev, "could not read PUNIT BIOS_CONFIG\n");
- } else {
- /* bits 26:27 mirror PMIC options */
- bios_status = (bios_status >> 26) & 3;
-
- if (bios_status == 1 || bios_status == 3) {
- dev_info(dev, "Detected Baytrail-CR platform\n");
- return true;
- }
-
- dev_info(dev, "BYT-CR not detected\n");
- }
- } else {
- dev_info(dev, "IOSF_MBI not available, no BYT-CR detection\n");
- }
-
- if (platform_get_resource(pdev, IORESOURCE_IRQ, 5) == NULL) {
- /*
- * Some devices detected as BYT-T have only a single IRQ listed,
- * causing platform_get_irq with index 5 to return -ENXIO.
- * The correct IRQ in this case is at index 0, as on BYT-CR.
- */
- dev_info(dev, "Falling back to Baytrail-CR platform\n");
- return true;
- }
-
- return false;
-}
-#else
-static int is_byt_cr(struct platform_device *pdev)
-{
- return 0;
-}
-#endif
-
static const struct sof_dev_desc sof_acpi_cherrytrail_desc = {
.machines = snd_soc_acpi_intel_cherrytrail_machines,
.resindex_lpe_base = 0,
@@ -169,7 +116,7 @@ static const struct sof_dev_desc sof_acpi_cherrytrail_desc = {
static const struct dev_pm_ops sof_acpi_pm = {
SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume)
SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume,
- NULL)
+ snd_sof_runtime_idle)
};
static void sof_acpi_probe_complete(struct device *dev)
@@ -200,7 +147,7 @@ static int sof_acpi_probe(struct platform_device *pdev)
return -ENODEV;
#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL)
- if (desc == &sof_acpi_baytrail_desc && is_byt_cr(pdev))
+ if (desc == &sof_acpi_baytrail_desc && soc_intel_is_byt_cr(pdev))
desc = &sof_acpi_baytrailcr_desc;
#endif
diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c
index b778dffb2d25..65d1bac4c6b8 100644
--- a/sound/soc/sof/sof-pci-dev.c
+++ b/sound/soc/sof/sof-pci-dev.c
@@ -129,6 +129,26 @@ static const struct sof_dev_desc cfl_desc = {
};
#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_LP) || \
+ IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_H)
+
+static const struct sof_dev_desc cml_desc = {
+ .machines = snd_soc_acpi_intel_cnl_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .resindex_dma_base = -1,
+ .chip_info = &cnl_chip_info,
+ .default_fw_path = "intel/sof",
+ .default_tplg_path = "intel/sof-tplg",
+ .nocodec_fw_filename = "sof-cnl.ri",
+ .nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
+ .ops = &sof_cnl_ops,
+ .arch_ops = &sof_xtensa_arch_ops
+};
+#endif
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE)
static const struct sof_dev_desc icl_desc = {
.machines = snd_soc_acpi_intel_icl_machines,
@@ -137,7 +157,7 @@ static const struct sof_dev_desc icl_desc = {
.resindex_imr_base = -1,
.irqindex_host_ipc = -1,
.resindex_dma_base = -1,
- .chip_info = &cnl_chip_info,
+ .chip_info = &icl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
.nocodec_fw_filename = "sof-icl.ri",
@@ -186,7 +206,7 @@ static const struct sof_dev_desc kbl_desc = {
static const struct dev_pm_ops sof_pci_pm = {
SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume)
SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume,
- NULL)
+ snd_sof_runtime_idle)
};
static void sof_pci_probe_complete(struct device *dev)
@@ -354,6 +374,14 @@ static const struct pci_device_id sof_pci_ids[] = {
{ PCI_DEVICE(0x8086, 0x34C8),
.driver_data = (unsigned long)&icl_desc},
#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_LP)
+ { PCI_DEVICE(0x8086, 0x02c8),
+ .driver_data = (unsigned long)&cml_desc},
+#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_H)
+ { PCI_DEVICE(0x8086, 0x06c8),
+ .driver_data = (unsigned long)&cml_desc},
+#endif
{ 0, }
};
MODULE_DEVICE_TABLE(pci, sof_pci_ids);
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 1e85d6f9c5c3..b8c0b2a22684 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -56,6 +56,12 @@
#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))
+
+#define DMA_CHAN_INVALID 0xFFFFFFFF
+
struct snd_sof_dev;
struct snd_sof_ipc_msg;
struct snd_sof_ipc;
@@ -137,6 +143,10 @@ struct snd_sof_dsp_ops {
struct snd_pcm_hw_params *params,
struct sof_ipc_stream_params *ipc_params); /* optional */
+ /* host stream hw_free */
+ int (*pcm_hw_free)(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream); /* optional */
+
/* host stream trigger */
int (*pcm_trigger)(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
@@ -166,7 +176,8 @@ struct snd_sof_dsp_ops {
int (*runtime_suspend)(struct snd_sof_dev *sof_dev,
int state); /* optional */
int (*runtime_resume)(struct snd_sof_dev *sof_dev); /* optional */
- void (*set_hw_params_upon_resume)(struct snd_sof_dev *sdev); /* optional */
+ int (*runtime_idle)(struct snd_sof_dev *sof_dev); /* optional */
+ int (*set_hw_params_upon_resume)(struct snd_sof_dev *sdev); /* optional */
/* DSP clocking */
int (*set_clk)(struct snd_sof_dev *sof_dev, u32 freq); /* optional */
@@ -226,7 +237,7 @@ struct snd_sof_dfsentry {
* or if it is accessible only when the DSP is in D0.
*/
enum sof_debugfs_access_type access_type;
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
+#if ENABLE_DEBUGFS_CACHEBUF
char *cache_buf; /* buffer to cache the contents of debugfs memory */
#endif
struct snd_sof_dev *sdev;
@@ -293,6 +304,8 @@ struct snd_sof_pcm {
struct snd_sof_control {
struct snd_sof_dev *sdev;
int comp_id;
+ int min_volume_step; /* min volume step for volume_table */
+ int max_volume_step; /* max volume step for volume_table */
int num_channels;
u32 readback_offset; /* offset to mmaped data if used */
struct sof_ipc_ctrl_data *control_data;
@@ -331,6 +344,7 @@ struct snd_sof_route {
struct snd_sof_dai {
struct snd_sof_dev *sdev;
const char *name;
+ const char *cpu_dai_name;
struct sof_ipc_comp_dai comp_dai;
struct sof_ipc_dai_config *dai_config;
@@ -417,6 +431,8 @@ struct snd_sof_dev {
u32 host_offset;
u32 dtrace_is_enabled;
u32 dtrace_error;
+ u32 dtrace_draining;
+
u32 msi_enabled;
void *private; /* core does not touch this */
@@ -431,6 +447,7 @@ int snd_sof_device_remove(struct device *dev);
int snd_sof_runtime_suspend(struct device *dev);
int snd_sof_runtime_resume(struct device *dev);
+int snd_sof_runtime_idle(struct device *dev);
int snd_sof_resume(struct device *dev);
int snd_sof_suspend(struct device *dev);
@@ -544,7 +561,7 @@ int snd_sof_debugfs_io_item(struct snd_sof_dev *sdev,
enum sof_debugfs_access_type access_type);
int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev,
void *base, size_t size,
- const char *name);
+ const char *name, mode_t mode);
int snd_sof_trace_update_pos(struct snd_sof_dev *sdev,
struct sof_ipc_dma_trace_posn *posn);
void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev);
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index c88afa872a58..432ae343f960 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -394,6 +394,8 @@ static const struct sof_process_types sof_process[] = {
{"KEYWORD_DETECT", SOF_PROCESS_KEYWORD_DETECT, SOF_COMP_KEYWORD_DETECT},
{"KPB", SOF_PROCESS_KPB, SOF_COMP_KPB},
{"CHAN_SELECTOR", SOF_PROCESS_CHAN_SELECTOR, SOF_COMP_SELECTOR},
+ {"MUX", SOF_PROCESS_MUX, SOF_COMP_MUX},
+ {"DEMUX", SOF_PROCESS_DEMUX, SOF_COMP_DEMUX},
};
static enum sof_ipc_process_type find_process(const char *name)
@@ -442,14 +444,15 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
return -EINVAL;
/* init the volume get/put data */
- scontrol->size = sizeof(struct sof_ipc_ctrl_data) +
- sizeof(struct sof_ipc_ctrl_value_chan) *
- le32_to_cpu(mc->num_channels);
+ scontrol->size = struct_size(scontrol->control_data, chanv,
+ le32_to_cpu(mc->num_channels));
scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL);
if (!scontrol->control_data)
return -ENOMEM;
scontrol->comp_id = sdev->next_comp_id;
+ scontrol->min_volume_step = le32_to_cpu(mc->min);
+ scontrol->max_volume_step = le32_to_cpu(mc->max);
scontrol->num_channels = le32_to_cpu(mc->num_channels);
/* set cmd for mixer control */
@@ -501,9 +504,8 @@ static int sof_control_load_enum(struct snd_soc_component *scomp,
return -EINVAL;
/* init the enum get/put data */
- scontrol->size = sizeof(struct sof_ipc_ctrl_data) +
- sizeof(struct sof_ipc_ctrl_value_chan) *
- le32_to_cpu(ec->num_channels);
+ scontrol->size = struct_size(scontrol->control_data, chanv,
+ le32_to_cpu(ec->num_channels));
scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL);
if (!scontrol->control_data)
return -ENOMEM;
@@ -777,6 +779,10 @@ static const struct sof_topology_token dmic_tokens[] = {
{SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH,
SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
offsetof(struct sof_ipc_dai_dmic_params, fifo_bits), 0},
+ {SOF_TKN_INTEL_DMIC_UNMUTE_RAMP_TIME_MS,
+ SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_dmic_params, unmute_ramp_time), 0},
+
};
/*
@@ -1550,6 +1556,9 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_volume *volume;
+ struct snd_sof_control *scontrol;
+ int min_step;
+ int max_step;
int ret;
volume = kzalloc(sizeof(*volume), GFP_KERNEL);
@@ -1592,6 +1601,17 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index,
swidget->private = volume;
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
+ if (scontrol->comp_id == swidget->comp_id) {
+ min_step = scontrol->min_volume_step;
+ max_step = scontrol->max_volume_step;
+ volume->min_value = scontrol->volume_table[min_step];
+ volume->max_value = scontrol->volume_table[max_step];
+ volume->channels = scontrol->num_channels;
+ break;
+ }
+ }
+
ret = sof_ipc_tx_message(sdev->ipc, volume->comp.hdr.cmd, volume,
sizeof(*volume), r, sizeof(*r));
if (ret >= 0)
@@ -2340,6 +2360,9 @@ static int sof_set_dai_config(struct snd_sof_dev *sdev, u32 size,
if (!dai->dai_config)
return -ENOMEM;
+ /* set cpu_dai_name */
+ dai->cpu_dai_name = link->cpus->dai_name;
+
found = 1;
}
}
@@ -2568,9 +2591,7 @@ err:
*/
static int sof_link_hda_process(struct snd_sof_dev *sdev,
struct snd_soc_dai_link *link,
- struct sof_ipc_dai_config *config,
- int tx_slot,
- int rx_slot)
+ struct sof_ipc_dai_config *config)
{
struct sof_ipc_reply reply;
u32 size = sizeof(*config);
@@ -2583,27 +2604,18 @@ static int sof_link_hda_process(struct snd_sof_dev *sdev,
continue;
if (strcmp(link->name, sof_dai->name) == 0) {
- if (sof_dai->comp_dai.direction ==
- SNDRV_PCM_STREAM_PLAYBACK) {
- if (!link->dpcm_playback)
- return -EINVAL;
-
- config->hda.link_dma_ch = tx_slot;
- } else {
- if (!link->dpcm_capture)
- return -EINVAL;
-
- config->hda.link_dma_ch = rx_slot;
- }
-
config->dai_index = sof_dai->comp_dai.dai_index;
found = 1;
+ config->hda.link_dma_ch = DMA_CHAN_INVALID;
+
/* save config in dai component */
sof_dai->dai_config = kmemdup(config, size, GFP_KERNEL);
if (!sof_dai->dai_config)
return -ENOMEM;
+ sof_dai->cpu_dai_name = link->cpus->dai_name;
+
/* send message to DSP */
ret = sof_ipc_tx_message(sdev->ipc,
config->hdr.cmd, config, size,
@@ -2639,18 +2651,12 @@ static int sof_link_hda_load(struct snd_soc_component *scomp, int index,
struct sof_ipc_dai_config *config)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_soc_dai_link_component dai_component;
struct snd_soc_tplg_private *private = &cfg->priv;
struct snd_soc_dai *dai;
u32 size = sizeof(*config);
- u32 tx_num = 0;
- u32 tx_slot = 0;
- u32 rx_num = 0;
- u32 rx_slot = 0;
int ret;
/* init IPC */
- memset(&dai_component, 0, sizeof(dai_component));
memset(&config->hda, 0, sizeof(struct sof_ipc_dai_hda_params));
config->hdr.size = size;
@@ -2664,30 +2670,14 @@ static int sof_link_hda_load(struct snd_soc_component *scomp, int index,
return ret;
}
- dai_component.dai_name = link->cpu_dai_name;
- dai = snd_soc_find_dai(&dai_component);
+ dai = snd_soc_find_dai(link->cpus);
if (!dai) {
dev_err(sdev->dev, "error: failed to find dai %s in %s",
- dai_component.dai_name, __func__);
+ link->cpus->dai_name, __func__);
return -EINVAL;
}
- if (link->dpcm_playback)
- tx_num = 1;
-
- if (link->dpcm_capture)
- rx_num = 1;
-
- ret = snd_soc_dai_get_channel_map(dai, &tx_num, &tx_slot,
- &rx_num, &rx_slot);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to get dma channel for HDA%d\n",
- config->dai_index);
-
- return ret;
- }
-
- ret = sof_link_hda_process(sdev, link, config, tx_slot, rx_slot);
+ ret = sof_link_hda_process(sdev, link, config);
if (ret < 0)
dev_err(sdev->dev, "error: failed to process hda dai link %s",
link->name);
@@ -2708,7 +2698,11 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
int ret;
int i = 0;
- link->platform_name = dev_name(sdev->dev);
+ if (!link->platforms) {
+ dev_err(sdev->dev, "error: no platforms\n");
+ return -EINVAL;
+ }
+ link->platforms->name = dev_name(sdev->dev);
/*
* Set nonatomic property for FE dai links as their trigger action
@@ -2801,30 +2795,16 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
static int sof_link_hda_unload(struct snd_sof_dev *sdev,
struct snd_soc_dai_link *link)
{
- struct snd_soc_dai_link_component dai_component;
struct snd_soc_dai *dai;
int ret = 0;
- memset(&dai_component, 0, sizeof(dai_component));
- dai_component.dai_name = link->cpu_dai_name;
- dai = snd_soc_find_dai(&dai_component);
+ dai = snd_soc_find_dai(link->cpus);
if (!dai) {
dev_err(sdev->dev, "error: failed to find dai %s in %s",
- dai_component.dai_name, __func__);
+ link->cpus->dai_name, __func__);
return -EINVAL;
}
- /*
- * FIXME: this call to hw_free is mainly to release the link DMA ID.
- * This is abusing the API and handling SOC internals is not
- * recommended. This part will be reworked.
- */
- if (dai->driver->ops->hw_free)
- ret = dai->driver->ops->hw_free(NULL, dai);
- if (ret < 0)
- dev_err(sdev->dev, "error: failed to free hda resource for %s\n",
- link->name);
-
return ret;
}
@@ -2998,6 +2978,49 @@ err:
return ret;
}
+/* Function to set the initial value of SOF kcontrols.
+ * The value will be stored in scontrol->control_data
+ */
+static int snd_sof_cache_kcontrol_val(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_control *scontrol = NULL;
+ int ipc_cmd, ctrl_type;
+ int ret = 0;
+
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
+
+ /* notify DSP of kcontrol values */
+ switch (scontrol->cmd) {
+ case SOF_CTRL_CMD_VOLUME:
+ case SOF_CTRL_CMD_ENUM:
+ case SOF_CTRL_CMD_SWITCH:
+ ipc_cmd = SOF_IPC_COMP_GET_VALUE;
+ ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_GET;
+ break;
+ case SOF_CTRL_CMD_BINARY:
+ ipc_cmd = SOF_IPC_COMP_GET_DATA;
+ ctrl_type = SOF_CTRL_TYPE_DATA_GET;
+ break;
+ default:
+ dev_err(sdev->dev,
+ "error: Invalid scontrol->cmd: %d\n",
+ scontrol->cmd);
+ return -EINVAL;
+ }
+ ret = snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
+ ipc_cmd, ctrl_type,
+ scontrol->cmd,
+ false);
+ if (ret < 0) {
+ dev_warn(sdev->dev,
+ "error: kcontrol value get for widget: %d\n",
+ scontrol->comp_id);
+ }
+ }
+
+ return ret;
+}
+
int snd_sof_complete_pipeline(struct snd_sof_dev *sdev,
struct snd_sof_widget *swidget)
{
@@ -3041,6 +3064,11 @@ static void sof_complete(struct snd_soc_component *scomp)
break;
}
}
+ /*
+ * cache initial values of SOF kcontrols by reading DSP value over
+ * IPC. It may be overwritten by alsa-mixer after booting up
+ */
+ snd_sof_cache_kcontrol_val(sdev);
}
/* manifest - optional to inform component of manifest */
diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c
index d588e4b70fad..befed975161c 100644
--- a/sound/soc/sof/trace.c
+++ b/sound/soc/sof/trace.c
@@ -13,10 +13,9 @@
#include "sof-priv.h"
#include "ops.h"
-static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev,
- loff_t pos, size_t buffer_size)
+static size_t sof_trace_avail(struct snd_sof_dev *sdev,
+ loff_t pos, size_t buffer_size)
{
- wait_queue_entry_t wait;
loff_t host_offset = READ_ONCE(sdev->host_offset);
/*
@@ -31,6 +30,28 @@ static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev,
if (host_offset > pos)
return host_offset - pos;
+ return 0;
+}
+
+static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev,
+ loff_t pos, size_t buffer_size)
+{
+ wait_queue_entry_t wait;
+ size_t ret = sof_trace_avail(sdev, pos, buffer_size);
+
+ /* data immediately available */
+ if (ret)
+ return ret;
+
+ if (!sdev->dtrace_is_enabled && sdev->dtrace_draining) {
+ /*
+ * tracing has ended and all traces have been
+ * read by client, return EOF
+ */
+ sdev->dtrace_draining = false;
+ return 0;
+ }
+
/* wait for available trace data from FW */
init_waitqueue_entry(&wait, current);
set_current_state(TASK_INTERRUPTIBLE);
@@ -42,12 +63,7 @@ static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev,
}
remove_wait_queue(&sdev->trace_sleep, &wait);
- /* return bytes available for copy */
- host_offset = READ_ONCE(sdev->host_offset);
- if (host_offset < pos)
- return buffer_size - pos;
-
- return host_offset - pos;
+ return sof_trace_avail(sdev, pos, buffer_size);
}
static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer,
@@ -97,10 +113,23 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer,
return count;
}
+static int sof_dfsentry_trace_release(struct inode *inode, struct file *file)
+{
+ struct snd_sof_dfsentry *dfse = inode->i_private;
+ struct snd_sof_dev *sdev = dfse->sdev;
+
+ /* avoid duplicate traces at next open */
+ if (!sdev->dtrace_is_enabled)
+ sdev->host_offset = 0;
+
+ return 0;
+}
+
static const struct file_operations sof_dfs_trace_fops = {
.open = simple_open,
.read = sof_dfsentry_trace_read,
.llseek = default_llseek,
+ .release = sof_dfsentry_trace_release,
};
static int trace_debugfs_create(struct snd_sof_dev *sdev)
@@ -132,7 +161,9 @@ static int trace_debugfs_create(struct snd_sof_dev *sdev)
int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev)
{
- struct sof_ipc_dma_trace_params params;
+ struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
+ struct sof_ipc_fw_version *v = &ready->version;
+ struct sof_ipc_dma_trace_params_ext params;
struct sof_ipc_reply ipc_reply;
int ret;
@@ -140,14 +171,23 @@ int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev)
return -EINVAL;
/* set IPC parameters */
- params.hdr.size = sizeof(params);
- params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_PARAMS;
+ params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG;
+ /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */
+ if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) {
+ params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext);
+ params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT;
+ params.timestamp_ns = ktime_get(); /* in nanosecond */
+ } else {
+ params.hdr.size = sizeof(struct sof_ipc_dma_trace_params);
+ params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS;
+ }
params.buffer.phy_addr = sdev->dmatp.addr;
params.buffer.size = sdev->dmatb.bytes;
params.buffer.pages = sdev->dma_trace_pages;
params.stream_tag = 0;
sdev->host_offset = 0;
+ sdev->dtrace_draining = false;
ret = snd_sof_dma_trace_init(sdev, &params.stream_tag);
if (ret < 0) {
@@ -284,6 +324,8 @@ void snd_sof_release_trace(struct snd_sof_dev *sdev)
"error: fail in snd_sof_dma_trace_release %d\n", ret);
sdev->dtrace_is_enabled = false;
+ sdev->dtrace_draining = true;
+ wake_up(&sdev->trace_sleep);
}
EXPORT_SYMBOL(snd_sof_release_trace);
diff --git a/sound/soc/sof/xtensa/Kconfig b/sound/soc/sof/xtensa/Kconfig
index 8a9343b85146..defd6d3dc03e 100644
--- a/sound/soc/sof/xtensa/Kconfig
+++ b/sound/soc/sof/xtensa/Kconfig
@@ -1,2 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_SOC_SOF_XTENSA
tristate