Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
27int 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
121struct 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}
147EXPORT_SYMBOL_NS(acp_dsp_stream_get, SND_SOC_SOF_AMD_COMMON);
148
149int 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}
167EXPORT_SYMBOL_NS(acp_dsp_stream_put, SND_SOC_SOF_AMD_COMMON);
168
169int 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}
181EXPORT_SYMBOL_NS(acp_dsp_stream_init, SND_SOC_SOF_AMD_COMMON);