Commit | Line | Data |
---|---|---|
a1d1e266 LG |
1 | // SPDX-License-Identifier: (GPL-2.0 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) 2018 Intel Corporation. All rights reserved. | |
7 | // | |
8 | // Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> | |
9 | // Ranjani Sridharan <ranjani.sridharan@linux.intel.com> | |
10 | // Rander Wang <rander.wang@intel.com> | |
11 | // Keyon Jie <yang.jie@linux.intel.com> | |
12 | // | |
13 | ||
14 | /* | |
15 | * Hardware interface for generic Intel audio DSP HDA IP | |
16 | */ | |
17 | ||
18 | #include <linux/pm_runtime.h> | |
19 | #include <sound/hdaudio_ext.h> | |
20 | #include <sound/hda_register.h> | |
21 | #include <sound/sof.h> | |
22 | #include "../ops.h" | |
23 | #include "hda.h" | |
24 | ||
25 | /* | |
26 | * set up one of BDL entries for a stream | |
27 | */ | |
28 | static int hda_setup_bdle(struct snd_sof_dev *sdev, | |
29 | struct snd_dma_buffer *dmab, | |
30 | struct hdac_stream *stream, | |
31 | struct sof_intel_dsp_bdl **bdlp, | |
32 | int offset, int size, int ioc) | |
33 | { | |
34 | struct hdac_bus *bus = sof_to_bus(sdev); | |
35 | struct sof_intel_dsp_bdl *bdl = *bdlp; | |
36 | ||
37 | while (size > 0) { | |
38 | dma_addr_t addr; | |
39 | int chunk; | |
40 | ||
41 | if (stream->frags >= HDA_DSP_MAX_BDL_ENTRIES) { | |
42 | dev_err(sdev->dev, "error: stream frags exceeded\n"); | |
43 | return -EINVAL; | |
44 | } | |
45 | ||
46 | addr = snd_sgbuf_get_addr(dmab, offset); | |
47 | /* program BDL addr */ | |
48 | bdl->addr_l = cpu_to_le32(lower_32_bits(addr)); | |
49 | bdl->addr_h = cpu_to_le32(upper_32_bits(addr)); | |
50 | /* program BDL size */ | |
51 | chunk = snd_sgbuf_get_chunk_size(dmab, offset, size); | |
52 | /* one BDLE should not cross 4K boundary */ | |
53 | if (bus->align_bdle_4k) { | |
54 | u32 remain = 0x1000 - (offset & 0xfff); | |
55 | ||
56 | if (chunk > remain) | |
57 | chunk = remain; | |
58 | } | |
59 | bdl->size = cpu_to_le32(chunk); | |
60 | /* only program IOC when the whole segment is processed */ | |
61 | size -= chunk; | |
62 | bdl->ioc = (size || !ioc) ? 0 : cpu_to_le32(0x01); | |
63 | bdl++; | |
64 | stream->frags++; | |
65 | offset += chunk; | |
66 | ||
67 | dev_vdbg(sdev->dev, "bdl, frags:%d, chunk size:0x%x;\n", | |
68 | stream->frags, chunk); | |
69 | } | |
70 | ||
71 | *bdlp = bdl; | |
72 | return offset; | |
73 | } | |
74 | ||
75 | /* | |
76 | * set up Buffer Descriptor List (BDL) for host memory transfer | |
77 | * BDL describes the location of the individual buffers and is little endian. | |
78 | */ | |
79 | int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, | |
80 | struct snd_dma_buffer *dmab, | |
81 | struct hdac_stream *stream) | |
82 | { | |
83 | struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; | |
84 | struct sof_intel_dsp_bdl *bdl; | |
85 | int i, offset, period_bytes, periods; | |
86 | int remain, ioc; | |
87 | ||
88 | period_bytes = stream->period_bytes; | |
89 | dev_dbg(sdev->dev, "period_bytes:0x%x\n", period_bytes); | |
90 | if (!period_bytes) | |
91 | period_bytes = stream->bufsize; | |
92 | ||
93 | periods = stream->bufsize / period_bytes; | |
94 | ||
95 | dev_dbg(sdev->dev, "periods:%d\n", periods); | |
96 | ||
97 | remain = stream->bufsize % period_bytes; | |
98 | if (remain) | |
99 | periods++; | |
100 | ||
101 | /* program the initial BDL entries */ | |
102 | bdl = (struct sof_intel_dsp_bdl *)stream->bdl.area; | |
103 | offset = 0; | |
104 | stream->frags = 0; | |
105 | ||
106 | /* | |
107 | * set IOC if don't use position IPC | |
108 | * and period_wakeup needed. | |
109 | */ | |
110 | ioc = hda->no_ipc_position ? | |
111 | !stream->no_period_wakeup : 0; | |
112 | ||
113 | for (i = 0; i < periods; i++) { | |
114 | if (i == (periods - 1) && remain) | |
115 | /* set the last small entry */ | |
116 | offset = hda_setup_bdle(sdev, dmab, | |
117 | stream, &bdl, offset, | |
118 | remain, 0); | |
119 | else | |
120 | offset = hda_setup_bdle(sdev, dmab, | |
121 | stream, &bdl, offset, | |
122 | period_bytes, ioc); | |
123 | } | |
124 | ||
125 | return offset; | |
126 | } | |
127 | ||
128 | int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, | |
129 | struct hdac_ext_stream *stream, | |
130 | int enable, u32 size) | |
131 | { | |
132 | struct hdac_stream *hstream = &stream->hstream; | |
133 | u32 mask; | |
134 | ||
135 | if (!sdev->bar[HDA_DSP_SPIB_BAR]) { | |
136 | dev_err(sdev->dev, "error: address of spib capability is NULL\n"); | |
137 | return -EINVAL; | |
138 | } | |
139 | ||
140 | mask = (1 << hstream->index); | |
141 | ||
142 | /* enable/disable SPIB for the stream */ | |
143 | snd_sof_dsp_update_bits(sdev, HDA_DSP_SPIB_BAR, | |
144 | SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, mask, | |
145 | enable << hstream->index); | |
146 | ||
147 | /* set the SPIB value */ | |
148 | sof_io_write(sdev, stream->spib_addr, size); | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
153 | /* get next unused stream */ | |
154 | struct hdac_ext_stream * | |
155 | hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction) | |
156 | { | |
157 | struct hdac_bus *bus = sof_to_bus(sdev); | |
6b2239e3 | 158 | struct sof_intel_hda_stream *hda_stream; |
a1d1e266 LG |
159 | struct hdac_ext_stream *stream = NULL; |
160 | struct hdac_stream *s; | |
161 | ||
162 | spin_lock_irq(&bus->reg_lock); | |
163 | ||
164 | /* get an unused stream */ | |
165 | list_for_each_entry(s, &bus->stream_list, list) { | |
166 | if (s->direction == direction && !s->opened) { | |
a1d1e266 | 167 | stream = stream_to_hdac_ext_stream(s); |
6b2239e3 RS |
168 | hda_stream = container_of(stream, |
169 | struct sof_intel_hda_stream, | |
170 | hda_stream); | |
171 | /* check if the host DMA channel is reserved */ | |
172 | if (hda_stream->host_reserved) | |
173 | continue; | |
174 | ||
175 | s->opened = true; | |
a1d1e266 LG |
176 | break; |
177 | } | |
178 | } | |
179 | ||
180 | spin_unlock_irq(&bus->reg_lock); | |
181 | ||
182 | /* stream found ? */ | |
183 | if (!stream) | |
184 | dev_err(sdev->dev, "error: no free %s streams\n", | |
185 | direction == SNDRV_PCM_STREAM_PLAYBACK ? | |
186 | "playback" : "capture"); | |
187 | ||
188 | return stream; | |
189 | } | |
190 | ||
191 | /* free a stream */ | |
192 | int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) | |
193 | { | |
194 | struct hdac_bus *bus = sof_to_bus(sdev); | |
195 | struct hdac_stream *s; | |
196 | ||
197 | spin_lock_irq(&bus->reg_lock); | |
198 | ||
199 | /* find used stream */ | |
200 | list_for_each_entry(s, &bus->stream_list, list) { | |
201 | if (s->direction == direction && | |
202 | s->opened && s->stream_tag == stream_tag) { | |
203 | s->opened = false; | |
204 | spin_unlock_irq(&bus->reg_lock); | |
205 | return 0; | |
206 | } | |
207 | } | |
208 | ||
209 | spin_unlock_irq(&bus->reg_lock); | |
210 | ||
211 | dev_dbg(sdev->dev, "stream_tag %d not opened!\n", stream_tag); | |
212 | return -ENODEV; | |
213 | } | |
214 | ||
215 | int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, | |
216 | struct hdac_ext_stream *stream, int cmd) | |
217 | { | |
218 | struct hdac_stream *hstream = &stream->hstream; | |
219 | int sd_offset = SOF_STREAM_SD_OFFSET(hstream); | |
220 | ||
221 | /* cmd must be for audio stream */ | |
222 | switch (cmd) { | |
223 | case SNDRV_PCM_TRIGGER_RESUME: | |
224 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
225 | case SNDRV_PCM_TRIGGER_START: | |
226 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, | |
227 | 1 << hstream->index, | |
228 | 1 << hstream->index); | |
229 | ||
230 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, | |
231 | sd_offset, | |
232 | SOF_HDA_SD_CTL_DMA_START | | |
233 | SOF_HDA_CL_DMA_SD_INT_MASK, | |
234 | SOF_HDA_SD_CTL_DMA_START | | |
235 | SOF_HDA_CL_DMA_SD_INT_MASK); | |
236 | ||
237 | hstream->running = true; | |
238 | break; | |
239 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
240 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
241 | case SNDRV_PCM_TRIGGER_STOP: | |
242 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, | |
243 | sd_offset, | |
244 | SOF_HDA_SD_CTL_DMA_START | | |
245 | SOF_HDA_CL_DMA_SD_INT_MASK, 0x0); | |
246 | ||
247 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset + | |
248 | SOF_HDA_ADSP_REG_CL_SD_STS, | |
249 | SOF_HDA_CL_DMA_SD_INT_MASK); | |
250 | ||
251 | hstream->running = false; | |
252 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, | |
253 | 1 << hstream->index, 0x0); | |
254 | break; | |
255 | default: | |
256 | dev_err(sdev->dev, "error: unknown command: %d\n", cmd); | |
257 | return -EINVAL; | |
258 | } | |
259 | ||
260 | return 0; | |
261 | } | |
262 | ||
263 | /* | |
264 | * prepare for common hdac registers settings, for both code loader | |
265 | * and normal stream. | |
266 | */ | |
267 | int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, | |
268 | struct hdac_ext_stream *stream, | |
269 | struct snd_dma_buffer *dmab, | |
270 | struct snd_pcm_hw_params *params) | |
271 | { | |
272 | struct hdac_bus *bus = sof_to_bus(sdev); | |
273 | struct hdac_stream *hstream = &stream->hstream; | |
274 | int sd_offset = SOF_STREAM_SD_OFFSET(hstream); | |
275 | int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; | |
276 | u32 val, mask; | |
277 | ||
278 | if (!stream) { | |
279 | dev_err(sdev->dev, "error: no stream available\n"); | |
280 | return -ENODEV; | |
281 | } | |
282 | ||
283 | /* decouple host and link DMA */ | |
284 | mask = 0x1 << hstream->index; | |
285 | snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, | |
286 | mask, mask); | |
287 | ||
288 | if (!dmab) { | |
289 | dev_err(sdev->dev, "error: no dma buffer allocated!\n"); | |
290 | return -ENODEV; | |
291 | } | |
292 | ||
293 | /* clear stream status */ | |
294 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, | |
295 | SOF_HDA_CL_DMA_SD_INT_MASK | | |
296 | SOF_HDA_SD_CTL_DMA_START, 0); | |
297 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, | |
298 | sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, | |
299 | SOF_HDA_CL_DMA_SD_INT_MASK, | |
300 | SOF_HDA_CL_DMA_SD_INT_MASK); | |
301 | ||
302 | /* stream reset */ | |
303 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, | |
304 | 0x1); | |
305 | udelay(3); | |
306 | do { | |
307 | val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, | |
308 | sd_offset); | |
309 | if (val & 0x1) | |
310 | break; | |
311 | } while (--timeout); | |
312 | if (timeout == 0) { | |
313 | dev_err(sdev->dev, "error: stream reset failed\n"); | |
314 | return -ETIMEDOUT; | |
315 | } | |
316 | ||
317 | timeout = HDA_DSP_STREAM_RESET_TIMEOUT; | |
318 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, | |
319 | 0x0); | |
320 | ||
321 | /* wait for hardware to report that stream is out of reset */ | |
322 | udelay(3); | |
323 | do { | |
324 | val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, | |
325 | sd_offset); | |
326 | if ((val & 0x1) == 0) | |
327 | break; | |
328 | } while (--timeout); | |
329 | if (timeout == 0) { | |
330 | dev_err(sdev->dev, "error: timeout waiting for stream reset\n"); | |
331 | return -ETIMEDOUT; | |
332 | } | |
333 | ||
334 | if (hstream->posbuf) | |
335 | *hstream->posbuf = 0; | |
336 | ||
337 | /* reset BDL address */ | |
338 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, | |
339 | sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, | |
340 | 0x0); | |
341 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, | |
342 | sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, | |
343 | 0x0); | |
344 | ||
345 | /* clear stream status */ | |
346 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, | |
347 | SOF_HDA_CL_DMA_SD_INT_MASK | | |
348 | SOF_HDA_SD_CTL_DMA_START, 0); | |
349 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, | |
350 | sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, | |
351 | SOF_HDA_CL_DMA_SD_INT_MASK, | |
352 | SOF_HDA_CL_DMA_SD_INT_MASK); | |
353 | ||
354 | hstream->frags = 0; | |
355 | ||
356 | ret = hda_dsp_stream_setup_bdl(sdev, dmab, hstream); | |
357 | if (ret < 0) { | |
358 | dev_err(sdev->dev, "error: set up of BDL failed\n"); | |
359 | return ret; | |
360 | } | |
361 | ||
362 | /* program stream tag to set up stream descriptor for DMA */ | |
363 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, | |
364 | SOF_HDA_CL_SD_CTL_STREAM_TAG_MASK, | |
365 | hstream->stream_tag << | |
366 | SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT); | |
367 | ||
368 | /* program cyclic buffer length */ | |
369 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, | |
370 | sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL, | |
371 | hstream->bufsize); | |
372 | ||
373 | /* | |
374 | * Recommended hardware programming sequence for HDAudio DMA format | |
375 | * | |
376 | * 1. Put DMA into coupled mode by clearing PPCTL.PROCEN bit | |
377 | * for corresponding stream index before the time of writing | |
378 | * format to SDxFMT register. | |
379 | * 2. Write SDxFMT | |
380 | * 3. Set PPCTL.PROCEN bit for corresponding stream index to | |
381 | * enable decoupled mode | |
382 | */ | |
383 | ||
384 | /* couple host and link DMA, disable DSP features */ | |
385 | snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, | |
386 | mask, 0); | |
387 | ||
388 | /* program stream format */ | |
389 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, | |
390 | sd_offset + | |
391 | SOF_HDA_ADSP_REG_CL_SD_FORMAT, | |
392 | 0xffff, hstream->format_val); | |
393 | ||
394 | /* decouple host and link DMA, enable DSP features */ | |
395 | snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, | |
396 | mask, mask); | |
397 | ||
398 | /* program last valid index */ | |
399 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, | |
400 | sd_offset + SOF_HDA_ADSP_REG_CL_SD_LVI, | |
401 | 0xffff, (hstream->frags - 1)); | |
402 | ||
403 | /* program BDL address */ | |
404 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, | |
405 | sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, | |
406 | (u32)hstream->bdl.addr); | |
407 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, | |
408 | sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, | |
409 | upper_32_bits(hstream->bdl.addr)); | |
410 | ||
411 | /* enable position buffer */ | |
412 | if (!(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) | |
413 | & SOF_HDA_ADSP_DPLBASE_ENABLE)) { | |
414 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPUBASE, | |
415 | upper_32_bits(bus->posbuf.addr)); | |
416 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, | |
417 | (u32)bus->posbuf.addr | | |
418 | SOF_HDA_ADSP_DPLBASE_ENABLE); | |
419 | } | |
420 | ||
421 | /* set interrupt enable bits */ | |
422 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, | |
423 | SOF_HDA_CL_DMA_SD_INT_MASK, | |
424 | SOF_HDA_CL_DMA_SD_INT_MASK); | |
425 | ||
426 | /* read FIFO size */ | |
427 | if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) { | |
428 | hstream->fifo_size = | |
429 | snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, | |
430 | sd_offset + | |
431 | SOF_HDA_ADSP_REG_CL_SD_FIFOSIZE); | |
432 | hstream->fifo_size &= 0xffff; | |
433 | hstream->fifo_size += 1; | |
434 | } else { | |
435 | hstream->fifo_size = 0; | |
436 | } | |
437 | ||
438 | return ret; | |
439 | } | |
440 | ||
93146bc2 RS |
441 | int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, |
442 | struct snd_pcm_substream *substream) | |
443 | { | |
444 | struct hdac_stream *stream = substream->runtime->private_data; | |
445 | struct hdac_ext_stream *link_dev = container_of(stream, | |
446 | struct hdac_ext_stream, | |
447 | hstream); | |
448 | struct hdac_bus *bus = sof_to_bus(sdev); | |
449 | u32 mask = 0x1 << stream->index; | |
450 | ||
451 | spin_lock(&bus->reg_lock); | |
452 | /* couple host and link DMA if link DMA channel is idle */ | |
453 | if (!link_dev->link_locked) | |
454 | snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, | |
455 | SOF_HDA_REG_PP_PPCTL, mask, 0); | |
456 | spin_unlock(&bus->reg_lock); | |
457 | ||
458 | return 0; | |
459 | } | |
460 | ||
a1d1e266 LG |
461 | irqreturn_t hda_dsp_stream_interrupt(int irq, void *context) |
462 | { | |
463 | struct hdac_bus *bus = context; | |
20d0aff7 KJ |
464 | struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); |
465 | u32 stream_mask; | |
a1d1e266 LG |
466 | u32 status; |
467 | ||
468 | if (!pm_runtime_active(bus->dev)) | |
469 | return IRQ_NONE; | |
470 | ||
471 | spin_lock(&bus->reg_lock); | |
472 | ||
473 | status = snd_hdac_chip_readl(bus, INTSTS); | |
20d0aff7 KJ |
474 | stream_mask = GENMASK(sof_hda->stream_max - 1, 0) | AZX_INT_CTRL_EN; |
475 | ||
476 | /* Not stream interrupt or register inaccessible, ignore it.*/ | |
477 | if (!(status & stream_mask) || status == 0xffffffff) { | |
a1d1e266 LG |
478 | spin_unlock(&bus->reg_lock); |
479 | return IRQ_NONE; | |
480 | } | |
481 | ||
482 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
483 | /* clear rirb int */ | |
484 | status = snd_hdac_chip_readb(bus, RIRBSTS); | |
485 | if (status & RIRB_INT_MASK) { | |
486 | if (status & RIRB_INT_RESPONSE) | |
487 | snd_hdac_bus_update_rirb(bus); | |
488 | snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); | |
489 | } | |
490 | #endif | |
491 | ||
492 | spin_unlock(&bus->reg_lock); | |
493 | ||
494 | return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED; | |
495 | } | |
496 | ||
497 | irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) | |
498 | { | |
499 | struct hdac_bus *bus = context; | |
500 | struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); | |
a1d1e266 | 501 | u32 status = snd_hdac_chip_readl(bus, INTSTS); |
8242d539 | 502 | struct hdac_stream *s; |
a1d1e266 LG |
503 | u32 sd_status; |
504 | ||
505 | /* check streams */ | |
506 | list_for_each_entry(s, &bus->stream_list, list) { | |
507 | if (status & (1 << s->index) && s->opened) { | |
508 | sd_status = snd_hdac_stream_readb(s, SD_STS); | |
509 | ||
510 | dev_vdbg(bus->dev, "stream %d status 0x%x\n", | |
511 | s->index, sd_status); | |
512 | ||
513 | snd_hdac_stream_writeb(s, SD_STS, SD_INT_MASK); | |
514 | ||
515 | if (!s->substream || | |
516 | !s->running || | |
517 | (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0) | |
518 | continue; | |
519 | ||
520 | /* Inform ALSA only in case not do that with IPC */ | |
521 | if (sof_hda->no_ipc_position) | |
8242d539 | 522 | snd_sof_pcm_period_elapsed(s->substream); |
a1d1e266 LG |
523 | |
524 | } | |
525 | } | |
526 | ||
527 | return IRQ_HANDLED; | |
528 | } | |
529 | ||
530 | int hda_dsp_stream_init(struct snd_sof_dev *sdev) | |
531 | { | |
532 | struct hdac_bus *bus = sof_to_bus(sdev); | |
533 | struct hdac_ext_stream *stream; | |
534 | struct hdac_stream *hstream; | |
535 | struct pci_dev *pci = to_pci_dev(sdev->dev); | |
e8e55dbe | 536 | struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); |
a1d1e266 LG |
537 | int sd_offset; |
538 | int i, num_playback, num_capture, num_total, ret; | |
539 | u32 gcap; | |
540 | ||
541 | gcap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCAP); | |
542 | dev_dbg(sdev->dev, "hda global caps = 0x%x\n", gcap); | |
543 | ||
544 | /* get stream count from GCAP */ | |
545 | num_capture = (gcap >> 8) & 0x0f; | |
546 | num_playback = (gcap >> 12) & 0x0f; | |
547 | num_total = num_playback + num_capture; | |
548 | ||
549 | dev_dbg(sdev->dev, "detected %d playback and %d capture streams\n", | |
550 | num_playback, num_capture); | |
551 | ||
552 | if (num_playback >= SOF_HDA_PLAYBACK_STREAMS) { | |
553 | dev_err(sdev->dev, "error: too many playback streams %d\n", | |
554 | num_playback); | |
555 | return -EINVAL; | |
556 | } | |
557 | ||
558 | if (num_capture >= SOF_HDA_CAPTURE_STREAMS) { | |
559 | dev_err(sdev->dev, "error: too many capture streams %d\n", | |
560 | num_playback); | |
561 | return -EINVAL; | |
562 | } | |
563 | ||
564 | /* | |
565 | * mem alloc for the position buffer | |
566 | * TODO: check position buffer update | |
567 | */ | |
568 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, | |
569 | SOF_HDA_DPIB_ENTRY_SIZE * num_total, | |
570 | &bus->posbuf); | |
571 | if (ret < 0) { | |
572 | dev_err(sdev->dev, "error: posbuffer dma alloc failed\n"); | |
573 | return -ENOMEM; | |
574 | } | |
575 | ||
576 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
577 | /* mem alloc for the CORB/RIRB ringbuffers */ | |
578 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, | |
579 | PAGE_SIZE, &bus->rb); | |
580 | if (ret < 0) { | |
581 | dev_err(sdev->dev, "error: RB alloc failed\n"); | |
582 | return -ENOMEM; | |
583 | } | |
584 | #endif | |
585 | ||
586 | /* create capture streams */ | |
587 | for (i = 0; i < num_capture; i++) { | |
588 | struct sof_intel_hda_stream *hda_stream; | |
589 | ||
590 | hda_stream = devm_kzalloc(sdev->dev, sizeof(*hda_stream), | |
591 | GFP_KERNEL); | |
592 | if (!hda_stream) | |
593 | return -ENOMEM; | |
594 | ||
7623ae79 RS |
595 | hda_stream->sdev = sdev; |
596 | ||
a1d1e266 LG |
597 | stream = &hda_stream->hda_stream; |
598 | ||
599 | stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + | |
600 | SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; | |
601 | ||
602 | stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + | |
603 | SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + | |
604 | SOF_HDA_PPLC_INTERVAL * i; | |
605 | ||
606 | /* do we support SPIB */ | |
607 | if (sdev->bar[HDA_DSP_SPIB_BAR]) { | |
608 | stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + | |
609 | SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + | |
610 | SOF_HDA_SPIB_SPIB; | |
611 | ||
612 | stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + | |
613 | SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + | |
614 | SOF_HDA_SPIB_MAXFIFO; | |
615 | } | |
616 | ||
617 | hstream = &stream->hstream; | |
618 | hstream->bus = bus; | |
619 | hstream->sd_int_sta_mask = 1 << i; | |
620 | hstream->index = i; | |
621 | sd_offset = SOF_STREAM_SD_OFFSET(hstream); | |
622 | hstream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + sd_offset; | |
623 | hstream->stream_tag = i + 1; | |
624 | hstream->opened = false; | |
625 | hstream->running = false; | |
626 | hstream->direction = SNDRV_PCM_STREAM_CAPTURE; | |
627 | ||
628 | /* memory alloc for stream BDL */ | |
629 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, | |
630 | HDA_DSP_BDL_SIZE, &hstream->bdl); | |
631 | if (ret < 0) { | |
632 | dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); | |
633 | return -ENOMEM; | |
634 | } | |
635 | hstream->posbuf = (__le32 *)(bus->posbuf.area + | |
636 | (hstream->index) * 8); | |
637 | ||
638 | list_add_tail(&hstream->list, &bus->stream_list); | |
639 | } | |
640 | ||
641 | /* create playback streams */ | |
642 | for (i = num_capture; i < num_total; i++) { | |
643 | struct sof_intel_hda_stream *hda_stream; | |
644 | ||
645 | hda_stream = devm_kzalloc(sdev->dev, sizeof(*hda_stream), | |
646 | GFP_KERNEL); | |
647 | if (!hda_stream) | |
648 | return -ENOMEM; | |
649 | ||
7623ae79 RS |
650 | hda_stream->sdev = sdev; |
651 | ||
a1d1e266 LG |
652 | stream = &hda_stream->hda_stream; |
653 | ||
654 | /* we always have DSP support */ | |
655 | stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + | |
656 | SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; | |
657 | ||
658 | stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + | |
659 | SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + | |
660 | SOF_HDA_PPLC_INTERVAL * i; | |
661 | ||
662 | /* do we support SPIB */ | |
663 | if (sdev->bar[HDA_DSP_SPIB_BAR]) { | |
664 | stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + | |
665 | SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + | |
666 | SOF_HDA_SPIB_SPIB; | |
667 | ||
668 | stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + | |
669 | SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + | |
670 | SOF_HDA_SPIB_MAXFIFO; | |
671 | } | |
672 | ||
673 | hstream = &stream->hstream; | |
674 | hstream->bus = bus; | |
675 | hstream->sd_int_sta_mask = 1 << i; | |
676 | hstream->index = i; | |
677 | sd_offset = SOF_STREAM_SD_OFFSET(hstream); | |
678 | hstream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + sd_offset; | |
679 | hstream->stream_tag = i - num_capture + 1; | |
680 | hstream->opened = false; | |
681 | hstream->running = false; | |
682 | hstream->direction = SNDRV_PCM_STREAM_PLAYBACK; | |
683 | ||
684 | /* mem alloc for stream BDL */ | |
685 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, | |
686 | HDA_DSP_BDL_SIZE, &hstream->bdl); | |
687 | if (ret < 0) { | |
688 | dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); | |
689 | return -ENOMEM; | |
690 | } | |
691 | ||
692 | hstream->posbuf = (__le32 *)(bus->posbuf.area + | |
693 | (hstream->index) * 8); | |
694 | ||
695 | list_add_tail(&hstream->list, &bus->stream_list); | |
696 | } | |
697 | ||
e8e55dbe KJ |
698 | /* store total stream count (playback + capture) from GCAP */ |
699 | sof_hda->stream_max = num_total; | |
700 | ||
a1d1e266 LG |
701 | return 0; |
702 | } | |
703 | ||
704 | void hda_dsp_stream_free(struct snd_sof_dev *sdev) | |
705 | { | |
706 | struct hdac_bus *bus = sof_to_bus(sdev); | |
707 | struct hdac_stream *s, *_s; | |
708 | struct hdac_ext_stream *stream; | |
709 | struct sof_intel_hda_stream *hda_stream; | |
710 | ||
711 | /* free position buffer */ | |
712 | if (bus->posbuf.area) | |
713 | snd_dma_free_pages(&bus->posbuf); | |
714 | ||
715 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
716 | /* free position buffer */ | |
717 | if (bus->rb.area) | |
718 | snd_dma_free_pages(&bus->rb); | |
719 | #endif | |
720 | ||
721 | list_for_each_entry_safe(s, _s, &bus->stream_list, list) { | |
722 | /* TODO: decouple */ | |
723 | ||
724 | /* free bdl buffer */ | |
725 | if (s->bdl.area) | |
726 | snd_dma_free_pages(&s->bdl); | |
727 | list_del(&s->list); | |
728 | stream = stream_to_hdac_ext_stream(s); | |
729 | hda_stream = container_of(stream, struct sof_intel_hda_stream, | |
730 | hda_stream); | |
731 | devm_kfree(sdev->dev, hda_stream); | |
732 | } | |
733 | } |