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 Advanced Micro Devices, Inc. 7 // 8 // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> 9 10 /* 11 * Hardware interface for generic AMD audio DSP ACP IP 12 */ 13 14 #include "../ops.h" 15 #include "acp-dsp-offset.h" 16 #include "acp.h" 17 18 #define PTE_GRP1_OFFSET 0x00000000 19 #define PTE_GRP2_OFFSET 0x00800000 20 #define PTE_GRP3_OFFSET 0x01000000 21 #define PTE_GRP4_OFFSET 0x01800000 22 #define PTE_GRP5_OFFSET 0x02000000 23 #define PTE_GRP6_OFFSET 0x02800000 24 #define PTE_GRP7_OFFSET 0x03000000 25 #define PTE_GRP8_OFFSET 0x03800000 26 27 int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *stream) 28 { 29 unsigned int pte_reg, pte_size, phy_addr_offset, index; 30 int stream_tag = stream->stream_tag; 31 u32 low, high, offset, reg_val; 32 dma_addr_t addr; 33 int page_idx; 34 35 switch (stream_tag) { 36 case 1: 37 pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_1; 38 pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1; 39 offset = offsetof(struct scratch_reg_conf, grp1_pte); 40 stream->reg_offset = PTE_GRP1_OFFSET; 41 break; 42 case 2: 43 pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_2; 44 pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2; 45 offset = offsetof(struct scratch_reg_conf, grp2_pte); 46 stream->reg_offset = PTE_GRP2_OFFSET; 47 break; 48 case 3: 49 pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_3; 50 pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3; 51 offset = offsetof(struct scratch_reg_conf, grp3_pte); 52 stream->reg_offset = PTE_GRP3_OFFSET; 53 break; 54 case 4: 55 pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_4; 56 pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4; 57 offset = offsetof(struct scratch_reg_conf, grp4_pte); 58 stream->reg_offset = PTE_GRP4_OFFSET; 59 break; 60 case 5: 61 pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5; 62 pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5; 63 offset = offsetof(struct scratch_reg_conf, grp5_pte); 64 stream->reg_offset = PTE_GRP5_OFFSET; 65 break; 66 case 6: 67 pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_6; 68 pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6; 69 offset = offsetof(struct scratch_reg_conf, grp6_pte); 70 stream->reg_offset = PTE_GRP6_OFFSET; 71 break; 72 case 7: 73 pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_7; 74 pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7; 75 offset = offsetof(struct scratch_reg_conf, grp7_pte); 76 stream->reg_offset = PTE_GRP7_OFFSET; 77 break; 78 case 8: 79 pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_8; 80 pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8; 81 offset = offsetof(struct scratch_reg_conf, grp8_pte); 82 stream->reg_offset = PTE_GRP8_OFFSET; 83 break; 84 default: 85 dev_err(sdev->dev, "Invalid stream tag %d\n", stream_tag); 86 return -EINVAL; 87 } 88 89 /* write phy_addr in scratch memory */ 90 91 phy_addr_offset = offsetof(struct scratch_reg_conf, reg_offset); 92 index = stream_tag - 1; 93 phy_addr_offset = phy_addr_offset + index * 4; 94 95 snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + 96 phy_addr_offset, stream->reg_offset); 97 98 /* Group Enable */ 99 reg_val = ACP_SRAM_PTE_OFFSET + offset; 100 snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_reg, reg_val | BIT(31)); 101 snd_sof_dsp_write(sdev, ACP_DSP_BAR, pte_size, PAGE_SIZE_4K_ENABLE); 102 103 for (page_idx = 0; page_idx < stream->num_pages; page_idx++) { 104 addr = snd_sgbuf_get_addr(stream->dmab, page_idx * PAGE_SIZE); 105 106 /* Load the low address of page int ACP SRAM through SRBM */ 107 low = lower_32_bits(addr); 108 high = upper_32_bits(addr); 109 110 snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset, low); 111 112 high |= BIT(31); 113 snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + offset + 4, high); 114 /* Move to next physically contiguous page */ 115 offset += 8; 116 } 117 118 return 0; 119 } 120 121 struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag) 122 { 123 struct acp_dev_data *adata = sdev->pdata->hw_pdata; 124 struct acp_dsp_stream *stream = adata->stream_buf; 125 int i; 126 127 for (i = 0; i < ACP_MAX_STREAM; i++, stream++) { 128 if (stream->active) 129 continue; 130 131 /* return stream if tag not specified*/ 132 if (!tag) { 133 stream->active = 1; 134 return stream; 135 } 136 137 /* check if this is the requested stream tag */ 138 if (stream->stream_tag == tag) { 139 stream->active = 1; 140 return stream; 141 } 142 } 143 144 dev_err(sdev->dev, "stream %d active or no inactive stream\n", tag); 145 return NULL; 146 } 147 EXPORT_SYMBOL_NS(acp_dsp_stream_get, SND_SOC_SOF_AMD_COMMON); 148 149 int acp_dsp_stream_put(struct snd_sof_dev *sdev, 150 struct acp_dsp_stream *acp_stream) 151 { 152 struct acp_dev_data *adata = sdev->pdata->hw_pdata; 153 struct acp_dsp_stream *stream = adata->stream_buf; 154 int i; 155 156 /* Free an active stream */ 157 for (i = 0; i < ACP_MAX_STREAM; i++, stream++) { 158 if (stream == acp_stream) { 159 stream->active = 0; 160 return 0; 161 } 162 } 163 164 dev_err(sdev->dev, "Cannot find active stream tag %d\n", acp_stream->stream_tag); 165 return -EINVAL; 166 } 167 EXPORT_SYMBOL_NS(acp_dsp_stream_put, SND_SOC_SOF_AMD_COMMON); 168 169 int acp_dsp_stream_init(struct snd_sof_dev *sdev) 170 { 171 struct acp_dev_data *adata = sdev->pdata->hw_pdata; 172 int i; 173 174 for (i = 0; i < ACP_MAX_STREAM; i++) { 175 adata->stream_buf[i].sdev = sdev; 176 adata->stream_buf[i].active = 0; 177 adata->stream_buf[i].stream_tag = i + 1; 178 } 179 return 0; 180 } 181 EXPORT_SYMBOL_NS(acp_dsp_stream_init, SND_SOC_SOF_AMD_COMMON); 182