Package: linux-source-5.2
Version: 5.2.9-2~bpo10+1
Severity: important
Tags: patch

Dear Maintainer,

   * What led up to the situation?
The Lenovo X1 Carbon Gen 7 laptops use the Whiskeylake CPU and when testing
with Debian we found the audio was not working.
Whiskeylake requires the new SOF audio driver to be enabled - but some fixes
also need to be backported to prevent firmware load issues seen during suspend
and resume
Note we are using the buster-backports 5.2 kernel as SOF audio driver support
is not available in the earlier kernels.

   * What exactly did you do (or not do) that was effective (or
     ineffective)?
Enabled SOF audio driver kernel configs and rebuilt the kernel. Tested and
debugged the suspend/resume issue and identified the required commits from the
working 5.3 kernel.org kernel that are needed to fix the issue.

   * What was the outcome of this action?
Audio is working correctly. The driver appears stable

   * What outcome did you expect instead?
NA

Notes:
The patch I'm uploading is a combo of the following applied to 5.2:
https://github.com/thesofproject/linux/commit/c760776089f147c4d28875619f3a917c02d42307
https://github.com/thesofproject/linux/commit/bb1ea3b31c28a131a5f5a50dd325198645526b19
https://github.com/thesofproject/linux/commit/64632de9140e52b72781fefe542314db7cd29d8c
https://github.com/thesofproject/linux/commit/bf705eaa7ce07f9c132f8e367fc2fc46b7842528
https://github.com/thesofproject/linux/commit/38d0e9fc227c7876d09754863adc88aeca6dd205
https://github.com/thesofproject/linux/commit/f5dbba9fee801f4678a50d92c785f7f24d4ee2c6
https://github.com/thesofproject/linux/commit/7623ae793c28cc0928c5d1292542dbb92fc2e9e2

The kconfig snipped I'm also uploading is based on config settings from the SOF
team

This is my first bug raised against Debian - Lenovo are actively focussing on
getting Debian working on our systems so I'm hoping to get a lot more involved.
Please do let me know if I've made any mistakes or things I can improve on for
future bugs.

Thanks
Mrk Pearson



-- System Information:
Debian Release: 10.1
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable')
Architecture: amd64 (x86_64)

Kernel: Linux 5.2.9 (SMP w/8 CPU cores)
Kernel taint flags: TAINT_UNSIGNED_MODULE
Locale: LANG=en_CA.UTF-8, LC_CTYPE=en_CA.UTF-8 (charmap=UTF-8), 
LANGUAGE=en_CA.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages linux-source-5.2 depends on:
ii  binutils  2.31.1-16
ii  xz-utils  5.2.4-1

Versions of packages linux-source-5.2 recommends:
ii  bc                    1.07.1-2+b1
ii  bison                 2:3.3.2.dfsg-1
ii  flex                  2.6.4-6.2
ii  gcc                   4:8.3.0-1
ii  libc6-dev [libc-dev]  2.28-10
ii  linux-config-5.2      5.2.9-2~bpo10+1
ii  make                  4.2.1-1.2

Versions of packages linux-source-5.2 suggests:
ii  libncurses-dev [ncurses-dev]  6.1+20181013-2+deb10u1
pn  libqt4-dev                    <none>
pn  pkg-config                    <none>

-- no debconf information
diff -Naurp linux-source-5.2-orig/sound/soc/sof/control.c 
linux-source-5.2/sound/soc/sof/control.c
--- linux-source-5.2-orig/sound/soc/sof/control.c       2019-08-16 
04:11:12.000000000 -0400
+++ linux-source-5.2/sound/soc/sof/control.c    2019-09-18 22:18:22.970932678 
-0400
@@ -39,26 +39,8 @@ int snd_sof_volume_get(struct snd_kcontr
        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_kcontr
                        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_kcontr
        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_kcontr
        }
 
        /* 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_kcontr
        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_kcontr
        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_kcontr
        }
 
        /* 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
        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
        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
        }
 
        /* 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_kcontro
        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_kcontro
                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_kcontro
        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_kcontro
        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_kcontro
                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_kco
        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_kco
                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_kco
        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_kco
        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_kco
                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 -Naurp linux-source-5.2-orig/sound/soc/sof/intel/hda-dai.c 
linux-source-5.2/sound/soc/sof/intel/hda-dai.c
--- linux-source-5.2-orig/sound/soc/sof/intel/hda-dai.c 2019-08-16 
04:11:12.000000000 -0400
+++ linux-source-5.2/sound/soc/sof/intel/hda-dai.c      2019-09-18 
22:17:59.327619433 -0400
@@ -30,62 +30,84 @@ 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);
+       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;
+       }
 
-       bus = sof_to_bus(sdev);
+       return false;
+}
 
-       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;
-               }
+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;
 
-               snd_soc_dai_set_dma_data(dai, &substream, stream);
-               *tx_slot = hdac_stream(stream)->stream_tag - 1;
+       int stream_dir = substream->stream;
 
-               dev_dbg(bus->dev, "link dma channel %d for playback", *tx_slot);
+       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 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;
+                               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 +144,51 @@ static int hda_link_dma_params(struct hd
        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 +202,31 @@ static int hda_link_hw_params(struct snd
        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 +259,7 @@ static int hda_link_pcm_prepare(struct s
        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 +276,24 @@ static int hda_link_pcm_trigger(struct s
 {
        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 +310,22 @@ static int hda_link_pcm_trigger(struct s
        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 +335,38 @@ static int hda_link_pcm_trigger(struct s
        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;
+       int ret;
 
-       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);
-               }
+       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);
 
-               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);
-               }
+       /* 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;
 
-               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);
-               }
-       }
+       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;
 
        return 0;
 }
@@ -293,7 +376,6 @@ static const struct snd_soc_dai_ops hda_
        .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 -Naurp linux-source-5.2-orig/sound/soc/sof/intel/hda-dsp.c 
linux-source-5.2/sound/soc/sof/intel/hda-dsp.c
--- linux-source-5.2-orig/sound/soc/sof/intel/hda-dsp.c 2019-08-16 
04:11:12.000000000 -0400
+++ linux-source-5.2/sound/soc/sof/intel/hda-dsp.c      2019-09-18 
22:18:13.739172971 -0400
@@ -454,18 +454,45 @@ int hda_dsp_suspend(struct snd_sof_dev *
        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;
+#endif
+       int stream_tag;
+
        /* 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;
+               stream_tag = hdac_stream(stream)->stream_tag;
+#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;
+                       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 -Naurp linux-source-5.2-orig/sound/soc/sof/intel/hda.h 
linux-source-5.2/sound/soc/sof/intel/hda.h
--- linux-source-5.2-orig/sound/soc/sof/intel/hda.h     2019-08-16 
04:11:12.000000000 -0400
+++ linux-source-5.2/sound/soc/sof/intel/hda.h  2019-09-18 22:18:37.726615756 
-0400
@@ -407,11 +407,15 @@ static inline struct hda_bus *sof_to_hbu
 }
 
 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 */
 };
 
+#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,7 +448,7 @@ int hda_dsp_suspend(struct snd_sof_dev *
 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_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);
diff -Naurp linux-source-5.2-orig/sound/soc/sof/intel/hda-stream.c 
linux-source-5.2/sound/soc/sof/intel/hda-stream.c
--- linux-source-5.2-orig/sound/soc/sof/intel/hda-stream.c      2019-08-16 
04:11:12.000000000 -0400
+++ linux-source-5.2/sound/soc/sof/intel/hda-stream.c   2019-09-18 
22:18:37.726615756 -0400
@@ -564,6 +564,8 @@ int hda_dsp_stream_init(struct snd_sof_d
                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 +619,8 @@ int hda_dsp_stream_init(struct snd_sof_d
                if (!hda_stream)
                        return -ENOMEM;
 
+               hda_stream->sdev = sdev;
+
                stream = &hda_stream->hda_stream;
 
                /* we always have DSP support */
diff -Naurp linux-source-5.2-orig/sound/soc/sof/ops.h 
linux-source-5.2/sound/soc/sof/ops.h
--- linux-source-5.2-orig/sound/soc/sof/ops.h   2019-08-16 04:11:12.000000000 
-0400
+++ linux-source-5.2/sound/soc/sof/ops.h        2019-09-18 22:18:13.739172971 
-0400
@@ -134,10 +134,11 @@ static inline int snd_sof_dsp_runtime_su
        return 0;
 }
 
-static inline void snd_sof_dsp_hw_params_upon_resume(struct snd_sof_dev *sdev)
+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)
diff -Naurp linux-source-5.2-orig/sound/soc/sof/pm.c 
linux-source-5.2/sound/soc/sof/pm.c
--- linux-source-5.2-orig/sound/soc/sof/pm.c    2019-08-16 04:11:12.000000000 
-0400
+++ linux-source-5.2/sound/soc/sof/pm.c 2019-09-18 22:18:13.739172971 -0400
@@ -153,6 +153,15 @@ static int sof_restore_pipelines(struct
                        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_so
                                 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_resum
        }
 
        /* 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 *de
        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 *de
 #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 */
diff -Naurp linux-source-5.2-orig/sound/soc/sof/sof-priv.h 
linux-source-5.2/sound/soc/sof/sof-priv.h
--- linux-source-5.2-orig/sound/soc/sof/sof-priv.h      2019-08-16 
04:11:12.000000000 -0400
+++ linux-source-5.2/sound/soc/sof/sof-priv.h   2019-09-18 22:18:13.743172859 
-0400
@@ -56,6 +56,8 @@
 #define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
        SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT)
 
+#define DMA_CHAN_INVALID       0xFFFFFFFF
+
 struct snd_sof_dev;
 struct snd_sof_ipc_msg;
 struct snd_sof_ipc;
@@ -166,7 +168,7 @@ 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 (*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 */
@@ -331,6 +333,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;
diff -Naurp linux-source-5.2-orig/sound/soc/sof/topology.c 
linux-source-5.2/sound/soc/sof/topology.c
--- linux-source-5.2-orig/sound/soc/sof/topology.c      2019-08-16 
04:11:12.000000000 -0400
+++ linux-source-5.2/sound/soc/sof/topology.c   2019-09-18 22:18:22.970932678 
-0400
@@ -2340,6 +2340,9 @@ static int sof_set_dai_config(struct snd
                        if (!dai->dai_config)
                                return -ENOMEM;
 
+                       /* set cpu_dai_name */
+                       dai->cpu_dai_name = link->cpu_dai_name;
+
                        found = 1;
                }
        }
@@ -2568,9 +2571,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 +2584,18 @@ static int sof_link_hda_process(struct s
                        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->cpu_dai_name;
+
                        /* send message to DSP */
                        ret = sof_ipc_tx_message(sdev->ipc,
                                                 config->hdr.cmd, config, size,
@@ -2643,10 +2635,6 @@ static int sof_link_hda_load(struct snd_
        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 */
@@ -2672,22 +2660,7 @@ static int sof_link_hda_load(struct snd_
                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);
@@ -2814,17 +2787,6 @@ static int sof_link_hda_unload(struct sn
                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 +2960,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 +3046,11 @@ static void sof_complete(struct snd_soc_
                        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 */
# Generic SOF selections
CONFIG_SND_SOC_SOF_TOPLEVEL=y
CONFIG_SND_SOC_SOF_PCI=m
CONFIG_SND_SOC_SOF_ACPI=m

# debug options
CONFIG_SND_SOC_SOF_DEBUG=y
CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC=y
CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE=y
CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE=y
CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST=y

# SOF Intel platform drivers
CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL=y
CONFIG_SND_SOC_SOF_BAYTRAIL_SUPPORT=y
CONFIG_SND_SOC_SOF_HASWELL_SUPPORT=y
CONFIG_SND_SOC_SOF_BROADWELL_SUPPORT=y
CONFIG_SND_SOC_SOF_MERRIFIELD_SUPPORT=y
CONFIG_SND_SOC_SOF_SKYLAKE_SUPPORT=y
CONFIG_SND_SOC_SOF_APOLLOLAKE_SUPPORT=y
CONFIG_SND_SOC_SOF_KABYLAKE_SUPPORT=y
CONFIG_SND_SOC_SOF_GEMINILAKE_SUPPORT=y
CONFIG_SND_SOC_SOF_CANNONLAKE_SUPPORT=y
CONFIG_SND_SOC_SOF_COFFEELAKE_SUPPORT=y
CONFIG_SND_SOC_SOF_ICELAKE_SUPPORT=y
CONFIG_SND_SOC_SOF_COMETLAKE_LP_SUPPORT=y
CONFIG_SND_SOC_SOF_COMETLAKE_H_SUPPORT=y
CONFIG_SND_SOC_SOF_TIGERLAKE_SUPPORT=y
CONFIG_SND_SOC_SOF_ELKHARTLAKE_SUPPORT=y

# Intel machine drivers
CONFIG_SND_SOC_INTEL_HASWELL_MACH=m
CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH=m
CONFIG_SND_SOC_INTEL_BROADWELL_MACH=m
CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH=m
CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH=m
CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH=m
CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH=m
CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH=m
CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH=m
CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH=m
CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH=m
CONFIG_SND_SOC_INTEL_BYT_CHT_CX2072X_MACH=m
CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH=m
CONFIG_SND_SOC_INTEL_BXT_RT298_MACH=m
CONFIG_SND_SOC_INTEL_BXT_PCM512x_MACH=m
CONFIG_SND_SOC_INTEL_BXT_WM8804_MACH=m
CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH=m
CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH=m
CONFIG_SND_SOC_INTEL_CML_LP_DA7219_MAX98357A_MACH=m
CONFIG_SND_SOC_INTEL_TGL_RT1308_MACH=m
CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT700_MACH=m
CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT711_RT1308_RT715_MACH=m
CONFIG_SND_HDA_INTEL=m

# enable HDaudio support in SST
CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC=y


# enable HDaudio in SOF. this might be redundant with sof-mach-driver-defconfig
CONFIG_SND_SOC_SOF_HDA_LINK=y
CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC=y

# machine driver for HDaudio support
CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH=m

# HDaudio configs
CONFIG_SND_HDA_INPUT_BEEP=y
CONFIG_SND_HDA_PATCH_LOADER=y
CONFIG_SND_HDA_CODEC_REALTEK=m
CONFIG_SND_HDA_CODEC_ANALOG=m
CONFIG_SND_HDA_CODEC_SIGMATEL=m
CONFIG_SND_HDA_CODEC_VIA=m
CONFIG_SND_HDA_CODEC_HDMI=m
CONFIG_SND_HDA_CODEC_CIRRUS=m
CONFIG_SND_HDA_CODEC_CONEXANT=m
CONFIG_SND_HDA_CODEC_CA0110=m
CONFIG_SND_HDA_CODEC_CA0132=m
CONFIG_SND_HDA_CODEC_CMEDIA=m
CONFIG_SND_HDA_CODEC_SI3054=m
CONFIG_SND_HDA_GENERIC=m

Reply via email to