162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or
462306a36Sopenharmony_ci// redistributing this file, you may do so under either license.
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc.
762306a36Sopenharmony_ci//
862306a36Sopenharmony_ci// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/*
1162306a36Sopenharmony_ci * Hardware interface for ACP DSP Firmware binaries loader
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/firmware.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/pci.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "../ops.h"
1962306a36Sopenharmony_ci#include "acp-dsp-offset.h"
2062306a36Sopenharmony_ci#include "acp.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define FW_BIN		0
2362306a36Sopenharmony_ci#define FW_DATA_BIN	1
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define FW_BIN_PTE_OFFSET	0x00
2662306a36Sopenharmony_ci#define FW_DATA_BIN_PTE_OFFSET	0x08
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define ACP_DSP_RUN	0x00
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciint acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
3162306a36Sopenharmony_ci		       u32 offset, void *dest, size_t size)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
3462306a36Sopenharmony_ci	switch (blk_type) {
3562306a36Sopenharmony_ci	case SOF_FW_BLK_TYPE_SRAM:
3662306a36Sopenharmony_ci		offset = offset - desc->sram_pte_offset;
3762306a36Sopenharmony_ci		memcpy_from_scratch(sdev, offset, dest, size);
3862306a36Sopenharmony_ci		break;
3962306a36Sopenharmony_ci	default:
4062306a36Sopenharmony_ci		dev_err(sdev->dev, "bad blk type 0x%x\n", blk_type);
4162306a36Sopenharmony_ci		return -EINVAL;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ciEXPORT_SYMBOL_NS(acp_dsp_block_read, SND_SOC_SOF_AMD_COMMON);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciint acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
4962306a36Sopenharmony_ci			u32 offset, void *src, size_t size)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct pci_dev *pci = to_pci_dev(sdev->dev);
5262306a36Sopenharmony_ci	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
5362306a36Sopenharmony_ci	struct acp_dev_data *adata;
5462306a36Sopenharmony_ci	void *dest;
5562306a36Sopenharmony_ci	u32 dma_size, page_count;
5662306a36Sopenharmony_ci	unsigned int size_fw;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	adata = sdev->pdata->hw_pdata;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	switch (blk_type) {
6162306a36Sopenharmony_ci	case SOF_FW_BLK_TYPE_IRAM:
6262306a36Sopenharmony_ci		if (!adata->bin_buf) {
6362306a36Sopenharmony_ci			size_fw = sdev->basefw.fw->size;
6462306a36Sopenharmony_ci			page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT;
6562306a36Sopenharmony_ci			dma_size = page_count * ACP_PAGE_SIZE;
6662306a36Sopenharmony_ci			adata->bin_buf = dma_alloc_coherent(&pci->dev, dma_size,
6762306a36Sopenharmony_ci							    &adata->sha_dma_addr,
6862306a36Sopenharmony_ci							    GFP_ATOMIC);
6962306a36Sopenharmony_ci			if (!adata->bin_buf)
7062306a36Sopenharmony_ci				return -ENOMEM;
7162306a36Sopenharmony_ci		}
7262306a36Sopenharmony_ci		adata->fw_bin_size = size + offset;
7362306a36Sopenharmony_ci		dest = adata->bin_buf + offset;
7462306a36Sopenharmony_ci		break;
7562306a36Sopenharmony_ci	case SOF_FW_BLK_TYPE_DRAM:
7662306a36Sopenharmony_ci		if (!adata->data_buf) {
7762306a36Sopenharmony_ci			adata->data_buf = dma_alloc_coherent(&pci->dev,
7862306a36Sopenharmony_ci							     ACP_DEFAULT_DRAM_LENGTH,
7962306a36Sopenharmony_ci							     &adata->dma_addr,
8062306a36Sopenharmony_ci							     GFP_ATOMIC);
8162306a36Sopenharmony_ci			if (!adata->data_buf)
8262306a36Sopenharmony_ci				return -ENOMEM;
8362306a36Sopenharmony_ci		}
8462306a36Sopenharmony_ci		dest = adata->data_buf + offset;
8562306a36Sopenharmony_ci		adata->fw_data_bin_size = size + offset;
8662306a36Sopenharmony_ci		break;
8762306a36Sopenharmony_ci	case SOF_FW_BLK_TYPE_SRAM:
8862306a36Sopenharmony_ci		offset = offset - desc->sram_pte_offset;
8962306a36Sopenharmony_ci		memcpy_to_scratch(sdev, offset, src, size);
9062306a36Sopenharmony_ci		return 0;
9162306a36Sopenharmony_ci	default:
9262306a36Sopenharmony_ci		dev_err(sdev->dev, "bad blk type 0x%x\n", blk_type);
9362306a36Sopenharmony_ci		return -EINVAL;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	memcpy(dest, src, size);
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ciEXPORT_SYMBOL_NS(acp_dsp_block_write, SND_SOC_SOF_AMD_COMMON);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ciint acp_get_bar_index(struct snd_sof_dev *sdev, u32 type)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	return type;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ciEXPORT_SYMBOL_NS(acp_get_bar_index, SND_SOC_SOF_AMD_COMMON);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev_data *adata)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct snd_sof_dev *sdev = adata->dev;
11062306a36Sopenharmony_ci	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
11162306a36Sopenharmony_ci	unsigned int low, high;
11262306a36Sopenharmony_ci	dma_addr_t addr;
11362306a36Sopenharmony_ci	u16 page_idx;
11462306a36Sopenharmony_ci	u32 offset;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	switch (type) {
11762306a36Sopenharmony_ci	case FW_BIN:
11862306a36Sopenharmony_ci		offset = FW_BIN_PTE_OFFSET;
11962306a36Sopenharmony_ci		addr = adata->sha_dma_addr;
12062306a36Sopenharmony_ci		break;
12162306a36Sopenharmony_ci	case FW_DATA_BIN:
12262306a36Sopenharmony_ci		offset = adata->fw_bin_page_count * 8;
12362306a36Sopenharmony_ci		addr = adata->dma_addr;
12462306a36Sopenharmony_ci		break;
12562306a36Sopenharmony_ci	default:
12662306a36Sopenharmony_ci		dev_err(sdev->dev, "Invalid data type %x\n", type);
12762306a36Sopenharmony_ci		return;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* Group Enable */
13162306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_BASE_ADDR_GRP_1,
13262306a36Sopenharmony_ci			  desc->sram_pte_offset | BIT(31));
13362306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1,
13462306a36Sopenharmony_ci			  PAGE_SIZE_4K_ENABLE);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	for (page_idx = 0; page_idx < num_pages; page_idx++) {
13762306a36Sopenharmony_ci		low = lower_32_bits(addr);
13862306a36Sopenharmony_ci		high = upper_32_bits(addr);
13962306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low);
14062306a36Sopenharmony_ci		high |= BIT(31);
14162306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high);
14262306a36Sopenharmony_ci		offset += 8;
14362306a36Sopenharmony_ci		addr += PAGE_SIZE;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* Flush ATU Cache after PTE Update */
14762306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci/* pre fw run operations */
15162306a36Sopenharmony_ciint acp_dsp_pre_fw_run(struct snd_sof_dev *sdev)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct pci_dev *pci = to_pci_dev(sdev->dev);
15462306a36Sopenharmony_ci	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
15562306a36Sopenharmony_ci	struct acp_dev_data *adata;
15662306a36Sopenharmony_ci	unsigned int src_addr, size_fw;
15762306a36Sopenharmony_ci	u32 page_count, dma_size;
15862306a36Sopenharmony_ci	int ret;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	adata = sdev->pdata->hw_pdata;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (adata->signed_fw_image)
16362306a36Sopenharmony_ci		size_fw = adata->fw_bin_size - ACP_FIRMWARE_SIGNATURE;
16462306a36Sopenharmony_ci	else
16562306a36Sopenharmony_ci		size_fw = adata->fw_bin_size;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT;
16862306a36Sopenharmony_ci	adata->fw_bin_page_count = page_count;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	configure_pte_for_fw_loading(FW_BIN, page_count, adata);
17162306a36Sopenharmony_ci	ret = configure_and_run_sha_dma(adata, adata->bin_buf, ACP_SYSTEM_MEMORY_WINDOW,
17262306a36Sopenharmony_ci					ACP_IRAM_BASE_ADDRESS, size_fw);
17362306a36Sopenharmony_ci	if (ret < 0) {
17462306a36Sopenharmony_ci		dev_err(sdev->dev, "SHA DMA transfer failed status: %d\n", ret);
17562306a36Sopenharmony_ci		return ret;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci	configure_pte_for_fw_loading(FW_DATA_BIN, ACP_DRAM_PAGE_COUNT, adata);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	src_addr = ACP_SYSTEM_MEMORY_WINDOW + page_count * ACP_PAGE_SIZE;
18062306a36Sopenharmony_ci	ret = configure_and_run_dma(adata, src_addr, ACP_DATA_RAM_BASE_ADDRESS,
18162306a36Sopenharmony_ci				    adata->fw_data_bin_size);
18262306a36Sopenharmony_ci	if (ret < 0) {
18362306a36Sopenharmony_ci		dev_err(sdev->dev, "acp dma configuration failed: %d\n", ret);
18462306a36Sopenharmony_ci		return ret;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	ret = acp_dma_status(adata, 0);
18862306a36Sopenharmony_ci	if (ret < 0)
18962306a36Sopenharmony_ci		dev_err(sdev->dev, "acp dma transfer status: %d\n", ret);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (desc->rev > 3) {
19262306a36Sopenharmony_ci		/* Cache Window enable */
19362306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_CACHE_OFFSET0, desc->sram_pte_offset);
19462306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_CACHE_SIZE0, SRAM1_SIZE | BIT(31));
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/* Free memory once DMA is complete */
19862306a36Sopenharmony_ci	dma_size =  (PAGE_ALIGN(sdev->basefw.fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE;
19962306a36Sopenharmony_ci	dma_free_coherent(&pci->dev, dma_size, adata->bin_buf, adata->sha_dma_addr);
20062306a36Sopenharmony_ci	dma_free_coherent(&pci->dev, ACP_DEFAULT_DRAM_LENGTH, adata->data_buf, adata->dma_addr);
20162306a36Sopenharmony_ci	adata->bin_buf = NULL;
20262306a36Sopenharmony_ci	adata->data_buf = NULL;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return ret;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ciEXPORT_SYMBOL_NS(acp_dsp_pre_fw_run, SND_SOC_SOF_AMD_COMMON);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ciint acp_sof_dsp_run(struct snd_sof_dev *sdev)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
21162306a36Sopenharmony_ci	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
21262306a36Sopenharmony_ci	int val;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL, ACP_DSP_RUN);
21562306a36Sopenharmony_ci	val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP0_RUNSTALL);
21662306a36Sopenharmony_ci	dev_dbg(sdev->dev, "ACP_DSP0_RUNSTALL : 0x%0x\n", val);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* Some platforms won't support fusion DSP,keep offset zero for no support */
21962306a36Sopenharmony_ci	if (desc->fusion_dsp_offset && adata->enable_fw_debug) {
22062306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->fusion_dsp_offset, ACP_DSP_RUN);
22162306a36Sopenharmony_ci		val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->fusion_dsp_offset);
22262306a36Sopenharmony_ci		dev_dbg(sdev->dev, "ACP_DSP0_FUSION_RUNSTALL : 0x%0x\n", val);
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci	return 0;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ciEXPORT_SYMBOL_NS(acp_sof_dsp_run, SND_SOC_SOF_AMD_COMMON);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ciint acp_sof_load_signed_firmware(struct snd_sof_dev *sdev)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct snd_sof_pdata *plat_data = sdev->pdata;
23162306a36Sopenharmony_ci	struct acp_dev_data *adata = plat_data->hw_pdata;
23262306a36Sopenharmony_ci	int ret;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	ret = request_firmware(&sdev->basefw.fw, adata->fw_code_bin, sdev->dev);
23562306a36Sopenharmony_ci	if (ret < 0) {
23662306a36Sopenharmony_ci		dev_err(sdev->dev, "sof signed firmware code bin is missing\n");
23762306a36Sopenharmony_ci		return ret;
23862306a36Sopenharmony_ci	} else {
23962306a36Sopenharmony_ci		dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_code_bin);
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci	ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM, 0,
24262306a36Sopenharmony_ci				      (void *)sdev->basefw.fw->data, sdev->basefw.fw->size);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	ret = request_firmware(&adata->fw_dbin, adata->fw_data_bin, sdev->dev);
24562306a36Sopenharmony_ci	if (ret < 0) {
24662306a36Sopenharmony_ci		dev_err(sdev->dev, "sof signed firmware data bin is missing\n");
24762306a36Sopenharmony_ci		return ret;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	} else {
25062306a36Sopenharmony_ci		dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_data_bin);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0,
25462306a36Sopenharmony_ci				      (void *)adata->fw_dbin->data, adata->fw_dbin->size);
25562306a36Sopenharmony_ci	return ret;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ciEXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, SND_SOC_SOF_AMD_COMMON);
258