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