1255 lines · 34.7 KB · C
RawDownload
// 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/acpi.h>#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:	case ACP7B_PCI_ID:	case ACP7F_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:	case ACP7B_PCI_ID:	case ACP7F_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_stat);		val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, acp_dma_dscr_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;} /* * psp_mbox_ready- function to poll ready bit of psp mbox * @adata: acp device data * @ack: bool variable to check ready bit status or psp ack */ static int psp_mbox_ready(struct acp_dev_data *adata, bool ack){	struct snd_sof_dev *sdev = adata->dev;	int ret, data; 	ret = read_poll_timeout(smn_read_register, data, data > 0 && data & MBOX_READY_MASK,				MBOX_DELAY_US, ACP_PSP_TIMEOUT_US, false, MP0_C2PMSG_114_REG); 	if (!ret)		return 0; 	dev_err(sdev->dev, "PSP error status %x\n", data & MBOX_STATUS_MASK); 	if (ack)		return -ETIMEDOUT; 	return -EBUSY;} /* * psp_send_cmd - function to send psp command over mbox * @adata: acp device data * @cmd: non zero integer value for command type */ static int psp_send_cmd(struct acp_dev_data *adata, int cmd){	struct snd_sof_dev *sdev = adata->dev;	int ret;	int data; 	if (!cmd)		return -EINVAL; 	/* Get a non-zero Doorbell value from PSP */	ret = read_poll_timeout(smn_read_register, data, data > 0, MBOX_DELAY_US,				ACP_PSP_TIMEOUT_US, false, MP0_C2PMSG_73_REG); 	if (ret) {		dev_err(sdev->dev, "Failed to get Doorbell from MBOX %x\n", MP0_C2PMSG_73_REG);		return ret;	} 	/* Check if PSP is ready for new command */	ret = psp_mbox_ready(adata, 0);	if (ret)		return ret; 	ret = amd_smn_write(0, MP0_C2PMSG_114_REG, cmd);	if (ret)		return ret; 	/* Ring the Doorbell for PSP */	ret = amd_smn_write(0, MP0_C2PMSG_73_REG, data);	if (ret)		return ret; 	/* Check MBOX ready as PSP ack */	ret = psp_mbox_ready(adata, 1); 	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;		}	} 	if ((adata->quirks && adata->quirks->signed_fw_image) ||	    adata->acp_sof_signed_firmware_image)		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_INCLUDE_HDR, ACP_SHA_HEADER); 	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); 	/* psp_send_cmd only required for vangogh platform */	if (adata->pci_rev == ACP_VANGOGH_PCI_ID &&	    !(adata->quirks && adata->quirks->skip_iram_dram_size_mod)) {		/* Modify IRAM and DRAM size */		ret = psp_send_cmd(adata, MBOX_ACP_IRAM_DRAM_FENCE_COMMAND | IRAM_DRAM_FENCE_2);		if (ret)			return ret;		ret = psp_send_cmd(adata, MBOX_ACP_IRAM_DRAM_FENCE_COMMAND | MBOX_ISREADY_FLAG);		if (ret)			return ret;	}	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;	} 	/* psp_send_cmd only required for renoir platform*/	if (adata->pci_rev == ACP_RN_PCI_ID) {		ret = psp_send_cmd(adata, MBOX_ACP_SHA_DMA_COMMAND);		if (ret)			return ret;	} 	ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER,					    fw_qualifier, fw_qualifier & DSP_FW_RUN_ENABLE,					    ACP_REG_POLL_INTERVAL, ACP_DMA_COMPLETE_TIMEOUT_US);	if (ret < 0) {		val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_PSP_ACK);		dev_err(sdev->dev, "PSP validation failed: fw_qualifier = %#x, ACP_SHA_PSP_ACK = %#x\n",			fw_qualifier, val);		return ret;	} 	return 0;} int acp_dma_status(struct acp_dev_data *adata, unsigned char ch){	struct snd_sof_dev *sdev = adata->dev;	unsigned int val;	unsigned int acp_dma_ch_sts;	int ret = 0; 	switch (adata->pci_rev) {	case ACP70_PCI_ID:	case ACP71_PCI_ID:	case ACP72_PCI_ID:	case ACP7B_PCI_ID:	case ACP7F_PCI_ID:		acp_dma_ch_sts = ACP70_DMA_CH_STS;		break;	default:		acp_dma_ch_sts = ACP_DMA_CH_STS;	}	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_init_scratch_mem_ipc_flags(struct snd_sof_dev *sdev){	u32 dsp_msg_write, dsp_ack_write, host_msg_write, host_ack_write; 	dsp_msg_write = sdev->debug_box.offset +			offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);	dsp_ack_write = sdev->debug_box.offset +			offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);	host_msg_write = sdev->debug_box.offset +			 offsetof(struct scratch_ipc_conf, sof_host_msg_write);	host_ack_write = sdev->debug_box.offset +			 offsetof(struct scratch_ipc_conf, sof_host_ack_write);	/* Initialize host message write flag */	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_msg_write, 0); 	/* Initialize host ack write flag */	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_ack_write, 0); 	/* Initialize DSP message write flag */	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write, 0); 	/* Initialize DSP ack write flag */	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write, 0); 	return 0;} static int acp_memory_init(struct snd_sof_dev *sdev){	struct acp_dev_data *adata = sdev->pdata->hw_pdata;	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); 	snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, desc->dsp_intr_base + DSP_SW_INTR_CNTL_OFFSET,				ACP_DSP_INTR_EN_MASK, ACP_DSP_INTR_EN_MASK);	acp_init_scratch_mem_ipc_flags(sdev);	init_dma_descriptor(adata); 	return 0;} static void amd_sof_handle_acp70_sdw_wake_event(struct acp_dev_data *adata){	struct amd_sdw_manager *amd_manager; 	if (adata->acp70_sdw0_wake_event) {		amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);		if (amd_manager)			pm_request_resume(amd_manager->dev);		adata->acp70_sdw0_wake_event = 0;	} 	if (adata->acp70_sdw1_wake_event) {		amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);		if (amd_manager)			pm_request_resume(amd_manager->dev);		adata->acp70_sdw1_wake_event = 0;	}} static int amd_sof_check_and_handle_acp70_sdw_wake_irq(struct snd_sof_dev *sdev){	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);	struct acp_dev_data *adata = sdev->pdata->hw_pdata;	u32 ext_intr_stat1;	int irq_flag = 0;	bool sdw_wake_irq = false; 	ext_intr_stat1 = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat1);	if (ext_intr_stat1 & ACP70_SDW0_HOST_WAKE_STAT) {		snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1,				  ACP70_SDW0_HOST_WAKE_STAT);		adata->acp70_sdw0_wake_event = true;		sdw_wake_irq = true;	} 	if (ext_intr_stat1 & ACP70_SDW1_HOST_WAKE_STAT) {		snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1,				  ACP70_SDW1_HOST_WAKE_STAT);		adata->acp70_sdw1_wake_event = true;		sdw_wake_irq = true;	} 	if (ext_intr_stat1 & ACP70_SDW0_PME_STAT) {		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_SW0_WAKE_EN, 0);		snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1, ACP70_SDW0_PME_STAT);		adata->acp70_sdw0_wake_event = true;		sdw_wake_irq = true;	} 	if (ext_intr_stat1 & ACP70_SDW1_PME_STAT) {		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_SW1_WAKE_EN, 0);		snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1, ACP70_SDW1_PME_STAT);		adata->acp70_sdw1_wake_event = true;		sdw_wake_irq = true;	} 	if (sdw_wake_irq) {		amd_sof_handle_acp70_sdw_wake_event(adata);		irq_flag = 1;	}	return irq_flag;} static irqreturn_t acp_irq_thread(int irq, void *context){	struct snd_sof_dev *sdev = context;	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);	unsigned int count = ACP_HW_SEM_RETRY_COUNT; 	spin_lock_irq(&sdev->ipc_lock);	/* Wait until acquired HW Semaphore lock or timeout */	while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset) && --count)		;

Lines 1–500 of 1255.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t