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