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