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 Properties of DAI
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 * @param fifo_address Fifo hw address for e.g. when connecting to dma.
179 * @param fifo_depth Fifo depth.
180 * @param dma_hs_id Dma handshake id.
181 * @param reg_init_delay Delay for initializing registers.
182 * @param stream_id Stream ID.
183 */
184 struct dai_properties {
185 uint32_t fifo_address; /* fifo address */
186 uint32_t fifo_depth; /* fifo depth */
187 uint32_t dma_hs_id; /* dma handshake id */
188 uint32_t reg_init_delay; /* delay for register init */
189 int stream_id; /* stream id */
190 };
191
192 /** Main dai config struct
193 * @brief Generic Dai interface configuration options.
194 *
195 * @param dai_type Type of the dai.
196 * @param dai_index Index of the dai.
197 * @param channels Number of audio channels, words in frame.
198 * @param rate Frame clock (WS) frequency, sampling rate.
199 * @param format Dai specific data stream format.
200 * @param options Dai specific configuration options.
201 * @param word_size Number of bits representing one data word.
202 * @param block_size Size of one RX/TX memory block (buffer) in bytes.
203 * @param link_config Dai specific link configuration.
204 */
205 struct dai_config {
206 enum dai_type type;
207 uint32_t dai_index;
208 uint8_t channels;
209 uint32_t rate;
210 uint16_t format;
211 uint8_t options;
212 uint8_t word_size;
213 size_t block_size;
214 uint16_t link_config;
215 };
216
217 struct dai_ts_cfg {
218 uint32_t walclk_rate; /* Rate in Hz, e.g. 19200000 */
219 int type; /* SSP, DMIC, HDA, etc. */
220 int direction; /* Playback, capture */
221 int index; /* For SSPx to select correct timestamp register */
222 int dma_id; /* DMA instance id */
223 int dma_chan_index; /* Used DMA channel */
224 int dma_chan_count; /* Channels in single DMA */
225 };
226
227 struct dai_ts_data {
228 uint64_t walclk; /* Wall clock */
229 uint64_t sample; /* Sample count */
230 uint32_t walclk_rate; /* Rate in Hz, e.g. 19200000 */
231 };
232
233 /**
234 * @cond INTERNAL_HIDDEN
235 *
236 * For internal use only, skip these in public documentation.
237 */
238 __subsystem struct dai_driver_api {
239 int (*probe)(const struct device *dev);
240 int (*remove)(const struct device *dev);
241 int (*config_set)(const struct device *dev, const struct dai_config *cfg,
242 const void *bespoke_cfg);
243 int (*config_get)(const struct device *dev, struct dai_config *cfg,
244 enum dai_dir dir);
245
246 const struct dai_properties *(*get_properties)(const struct device *dev,
247 enum dai_dir dir,
248 int stream_id);
249
250 int (*trigger)(const struct device *dev, enum dai_dir dir,
251 enum dai_trigger_cmd cmd);
252
253 /* optional methods */
254 int (*ts_config)(const struct device *dev, struct dai_ts_cfg *cfg);
255 int (*ts_start)(const struct device *dev, struct dai_ts_cfg *cfg);
256 int (*ts_stop)(const struct device *dev, struct dai_ts_cfg *cfg);
257 int (*ts_get)(const struct device *dev, struct dai_ts_cfg *cfg,
258 struct dai_ts_data *tsd);
259 };
260
261 /**
262 * @endcond
263 */
264
265 /**
266 * @brief Probe operation of DAI driver.
267 *
268 * The function will be called to power up the device and update for example
269 * possible reference count of the users. It can be used also to initialize
270 * internal variables and memory allocation.
271 *
272 * @param dev Pointer to the device structure for the driver instance.
273 *
274 * @retval 0 If successful.
275 */
dai_probe(const struct device * dev)276 static inline int dai_probe(const struct device *dev)
277 {
278 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
279
280 return api->probe(dev);
281 }
282
283 /**
284 * @brief Remove operation of DAI driver.
285 *
286 * The function will be called to unregister/unbind the device, for example to
287 * power down the device or decrease the usage reference count.
288 *
289 * @param dev Pointer to the device structure for the driver instance.
290 *
291 * @retval 0 If successful.
292 */
dai_remove(const struct device * dev)293 static inline int dai_remove(const struct device *dev)
294 {
295 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
296
297 return api->remove(dev);
298 }
299
300 /**
301 * @brief Configure operation of a DAI driver.
302 *
303 * The dir parameter specifies if Transmit (TX) or Receive (RX) direction
304 * will be configured by data provided via cfg parameter.
305 *
306 * The function can be called in NOT_READY or READY state only. If executed
307 * successfully the function will change the interface state to READY.
308 *
309 * If the function is called with the parameter cfg->frame_clk_freq set to 0
310 * the interface state will be changed to NOT_READY.
311 *
312 * @param dev Pointer to the device structure for the driver instance.
313 * @param cfg Pointer to the structure containing configuration parameters.
314 * @param bespoke_cfg Pointer to the structure containing bespoke config.
315 *
316 * @retval 0 If successful.
317 * @retval -EINVAL Invalid argument.
318 * @retval -ENOSYS DAI_DIR_BOTH value is not supported.
319 */
dai_config_set(const struct device * dev,const struct dai_config * cfg,const void * bespoke_cfg)320 static inline int dai_config_set(const struct device *dev,
321 const struct dai_config *cfg,
322 const void *bespoke_cfg)
323 {
324 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
325
326 return api->config_set(dev, cfg, bespoke_cfg);
327 }
328
329 /**
330 * @brief Fetch configuration information of a DAI driver
331 *
332 * @param dev Pointer to the device structure for the driver instance
333 * @param cfg Pointer to the config structure to be filled by the instance
334 * @param dir Stream direction: RX or TX as defined by DAI_DIR_*
335 * @retval 0 if success, negative if invalid parameters or dai un-configured
336 */
dai_config_get(const struct device * dev,struct dai_config * cfg,enum dai_dir dir)337 static inline int dai_config_get(const struct device *dev,
338 struct dai_config *cfg,
339 enum dai_dir dir)
340 {
341 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
342
343 return api->config_get(dev, cfg, dir);
344 }
345
346 /**
347 * @brief Fetch properties of a DAI driver
348 *
349 * @param dev Pointer to the device structure for the driver instance
350 * @param dir Stream direction: RX or TX as defined by DAI_DIR_*
351 * @param stream_id Stream id: some drivers may have stream specific
352 * properties, this id specifies the stream.
353 * @retval Pointer to the structure containing properties,
354 * or NULL if error or no properties
355 */
dai_get_properties(const struct device * dev,enum dai_dir dir,int stream_id)356 static inline const struct dai_properties *dai_get_properties(const struct device *dev,
357 enum dai_dir dir,
358 int stream_id)
359 {
360 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
361
362 return api->get_properties(dev, dir, stream_id);
363 }
364
365 /**
366 * @brief Send a trigger command.
367 *
368 * @param dev Pointer to the device structure for the driver instance.
369 * @param dir Stream direction: RX, TX, or both, as defined by DAI_DIR_*.
370 * The DAI_DIR_BOTH value may not be supported by some drivers.
371 * For those, triggering need to be done separately for the RX
372 * and TX streams.
373 * @param cmd Trigger command.
374 *
375 * @retval 0 If successful.
376 * @retval -EINVAL Invalid argument.
377 * @retval -EIO The trigger cannot be executed in the current state or a DMA
378 * channel cannot be allocated.
379 * @retval -ENOMEM RX/TX memory block not available.
380 * @retval -ENOSYS DAI_DIR_BOTH value is not supported.
381 */
dai_trigger(const struct device * dev,enum dai_dir dir,enum dai_trigger_cmd cmd)382 static inline int dai_trigger(const struct device *dev,
383 enum dai_dir dir,
384 enum dai_trigger_cmd cmd)
385 {
386 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
387
388 return api->trigger(dev, dir, cmd);
389 }
390
391 /**
392 * Configures timestamping in attached DAI.
393 * @param dev Component device.
394 * @param cfg Timestamp config.
395 *
396 * Optional method.
397 *
398 * @retval 0 If successful.
399 */
dai_ts_config(const struct device * dev,struct dai_ts_cfg * cfg)400 static inline int dai_ts_config(const struct device *dev, struct dai_ts_cfg *cfg)
401 {
402 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
403
404 if (!api->ts_config)
405 return -EINVAL;
406
407 return api->ts_config(dev, cfg);
408 }
409
410 /**
411 * Starts timestamping.
412 * @param dev Component device.
413 * @param cfg Timestamp config.
414 *
415 * Optional method
416 *
417 * @retval 0 If successful.
418 */
dai_ts_start(const struct device * dev,struct dai_ts_cfg * cfg)419 static inline int dai_ts_start(const struct device *dev, struct dai_ts_cfg *cfg)
420 {
421 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
422
423 if (!api->ts_start)
424 return -EINVAL;
425
426 return api->ts_start(dev, cfg);
427 }
428
429 /**
430 * Stops timestamping.
431 * @param dev Component device.
432 * @param cfg Timestamp config.
433 *
434 * Optional method.
435 *
436 * @retval 0 If successful.
437 */
dai_ts_stop(const struct device * dev,struct dai_ts_cfg * cfg)438 static inline int dai_ts_stop(const struct device *dev, struct dai_ts_cfg *cfg)
439 {
440 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
441
442 if (!api->ts_stop)
443 return -EINVAL;
444
445 return api->ts_stop(dev, cfg);
446 }
447
448 /**
449 * Gets timestamp.
450 * @param dev Component device.
451 * @param cfg Timestamp config.
452 * @param tsd Receives timestamp data.
453 *
454 * Optional method.
455 *
456 * @retval 0 If successful.
457 */
dai_ts_get(const struct device * dev,struct dai_ts_cfg * cfg,struct dai_ts_data * tsd)458 static inline int dai_ts_get(const struct device *dev, struct dai_ts_cfg *cfg,
459 struct dai_ts_data *tsd)
460 {
461 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
462
463 if (!api->ts_get)
464 return -EINVAL;
465
466 return api->ts_get(dev, cfg, tsd);
467 }
468
469 /**
470 * @}
471 */
472
473 #ifdef __cplusplus
474 }
475 #endif
476
477 #endif /* ZEPHYR_INCLUDE_DRIVERS_DAI_H_ */
478