1 /*
2  * Copyright (c) 2022 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief Public APIs for the DAI (Digital Audio Interface) bus drivers.
10  */
11 
12 #ifndef ZEPHYR_INCLUDE_DRIVERS_DAI_H_
13 #define ZEPHYR_INCLUDE_DRIVERS_DAI_H_
14 
15 /**
16  * @defgroup dai_interface DAI Interface
17  * @since 3.1
18  * @version 0.1.0
19  * @ingroup io_interfaces
20  * @brief DAI Interface
21  *
22  * The DAI API provides support for the standard I2S (SSP) and its common variants.
23  * It supports also DMIC, HDA and SDW backends. The API has a config function
24  * with bespoke data argument for device/vendor specific config. There are also
25  * optional timestamping functions to get device specific audio clock time.
26  * @{
27  */
28 
29 #include <errno.h>
30 
31 #include <zephyr/types.h>
32 #include <zephyr/device.h>
33 
34 #ifdef __cplusplus
35 extern "C" {
36 #endif
37 
38 /** Used to extract the clock configuration from the format attribute of struct dai_config */
39 #define DAI_FORMAT_CLOCK_PROVIDER_MASK 0xf000
40 /** Used to extract the protocol from the format attribute of struct dai_config */
41 #define DAI_FORMAT_PROTOCOL_MASK 0x000f
42 /** Used to extract the clock inversion from the format attribute of struct dai_config */
43 #define DAI_FORMAT_CLOCK_INVERSION_MASK 0x0f00
44 
45 /** @brief DAI clock configurations
46  *
47  * This is used to describe all of the possible
48  * clock-related configurations w.r.t the DAI
49  * and the codec.
50  */
51 enum dai_clock_provider {
52 	/**< codec BLCK provider, codec FSYNC provider */
53 	DAI_CBP_CFP = (0 << 12),
54 	/**< codec BCLK consumer, codec FSYNC provider */
55 	DAI_CBC_CFP = (2 << 12),
56 	/**< codec BCLK provider, codec FSYNC consumer */
57 	DAI_CBP_CFC = (3 << 12),
58 	/**< codec BCLK consumer, codec FSYNC consumer */
59 	DAI_CBC_CFC = (4 << 12),
60 };
61 
62 /** @brief DAI protocol
63  *
64  * The communication between the DAI and the CODEC
65  * may use different protocols depending on the scenario.
66  */
67 enum dai_protocol {
68 	DAI_PROTO_I2S = 1, /**< I2S */
69 	DAI_PROTO_RIGHT_J, /**< Right Justified */
70 	DAI_PROTO_LEFT_J, /**< Left Justified */
71 	DAI_PROTO_DSP_A, /**< TDM, FSYNC asserted 1 BCLK early */
72 	DAI_PROTO_DSP_B, /**< TDM, FSYNC asserted at the same time as MSB */
73 	DAI_PROTO_PDM, /**< Pulse Density Modulation */
74 };
75 
76 /** @brief DAI clock inversion
77  *
78  * Some applications may require a different
79  * clock polarity (FSYNC/BCLK) compared to
80  * the default one chosen based on the protocol.
81  */
82 enum dai_clock_inversion {
83 	/**< no BCLK inversion, no FSYNC inversion */
84 	DAI_INVERSION_NB_NF = 0,
85 	/**< no BCLK inversion, FSYNC inversion */
86 	DAI_INVERSION_NB_IF = (2 << 8),
87 	 /**< BCLK inversion, no FSYNC inversion */
88 	DAI_INVERSION_IB_NF = (3 << 8),
89 	/**< BCLK inversion, FSYNC inversion */
90 	DAI_INVERSION_IB_IF = (4 << 8),
91 };
92 
93 /** @brief Types of DAI
94  *
95  * The type of the DAI. This ID type is used to configure bespoke DAI HW
96  * settings.
97  *
98  * DAIs have a lot of physical link feature variability and therefore need
99  * different configuration data to cater for different use cases. We
100  * usually need to pass extra bespoke configuration prior to DAI start.
101  */
102 enum dai_type {
103 	DAI_LEGACY_I2S = 0,	/**< Legacy I2S compatible with i2s.h */
104 	DAI_INTEL_SSP,		/**< Intel SSP */
105 	DAI_INTEL_DMIC,		/**< Intel DMIC */
106 	DAI_INTEL_HDA,		/**< Intel HD/A */
107 	DAI_INTEL_ALH,		/**< Intel ALH */
108 	DAI_IMX_SAI,		/**< i.MX SAI */
109 	DAI_IMX_ESAI,		/**< i.MX ESAI */
110 	DAI_AMD_BT,		/**< Amd BT */
111 	DAI_AMD_SP,		/**< Amd SP */
112 	DAI_AMD_DMIC,		/**< Amd DMIC */
113 	DAI_MEDIATEK_AFE,	/**< Mtk AFE */
114 	DAI_INTEL_SSP_NHLT,	/**< nhlt ssp */
115 	DAI_INTEL_DMIC_NHLT,	/**< nhlt ssp */
116 	DAI_INTEL_HDA_NHLT,	/**< nhlt Intel HD/A */
117 	DAI_INTEL_ALH_NHLT,	/**< nhlt Intel ALH */
118 	DAI_IMX_MICFIL,		/**< i.MX PDM MICFIL */
119 };
120 
121 /**
122  * @brief DAI Direction
123  */
124 enum dai_dir {
125 	/** Transmit data */
126 	DAI_DIR_TX = 0,
127 	/** Receive data */
128 	DAI_DIR_RX,
129 	/** Both receive and transmit data */
130 	DAI_DIR_BOTH,
131 };
132 
133 /** Interface state */
134 enum dai_state {
135 	/** @brief The interface is not ready.
136 	 *
137 	 * The interface was initialized but is not yet ready to receive /
138 	 * transmit data. Call dai_config_set() to configure interface and change
139 	 * its state to READY.
140 	 */
141 	DAI_STATE_NOT_READY = 0,
142 	/** The interface is ready to receive / transmit data. */
143 	DAI_STATE_READY,
144 	/** The interface is receiving / transmitting data. */
145 	DAI_STATE_RUNNING,
146 	/** The interface is clocking but not receiving / transmitting data. */
147 	DAI_STATE_PRE_RUNNING,
148 	/** The interface paused */
149 	DAI_STATE_PAUSED,
150 	/** The interface is draining its transmit queue. */
151 	DAI_STATE_STOPPING,
152 	/** TX buffer underrun or RX buffer overrun has occurred. */
153 	DAI_STATE_ERROR,
154 };
155 
156 /** Trigger command */
157 enum dai_trigger_cmd {
158 	/** @brief Start the transmission / reception of data.
159 	 *
160 	 * If DAI_DIR_TX is set some data has to be queued for transmission by
161 	 * the dai_write() function. This trigger can be used in READY state
162 	 * only and changes the interface state to RUNNING.
163 	 */
164 	DAI_TRIGGER_START = 0,
165 	/** @brief Optional - Pre Start the transmission / reception of data.
166 	 *
167 	 * Allows the DAI and downstream codecs to prepare for audio Tx/Rx by
168 	 * starting any required clocks for downstream PLL/FLL locking.
169 	 */
170 	DAI_TRIGGER_PRE_START,
171 	/** @brief Stop the transmission / reception of data.
172 	 *
173 	 * Stop the transmission / reception of data at the end of the current
174 	 * memory block. This trigger can be used in RUNNING state only and at
175 	 * first changes the interface state to STOPPING. When the current TX /
176 	 * RX block is transmitted / received the state is changed to READY.
177 	 * Subsequent START trigger will resume transmission / reception where
178 	 * it stopped.
179 	 */
180 	DAI_TRIGGER_STOP,
181 	/** @brief Pause the transmission / reception of data.
182 	 *
183 	 * Pause the transmission / reception of data at the end of the current
184 	 * memory block. Behavior is implementation specific but usually this
185 	 * state doesn't completely stop the clocks or transmission. The DAI could
186 	 * be transmitting 0's (silence), but it is not consuming data from outside.
187 	 */
188 	DAI_TRIGGER_PAUSE,
189 	/** @brief Optional - Post Stop the transmission / reception of data.
190 	 *
191 	 * Allows the DAI and downstream codecs to shutdown cleanly after audio
192 	 * Tx/Rx by stopping any required clocks for downstream audio completion.
193 	 */
194 	DAI_TRIGGER_POST_STOP,
195 	/** @brief Empty the transmit queue.
196 	 *
197 	 * Send all data in the transmit queue and stop the transmission.
198 	 * If the trigger is applied to the RX queue it has the same effect as
199 	 * DAI_TRIGGER_STOP. This trigger can be used in RUNNING state only and
200 	 * at first changes the interface state to STOPPING. When all TX blocks
201 	 * are transmitted the state is changed to READY.
202 	 */
203 	DAI_TRIGGER_DRAIN,
204 	/** @brief Discard the transmit / receive queue.
205 	 *
206 	 * Stop the transmission / reception immediately and discard the
207 	 * contents of the respective queue. This trigger can be used in any
208 	 * state other than NOT_READY and changes the interface state to READY.
209 	 */
210 	DAI_TRIGGER_DROP,
211 	/** @brief Prepare the queues after underrun/overrun error has occurred.
212 	 *
213 	 * This trigger can be used in ERROR state only and changes the
214 	 * interface state to READY.
215 	 */
216 	DAI_TRIGGER_PREPARE,
217 	/** @brief Reset
218 	 *
219 	 * This trigger frees resources and moves the driver back to initial
220 	 * state.
221 	 */
222 	DAI_TRIGGER_RESET,
223 	/** @brief Copy
224 	 *
225 	 * This trigger prepares for data copying.
226 	 */
227 	DAI_TRIGGER_COPY,
228 };
229 
230 /** @brief DAI properties
231  *
232  * This struct is used with APIs get_properties function to query DAI
233  * properties like fifo address and dma handshake. These are needed
234  * for example to setup dma outside the driver code.
235  */
236 struct dai_properties {
237 	/** Fifo hw address for e.g. when connecting to dma. */
238 	uint32_t fifo_address;
239 	/** Fifo depth. */
240 	uint32_t fifo_depth;
241 	/** DMA handshake id. */
242 	uint32_t dma_hs_id;
243 	/** Delay for initializing registers. */
244 	uint32_t reg_init_delay;
245 	/** Stream ID. */
246 	int stream_id;
247 };
248 
249 /** @brief Main DAI config structure
250  *
251  * Generic DAI interface configuration options.
252  */
253 struct dai_config {
254 	/** Type of the DAI. */
255 	enum dai_type type;
256 	/** Index of the DAI. */
257 	uint32_t dai_index;
258 	/** Number of audio channels, words in frame. */
259 	uint8_t channels;
260 	/** Frame clock (WS) frequency, sampling rate. */
261 	uint32_t rate;
262 	/** DAI specific data stream format. */
263 	uint16_t format;
264 	/** DAI specific configuration options. */
265 	uint8_t options;
266 	/** Number of bits representing one data word. */
267 	uint8_t word_size;
268 	/** Size of one RX/TX memory block (buffer) in bytes. */
269 	size_t block_size;
270 	/** DAI specific link configuration. */
271 	uint16_t link_config;
272 	/**< tdm slot group number*/
273 	uint32_t  tdm_slot_group;
274 };
275 
276 /**
277  * @brief DAI timestamp configuration
278  */
279 struct dai_ts_cfg {
280 	/** Rate in Hz, e.g. 19200000 */
281 	uint32_t walclk_rate;
282 	/** Type of the DAI (SSP, DMIC, HDA, etc.). */
283 	int type;
284 	/** Direction (playback/capture) */
285 	int direction;
286 	/** Index for SSPx to select correct timestamp register */
287 	int index;
288 	/** DMA instance id */
289 	int dma_id;
290 	/** Used DMA channel index */
291 	int dma_chan_index;
292 	/** Number of channels in single DMA */
293 	int dma_chan_count;
294 };
295 
296 /**
297  * @brief DAI timestamp data
298  */
299 struct dai_ts_data {
300 	/** Wall clock */
301 	uint64_t walclk;
302 	/** Sample count */
303 	uint64_t sample;
304 	/** Rate in Hz, e.g. 19200000 */
305 	uint32_t walclk_rate;
306 };
307 
308 /**
309  * @cond INTERNAL_HIDDEN
310  *
311  * For internal use only, skip these in public documentation.
312  */
313 __subsystem struct dai_driver_api {
314 	int (*probe)(const struct device *dev);
315 	int (*remove)(const struct device *dev);
316 	int (*config_set)(const struct device *dev, const struct dai_config *cfg,
317 			  const void *bespoke_cfg);
318 	int (*config_get)(const struct device *dev, struct dai_config *cfg,
319 			  enum dai_dir dir);
320 
321 	const struct dai_properties *(*get_properties)(const struct device *dev,
322 						       enum dai_dir dir,
323 						       int stream_id);
324 
325 	int (*trigger)(const struct device *dev, enum dai_dir dir,
326 		       enum dai_trigger_cmd cmd);
327 
328 	/* optional methods */
329 	int (*ts_config)(const struct device *dev, struct dai_ts_cfg *cfg);
330 	int (*ts_start)(const struct device *dev, struct dai_ts_cfg *cfg);
331 	int (*ts_stop)(const struct device *dev, struct dai_ts_cfg *cfg);
332 	int (*ts_get)(const struct device *dev, struct dai_ts_cfg *cfg,
333 		      struct dai_ts_data *tsd);
334 	int (*config_update)(const struct device *dev, const void *bespoke_cfg,
335 			     size_t size);
336 };
337 
338 /**
339  * @endcond
340  */
341 
342 /**
343  * @brief Probe operation of DAI driver.
344  *
345  * The function will be called to power up the device and update for example
346  * possible reference count of the users. It can be used also to initialize
347  * internal variables and memory allocation.
348  *
349  * @param dev Pointer to the device structure for the driver instance.
350  *
351  * @retval 0 If successful.
352  */
dai_probe(const struct device * dev)353 static inline int dai_probe(const struct device *dev)
354 {
355 	const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
356 
357 	return api->probe(dev);
358 }
359 
360 /**
361  * @brief Remove operation of DAI driver.
362  *
363  * The function will be called to unregister/unbind the device, for example to
364  * power down the device or decrease the usage reference count.
365  *
366  * @param dev Pointer to the device structure for the driver instance.
367  *
368  * @retval 0 If successful.
369  */
dai_remove(const struct device * dev)370 static inline int dai_remove(const struct device *dev)
371 {
372 	const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
373 
374 	return api->remove(dev);
375 }
376 
377 /**
378  * @brief Configure operation of a DAI driver.
379  *
380  * The dir parameter specifies if Transmit (TX) or Receive (RX) direction
381  * will be configured by data provided via cfg parameter.
382  *
383  * The function can be called in NOT_READY or READY state only. If executed
384  * successfully the function will change the interface state to READY.
385  *
386  * If the function is called with the parameter cfg->frame_clk_freq set to 0
387  * the interface state will be changed to NOT_READY.
388  *
389  * @param dev Pointer to the device structure for the driver instance.
390  * @param cfg Pointer to the structure containing configuration parameters.
391  * @param bespoke_cfg Pointer to the structure containing bespoke config.
392  *
393  * @retval 0 If successful.
394  * @retval -EINVAL Invalid argument.
395  * @retval -ENOSYS DAI_DIR_BOTH value is not supported.
396  */
dai_config_set(const struct device * dev,const struct dai_config * cfg,const void * bespoke_cfg)397 static inline int dai_config_set(const struct device *dev,
398 				 const struct dai_config *cfg,
399 				 const void *bespoke_cfg)
400 {
401 	const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
402 
403 	return api->config_set(dev, cfg, bespoke_cfg);
404 }
405 
406 /**
407  * @brief Fetch configuration information of a DAI driver
408  *
409  * @param dev Pointer to the device structure for the driver instance
410  * @param cfg Pointer to the config structure to be filled by the instance
411  * @param dir Stream direction: RX or TX as defined by DAI_DIR_*
412  * @retval 0 if success, negative if invalid parameters or DAI un-configured
413  */
dai_config_get(const struct device * dev,struct dai_config * cfg,enum dai_dir dir)414 static inline int dai_config_get(const struct device *dev,
415 				 struct dai_config *cfg,
416 				 enum dai_dir dir)
417 {
418 	const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
419 
420 	return api->config_get(dev, cfg, dir);
421 }
422 
423 /**
424  * @brief Fetch properties of a DAI driver
425  *
426  * @param dev Pointer to the device structure for the driver instance
427  * @param dir Stream direction: RX or TX as defined by DAI_DIR_*
428  * @param stream_id Stream id: some drivers may have stream specific
429  *        properties, this id specifies the stream.
430  * @retval Pointer to the structure containing properties,
431  *         or NULL if error or no properties
432  */
dai_get_properties(const struct device * dev,enum dai_dir dir,int stream_id)433 static inline const struct dai_properties *dai_get_properties(const struct device *dev,
434 							      enum dai_dir dir,
435 							      int stream_id)
436 {
437 	const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
438 
439 	return api->get_properties(dev, dir, stream_id);
440 }
441 
442 /**
443  * @brief Send a trigger command.
444  *
445  * @param dev Pointer to the device structure for the driver instance.
446  * @param dir Stream direction: RX, TX, or both, as defined by DAI_DIR_*.
447  *            The DAI_DIR_BOTH value may not be supported by some drivers.
448  *            For those, triggering need to be done separately for the RX
449  *            and TX streams.
450  * @param cmd Trigger command.
451  *
452  * @retval 0 If successful.
453  * @retval -EINVAL Invalid argument.
454  * @retval -EIO The trigger cannot be executed in the current state or a DMA
455  *         channel cannot be allocated.
456  * @retval -ENOMEM RX/TX memory block not available.
457  * @retval -ENOSYS DAI_DIR_BOTH value is not supported.
458  */
dai_trigger(const struct device * dev,enum dai_dir dir,enum dai_trigger_cmd cmd)459 static inline int dai_trigger(const struct device *dev,
460 			      enum dai_dir dir,
461 			      enum dai_trigger_cmd cmd)
462 {
463 	const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
464 
465 	return api->trigger(dev, dir, cmd);
466 }
467 
468 /**
469  * Configures timestamping in attached DAI.
470  * @param dev Component device.
471  * @param cfg Timestamp config.
472  *
473  * Optional method.
474  *
475  * @retval 0 If successful.
476  */
dai_ts_config(const struct device * dev,struct dai_ts_cfg * cfg)477 static inline int dai_ts_config(const struct device *dev, struct dai_ts_cfg *cfg)
478 {
479 	const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
480 
481 	if (!api->ts_config) {
482 		return -EINVAL;
483 	}
484 
485 	return api->ts_config(dev, cfg);
486 }
487 
488 /**
489  * Starts timestamping.
490  * @param dev Component device.
491  * @param cfg Timestamp config.
492  *
493  * Optional method
494  *
495  * @retval 0 If successful.
496  */
dai_ts_start(const struct device * dev,struct dai_ts_cfg * cfg)497 static inline int dai_ts_start(const struct device *dev, struct dai_ts_cfg *cfg)
498 {
499 	const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
500 
501 	if (!api->ts_start) {
502 		return -EINVAL;
503 	}
504 
505 	return api->ts_start(dev, cfg);
506 }
507 
508 /**
509  * Stops timestamping.
510  * @param dev Component device.
511  * @param cfg Timestamp config.
512  *
513  * Optional method.
514  *
515  * @retval 0 If successful.
516  */
dai_ts_stop(const struct device * dev,struct dai_ts_cfg * cfg)517 static inline int dai_ts_stop(const struct device *dev, struct dai_ts_cfg *cfg)
518 {
519 	const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
520 
521 	if (!api->ts_stop) {
522 		return -EINVAL;
523 	}
524 
525 	return api->ts_stop(dev, cfg);
526 }
527 
528 /**
529  * Gets timestamp.
530  * @param dev Component device.
531  * @param cfg Timestamp config.
532  * @param tsd Receives timestamp data.
533  *
534  * Optional method.
535  *
536  * @retval 0 If successful.
537  */
dai_ts_get(const struct device * dev,struct dai_ts_cfg * cfg,struct dai_ts_data * tsd)538 static inline int dai_ts_get(const struct device *dev, struct dai_ts_cfg *cfg,
539 			     struct dai_ts_data *tsd)
540 {
541 	const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
542 
543 	if (!api->ts_get) {
544 		return -EINVAL;
545 	}
546 
547 	return api->ts_get(dev, cfg, tsd);
548 }
549 
550 /**
551  * @brief Update DAI configuration at runtime.
552  *
553  * This function updates the configuration of a DAI interface at runtime.
554  * It allows setting bespoke configuration parameters that are specific to
555  * the DAI implementation, enabling updates outside of the regular flow with
556  * the full configuration blob. The details of the bespoke configuration are
557  * specific to each DAI implementation. This function should only be called
558  * when the DAI is in the READY state, ensuring that the configuration updates
559  * are applied before data transmission or reception begins.
560  *
561  * @param dev Pointer to the device structure for the driver instance.
562  * @param bespoke_cfg Pointer to the buffer containing bespoke configuration parameters.
563  * @param size Size of the bespoke_cfg buffer in bytes.
564  *
565  * @retval 0 If successful.
566  * @retval -ENOSYS If the configuration update operation is not implemented.
567  * @retval Negative errno code if failure.
568  */
dai_config_update(const struct device * dev,const void * bespoke_cfg,size_t size)569 static inline int dai_config_update(const struct device *dev,
570 									const void *bespoke_cfg,
571 									size_t size)
572 {
573 	const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
574 
575 	if (!api->config_update) {
576 		return -ENOSYS;
577 	}
578 
579 	return api->config_update(dev, bespoke_cfg, size);
580 }
581 
582 /**
583  * @}
584  */
585 
586 #ifdef __cplusplus
587 }
588 #endif
589 
590 #endif /* ZEPHYR_INCLUDE_DRIVERS_DAI_H_ */
591