| 1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) |
| 2 | // |
| 3 | // This file is provided under a dual BSD/GPLv2 license. When using or |
| 4 | // redistributing this file, you may do so under either license. |
| 5 | // |
| 6 | // Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. |
| 7 | // |
| 8 | // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> |
| 9 | |
| 10 | /* |
| 11 | * Hardware interface for ACP DSP Firmware binaries loader |
| 12 | */ |
| 13 | |
| 14 | #include <linux/firmware.h> |
| 15 | #include <linux/module.h> |
| 16 | #include <linux/pci.h> |
| 17 | |
| 18 | #include "../ops.h" |
| 19 | #include "acp-dsp-offset.h" |
| 20 | #include "acp.h" |
| 21 | |
| 22 | #define FW_BIN 0 |
| 23 | #define FW_DATA_BIN 1 |
| 24 | #define FW_SRAM_DATA_BIN 2 |
| 25 | |
| 26 | #define FW_BIN_PTE_OFFSET 0x00 |
| 27 | #define FW_DATA_BIN_PTE_OFFSET 0x08 |
| 28 | |
| 29 | #define ACP_DSP_RUN 0x00 |
| 30 | |
| 31 | int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, |
| 32 | u32 offset, void *dest, size_t size) |
| 33 | { |
| 34 | const struct sof_amd_acp_desc *desc = get_chip_info(pdata: sdev->pdata); |
| 35 | switch (blk_type) { |
| 36 | case SOF_FW_BLK_TYPE_SRAM: |
| 37 | offset = offset - desc->sram_pte_offset; |
| 38 | memcpy_from_scratch(sdev, offset, dst: dest, bytes: size); |
| 39 | break; |
| 40 | default: |
| 41 | dev_err(sdev->dev, "bad blk type 0x%x\n" , blk_type); |
| 42 | return -EINVAL; |
| 43 | } |
| 44 | |
| 45 | return 0; |
| 46 | } |
| 47 | EXPORT_SYMBOL_NS(acp_dsp_block_read, "SND_SOC_SOF_AMD_COMMON" ); |
| 48 | |
| 49 | int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, |
| 50 | u32 offset, void *src, size_t size) |
| 51 | { |
| 52 | struct pci_dev *pci = to_pci_dev(sdev->dev); |
| 53 | struct acp_dev_data *adata; |
| 54 | void *dest; |
| 55 | u32 dma_size, page_count; |
| 56 | unsigned int size_fw; |
| 57 | |
| 58 | adata = sdev->pdata->hw_pdata; |
| 59 | |
| 60 | switch (blk_type) { |
| 61 | case SOF_FW_BLK_TYPE_IRAM: |
| 62 | if (!adata->bin_buf) { |
| 63 | size_fw = sdev->basefw.fw->size; |
| 64 | page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; |
| 65 | dma_size = page_count * ACP_PAGE_SIZE; |
| 66 | adata->bin_buf = dma_alloc_coherent(dev: &pci->dev, size: dma_size, |
| 67 | dma_handle: &adata->sha_dma_addr, |
| 68 | GFP_KERNEL); |
| 69 | if (!adata->bin_buf) |
| 70 | return -ENOMEM; |
| 71 | } |
| 72 | adata->fw_bin_size = size + offset; |
| 73 | dest = adata->bin_buf + offset; |
| 74 | break; |
| 75 | case SOF_FW_BLK_TYPE_DRAM: |
| 76 | if (!adata->data_buf) { |
| 77 | adata->data_buf = dma_alloc_coherent(dev: &pci->dev, |
| 78 | ACP_DEFAULT_DRAM_LENGTH, |
| 79 | dma_handle: &adata->dma_addr, |
| 80 | GFP_KERNEL); |
| 81 | if (!adata->data_buf) |
| 82 | return -ENOMEM; |
| 83 | } |
| 84 | dest = adata->data_buf + offset; |
| 85 | adata->fw_data_bin_size = size + offset; |
| 86 | adata->is_dram_in_use = true; |
| 87 | break; |
| 88 | case SOF_FW_BLK_TYPE_SRAM: |
| 89 | if (!adata->sram_data_buf) { |
| 90 | adata->sram_data_buf = dma_alloc_coherent(dev: &pci->dev, |
| 91 | ACP_DEFAULT_SRAM_LENGTH, |
| 92 | dma_handle: &adata->sram_dma_addr, |
| 93 | GFP_KERNEL); |
| 94 | if (!adata->sram_data_buf) |
| 95 | return -ENOMEM; |
| 96 | } |
| 97 | adata->fw_sram_data_bin_size = size + offset; |
| 98 | dest = adata->sram_data_buf + offset; |
| 99 | adata->is_sram_in_use = true; |
| 100 | break; |
| 101 | default: |
| 102 | dev_err(sdev->dev, "bad blk type 0x%x\n" , blk_type); |
| 103 | return -EINVAL; |
| 104 | } |
| 105 | |
| 106 | memcpy(dest, src, size); |
| 107 | return 0; |
| 108 | } |
| 109 | EXPORT_SYMBOL_NS(acp_dsp_block_write, "SND_SOC_SOF_AMD_COMMON" ); |
| 110 | |
| 111 | int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type) |
| 112 | { |
| 113 | return type; |
| 114 | } |
| 115 | EXPORT_SYMBOL_NS(acp_get_bar_index, "SND_SOC_SOF_AMD_COMMON" ); |
| 116 | |
| 117 | static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev_data *adata) |
| 118 | { |
| 119 | struct snd_sof_dev *sdev = adata->dev; |
| 120 | const struct sof_amd_acp_desc *desc = get_chip_info(pdata: sdev->pdata); |
| 121 | unsigned int low, high; |
| 122 | dma_addr_t addr; |
| 123 | u16 page_idx; |
| 124 | u32 offset; |
| 125 | |
| 126 | switch (type) { |
| 127 | case FW_BIN: |
| 128 | offset = FW_BIN_PTE_OFFSET; |
| 129 | addr = adata->sha_dma_addr; |
| 130 | break; |
| 131 | case FW_DATA_BIN: |
| 132 | offset = adata->fw_bin_page_count * 8; |
| 133 | addr = adata->dma_addr; |
| 134 | break; |
| 135 | case FW_SRAM_DATA_BIN: |
| 136 | offset = (adata->fw_bin_page_count + ACP_DRAM_PAGE_COUNT) * 8; |
| 137 | addr = adata->sram_dma_addr; |
| 138 | break; |
| 139 | default: |
| 140 | dev_err(sdev->dev, "Invalid data type %x\n" , type); |
| 141 | return; |
| 142 | } |
| 143 | |
| 144 | /* Group Enable */ |
| 145 | snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_BASE_ADDR_GRP_1, |
| 146 | value: desc->sram_pte_offset | BIT(31)); |
| 147 | snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1, |
| 148 | PAGE_SIZE_4K_ENABLE); |
| 149 | |
| 150 | for (page_idx = 0; page_idx < num_pages; page_idx++) { |
| 151 | low = lower_32_bits(addr); |
| 152 | high = upper_32_bits(addr); |
| 153 | snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, value: low); |
| 154 | high |= BIT(31); |
| 155 | snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, value: high); |
| 156 | offset += 8; |
| 157 | addr += PAGE_SIZE; |
| 158 | } |
| 159 | |
| 160 | /* Flush ATU Cache after PTE Update */ |
| 161 | snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID); |
| 162 | } |
| 163 | |
| 164 | /* pre fw run operations */ |
| 165 | int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) |
| 166 | { |
| 167 | struct pci_dev *pci = to_pci_dev(sdev->dev); |
| 168 | const struct sof_amd_acp_desc *desc = get_chip_info(pdata: sdev->pdata); |
| 169 | struct acp_dev_data *adata; |
| 170 | unsigned int src_addr, size_fw, dest_addr; |
| 171 | u32 page_count, dma_size; |
| 172 | int ret; |
| 173 | |
| 174 | adata = sdev->pdata->hw_pdata; |
| 175 | |
| 176 | if (adata->quirks && adata->quirks->signed_fw_image) |
| 177 | size_fw = adata->fw_bin_size - ACP_FIRMWARE_SIGNATURE; |
| 178 | else |
| 179 | size_fw = adata->fw_bin_size; |
| 180 | |
| 181 | page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; |
| 182 | adata->fw_bin_page_count = page_count; |
| 183 | |
| 184 | configure_pte_for_fw_loading(FW_BIN, num_pages: page_count, adata); |
| 185 | ret = configure_and_run_sha_dma(adata, image_addr: adata->bin_buf, ACP_SYSTEM_MEMORY_WINDOW, |
| 186 | ACP_IRAM_BASE_ADDRESS, image_length: size_fw); |
| 187 | if (ret < 0) { |
| 188 | dev_err(sdev->dev, "SHA DMA transfer failed status: %d\n" , ret); |
| 189 | return ret; |
| 190 | } |
| 191 | if (adata->is_dram_in_use) { |
| 192 | configure_pte_for_fw_loading(FW_DATA_BIN, ACP_DRAM_PAGE_COUNT, adata); |
| 193 | src_addr = ACP_SYSTEM_MEMORY_WINDOW + (page_count * ACP_PAGE_SIZE); |
| 194 | dest_addr = ACP_DRAM_BASE_ADDRESS; |
| 195 | |
| 196 | ret = configure_and_run_dma(adata, src_addr, dest_addr, dsp_data_size: adata->fw_data_bin_size); |
| 197 | if (ret < 0) { |
| 198 | dev_err(sdev->dev, "acp dma configuration failed: %d\n" , ret); |
| 199 | return ret; |
| 200 | } |
| 201 | ret = acp_dma_status(adata, ch: 0); |
| 202 | if (ret < 0) |
| 203 | dev_err(sdev->dev, "acp dma transfer status: %d\n" , ret); |
| 204 | } |
| 205 | if (adata->is_sram_in_use) { |
| 206 | configure_pte_for_fw_loading(FW_SRAM_DATA_BIN, ACP_SRAM_PAGE_COUNT, adata); |
| 207 | src_addr = ACP_SYSTEM_MEMORY_WINDOW + ACP_DEFAULT_SRAM_LENGTH + |
| 208 | (page_count * ACP_PAGE_SIZE); |
| 209 | if (adata->pci_rev > ACP63_PCI_ID) |
| 210 | dest_addr = ACP7X_SRAM_BASE_ADDRESS; |
| 211 | else |
| 212 | dest_addr = ACP_SRAM_BASE_ADDRESS; |
| 213 | |
| 214 | ret = configure_and_run_dma(adata, src_addr, dest_addr, |
| 215 | dsp_data_size: adata->fw_sram_data_bin_size); |
| 216 | if (ret < 0) { |
| 217 | dev_err(sdev->dev, "acp dma configuration failed: %d\n" , ret); |
| 218 | return ret; |
| 219 | } |
| 220 | ret = acp_dma_status(adata, ch: 0); |
| 221 | if (ret < 0) |
| 222 | dev_err(sdev->dev, "acp dma transfer status: %d\n" , ret); |
| 223 | } |
| 224 | |
| 225 | if (adata->pci_rev > ACP_RN_PCI_ID) { |
| 226 | /* Cache Window enable */ |
| 227 | snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_CACHE_OFFSET0, value: desc->sram_pte_offset); |
| 228 | snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_CACHE_SIZE0, SRAM1_SIZE | BIT(31)); |
| 229 | } |
| 230 | |
| 231 | /* Free memory once DMA is complete */ |
| 232 | dma_size = (PAGE_ALIGN(sdev->basefw.fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE; |
| 233 | dma_free_coherent(dev: &pci->dev, size: dma_size, cpu_addr: adata->bin_buf, dma_handle: adata->sha_dma_addr); |
| 234 | adata->bin_buf = NULL; |
| 235 | if (adata->is_dram_in_use) { |
| 236 | dma_free_coherent(dev: &pci->dev, ACP_DEFAULT_DRAM_LENGTH, cpu_addr: adata->data_buf, |
| 237 | dma_handle: adata->dma_addr); |
| 238 | adata->data_buf = NULL; |
| 239 | } |
| 240 | if (adata->is_sram_in_use) { |
| 241 | dma_free_coherent(dev: &pci->dev, ACP_DEFAULT_SRAM_LENGTH, cpu_addr: adata->sram_data_buf, |
| 242 | dma_handle: adata->sram_dma_addr); |
| 243 | adata->sram_data_buf = NULL; |
| 244 | } |
| 245 | return ret; |
| 246 | } |
| 247 | EXPORT_SYMBOL_NS(acp_dsp_pre_fw_run, "SND_SOC_SOF_AMD_COMMON" ); |
| 248 | |
| 249 | int acp_sof_dsp_run(struct snd_sof_dev *sdev) |
| 250 | { |
| 251 | struct acp_dev_data *adata = sdev->pdata->hw_pdata; |
| 252 | const struct sof_amd_acp_desc *desc = get_chip_info(pdata: sdev->pdata); |
| 253 | int val; |
| 254 | |
| 255 | snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL, ACP_DSP_RUN); |
| 256 | val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL); |
| 257 | dev_dbg(sdev->dev, "ACP_DSP0_RUNSTALL : 0x%0x\n" , val); |
| 258 | |
| 259 | /* Some platforms won't support fusion DSP,keep offset zero for no support */ |
| 260 | if (desc->fusion_dsp_offset && adata->enable_fw_debug) { |
| 261 | snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset: desc->fusion_dsp_offset, ACP_DSP_RUN); |
| 262 | val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, offset: desc->fusion_dsp_offset); |
| 263 | dev_dbg(sdev->dev, "ACP_DSP0_FUSION_RUNSTALL : 0x%0x\n" , val); |
| 264 | } |
| 265 | return 0; |
| 266 | } |
| 267 | EXPORT_SYMBOL_NS(acp_sof_dsp_run, "SND_SOC_SOF_AMD_COMMON" ); |
| 268 | |
| 269 | int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev) |
| 270 | { |
| 271 | struct snd_sof_pdata *plat_data = sdev->pdata; |
| 272 | struct acp_dev_data *adata = plat_data->hw_pdata; |
| 273 | const char *fw_filename; |
| 274 | int ret; |
| 275 | |
| 276 | fw_filename = kasprintf(GFP_KERNEL, fmt: "%s/%s" , |
| 277 | plat_data->fw_filename_prefix, |
| 278 | adata->fw_code_bin); |
| 279 | if (!fw_filename) |
| 280 | return -ENOMEM; |
| 281 | |
| 282 | ret = request_firmware(fw: &sdev->basefw.fw, name: fw_filename, device: sdev->dev); |
| 283 | if (ret < 0) { |
| 284 | kfree(objp: fw_filename); |
| 285 | dev_err(sdev->dev, "sof signed firmware code bin is missing\n" ); |
| 286 | return ret; |
| 287 | } else { |
| 288 | dev_dbg(sdev->dev, "request_firmware %s successful\n" , fw_filename); |
| 289 | } |
| 290 | kfree(objp: fw_filename); |
| 291 | |
| 292 | ret = snd_sof_dsp_block_write(sdev, blk_type: SOF_FW_BLK_TYPE_IRAM, offset: 0, |
| 293 | src: (void *)sdev->basefw.fw->data, |
| 294 | bytes: sdev->basefw.fw->size); |
| 295 | if (ret < 0) |
| 296 | return ret; |
| 297 | |
| 298 | fw_filename = kasprintf(GFP_KERNEL, fmt: "%s/%s" , |
| 299 | plat_data->fw_filename_prefix, |
| 300 | adata->fw_data_bin); |
| 301 | if (!fw_filename) |
| 302 | return -ENOMEM; |
| 303 | |
| 304 | ret = request_firmware(fw: &adata->fw_dbin, name: fw_filename, device: sdev->dev); |
| 305 | if (ret < 0) { |
| 306 | kfree(objp: fw_filename); |
| 307 | dev_err(sdev->dev, "sof signed firmware data bin is missing\n" ); |
| 308 | return ret; |
| 309 | |
| 310 | } else { |
| 311 | dev_dbg(sdev->dev, "request_firmware %s successful\n" , fw_filename); |
| 312 | } |
| 313 | kfree(objp: fw_filename); |
| 314 | |
| 315 | ret = snd_sof_dsp_block_write(sdev, blk_type: SOF_FW_BLK_TYPE_DRAM, offset: 0, |
| 316 | src: (void *)adata->fw_dbin->data, |
| 317 | bytes: adata->fw_dbin->size); |
| 318 | return ret; |
| 319 | } |
| 320 | EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, "SND_SOC_SOF_AMD_COMMON" ); |
| 321 | |