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	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
127struct 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}
153EXPORT_SYMBOL_NS(acp_dsp_stream_get, SND_SOC_SOF_AMD_COMMON);
154
155int 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}
173EXPORT_SYMBOL_NS(acp_dsp_stream_put, SND_SOC_SOF_AMD_COMMON);
174
175int 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}
187EXPORT_SYMBOL_NS(acp_dsp_stream_init, SND_SOC_SOF_AMD_COMMON);
188