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