ASoC: SOF: amd: Add fw loader and renoir dsp ops to load firmware
authorAjit Kumar Pandey <AjitKumar.Pandey@amd.com>
Wed, 17 Nov 2021 09:37:16 +0000 (11:37 +0200)
committerMark Brown <broonie@kernel.org>
Wed, 17 Nov 2021 17:35:46 +0000 (17:35 +0000)
Add acp-loader module with ops callback to load and run firmware
on ACP DSP block on Renoir platform.

Signed-off-by: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
Reviewed-by: Bard Liao <bard.liao@intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com>
Link: https://lore.kernel.org/r/20211117093734.17407-4-daniel.baluta@oss.nxp.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sof/amd/Makefile
sound/soc/sof/amd/acp-dsp-offset.h
sound/soc/sof/amd/acp-loader.c [new file with mode: 0644]
sound/soc/sof/amd/acp.h
sound/soc/sof/amd/renoir.c

index ac2ecd2..031fb94 100644 (file)
@@ -4,7 +4,7 @@
 #
 # Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
 
-snd-sof-amd-acp-objs := acp.o
+snd-sof-amd-acp-objs := acp.o acp-loader.o
 snd-sof-amd-renoir-objs := renoir.o
 
 obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o
index bfb0239..f4bc7e9 100644 (file)
@@ -24,6 +24,9 @@
 #define ACP_DMA_CH_GROUP                       0xEC
 #define ACP_DMA_CH_RST_STS                     0xF0
 
+/* Registers from ACP_DSP_0 block */
+#define ACP_DSP0_RUNSTALL                      0x414
+
 /* Registers from ACP_AXI2AXIATU block */
 #define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1         0xC00
 #define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1         0xC04
diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c
new file mode 100644 (file)
index 0000000..2dc15ae
--- /dev/null
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/*
+ * Hardware interface for ACP DSP Firmware binaries loader
+ */
+
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "../ops.h"
+#include "acp-dsp-offset.h"
+#include "acp.h"
+
+#define FW_BIN         0
+#define FW_DATA_BIN    1
+
+#define FW_BIN_PTE_OFFSET      0x00
+#define FW_DATA_BIN_PTE_OFFSET 0x08
+
+#define ACP_DSP_RUN    0x00
+
+int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
+                      u32 offset, void *dest, size_t size)
+{
+       switch (blk_type) {
+       case SOF_FW_BLK_TYPE_SRAM:
+               offset = offset - ACP_SCRATCH_MEMORY_ADDRESS;
+               memcpy_from_scratch(sdev, offset, dest, size);
+               break;
+       default:
+               dev_err(sdev->dev, "bad blk type 0x%x\n", blk_type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_NS(acp_dsp_block_read, SND_SOC_SOF_AMD_COMMON);
+
+int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
+                       u32 offset, void *src, size_t size)
+{
+       struct snd_sof_pdata *plat_data = sdev->pdata;
+       struct pci_dev *pci = to_pci_dev(sdev->dev);
+       struct acp_dev_data *adata;
+       void *dest;
+       u32 dma_size, page_count;
+       unsigned int size_fw;
+
+       adata = sdev->pdata->hw_pdata;
+
+       switch (blk_type) {
+       case SOF_FW_BLK_TYPE_IRAM:
+               if (!adata->bin_buf) {
+                       size_fw = plat_data->fw->size;
+                       page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT;
+                       dma_size = page_count * ACP_PAGE_SIZE;
+                       adata->bin_buf = dma_alloc_coherent(&pci->dev, dma_size,
+                                                           &adata->sha_dma_addr,
+                                                           GFP_ATOMIC);
+                       if (!adata->bin_buf)
+                               return -ENOMEM;
+               }
+               adata->fw_bin_size = size + offset;
+               dest = adata->bin_buf + offset;
+               break;
+       case SOF_FW_BLK_TYPE_DRAM:
+               if (!adata->data_buf) {
+                       adata->data_buf = dma_alloc_coherent(&pci->dev,
+                                                            ACP_DEFAULT_DRAM_LENGTH,
+                                                            &adata->dma_addr,
+                                                            GFP_ATOMIC);
+                       if (!adata->data_buf)
+                               return -ENOMEM;
+               }
+               dest = adata->data_buf + offset;
+               adata->fw_data_bin_size = size + offset;
+               break;
+       case SOF_FW_BLK_TYPE_SRAM:
+               offset = offset - ACP_SCRATCH_MEMORY_ADDRESS;
+               memcpy_to_scratch(sdev, offset, src, size);
+               return 0;
+       default:
+               dev_err(sdev->dev, "bad blk type 0x%x\n", blk_type);
+               return -EINVAL;
+       }
+
+       memcpy(dest, src, size);
+       return 0;
+}
+EXPORT_SYMBOL_NS(acp_dsp_block_write, SND_SOC_SOF_AMD_COMMON);
+
+int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type)
+{
+       return type;
+}
+EXPORT_SYMBOL_NS(acp_get_bar_index, SND_SOC_SOF_AMD_COMMON);
+
+static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev_data *adata)
+{
+       struct snd_sof_dev *sdev;
+       unsigned int low, high;
+       dma_addr_t addr;
+       u16 page_idx;
+       u32 offset;
+
+       sdev = adata->dev;
+
+       switch (type) {
+       case FW_BIN:
+               offset = FW_BIN_PTE_OFFSET;
+               addr = adata->sha_dma_addr;
+               break;
+       case FW_DATA_BIN:
+               offset = adata->fw_bin_page_count * 8;
+               addr = adata->dma_addr;
+               break;
+       default:
+               dev_err(sdev->dev, "Invalid data type %x\n", type);
+               return;
+       }
+
+       for (page_idx = 0; page_idx < num_pages; page_idx++) {
+               low = lower_32_bits(addr);
+               high = upper_32_bits(addr);
+               snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low);
+               high |= BIT(31);
+               snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high);
+               offset += 8;
+               addr += PAGE_SIZE;
+       }
+}
+
+/* pre fw run operations */
+int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev)
+{
+       struct pci_dev *pci = to_pci_dev(sdev->dev);
+       struct snd_sof_pdata *plat_data = sdev->pdata;
+       struct acp_dev_data *adata;
+       unsigned int src_addr, size_fw;
+       u32 page_count, dma_size;
+       int ret;
+
+       adata = sdev->pdata->hw_pdata;
+       size_fw = adata->fw_bin_size;
+
+       page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT;
+       adata->fw_bin_page_count = page_count;
+
+       configure_pte_for_fw_loading(FW_BIN, page_count, adata);
+       ret = configure_and_run_sha_dma(adata, adata->bin_buf, ACP_SYSTEM_MEMORY_WINDOW,
+                                       ACP_IRAM_BASE_ADDRESS, size_fw);
+       if (ret < 0) {
+               dev_err(sdev->dev, "SHA DMA transfer failed status: %d\n", ret);
+               return ret;
+       }
+       configure_pte_for_fw_loading(FW_DATA_BIN, ACP_DRAM_PAGE_COUNT, adata);
+
+       src_addr = ACP_SYSTEM_MEMORY_WINDOW + page_count * ACP_PAGE_SIZE;
+       ret = configure_and_run_dma(adata, src_addr, ACP_DATA_RAM_BASE_ADDRESS,
+                                   adata->fw_data_bin_size);
+       if (ret < 0) {
+               dev_err(sdev->dev, "acp dma configuration failed: %d\n", ret);
+               return ret;
+       }
+
+       ret = acp_dma_status(adata, 0);
+       if (ret < 0)
+               dev_err(sdev->dev, "acp dma transfer status: %d\n", ret);
+
+       /* Free memory once DMA is complete */
+       dma_size =  (PAGE_ALIGN(plat_data->fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE;
+       dma_free_coherent(&pci->dev, dma_size, adata->bin_buf, adata->sha_dma_addr);
+       dma_free_coherent(&pci->dev, ACP_DEFAULT_DRAM_LENGTH, adata->data_buf, adata->dma_addr);
+       adata->bin_buf = NULL;
+       adata->data_buf = NULL;
+
+       return ret;
+}
+EXPORT_SYMBOL_NS(acp_dsp_pre_fw_run, SND_SOC_SOF_AMD_COMMON);
+
+int acp_sof_dsp_run(struct snd_sof_dev *sdev)
+{
+       int val;
+
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL, ACP_DSP_RUN);
+       val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL);
+       dev_dbg(sdev->dev, "ACP_DSP0_RUNSTALL : 0x%0x\n", val);
+
+       return 0;
+}
+EXPORT_SYMBOL_NS(acp_sof_dsp_run, SND_SOC_SOF_AMD_COMMON);
index ff01d0e..e755a31 100644 (file)
@@ -11,6 +11,8 @@
 #ifndef __SOF_AMD_ACP_H
 #define __SOF_AMD_ACP_H
 
+#include "../sof-priv.h"
+
 #define ACP_DSP_BAR    0
 
 #define ACP_REG_POLL_INTERVAL                   500
 #define ACP_MAX_DESC                           128
 #define ACPBUS_REG_BASE_OFFSET                 ACP_DMA_CNTL_0
 
+#define ACP_DEFAULT_DRAM_LENGTH                        0x00080000
+#define ACP_SCRATCH_MEMORY_ADDRESS             0x02050000
+#define ACP_SYSTEM_MEMORY_WINDOW               0x4000000
+#define ACP_IRAM_BASE_ADDRESS                  0x000000
+#define ACP_DATA_RAM_BASE_ADDRESS              0x01000000
+#define ACP_DRAM_PAGE_COUNT                    128
+
 struct  acp_atu_grp_pte {
        u32 low;
        u32 high;
@@ -106,6 +115,13 @@ struct  scratch_reg_conf {
 /* Common device data struct for ACP devices */
 struct acp_dev_data {
        struct snd_sof_dev  *dev;
+       unsigned int fw_bin_size;
+       unsigned int fw_data_bin_size;
+       u32 fw_bin_page_count;
+       dma_addr_t sha_dma_addr;
+       u8 *bin_buf;
+       dma_addr_t dma_addr;
+       u8 *data_buf;
        struct dma_descriptor dscr_info[ACP_MAX_DESC];
 };
 
@@ -123,5 +139,16 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
 int amd_sof_acp_probe(struct snd_sof_dev *sdev);
 int amd_sof_acp_remove(struct snd_sof_dev *sdev);
 
+/* DSP Loader callbacks */
+int acp_sof_dsp_run(struct snd_sof_dev *sdev);
+int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev);
+int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type);
+
+/* Block IO callbacks */
+int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
+                       u32 offset, void *src, size_t size);
+int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
+                      u32 offset, void *dest, size_t size);
+
 extern const struct snd_sof_dsp_ops sof_renoir_ops;
 #endif
index 3d1dc6c..bca8078 100644 (file)
@@ -26,6 +26,21 @@ const struct snd_sof_dsp_ops sof_renoir_ops = {
        /* Register IO */
        .write                  = sof_io_write,
        .read                   = sof_io_read,
+
+       /* Block IO */
+       .block_read             = acp_dsp_block_read,
+       .block_write            = acp_dsp_block_write,
+
+       /* Module loading */
+       .load_module            = snd_sof_parse_module_memcpy,
+
+       /*Firmware loading */
+       .load_firmware          = snd_sof_load_firmware_memcpy,
+       .pre_fw_run             = acp_dsp_pre_fw_run,
+       .get_bar_index          = acp_get_bar_index,
+
+       /* DSP core boot */
+       .run                    = acp_sof_dsp_run,
 };
 EXPORT_SYMBOL(sof_renoir_ops);