| // 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 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) |
| { |
| 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 = 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 */ |
| reg_val = ACP_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; |
| } |
| |
| 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_NS(acp_dsp_stream_get, SND_SOC_SOF_AMD_COMMON); |
| |
| 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_NS(acp_dsp_stream_put, SND_SOC_SOF_AMD_COMMON); |
| |
| 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_NS(acp_dsp_stream_init, SND_SOC_SOF_AMD_COMMON); |