ASoC: SOF: Intel: Add ipc4 library loading implementation
authorPeter Ujfalusi <peter.ujfalusi@linux.intel.com>
Thu, 20 Oct 2022 12:12:34 +0000 (15:12 +0300)
committerMark Brown <broonie@kernel.org>
Fri, 21 Oct 2022 12:05:07 +0000 (13:05 +0100)
On Intel HDA platforms the library loading is done via DMA and an IPC
message is also need to be sent to initiate the downloading of the new
library.

Co-developed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Chao Song <chao.song@intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://lore.kernel.org/r/20221020121238.18339-16-peter.ujfalusi@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
include/sound/sof/ipc4/header.h
sound/soc/sof/intel/apl.c
sound/soc/sof/intel/cnl.c
sound/soc/sof/intel/hda-loader.c
sound/soc/sof/intel/hda.h
sound/soc/sof/intel/icl.c
sound/soc/sof/intel/mtl.c
sound/soc/sof/intel/tgl.c

index 99efe0e..622193b 100644 (file)
@@ -185,6 +185,10 @@ enum sof_ipc4_pipeline_state {
 #define SOF_IPC4_GLB_PIPE_STATE_MASK           GENMASK(15, 0)
 #define SOF_IPC4_GLB_PIPE_STATE(x)             ((x) << SOF_IPC4_GLB_PIPE_STATE_SHIFT)
 
+/* load library ipc msg */
+#define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT 16
+#define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x)    ((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT)
+
 enum sof_ipc4_channel_config {
        /* one channel only. */
        SOF_IPC4_CHANNEL_CONFIG_MONO,
index 1549ca7..d93b4ea 100644 (file)
@@ -62,6 +62,9 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev)
 
                ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_5;
 
+               /* External library loading support */
+               ipc4_data->load_library = hda_dsp_ipc4_load_library;
+
                /* doorbell */
                sof_apl_ops.irq_thread  = hda_dsp_ipc4_irq_thread;
 
index 19d0b19..f1e74b4 100644 (file)
@@ -389,6 +389,9 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev)
 
                ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_8;
 
+               /* External library loading support */
+               ipc4_data->load_library = hda_dsp_ipc4_load_library;
+
                /* doorbell */
                sof_cnl_ops.irq_thread  = cnl_ipc4_irq_thread;
 
index 5ed524e..3820454 100644 (file)
@@ -19,7 +19,9 @@
 #include <sound/hdaudio_ext.h>
 #include <sound/hda_register.h>
 #include <sound/sof.h>
+#include <sound/sof/ipc4/header.h>
 #include "ext_manifest.h"
+#include "../ipc4-priv.h"
 #include "../ops.h"
 #include "../sof-priv.h"
 #include "hda.h"
@@ -518,6 +520,70 @@ cleanup:
        return ret;
 }
 
+int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
+                             struct sof_ipc4_fw_library *fw_lib, bool reload)
+{
+       struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+       struct hdac_ext_stream *hext_stream;
+       struct firmware stripped_firmware;
+       struct sof_ipc4_msg msg = {};
+       struct snd_dma_buffer dmab;
+       int ret, ret1;
+
+       /* IMR booting will restore the libraries as well, skip the loading */
+       if (reload && hda->booted_from_imr)
+               return 0;
+
+       /* the fw_lib has been verified during loading, we can trust the validity here */
+       stripped_firmware.data = fw_lib->sof_fw.fw->data + fw_lib->sof_fw.payload_offset;
+       stripped_firmware.size = fw_lib->sof_fw.fw->size - fw_lib->sof_fw.payload_offset;
+
+       /* prepare DMA for code loader stream */
+       hext_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT,
+                                           stripped_firmware.size,
+                                           &dmab, SNDRV_PCM_STREAM_PLAYBACK);
+       if (IS_ERR(hext_stream)) {
+               dev_err(sdev->dev, "%s: DMA prepare failed\n", __func__);
+               return PTR_ERR(hext_stream);
+       }
+
+       memcpy(dmab.area, stripped_firmware.data, stripped_firmware.size);
+
+       msg.primary = hext_stream->hstream.stream_tag - 1;
+       msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_LOAD_LIBRARY);
+       msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+       msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
+       msg.primary |= SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(fw_lib->id);
+
+       ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_START);
+       if (ret < 0) {
+               dev_err(sdev->dev, "%s: DMA trigger start failed\n", __func__);
+               goto cleanup;
+       }
+
+       ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
+
+       ret1 = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP);
+       if (ret1 < 0) {
+               dev_err(sdev->dev, "%s: DMA trigger stop failed\n", __func__);
+               if (!ret)
+                       ret = ret1;
+       }
+
+cleanup:
+       /* clean up even in case of error and return the first error */
+       ret1 = hda_cl_cleanup(sdev, &dmab, hext_stream);
+       if (ret1 < 0) {
+               dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__);
+
+               /* set return value to indicate cleanup failure */
+               if (!ret)
+                       ret = ret1;
+       }
+
+       return ret;
+}
+
 /* pre fw run operations */
 int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev)
 {
index d004bcb..4b9f381 100644 (file)
@@ -857,4 +857,7 @@ int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
 void hda_ipc4_dump(struct snd_sof_dev *sdev);
 extern struct sdw_intel_ops sdw_callback;
 
+struct sof_ipc4_fw_library;
+int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
+                             struct sof_ipc4_fw_library *fw_lib, bool reload);
 #endif
index 6d58771..f95b2ec 100644 (file)
@@ -130,6 +130,9 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev)
 
                ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
 
+               /* External library loading support */
+               ipc4_data->load_library = hda_dsp_ipc4_load_library;
+
                /* doorbell */
                sof_icl_ops.irq_thread  = cnl_ipc4_irq_thread;
 
index 1029853..459da05 100644 (file)
@@ -641,6 +641,9 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
 
        ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
 
+       /* External library loading support */
+       ipc4_data->load_library = hda_dsp_ipc4_load_library;
+
        /* set DAI ops */
        hda_set_dai_drv_ops(sdev, &sof_mtl_ops);
 
index 9ae2890..143447f 100644 (file)
@@ -85,6 +85,9 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
 
                ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
 
+               /* External library loading support */
+               ipc4_data->load_library = hda_dsp_ipc4_load_library;
+
                /* doorbell */
                sof_tgl_ops.irq_thread  = cnl_ipc4_irq_thread;