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