// 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, 2023 Advanced Micro Devices, Inc. All rights reserved.
//
// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
// Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
/*
* Hardware interface for generic AMD ACP processor
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <asm/amd/node.h>
#include "../ops.h"
#include "acp.h"
#include "acp-dsp-offset.h"
static bool enable_fw_debug;
module_param(enable_fw_debug, bool, 0444);
MODULE_PARM_DESC(enable_fw_debug, "Enable Firmware debug");
static struct acp_quirk_entry quirk_valve_galileo = {
.signed_fw_image = true,
.skip_iram_dram_size_mod = true,
.post_fw_run_delay = true,
};
const struct dmi_system_id acp_sof_quirk_table[] = {
{
/* Steam Deck OLED device */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"),
},
.driver_data = &quirk_valve_galileo,
},
{}
};
EXPORT_SYMBOL_GPL(acp_sof_quirk_table);
static void init_dma_descriptor(struct acp_dev_data *adata)
{
struct snd_sof_dev *sdev = adata->dev;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
unsigned int addr;
unsigned int acp_dma_desc_base_addr, acp_dma_desc_max_num_dscr;
addr = desc->sram_pte_offset + sdev->debug_box.offset +
offsetof(struct scratch_reg_conf, dma_desc);
switch (acp_data->pci_rev) {
case ACP70_PCI_ID:
case ACP71_PCI_ID:
case ACP72_PCI_ID:
acp_dma_desc_base_addr = ACP70_DMA_DESC_BASE_ADDR;
acp_dma_desc_max_num_dscr = ACP70_DMA_DESC_MAX_NUM_DSCR;
break;
default:
acp_dma_desc_base_addr = ACP_DMA_DESC_BASE_ADDR;
acp_dma_desc_max_num_dscr = ACP_DMA_DESC_MAX_NUM_DSCR;
}
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 + sdev->debug_box.offset +
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;
struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
unsigned int val, status;
unsigned int acp_dma_cntl_0, acp_dma_ch_rst_sts, acp_dma_dscr_err_sts_0;
unsigned int acp_dma_dscr_cnt_0, acp_dma_prio_0, acp_dma_dscr_strt_idx_0;
int ret;
switch (acp_data->pci_rev) {
case ACP70_PCI_ID:
case ACP71_PCI_ID:
case ACP72_PCI_ID:
acp_dma_cntl_0 = ACP70_DMA_CNTL_0;
acp_dma_ch_rst_sts = ACP70_DMA_CH_RST_STS;
acp_dma_dscr_err_sts_0 = ACP70_DMA_ERR_STS_0;
acp_dma_dscr_cnt_0 = ACP70_DMA_DSCR_CNT_0;
acp_dma_prio_0 = ACP70_DMA_PRIO_0;
acp_dma_dscr_strt_idx_0 = ACP70_DMA_DSCR_STRT_IDX_0;
break;
default:
acp_dma_cntl_0 = ACP_DMA_CNTL_0;
acp_dma_ch_rst_sts = ACP_DMA_CH_RST_STS;
acp_dma_dscr_err_sts_0 = ACP_DMA_ERR_STS_0;
acp_dma_dscr_cnt_0 = ACP_DMA_DSCR_CNT_0;
acp_dma_prio_0 = ACP_DMA_PRIO_0;
acp_dma_dscr_strt_idx_0 = ACP_DMA_DSCR_STRT_IDX_0;
}
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, desc->acp_error