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