188 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
 | |
| //
 | |
| // 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 Advanced Micro Devices, Inc.
 | |
| //
 | |
| // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
 | |
| 
 | |
| /*
 | |
|  * Hardware interface for generic AMD audio DSP ACP IP
 | |
|  */
 | |
| 
 | |
| #include "../ops.h"
 | |
| #include "acp-dsp-offset.h"
 | |
| #include "acp.h"
 | |
| 
 | |
| #define PTE_GRP1_OFFSET		0x00000000
 | |
| #define PTE_GRP2_OFFSET		0x00800000
 | |
| #define PTE_GRP3_OFFSET		0x01000000
 | |
| #define PTE_GRP4_OFFSET		0x01800000
 | |
| #define PTE_GRP5_OFFSET		0x02000000
 | |
| #define PTE_GRP6_OFFSET		0x02800000
 | |
| #define PTE_GRP7_OFFSET		0x03000000
 | |
| #define PTE_GRP8_OFFSET		0x03800000
 | |
| 
 | |
| int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *stream)
 | |
| {
 | |
| 	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
 | |
| 	unsigned int pte_reg, pte_size, phy_addr_offset, index;
 | |
| 	int stream_tag = stream->stream_tag;
 | |
| 	u32 low, high, offset, reg_val;
 | |
| 	dma_addr_t addr;
 | |
| 	int page_idx;
 | |
| 
 | |
| 	switch (stream_tag) {
 | |
| 	case 1:
 | |
| 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_1;
 | |
| 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1;
 | |
| 		offset = offsetof(struct scratch_reg_conf, grp1_pte);
 | |
| 		stream->reg_offset = PTE_GRP1_OFFSET;
 | |
| 		break;
 | |
| 	case 2:
 | |
| 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_2;
 | |
| 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2;
 | |
| 		offset = offsetof(struct scratch_reg_conf, grp2_pte);
 | |
| 		stream->reg_offset = PTE_GRP2_OFFSET;
 | |
| 		break;
 | |
| 	case 3:
 | |
| 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_3;
 | |
| 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3;
 | |
| 		offset = offsetof(struct scratch_reg_conf, grp3_pte);
 | |
| 		stream->reg_offset = PTE_GRP3_OFFSET;
 | |
| 		break;
 | |
| 	case 4:
 | |
| 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_4;
 | |
| 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4;
 | |
| 		offset = offsetof(struct scratch_reg_conf, grp4_pte);
 | |
| 		stream->reg_offset = PTE_GRP4_OFFSET;
 | |
| 		break;
 | |
| 	case 5:
 | |
| 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5;
 | |
| 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5;
 | |
| 		offset = offsetof(struct scratch_reg_conf, grp5_pte);
 | |
| 		stream->reg_offset = PTE_GRP5_OFFSET;
 | |
| 		break;
 | |
| 	case 6:
 | |
| 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_6;
 | |
| 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6;
 | |
| 		offset = offsetof(struct scratch_reg_conf, grp6_pte);
 | |
| 		stream->reg_offset = PTE_GRP6_OFFSET;
 | |
| 		break;
 | |
| 	case 7:
 | |
| 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_7;
 | |
| 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7;
 | |
| 		offset = offsetof(struct scratch_reg_conf, grp7_pte);
 | |
| 		stream->reg_offset = PTE_GRP7_OFFSET;
 | |
| 		break;
 | |
| 	case 8:
 | |
| 		pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_8;
 | |
| 		pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8;
 | |
| 		offset = offsetof(struct scratch_reg_conf, grp8_pte);
 | |
| 		stream->reg_offset = PTE_GRP8_OFFSET;
 | |
| 		break;
 | |
| 	default:
 | |
| 		dev_err(sdev->dev, "Invalid stream tag %d\n", stream_tag);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	/* write phy_addr in scratch memory */
 | |
| 
 | |
| 	phy_addr_offset = sdev->debug_box.offset +
 | |
| 			  offsetof(struct scratch_reg_conf, reg_offset);
 | |
| 	index = stream_tag - 1;
 | |
| 	phy_addr_offset = phy_addr_offset + index * 4;
 | |
| 
 | |
| 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 +
 | |
| 			  phy_addr_offset, stream->reg_offset);
 | |
| 
 | |
| 	/* Group Enable */
 | |
| 	offset = offset + sdev->debug_box.offset;
 | |
| 	reg_val = desc->sram_pte_offset + offset;
 | |
| 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_reg, reg_val | BIT(31));
 | |
| 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_size, PAGE_SIZE_4K_ENABLE);
 | |
| 
 | |
| 	for (page_idx = 0; page_idx < stream->num_pages; page_idx++) {
 | |
| 		addr = snd_sgbuf_get_addr(stream->dmab, page_idx * PAGE_SIZE);
 | |
| 
 | |
| 		/* Load the low address of page int ACP SRAM through SRBM */
 | |
| 		low = lower_32_bits(addr);
 | |
| 		high = upper_32_bits(addr);
 | |
| 
 | |
| 		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low);
 | |
| 
 | |
| 		high |= BIT(31);
 | |
| 		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high);
 | |
| 		/* Move to next physically contiguous page */
 | |
| 		offset += 8;
 | |
| 	}
 | |
| 
 | |
| 	/* Flush ATU Cache after PTE Update */
 | |
| 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag)
 | |
| {
 | |
| 	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
 | |
| 	struct acp_dsp_stream *stream = adata->stream_buf;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < ACP_MAX_STREAM; i++, stream++) {
 | |
| 		if (stream->active)
 | |
| 			continue;
 | |
| 
 | |
| 		/* return stream if tag not specified*/
 | |
| 		if (!tag) {
 | |
| 			stream->active = 1;
 | |
| 			return stream;
 | |
| 		}
 | |
| 
 | |
| 		/* check if this is the requested stream tag */
 | |
| 		if (stream->stream_tag == tag) {
 | |
| 			stream->active = 1;
 | |
| 			return stream;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	dev_err(sdev->dev, "stream %d active or no inactive stream\n", tag);
 | |
| 	return NULL;
 | |
| }
 | |
| EXPORT_SYMBOL(acp_dsp_stream_get);
 | |
| 
 | |
| int acp_dsp_stream_put(struct snd_sof_dev *sdev,
 | |
| 		       struct acp_dsp_stream *acp_stream)
 | |
| {
 | |
| 	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
 | |
| 	struct acp_dsp_stream *stream = adata->stream_buf;
 | |
| 	int i;
 | |
| 
 | |
| 	/* Free an active stream */
 | |
| 	for (i = 0; i < ACP_MAX_STREAM; i++, stream++) {
 | |
| 		if (stream == acp_stream) {
 | |
| 			stream->active = 0;
 | |
| 			return 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	dev_err(sdev->dev, "Cannot find active stream tag %d\n", acp_stream->stream_tag);
 | |
| 	return -EINVAL;
 | |
| }
 | |
| EXPORT_SYMBOL(acp_dsp_stream_put);
 | |
| 
 | |
| int acp_dsp_stream_init(struct snd_sof_dev *sdev)
 | |
| {
 | |
| 	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < ACP_MAX_STREAM; i++) {
 | |
| 		adata->stream_buf[i].sdev = sdev;
 | |
| 		adata->stream_buf[i].active = 0;
 | |
| 		adata->stream_buf[i].stream_tag = i + 1;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| EXPORT_SYMBOL(acp_dsp_stream_init);
 |