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