1 /* Copyright (c) 2022 Intel Corporation
2 * SPDX-License-Identifier: Apache-2.0
3 */
4
5 #ifndef ZEPHYR_INCLUDE_INTEL_ADSP_HDA_H
6 #define ZEPHYR_INCLUDE_INTEL_ADSP_HDA_H
7
8 #include <zephyr/cache.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/device.h>
11 #include <adsp_shim.h>
12 #include <adsp_memory.h>
13
14 /**
15 * @brief HDA stream functionality for Intel ADSP
16 *
17 * Provides low level calls to support Intel ADSP HDA streams with
18 * minimal abstraction that allows testing the hardware
19 * and its demands separately from the intended DMA API
20 * usage. The only requirement is that you define the base
21 * addresses, the stream count, and the size of the ip blocks.
22 */
23
24 /* The read/write positions are masked to 24 bits */
25 #define HDA_RWP_MASK 0x00FFFFFF
26
27 /* Buffers must be 128 byte aligned, this mask enforces that */
28 #define HDA_ALIGN_MASK 0xFFFFFF80
29
30
31 /* Calculate base address of the stream registers */
32 #define HDA_ADDR(base, regblock_size, stream) ((base) + (stream)*(regblock_size))
33
34 /* Gateway Control and Status Register */
35 #define DGCS(base, regblock_size, stream) \
36 ((volatile uint32_t *)HDA_ADDR(base, regblock_size, stream))
37 #define DGCS_SCS BIT(31) /* Sample container size */
38 #define DGCS_GEN BIT(26) /* Gateway Enable */
39 #define DGCS_L1ETP BIT(25) /* L1 Enter Prevent */
40 #define DGCS_L1EXP BIT(24) /* L1 Exit Prevent */
41 #define DGCS_FWCB BIT(23) /* Firmware Control Buffer */
42 #define DGCS_GBUSY BIT(15) /* Gateway Busy */
43 #define DGCS_TE BIT(14) /* Transfer Error */
44 #define DGCS_BSC BIT(11) /* Buffer Segment Completion */
45 #define DGCS_BOR BIT(10) /* Buffer Overrun */
46 #define DGCS_BUR BIT(10) /* Buffer Underrun */
47 #define DGCS_BF BIT(9) /* Buffer Full */
48 #define DGCS_BNE BIT(8) /* Buffer Not Empty */
49 #define DGCS_FIFORDY BIT(5) /* Enable FIFO */
50 #define DGCS_BSCIE BIT(3) /* Buffer Segment Completion Interrupt Enable */
51
52 /* Gateway Buffer Base Address */
53 #define DGBBA(base, regblock_size, stream) \
54 ((volatile uint32_t *)(HDA_ADDR(base, regblock_size, stream) + 0x04))
55
56 /* Gateway Buffer Size */
57 #define DGBS(base, regblock_size, stream) \
58 ((volatile uint32_t *)(HDA_ADDR(base, regblock_size, stream) + 0x08))
59
60 /* Gateway Buffer Position Increment */
61 #define DGBFPI(base, regblock_size, stream) \
62 ((volatile uint32_t *)(HDA_ADDR(base, regblock_size, stream) + 0x0c))
63
64 /* Gateway Buffer Read Position */
65 #define DGBRP(base, regblock_size, stream) \
66 ((volatile uint32_t *)(HDA_ADDR(base, regblock_size, stream) + 0x10))
67
68 /* Gateway Buffer Write Position */
69 #define DGBWP(base, regblock_size, stream) \
70 ((volatile uint32_t *)(HDA_ADDR(base, regblock_size, stream) + 0x14))
71
72 /* Gateway Buffer Segment Position */
73 #define DGBSP(base, regblock_size, stream) \
74 ((volatile uint32_t *)(HDA_ADDR(base, regblock_size, stream) + 0x18))
75
76 /* Gateway Minimum Buffer Size */
77 #define DGMBS(base, regblock_size, stream) \
78 ((volatile uint32_t *)(HDA_ADDR(base, regblock_size, stream) + 0x1c))
79
80 /* Gateway Linear Link Position Increment */
81 #define DGLLPI(base, regblock_size, stream) \
82 ((volatile uint32_t *)(HDA_ADDR(base, regblock_size, stream) + 0x24))
83
84 /* Gateway Linear Position In Buffer Increment */
85 #define DGLPIBI(base, regblock_size, stream) \
86 ((volatile uint32_t *)(HDA_ADDR(base, regblock_size, stream) + 0x28))
87
88 /**
89 * @brief Dump all the useful registers of an HDA stream to printk
90 *
91 * This can be invaluable when finding out why HDA isn't doing (or maybe is)
92 * doing what you want it to do. Macro so you get the file and line
93 * of the call site included.
94 *
95 * @param name String that contains a name of the hda stream (or anything really)
96 * @param base Base address of the IP register block
97 * @param regblock_size Register block size
98 * @param sid Stream ID
99 */
100 #define intel_adsp_hda_dbg(name, base, regblock_size, sid) \
101 printk("%s:%u %s(%u:0x%p), dgcs: 0x%x, dgbba 0x%x, " \
102 "dgbs %u, dgbrp %u, dgbwp %u, dgbsp %u, " \
103 "dgmbs %u, dgbllpi 0x%x, dglpibi 0x%x\n", \
104 __FILE__, __LINE__, name, \
105 sid, DGCS(base, regblock_size, sid), \
106 *DGCS(base, regblock_size, sid), \
107 *DGBBA(base, regblock_size, sid), \
108 *DGBS(base, regblock_size, sid), \
109 *DGBRP(base, regblock_size, sid), \
110 *DGBWP(base, regblock_size, sid), \
111 *DGBSP(base, regblock_size, sid), \
112 *DGMBS(base, regblock_size, sid), \
113 *DGLLPI(base, regblock_size, sid), \
114 *DGLPIBI(base, regblock_size, sid))
115
116
117
118 /**
119 * @brief Initialize an HDA stream for use with the firmware
120 *
121 * @param base Base address of the IP register block
122 * @param sid Stream ID
123 */
intel_adsp_hda_init(uint32_t base,uint32_t regblock_size,uint32_t sid)124 static inline void intel_adsp_hda_init(uint32_t base, uint32_t regblock_size, uint32_t sid)
125 {
126 *DGCS(base, regblock_size, sid) |= DGCS_FWCB;
127 }
128
129 /**
130 * @brief Set the buffer, size, and element size for an HDA stream
131 *
132 * Sanity checks that the buffer address and size are valid and that the
133 * stream isn't enabled or busy.
134 *
135 * Prior to enabling an HDA stream to/from the host this is the minimum configuration
136 * that is required. It must be set *after* the host has configured its own buffers.
137 *
138 *
139 * @param base Base address of the IP register block
140 * @param regblock_size Register block size
141 * @param sid Stream ID
142 * @param buf Buffer address to use for the shared FIFO. Must be in L2 and 128 byte aligned.
143 * @param buf_size Buffer size in bytes Must be 128 byte aligned
144 *
145 * @retval -EBUSY if the HDA stream is already enabled
146 * @retval -EINVAL if the buf is not in L2, buf isn't aligned on 128 byte boundaries
147 * @retval 0 on Success
148 */
intel_adsp_hda_set_buffer(uint32_t base,uint32_t regblock_size,uint32_t sid,uint8_t * buf,uint32_t buf_size)149 static inline int intel_adsp_hda_set_buffer(uint32_t base,
150 uint32_t regblock_size,
151 uint32_t sid,
152 uint8_t *buf,
153 uint32_t buf_size)
154 {
155 /* While we don't actually care if the pointer is in the cached
156 * region or not, we do need a consistent address space to check
157 * against for our assertion. This is cheap.
158 */
159 uint32_t addr = (uint32_t)arch_xtensa_cached_ptr(buf);
160 uint32_t aligned_addr = addr & HDA_ALIGN_MASK;
161 uint32_t aligned_size = buf_size & HDA_ALIGN_MASK;
162
163 __ASSERT(aligned_addr == addr, "Buffer must be 128 byte aligned");
164 __ASSERT(aligned_addr >= L2_SRAM_BASE
165 && aligned_addr < L2_SRAM_BASE + L2_SRAM_SIZE,
166 "Buffer must be in L2 address space");
167 __ASSERT(aligned_size == buf_size,
168 "Buffer must be 128 byte aligned in size");
169
170 __ASSERT(aligned_addr + aligned_size < L2_SRAM_BASE + L2_SRAM_SIZE,
171 "Buffer must end in L2 address space");
172
173 if (*DGCS(base, regblock_size, sid) & DGCS_GEN) {
174 return -EBUSY;
175 }
176
177 if (*DGCS(base, regblock_size, sid) & DGCS_GBUSY) {
178 return -EBUSY;
179 }
180
181 *DGBBA(base, regblock_size, sid) = aligned_addr;
182 *DGBS(base, regblock_size, sid) = aligned_size;
183
184 return 0;
185 }
186
187 /**
188 * @brief Get the buffer size
189 *
190 * @param base Base address of the IP register block
191 * @param regblock_size Register block size
192 * @param sid Stream ID
193 *
194 * @retval buf_size Buffer size in bytes
195 */
intel_adsp_hda_get_buffer_size(uint32_t base,uint32_t regblock_size,uint32_t sid)196 static inline uint32_t intel_adsp_hda_get_buffer_size(uint32_t base,
197 uint32_t regblock_size,
198 uint32_t sid)
199 {
200 return *DGBS(base, regblock_size, sid);
201 }
202
203 /**
204 * @brief Enable the stream
205 *
206 * @param base Base address of the IP register block
207 * @param regblock_size Register block size
208 * @param sid Stream ID
209 */
intel_adsp_hda_enable(uint32_t base,uint32_t regblock_size,uint32_t sid,bool set_fifordy)210 static inline void intel_adsp_hda_enable(uint32_t base, uint32_t regblock_size,
211 uint32_t sid, bool set_fifordy)
212 {
213 *DGCS(base, regblock_size, sid) |= DGCS_GEN;
214
215 if (set_fifordy) {
216 *DGCS(base, regblock_size, sid) |= DGCS_FIFORDY;
217 }
218 }
219
220 /**
221 * @brief Disable stream
222 *
223 * @param base Base address of the IP register block
224 * @param regblock_size Register block size
225 * @param sid Stream ID
226 */
intel_adsp_hda_disable(uint32_t base,uint32_t regblock_size,uint32_t sid)227 static inline void intel_adsp_hda_disable(uint32_t base, uint32_t regblock_size, uint32_t sid)
228 {
229 *DGCS(base, regblock_size, sid) &= ~(DGCS_GEN | DGCS_FIFORDY);
230 }
231
232 /**
233 * @brief Check if stream is enabled
234 *
235 * @param base Base address of the IP register block
236 * @param regblock_size Register block size
237 * @param sid Stream ID
238 */
intel_adsp_hda_is_enabled(uint32_t base,uint32_t regblock_size,uint32_t sid)239 static inline bool intel_adsp_hda_is_enabled(uint32_t base, uint32_t regblock_size, uint32_t sid)
240 {
241 return *DGCS(base, regblock_size, sid) & (DGCS_GEN | DGCS_FIFORDY);
242 }
243
244 /**
245 * @brief Determine the number of unused bytes in the buffer
246 *
247 * This is useful primarily for a host in (dsp -> host) stream.
248 *
249 * @param base Base address of the IP register block
250 * @param regblock_size Register block size
251 * @param sid Stream ID within the register block
252 *
253 * @retval n Number of unused bytes
254 */
intel_adsp_hda_unused(uint32_t base,uint32_t regblock_size,uint32_t sid)255 static inline uint32_t intel_adsp_hda_unused(uint32_t base, uint32_t regblock_size, uint32_t sid)
256 {
257 uint32_t dgcs = *DGCS(base, regblock_size, sid);
258 uint32_t dgbs = *DGBS(base, regblock_size, sid);
259
260 /* Check if buffer is empty */
261 if ((dgcs & DGCS_BNE) == 0) {
262 return dgbs;
263 }
264
265 /* Check if the buffer is full */
266 if (dgcs & DGCS_BF) {
267 return 0;
268 }
269
270 int32_t rp = *DGBRP(base, regblock_size, sid);
271 int32_t wp = *DGBWP(base, regblock_size, sid);
272 int32_t size = rp - wp;
273
274 if (size <= 0) {
275 size += dgbs;
276 }
277
278 return size;
279 }
280
281 /**
282 * @brief Commit a number of bytes that have been transferred to/from host
283 *
284 * Writes the length to BFPI. For host transfers LLPI and LPIB are
285 * also written to with the given length.
286 *
287 * This then updates the read or write position depending on the direction.
288 *
289 * LPIBI writes here can be seen on the host side of the transfer in the
290 * matching LPIB register.
291 *
292 * LLPI seems to, from behavior, inform the hardware to actually read/write
293 * from the buffer. Without updating BFPI AND LLPI, the transfer doesn't
294 * happen in testing for host transfers.
295 *
296 * @param base Base address of the IP register block
297 * @param regblock_size Register block size
298 * @param sid Stream ID within the register block
299 * @param len Len to increment postion by
300 */
intel_adsp_hda_host_commit(uint32_t base,uint32_t regblock_size,uint32_t sid,uint32_t len)301 static inline void intel_adsp_hda_host_commit(uint32_t base,
302 uint32_t regblock_size,
303 uint32_t sid,
304 uint32_t len)
305 {
306 *DGBFPI(base, regblock_size, sid) = len;
307 *DGLLPI(base, regblock_size, sid) = len;
308 *DGLPIBI(base, regblock_size, sid) = len;
309 }
310
311 /**
312 * @brief Commit a number of bytes that have been transferred to/from link
313 *
314 * Writes the length to BFPI.
315 *
316 * @seealso intel_adsp_hda_host_commit
317 *
318 * @param base Base address of the IP register block
319 * @param regblock_size Register block size
320 * @param sid Stream ID within the register block
321 * @param len Len to increment postion by
322 */
intel_adsp_hda_link_commit(uint32_t base,uint32_t regblock_size,uint32_t sid,uint32_t len)323 static inline void intel_adsp_hda_link_commit(uint32_t base,
324 uint32_t regblock_size,
325 uint32_t sid,
326 uint32_t len)
327 {
328 *DGBFPI(base, regblock_size, sid) = len;
329 }
330
331 /**
332 * @brief Read the buffer full bit of the given stream.
333 *
334 * @param base Base address of the IP register block
335 * @param regblock_size Register block size
336 * @param sid Stream ID within the register block
337 *
338 * @retval true If the buffer full flag is set
339 */
intel_adsp_hda_buf_full(uint32_t base,uint32_t regblock_size,uint32_t sid)340 static inline bool intel_adsp_hda_buf_full(uint32_t base, uint32_t regblock_size, uint32_t sid)
341 {
342 return *DGCS(base, regblock_size, sid) & DGCS_BF;
343 }
344
345 /**
346 * @brief Check if the write and read position are equal
347 *
348 * For HDA this does not mean that the buffer is full or empty
349 * there are bit flags for those cases.
350 *
351 * Useful for waiting on the hardware to catch up to
352 * reads or writes (e.g. after a intel_adsp_hda_commit)
353 *
354 * @param base Base address of the IP register block
355 * @param regblock_size Register block size
356 * @param sid Stream D
357 *
358 * @retval true If the read and write positions are equal
359 */
intel_adsp_hda_wp_rp_eq(uint32_t base,uint32_t regblock_size,uint32_t sid)360 static inline bool intel_adsp_hda_wp_rp_eq(uint32_t base, uint32_t regblock_size, uint32_t sid)
361 {
362 return *DGBWP(base, regblock_size, sid) == *DGBRP(base, regblock_size, sid);
363 }
364
intel_adsp_hda_is_buffer_overrun(uint32_t base,uint32_t regblock_size,uint32_t sid)365 static inline bool intel_adsp_hda_is_buffer_overrun(uint32_t base, uint32_t regblock_size,
366 uint32_t sid)
367 {
368 return (*DGCS(base, regblock_size, sid) & DGCS_BOR) == DGCS_BOR ? 1 : 0;
369 }
370
intel_adsp_hda_is_buffer_underrun(uint32_t base,uint32_t regblock_size,uint32_t sid)371 static inline bool intel_adsp_hda_is_buffer_underrun(uint32_t base, uint32_t regblock_size,
372 uint32_t sid)
373 {
374 return (*DGCS(base, regblock_size, sid) & DGCS_BUR) == DGCS_BUR ? 1 : 0;
375 }
376
intel_adsp_hda_overrun_clear(uint32_t base,uint32_t regblock_size,uint32_t sid)377 static inline void intel_adsp_hda_overrun_clear(uint32_t base, uint32_t regblock_size,
378 uint32_t sid)
379 {
380 *DGCS(base, regblock_size, sid) |= DGCS_BOR;
381 }
382
intel_adsp_hda_underrun_clear(uint32_t base,uint32_t regblock_size,uint32_t sid)383 static inline void intel_adsp_hda_underrun_clear(uint32_t base, uint32_t regblock_size,
384 uint32_t sid)
385 {
386 *DGCS(base, regblock_size, sid) |= DGCS_BUR;
387 }
388
389 /**
390 * @brief Set the buffer segment ptr
391 *
392 * @param base Base address of the IP register block
393 * @param regblock_size Register block size
394 * @param sid Stream ID
395 * @param size
396 */
intel_adsp_hda_set_buffer_segment_ptr(uint32_t base,uint32_t regblock_size,uint32_t sid,uint32_t size)397 static inline void intel_adsp_hda_set_buffer_segment_ptr(uint32_t base, uint32_t regblock_size,
398 uint32_t sid, uint32_t size)
399 {
400 *DGBSP(base, regblock_size, sid) = size;
401 }
402
403 /**
404 * @brief Get the buffer segment ptr
405 *
406 * @param base Base address of the IP register block
407 * @param regblock_size Register block size
408 * @param sid Stream ID
409 *
410 * @retval buffer segment ptr
411 */
intel_adsp_hda_get_buffer_segment_ptr(uint32_t base,uint32_t regblock_size,uint32_t sid)412 static inline uint32_t intel_adsp_hda_get_buffer_segment_ptr(uint32_t base, uint32_t regblock_size,
413 uint32_t sid)
414 {
415 return *DGBSP(base, regblock_size, sid);
416 }
417
418 /**
419 * @brief Enable BSC interrupt
420 *
421 * @param base Base address of the IP register block
422 * @param regblock_size Register block size
423 * @param sid Stream ID
424 */
intel_adsp_hda_enable_buffer_interrupt(uint32_t base,uint32_t regblock_size,uint32_t sid)425 static inline void intel_adsp_hda_enable_buffer_interrupt(uint32_t base, uint32_t regblock_size,
426 uint32_t sid)
427 {
428 *DGCS(base, regblock_size, sid) |= DGCS_BSCIE;
429 }
430
431 /**
432 * @brief Disable BSC interrupt
433 *
434 * @param base Base address of the IP register block
435 * @param regblock_size Register block size
436 * @param sid Stream ID
437 */
intel_adsp_hda_disable_buffer_interrupt(uint32_t base,uint32_t regblock_size,uint32_t sid)438 static inline void intel_adsp_hda_disable_buffer_interrupt(uint32_t base, uint32_t regblock_size,
439 uint32_t sid)
440 {
441 *DGCS(base, regblock_size, sid) &= ~DGCS_BSCIE;
442 }
443
444 /**
445 * @brief Clear BSC interrupt
446 *
447 * @param base Base address of the IP register block
448 * @param regblock_size Register block size
449 * @param sid Stream ID
450 */
intel_adsp_hda_clear_buffer_interrupt(uint32_t base,uint32_t regblock_size,uint32_t sid)451 static inline void intel_adsp_hda_clear_buffer_interrupt(uint32_t base, uint32_t regblock_size,
452 uint32_t sid)
453 {
454 *DGCS(base, regblock_size, sid) |= DGCS_BSC;
455 }
456
457 /**
458 * @brief Get status of BSC interrupt
459 *
460 * @param base Base address of the IP register block
461 * @param regblock_size Register block size
462 * @param sid Stream ID
463 *
464 * @retval interrupt status
465 */
intel_adsp_hda_check_buffer_interrupt(uint32_t base,uint32_t regblock_size,uint32_t sid)466 static inline uint32_t intel_adsp_hda_check_buffer_interrupt(uint32_t base, uint32_t regblock_size,
467 uint32_t sid)
468 {
469 return (*DGCS(base, regblock_size, sid) & DGCS_BSC) == DGCS_BSC;
470 }
471
472 /**
473 * @brief Set the Sample Container Size (SCS)
474 *
475 * Sample Container Size indicates the container size of the audio samples in local memory
476 * SCS bit must cleared to 0 for 32bit sample size (HD Audio container size)
477 * SCS bit must be set to 1 for non 32bit sample sizes
478 *
479 * @param base Base address of the IP register block
480 * @param regblock_size Register block size
481 * @param sid Stream ID
482 * @param sample_size
483 */
intel_adsp_hda_set_sample_container_size(uint32_t base,uint32_t regblock_size,uint32_t sid,uint32_t sample_size)484 static inline void intel_adsp_hda_set_sample_container_size(uint32_t base, uint32_t regblock_size,
485 uint32_t sid, uint32_t sample_size)
486 {
487 if (sample_size <= 3) {
488 *DGCS(base, regblock_size, sid) |= DGCS_SCS;
489 } else {
490 *DGCS(base, regblock_size, sid) &= ~DGCS_SCS;
491 }
492 }
493
494 #endif /* ZEPHYR_INCLUDE_INTEL_ADSP_HDA_H */
495