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 /* Flush ATU Cache after PTE Update */ 119 snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID); 120 121 return 0; 122 } 123 124 struct acp_dsp_stream *acp_dsp_stream_get(struct snd_sof_dev *sdev, int tag) 125 { 126 struct acp_dev_data *adata = sdev->pdata->hw_pdata; 127 struct acp_dsp_stream *stream = adata->stream_buf; 128 int i; 129 130 for (i = 0; i < ACP_MAX_STREAM; i++, stream++) { 131 if (stream->active) 132 continue; 133 134 /* return stream if tag not specified*/ 135 if (!tag) { 136 stream->active = 1; 137 return stream; 138 } 139 140 /* check if this is the requested stream tag */ 141 if (stream->stream_tag == tag) { 142 stream->active = 1; 143 return stream; 144 } 145 } 146 147 dev_err(sdev->dev, "stream %d active or no inactive stream\n", tag); 148 return NULL; 149 } 150 EXPORT_SYMBOL_NS(acp_dsp_stream_get, SND_SOC_SOF_AMD_COMMON); 151 152 int acp_dsp_stream_put(struct snd_sof_dev *sdev, 153 struct acp_dsp_stream *acp_stream) 154 { 155 struct acp_dev_data *adata = sdev->pdata->hw_pdata; 156 struct acp_dsp_stream *stream = adata->stream_buf; 157 int i; 158 159 /* Free an active stream */ 160 for (i = 0; i < ACP_MAX_STREAM; i++, stream++) { 161 if (stream == acp_stream) { 162 stream->active = 0; 163 return 0; 164 } 165 } 166 167 dev_err(sdev->dev, "Cannot find active stream tag %d\n", acp_stream->stream_tag); 168 return -EINVAL; 169 } 170 EXPORT_SYMBOL_NS(acp_dsp_stream_put, SND_SOC_SOF_AMD_COMMON); 171 172 int acp_dsp_stream_init(struct snd_sof_dev *sdev) 173 { 174 struct acp_dev_data *adata = sdev->pdata->hw_pdata; 175 int i; 176 177 for (i = 0; i < ACP_MAX_STREAM; i++) { 178 adata->stream_buf[i].sdev = sdev; 179 adata->stream_buf[i].active = 0; 180 adata->stream_buf[i].stream_tag = i + 1; 181 } 182 return 0; 183 } 184 EXPORT_SYMBOL_NS(acp_dsp_stream_init, SND_SOC_SOF_AMD_COMMON); 185