ASoC: SOF: amd: Add helper callbacks for ACP's DMA configuration
authorAjit Kumar Pandey <AjitKumar.Pandey@amd.com>
Wed, 17 Nov 2021 09:37:15 +0000 (11:37 +0200)
committerMark Brown <broonie@kernel.org>
Wed, 17 Nov 2021 17:35:45 +0000 (17:35 +0000)
ACP DMA is used for loading SOF firmware into DSP memory and data
transfer from system memory to DSP memory. Add helper callbacks to
initialize and configure ACP DMA block for fw loading.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
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-3-daniel.baluta@oss.nxp.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sof/amd/acp-dsp-offset.h
sound/soc/sof/amd/acp.c
sound/soc/sof/amd/acp.h

index 2cc2a9a..bfb0239 100644 (file)
 #ifndef _ACP_DSP_IP_OFFSET_H
 #define _ACP_DSP_IP_OFFSET_H
 
+/* Registers from ACP_DMA_0 block */
+#define ACP_DMA_CNTL_0                         0x00
+#define ACP_DMA_DSCR_STRT_IDX_0                        0x20
+#define ACP_DMA_DSCR_CNT_0                     0x40
+#define ACP_DMA_PRIO_0                         0x60
+#define ACP_DMA_CUR_DSCR_0                     0x80
+#define ACP_DMA_ERR_STS_0                      0xC0
+#define ACP_DMA_DESC_BASE_ADDR                 0xE0
+#define ACP_DMA_DESC_MAX_NUM_DSCR              0xE4
+#define ACP_DMA_CH_STS                         0xE8
+#define ACP_DMA_CH_GROUP                       0xEC
+#define ACP_DMA_CH_RST_STS                     0xF0
+
+/* Registers from ACP_AXI2AXIATU block */
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1         0xC00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1         0xC04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2         0xC08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2         0xC0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3         0xC10
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3         0xC14
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4         0xC18
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4         0xC1C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5         0xC20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5         0xC24
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6         0xC28
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6         0xC2C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7         0xC30
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7         0xC34
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8         0xC38
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8         0xC3C
+#define ACPAXI2AXI_ATU_CTRL                    0xC40
 #define ACP_SOFT_RESET                         0x1000
 
 /* Registers from ACP_PGFSM block */
 #define ACP_PGFSM_CONTROL                      0x141C
 #define ACP_PGFSM_STATUS                       0x1420
 
+/* Registers from ACP_INTR block */
+#define ACP_DSP_SW_INTR_CNTL                   0x1814
+#define ACP_ERROR_STATUS                       0x18C4
+
+/* Registers from ACP_SHA block */
+#define ACP_SHA_DSP_FW_QUALIFIER               0x1C70
+#define ACP_SHA_DMA_CMD                                0x1CB0
+#define ACP_SHA_MSG_LENGTH                     0x1CB4
+#define ACP_SHA_DMA_STRT_ADDR                  0x1CB8
+#define ACP_SHA_DMA_DESTINATION_ADDR           0x1CBC
+#define ACP_SHA_DMA_CMD_STS                    0x1CC0
+#define ACP_SHA_DMA_ERR_STATUS                 0x1CC4
+#define ACP_SHA_TRANSFER_BYTE_CNT              0x1CC8
+
+#define ACP_SCRATCH_REG_0                      0x10000
+
 #endif
index 687a674..3778f78 100644 (file)
 #include "acp.h"
 #include "acp-dsp-offset.h"
 
+static void configure_acp_groupregisters(struct acp_dev_data *adata)
+{
+       struct snd_sof_dev *sdev = adata->dev;
+
+       /* Group Enable */
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_BASE_ADDR_GRP_1,
+                         ACP_SRAM_PTE_OFFSET | BIT(31));
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1,
+                         PAGE_SIZE_4K_ENABLE);
+
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID);
+}
+
+static void init_dma_descriptor(struct acp_dev_data *adata)
+{
+       struct snd_sof_dev *sdev = adata->dev;
+       unsigned int addr;
+
+       addr = ACP_SRAM_PTE_OFFSET + offsetof(struct scratch_reg_conf, dma_desc);
+
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DESC_BASE_ADDR, addr);
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DESC_MAX_NUM_DSCR, ACP_MAX_DESC_CNT);
+}
+
+static void configure_dma_descriptor(struct acp_dev_data *adata, unsigned short idx,
+                                    struct dma_descriptor *dscr_info)
+{
+       struct snd_sof_dev *sdev = adata->dev;
+       unsigned int offset;
+
+       offset = ACP_SCRATCH_REG_0 + offsetof(struct scratch_reg_conf, dma_desc) +
+                idx * sizeof(struct dma_descriptor);
+
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset, dscr_info->src_addr);
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset + 0x4, dscr_info->dest_addr);
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset + 0x8, dscr_info->tx_cnt.u32_all);
+}
+
+static int config_dma_channel(struct acp_dev_data *adata, unsigned int ch,
+                             unsigned int idx, unsigned int dscr_count)
+{
+       struct snd_sof_dev *sdev = adata->dev;
+       unsigned int val, status;
+       int ret;
+
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32),
+                         ACP_DMA_CH_RST | ACP_DMA_CH_GRACEFUL_RST_EN);
+
+       ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_DMA_CH_RST_STS, val,
+                                           val & (1 << ch), ACP_REG_POLL_INTERVAL,
+                                           ACP_REG_POLL_TIMEOUT_US);
+       if (ret < 0) {
+               status = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_ERROR_STATUS);
+               val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DMA_ERR_STS_0 + ch * sizeof(u32));
+
+               dev_err(sdev->dev, "ACP_DMA_ERR_STS :0x%x ACP_ERROR_STATUS :0x%x\n", val, status);
+               return ret;
+       }
+
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, (ACP_DMA_CNTL_0 + ch * sizeof(u32)), 0);
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DSCR_CNT_0 + ch * sizeof(u32), dscr_count);
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DSCR_STRT_IDX_0 + ch * sizeof(u32), idx);
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_PRIO_0 + ch * sizeof(u32), 0);
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32), ACP_DMA_CH_RUN);
+
+       return ret;
+}
+
+static int acpbus_dma_start(struct acp_dev_data *adata, unsigned int ch,
+                           unsigned int dscr_count, struct dma_descriptor *dscr_info)
+{
+       struct snd_sof_dev *sdev = adata->dev;
+       int ret;
+       u16 dscr;
+
+       if (!dscr_info || !dscr_count)
+               return -EINVAL;
+
+       for (dscr = 0; dscr < dscr_count; dscr++)
+               configure_dma_descriptor(adata, dscr, dscr_info++);
+
+       ret = config_dma_channel(adata, ch, 0, dscr_count);
+       if (ret < 0)
+               dev_err(sdev->dev, "config dma ch failed:%d\n", ret);
+
+       return ret;
+}
+
+int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr,
+                         unsigned int dest_addr, int dsp_data_size)
+{
+       struct snd_sof_dev *sdev = adata->dev;
+       unsigned int desc_count, index;
+       int ret;
+
+       for (desc_count = 0; desc_count < ACP_MAX_DESC && dsp_data_size >= 0;
+            desc_count++, dsp_data_size -= ACP_PAGE_SIZE) {
+               adata->dscr_info[desc_count].src_addr = src_addr + desc_count * ACP_PAGE_SIZE;
+               adata->dscr_info[desc_count].dest_addr = dest_addr + desc_count * ACP_PAGE_SIZE;
+               adata->dscr_info[desc_count].tx_cnt.bits.count = ACP_PAGE_SIZE;
+               if (dsp_data_size < ACP_PAGE_SIZE)
+                       adata->dscr_info[desc_count].tx_cnt.bits.count = dsp_data_size;
+       }
+
+       ret = acpbus_dma_start(adata, 0, desc_count, adata->dscr_info);
+       if (ret)
+               dev_err(sdev->dev, "acpbus_dma_start failed\n");
+
+       /* Clear descriptor array */
+       for (index = 0; index < desc_count; index++)
+               memset(&adata->dscr_info[index], 0x00, sizeof(struct dma_descriptor));
+
+       return ret;
+}
+
+int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
+                             unsigned int start_addr, unsigned int dest_addr,
+                             unsigned int image_length)
+{
+       struct snd_sof_dev *sdev = adata->dev;
+       unsigned int tx_count, fw_qualifier, val;
+       int ret;
+
+       if (!image_addr) {
+               dev_err(sdev->dev, "SHA DMA image address is NULL\n");
+               return -EINVAL;
+       }
+
+       val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD);
+       if (val & ACP_SHA_RUN) {
+               snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RESET);
+               ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD_STS,
+                                                   val, val & ACP_SHA_RESET,
+                                                   ACP_REG_POLL_INTERVAL,
+                                                   ACP_REG_POLL_TIMEOUT_US);
+               if (ret < 0) {
+                       dev_err(sdev->dev, "SHA DMA Failed to Reset\n");
+                       return ret;
+               }
+       }
+
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr);
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_DESTINATION_ADDR, dest_addr);
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_MSG_LENGTH, image_length);
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RUN);
+
+       ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_TRANSFER_BYTE_CNT,
+                                           tx_count, tx_count == image_length,
+                                           ACP_REG_POLL_INTERVAL, ACP_DMA_COMPLETE_TIMEOUT_US);
+       if (ret < 0) {
+               dev_err(sdev->dev, "SHA DMA Failed to Transfer Length %x\n", tx_count);
+               return ret;
+       }
+
+       snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER, DSP_FW_RUN_ENABLE);
+
+       fw_qualifier = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER);
+       if (!(fw_qualifier & DSP_FW_RUN_ENABLE)) {
+               dev_err(sdev->dev, "PSP validation failed\n");
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+int acp_dma_status(struct acp_dev_data *adata, unsigned char ch)
+{
+       struct snd_sof_dev *sdev = adata->dev;
+       unsigned int val;
+       int ret = 0;
+
+       val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32));
+       if (val & ACP_DMA_CH_RUN) {
+               ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_DMA_CH_STS, val, !val,
+                                                   ACP_REG_POLL_INTERVAL,
+                                                   ACP_DMA_COMPLETE_TIMEOUT_US);
+               if (ret < 0)
+                       dev_err(sdev->dev, "DMA_CHANNEL %d status timeout\n", ch);
+       }
+
+       return ret;
+}
+
+void memcpy_from_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *dst, size_t bytes)
+{
+       unsigned int reg_offset = offset + ACP_SCRATCH_REG_0;
+       int i, j;
+
+       for (i = 0, j = 0; i < bytes; i = i + 4, j++)
+               dst[j] = snd_sof_dsp_read(sdev, ACP_DSP_BAR, reg_offset + i);
+}
+
+void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes)
+{
+       unsigned int reg_offset = offset + ACP_SCRATCH_REG_0;
+       int i, j;
+
+       for (i = 0, j = 0; i < bytes; i = i + 4, j++)
+               snd_sof_dsp_write(sdev, ACP_DSP_BAR, reg_offset + i, src[j]);
+}
+
+static int acp_memory_init(struct snd_sof_dev *sdev)
+{
+       struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+
+       snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_CNTL,
+                               ACP_DSP_INTR_EN_MASK, ACP_DSP_INTR_EN_MASK);
+       configure_acp_groupregisters(adata);
+       init_dma_descriptor(adata);
+
+       return 0;
+}
+
 static int acp_power_on(struct snd_sof_dev *sdev)
 {
        unsigned int val;
@@ -86,6 +299,7 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
        struct pci_dev *pci = to_pci_dev(sdev->dev);
        struct acp_dev_data *adata;
        unsigned int addr;
+       int ret;
 
        adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data),
                             GFP_KERNEL);
@@ -104,7 +318,13 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
 
        sdev->pdata->hw_pdata = adata;
 
-       return acp_init(sdev);
+       ret = acp_init(sdev);
+       if (ret < 0)
+               return ret;
+
+       acp_memory_init(sdev);
+
+       return 0;
 }
 EXPORT_SYMBOL_NS(amd_sof_acp_probe, SND_SOC_SOF_AMD_COMMON);
 
index c7ac8f9..ff01d0e 100644 (file)
@@ -15,6 +15,7 @@
 
 #define ACP_REG_POLL_INTERVAL                   500
 #define ACP_REG_POLL_TIMEOUT_US                 2000
+#define ACP_DMA_COMPLETE_TIMEOUT_US            5000
 
 #define ACP_PGFSM_CNTL_POWER_ON_MASK           0x01
 #define ACP_PGFSM_STATUS_MASK                  0x03
 #define ACP_RELEASE_RESET                      0x00
 #define ACP_SOFT_RESET_DONE_MASK               0x00010001
 
+#define ACP_DSP_INTR_EN_MASK                   0x00000001
+#define ACP_SRAM_PTE_OFFSET                    0x02050000
+#define PAGE_SIZE_4K_ENABLE                    0x2
+#define ACP_PAGE_SIZE                          0x1000
+#define ACP_DMA_CH_RUN                         0x02
+#define ACP_MAX_DESC_CNT                       0x02
+#define DSP_FW_RUN_ENABLE                      0x01
+#define ACP_SHA_RUN                            0x01
+#define ACP_SHA_RESET                          0x02
+#define ACP_DMA_CH_RST                         0x01
+#define ACP_DMA_CH_GRACEFUL_RST_EN             0x10
+#define ACP_ATU_CACHE_INVALID                  0x01
+#define ACP_MAX_DESC                           128
+#define ACPBUS_REG_BASE_OFFSET                 ACP_DMA_CNTL_0
+
+struct  acp_atu_grp_pte {
+       u32 low;
+       u32 high;
+};
+
+union dma_tx_cnt {
+       struct {
+               unsigned int count : 19;
+               unsigned int reserved : 12;
+               unsigned ioc : 1;
+       } bitfields, bits;
+       unsigned int u32_all;
+       signed int i32_all;
+};
+
+struct dma_descriptor {
+       unsigned int src_addr;
+       unsigned int dest_addr;
+       union dma_tx_cnt tx_cnt;
+       unsigned int reserved;
+};
+
+/* Scratch memory structure for communication b/w host and dsp */
+struct  scratch_ipc_conf {
+       /* DSP mailbox */
+       u8 sof_out_box[512];
+       /* Host mailbox */
+       u8 sof_in_box[512];
+       /* Debug memory */
+       u8 sof_debug_box[1024];
+       /* Exception memory*/
+       u8 sof_except_box[1024];
+       /* Stream buffer */
+       u8 sof_stream_box[1024];
+       /* Trace buffer */
+       u8 sof_trace_box[1024];
+       /* Host msg flag */
+       u32 sof_host_msg_write;
+       /* Host ack flag*/
+       u32 sof_host_ack_write;
+       /* DSP msg flag */
+       u32 sof_dsp_msg_write;
+       /* Dsp ack flag */
+       u32 sof_dsp_ack_write;
+};
+
+struct  scratch_reg_conf {
+       struct scratch_ipc_conf info;
+       struct acp_atu_grp_pte grp1_pte[16];
+       struct acp_atu_grp_pte grp2_pte[16];
+       struct acp_atu_grp_pte grp3_pte[16];
+       struct acp_atu_grp_pte grp4_pte[16];
+       struct acp_atu_grp_pte grp5_pte[16];
+       struct acp_atu_grp_pte grp6_pte[16];
+       struct acp_atu_grp_pte grp7_pte[16];
+       struct acp_atu_grp_pte grp8_pte[16];
+       struct dma_descriptor dma_desc[64];
+       unsigned int reg_offset[8];
+       unsigned int buf_size[8];
+       u8 acp_tx_fifo_buf[256];
+       u8 acp_rx_fifo_buf[256];
+       unsigned int    reserve[];
+};
+
 /* Common device data struct for ACP devices */
 struct acp_dev_data {
        struct snd_sof_dev  *dev;
+       struct dma_descriptor dscr_info[ACP_MAX_DESC];
 };
 
+void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes);
+void memcpy_from_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *dst, size_t bytes);
+
+int acp_dma_status(struct acp_dev_data *adata, unsigned char ch);
+int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr,
+                         unsigned int dest_addr, int dsp_data_size);
+int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
+                             unsigned int start_addr, unsigned int dest_addr,
+                             unsigned int image_length);
+
 /* ACP device probe/remove */
 int amd_sof_acp_probe(struct snd_sof_dev *sdev);
 int amd_sof_acp_remove(struct snd_sof_dev *sdev);