Skip to content

Commit 45864e4

Browse files
crojewsk-intelbroonie
authored andcommitted
ASoC: Intel: avs: Implement CLDMA transfer
SKL and KBL rely on a dedicated HDAudio DMA stream for code loading and authentication. The implementation of this specific mechanism for SKL-based platforms re-uses HDAudio DMA (streaming) functions found in HDA library to avoid duplication of functionality. Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com> Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> Link: https://lore.kernel.org/r/20220311153544.136854-16-cezary.rojewski@intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent b27f452 commit 45864e4

File tree

4 files changed

+348
-0
lines changed

4 files changed

+348
-0
lines changed

sound/soc/intel/avs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22

33
snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o
4+
snd-soc-avs-objs += cldma.o
45

56
obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o

sound/soc/intel/avs/cldma.c

Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
//
3+
// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
4+
//
5+
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
6+
//
7+
8+
#include <linux/pci.h>
9+
#include <sound/hda_register.h>
10+
#include <sound/hdaudio_ext.h>
11+
#include "cldma.h"
12+
#include "registers.h"
13+
14+
/* Stream Registers */
15+
#define AZX_CL_SD_BASE 0x80
16+
#define AZX_SD_CTL_STRM_MASK GENMASK(23, 20)
17+
#define AZX_SD_CTL_STRM(s) (((s)->stream_tag << 20) & AZX_SD_CTL_STRM_MASK)
18+
#define AZX_SD_BDLPL_BDLPLBA_MASK GENMASK(31, 7)
19+
#define AZX_SD_BDLPL_BDLPLBA(lb) ((lb) & AZX_SD_BDLPL_BDLPLBA_MASK)
20+
21+
/* Software Position Based FIFO Capability Registers */
22+
#define AZX_CL_SPBFCS 0x20
23+
#define AZX_REG_CL_SPBFCTL (AZX_CL_SPBFCS + 0x4)
24+
#define AZX_REG_CL_SD_SPIB (AZX_CL_SPBFCS + 0x8)
25+
26+
#define AVS_CL_OP_INTERVAL_US 3
27+
#define AVS_CL_OP_TIMEOUT_US 300
28+
#define AVS_CL_IOC_TIMEOUT_MS 300
29+
#define AVS_CL_STREAM_INDEX 0
30+
31+
struct hda_cldma {
32+
struct device *dev;
33+
struct hdac_bus *bus;
34+
void __iomem *dsp_ba;
35+
36+
unsigned int buffer_size;
37+
unsigned int num_periods;
38+
unsigned int stream_tag;
39+
void __iomem *sd_addr;
40+
41+
struct snd_dma_buffer dmab_data;
42+
struct snd_dma_buffer dmab_bdl;
43+
struct delayed_work memcpy_work;
44+
struct completion completion;
45+
46+
/* runtime */
47+
void *position;
48+
unsigned int remaining;
49+
unsigned int sd_status;
50+
};
51+
52+
static void cldma_memcpy_work(struct work_struct *work);
53+
54+
struct hda_cldma code_loader = {
55+
.stream_tag = AVS_CL_STREAM_INDEX + 1,
56+
.memcpy_work = __DELAYED_WORK_INITIALIZER(code_loader.memcpy_work, cldma_memcpy_work, 0),
57+
.completion = COMPLETION_INITIALIZER(code_loader.completion),
58+
};
59+
60+
void hda_cldma_fill(struct hda_cldma *cl)
61+
{
62+
unsigned int size, offset;
63+
64+
if (cl->remaining > cl->buffer_size)
65+
size = cl->buffer_size;
66+
else
67+
size = cl->remaining;
68+
69+
offset = snd_hdac_stream_readl(cl, CL_SD_SPIB);
70+
if (offset + size > cl->buffer_size) {
71+
unsigned int ss;
72+
73+
ss = cl->buffer_size - offset;
74+
memcpy(cl->dmab_data.area + offset, cl->position, ss);
75+
offset = 0;
76+
size -= ss;
77+
cl->position += ss;
78+
cl->remaining -= ss;
79+
}
80+
81+
memcpy(cl->dmab_data.area + offset, cl->position, size);
82+
cl->position += size;
83+
cl->remaining -= size;
84+
85+
snd_hdac_stream_writel(cl, CL_SD_SPIB, offset + size);
86+
}
87+
88+
static void cldma_memcpy_work(struct work_struct *work)
89+
{
90+
struct hda_cldma *cl = container_of(work, struct hda_cldma, memcpy_work.work);
91+
int ret;
92+
93+
ret = hda_cldma_start(cl);
94+
if (ret < 0) {
95+
dev_err(cl->dev, "cldma set RUN failed: %d\n", ret);
96+
return;
97+
}
98+
99+
while (true) {
100+
ret = wait_for_completion_timeout(&cl->completion,
101+
msecs_to_jiffies(AVS_CL_IOC_TIMEOUT_MS));
102+
if (!ret) {
103+
dev_err(cl->dev, "cldma IOC timeout\n");
104+
break;
105+
}
106+
107+
if (!(cl->sd_status & SD_INT_COMPLETE)) {
108+
dev_err(cl->dev, "cldma transfer error, SD status: 0x%08x\n",
109+
cl->sd_status);
110+
break;
111+
}
112+
113+
if (!cl->remaining)
114+
break;
115+
116+
reinit_completion(&cl->completion);
117+
hda_cldma_fill(cl);
118+
/* enable CLDMA interrupt */
119+
snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA,
120+
AVS_ADSP_ADSPIC_CLDMA);
121+
}
122+
}
123+
124+
void hda_cldma_transfer(struct hda_cldma *cl, unsigned long start_delay)
125+
{
126+
if (!cl->remaining)
127+
return;
128+
129+
reinit_completion(&cl->completion);
130+
/* fill buffer with the first chunk before scheduling run */
131+
hda_cldma_fill(cl);
132+
133+
schedule_delayed_work(&cl->memcpy_work, start_delay);
134+
}
135+
136+
int hda_cldma_start(struct hda_cldma *cl)
137+
{
138+
unsigned int reg;
139+
140+
/* enable interrupts */
141+
snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA,
142+
AVS_ADSP_ADSPIC_CLDMA);
143+
snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START,
144+
SD_INT_MASK | SD_CTL_DMA_START);
145+
146+
/* await DMA engine start */
147+
return snd_hdac_stream_readb_poll(cl, SD_CTL, reg, reg & SD_CTL_DMA_START,
148+
AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
149+
}
150+
151+
int hda_cldma_stop(struct hda_cldma *cl)
152+
{
153+
unsigned int reg;
154+
int ret;
155+
156+
/* disable interrupts */
157+
snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0);
158+
snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START, 0);
159+
160+
/* await DMA engine stop */
161+
ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & SD_CTL_DMA_START),
162+
AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
163+
cancel_delayed_work_sync(&cl->memcpy_work);
164+
165+
return ret;
166+
}
167+
168+
int hda_cldma_reset(struct hda_cldma *cl)
169+
{
170+
unsigned int reg;
171+
int ret;
172+
173+
ret = hda_cldma_stop(cl);
174+
if (ret < 0) {
175+
dev_err(cl->dev, "cldma stop failed: %d\n", ret);
176+
return ret;
177+
}
178+
179+
snd_hdac_stream_updateb(cl, SD_CTL, 1, 1);
180+
ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, (reg & 1), AVS_CL_OP_INTERVAL_US,
181+
AVS_CL_OP_TIMEOUT_US);
182+
if (ret < 0) {
183+
dev_err(cl->dev, "cldma set SRST failed: %d\n", ret);
184+
return ret;
185+
}
186+
187+
snd_hdac_stream_updateb(cl, SD_CTL, 1, 0);
188+
ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & 1), AVS_CL_OP_INTERVAL_US,
189+
AVS_CL_OP_TIMEOUT_US);
190+
if (ret < 0) {
191+
dev_err(cl->dev, "cldma unset SRST failed: %d\n", ret);
192+
return ret;
193+
}
194+
195+
return 0;
196+
}
197+
198+
void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size)
199+
{
200+
/* setup runtime */
201+
cl->position = data;
202+
cl->remaining = size;
203+
}
204+
205+
static void cldma_setup_bdle(struct hda_cldma *cl, u32 bdle_size)
206+
{
207+
struct snd_dma_buffer *dmab = &cl->dmab_data;
208+
__le32 *bdl = (__le32 *)cl->dmab_bdl.area;
209+
int remaining = cl->buffer_size;
210+
int offset = 0;
211+
212+
cl->num_periods = 0;
213+
214+
while (remaining > 0) {
215+
phys_addr_t addr;
216+
int chunk;
217+
218+
addr = snd_sgbuf_get_addr(dmab, offset);
219+
bdl[0] = cpu_to_le32(lower_32_bits(addr));
220+
bdl[1] = cpu_to_le32(upper_32_bits(addr));
221+
chunk = snd_sgbuf_get_chunk_size(dmab, offset, bdle_size);
222+
bdl[2] = cpu_to_le32(chunk);
223+
224+
remaining -= chunk;
225+
/* set IOC only for the last entry */
226+
bdl[3] = (remaining > 0) ? 0 : cpu_to_le32(0x01);
227+
228+
bdl += 4;
229+
offset += chunk;
230+
cl->num_periods++;
231+
}
232+
}
233+
234+
void hda_cldma_setup(struct hda_cldma *cl)
235+
{
236+
dma_addr_t bdl_addr = cl->dmab_bdl.addr;
237+
238+
cldma_setup_bdle(cl, cl->buffer_size / 2);
239+
240+
snd_hdac_stream_writel(cl, SD_BDLPL, AZX_SD_BDLPL_BDLPLBA(lower_32_bits(bdl_addr)));
241+
snd_hdac_stream_writel(cl, SD_BDLPU, upper_32_bits(bdl_addr));
242+
243+
snd_hdac_stream_writel(cl, SD_CBL, cl->buffer_size);
244+
snd_hdac_stream_writeb(cl, SD_LVI, cl->num_periods - 1);
245+
246+
snd_hdac_stream_updatel(cl, SD_CTL, AZX_SD_CTL_STRM_MASK, AZX_SD_CTL_STRM(cl));
247+
/* enable spib */
248+
snd_hdac_stream_writel(cl, CL_SPBFCTL, 1);
249+
}
250+
251+
static irqreturn_t cldma_irq_handler(int irq, void *dev_id)
252+
{
253+
struct hda_cldma *cl = dev_id;
254+
u32 adspis;
255+
256+
adspis = snd_hdac_adsp_readl(cl, AVS_ADSP_REG_ADSPIS);
257+
if (adspis == UINT_MAX)
258+
return IRQ_NONE;
259+
if (!(adspis & AVS_ADSP_ADSPIS_CLDMA))
260+
return IRQ_NONE;
261+
262+
cl->sd_status = snd_hdac_stream_readb(cl, SD_STS);
263+
dev_warn(cl->dev, "%s sd_status: 0x%08x\n", __func__, cl->sd_status);
264+
265+
/* disable CLDMA interrupt */
266+
snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0);
267+
268+
complete(&cl->completion);
269+
270+
return IRQ_HANDLED;
271+
}
272+
273+
int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba,
274+
unsigned int buffer_size)
275+
{
276+
struct pci_dev *pci = to_pci_dev(bus->dev);
277+
int ret;
278+
279+
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev, buffer_size, &cl->dmab_data);
280+
if (ret < 0)
281+
return ret;
282+
283+
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, bus->dev, BDL_SIZE, &cl->dmab_bdl);
284+
if (ret < 0)
285+
goto alloc_err;
286+
287+
cl->dev = bus->dev;
288+
cl->bus = bus;
289+
cl->dsp_ba = dsp_ba;
290+
cl->buffer_size = buffer_size;
291+
cl->sd_addr = dsp_ba + AZX_CL_SD_BASE;
292+
293+
ret = pci_request_irq(pci, 0, cldma_irq_handler, NULL, cl, "CLDMA");
294+
if (ret < 0) {
295+
dev_err(cl->dev, "Failed to request CLDMA IRQ handler: %d\n", ret);
296+
goto req_err;
297+
}
298+
299+
return 0;
300+
301+
req_err:
302+
snd_dma_free_pages(&cl->dmab_bdl);
303+
alloc_err:
304+
snd_dma_free_pages(&cl->dmab_data);
305+
306+
return ret;
307+
}
308+
309+
void hda_cldma_free(struct hda_cldma *cl)
310+
{
311+
struct pci_dev *pci = to_pci_dev(cl->dev);
312+
313+
pci_free_irq(pci, 0, cl);
314+
snd_dma_free_pages(&cl->dmab_data);
315+
snd_dma_free_pages(&cl->dmab_bdl);
316+
}

sound/soc/intel/avs/cldma.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
4+
*
5+
* Author: Cezary Rojewski <cezary.rojewski@intel.com>
6+
*/
7+
8+
#ifndef __SOUND_SOC_INTEL_AVS_CLDMA_H
9+
#define __SOUND_SOC_INTEL_AVS_CLDMA_H
10+
11+
#define AVS_CL_DEFAULT_BUFFER_SIZE (32 * PAGE_SIZE)
12+
13+
struct hda_cldma;
14+
extern struct hda_cldma code_loader;
15+
16+
void hda_cldma_fill(struct hda_cldma *cl);
17+
void hda_cldma_transfer(struct hda_cldma *cl, unsigned long start_delay);
18+
19+
int hda_cldma_start(struct hda_cldma *cl);
20+
int hda_cldma_stop(struct hda_cldma *cl);
21+
int hda_cldma_reset(struct hda_cldma *cl);
22+
23+
void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size);
24+
void hda_cldma_setup(struct hda_cldma *cl);
25+
int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba,
26+
unsigned int buffer_size);
27+
void hda_cldma_free(struct hda_cldma *cl);
28+
29+
#endif

sound/soc/intel/avs/registers.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
#define AVS_ADSP_REG_ADSPIS (AVS_ADSP_GEN_BASE + 0x0C)
2323

2424
#define AVS_ADSP_ADSPIC_IPC BIT(0)
25+
#define AVS_ADSP_ADSPIC_CLDMA BIT(1)
2526
#define AVS_ADSP_ADSPIS_IPC BIT(0)
27+
#define AVS_ADSP_ADSPIS_CLDMA BIT(1)
2628

2729
#define AVS_ADSPCS_CRST_MASK(cm) (cm)
2830
#define AVS_ADSPCS_CSTALL_MASK(cm) ((cm) << 8)

0 commit comments

Comments
 (0)