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