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 };
119
120 /**
121 * @brief DAI Direction
122 */
123 enum dai_dir {
124 /** Transmit data */
125 DAI_DIR_TX = 0,
126 /** Receive data */
127 DAI_DIR_RX,
128 /** Both receive and transmit data */
129 DAI_DIR_BOTH,
130 };
131
132 /** Interface state */
133 enum dai_state {
134 /** @brief The interface is not ready.
135 *
136 * The interface was initialized but is not yet ready to receive /
137 * transmit data. Call dai_config_set() to configure interface and change
138 * its state to READY.
139 */
140 DAI_STATE_NOT_READY = 0,
141 /** The interface is ready to receive / transmit data. */
142 DAI_STATE_READY,
143 /** The interface is receiving / transmitting data. */
144 DAI_STATE_RUNNING,
145 /** The interface is clocking but not receiving / transmitting data. */
146 DAI_STATE_PRE_RUNNING,
147 /** The interface paused */
148 DAI_STATE_PAUSED,
149 /** The interface is draining its transmit queue. */
150 DAI_STATE_STOPPING,
151 /** TX buffer underrun or RX buffer overrun has occurred. */
152 DAI_STATE_ERROR,
153 };
154
155 /** Trigger command */
156 enum dai_trigger_cmd {
157 /** @brief Start the transmission / reception of data.
158 *
159 * If DAI_DIR_TX is set some data has to be queued for transmission by
160 * the dai_write() function. This trigger can be used in READY state
161 * only and changes the interface state to RUNNING.
162 */
163 DAI_TRIGGER_START = 0,
164 /** @brief Optional - Pre Start the transmission / reception of data.
165 *
166 * Allows the DAI and downstream codecs to prepare for audio Tx/Rx by
167 * starting any required clocks for downstream PLL/FLL locking.
168 */
169 DAI_TRIGGER_PRE_START,
170 /** @brief Stop the transmission / reception of data.
171 *
172 * Stop the transmission / reception of data at the end of the current
173 * memory block. This trigger can be used in RUNNING state only and at
174 * first changes the interface state to STOPPING. When the current TX /
175 * RX block is transmitted / received the state is changed to READY.
176 * Subsequent START trigger will resume transmission / reception where
177 * it stopped.
178 */
179 DAI_TRIGGER_STOP,
180 /** @brief Pause the transmission / reception of data.
181 *
182 * Pause the transmission / reception of data at the end of the current
183 * memory block. Behavior is implementation specific but usually this
184 * state doesn't completely stop the clocks or transmission. The DAI could
185 * be transmitting 0's (silence), but it is not consuming data from outside.
186 */
187 DAI_TRIGGER_PAUSE,
188 /** @brief Optional - Post Stop the transmission / reception of data.
189 *
190 * Allows the DAI and downstream codecs to shutdown cleanly after audio
191 * Tx/Rx by stopping any required clocks for downstream audio completion.
192 */
193 DAI_TRIGGER_POST_STOP,
194 /** @brief Empty the transmit queue.
195 *
196 * Send all data in the transmit queue and stop the transmission.
197 * If the trigger is applied to the RX queue it has the same effect as
198 * DAI_TRIGGER_STOP. This trigger can be used in RUNNING state only and
199 * at first changes the interface state to STOPPING. When all TX blocks
200 * are transmitted the state is changed to READY.
201 */
202 DAI_TRIGGER_DRAIN,
203 /** @brief Discard the transmit / receive queue.
204 *
205 * Stop the transmission / reception immediately and discard the
206 * contents of the respective queue. This trigger can be used in any
207 * state other than NOT_READY and changes the interface state to READY.
208 */
209 DAI_TRIGGER_DROP,
210 /** @brief Prepare the queues after underrun/overrun error has occurred.
211 *
212 * This trigger can be used in ERROR state only and changes the
213 * interface state to READY.
214 */
215 DAI_TRIGGER_PREPARE,
216 /** @brief Reset
217 *
218 * This trigger frees resources and moves the driver back to initial
219 * state.
220 */
221 DAI_TRIGGER_RESET,
222 /** @brief Copy
223 *
224 * This trigger prepares for data copying.
225 */
226 DAI_TRIGGER_COPY,
227 };
228
229 /** @brief DAI properties
230 *
231 * This struct is used with APIs get_properties function to query DAI
232 * properties like fifo address and dma handshake. These are needed
233 * for example to setup dma outside the driver code.
234 */
235 struct dai_properties {
236 /** Fifo hw address for e.g. when connecting to dma. */
237 uint32_t fifo_address;
238 /** Fifo depth. */
239 uint32_t fifo_depth;
240 /** DMA handshake id. */
241 uint32_t dma_hs_id;
242 /** Delay for initializing registers. */
243 uint32_t reg_init_delay;
244 /** Stream ID. */
245 int stream_id;
246 };
247
248 /** @brief Main DAI config structure
249 *
250 * Generic DAI interface configuration options.
251 */
252 struct dai_config {
253 /** Type of the DAI. */
254 enum dai_type type;
255 /** Index of the DAI. */
256 uint32_t dai_index;
257 /** Number of audio channels, words in frame. */
258 uint8_t channels;
259 /** Frame clock (WS) frequency, sampling rate. */
260 uint32_t rate;
261 /** DAI specific data stream format. */
262 uint16_t format;
263 /** DAI specific configuration options. */
264 uint8_t options;
265 /** Number of bits representing one data word. */
266 uint8_t word_size;
267 /** Size of one RX/TX memory block (buffer) in bytes. */
268 size_t block_size;
269 /** DAI specific link configuration. */
270 uint16_t link_config;
271 /**< tdm slot group number*/
272 uint32_t tdm_slot_group;
273 };
274
275 /**
276 * @brief DAI timestamp configuration
277 */
278 struct dai_ts_cfg {
279 /** Rate in Hz, e.g. 19200000 */
280 uint32_t walclk_rate;
281 /** Type of the DAI (SSP, DMIC, HDA, etc.). */
282 int type;
283 /** Direction (playback/capture) */
284 int direction;
285 /** Index for SSPx to select correct timestamp register */
286 int index;
287 /** DMA instance id */
288 int dma_id;
289 /** Used DMA channel index */
290 int dma_chan_index;
291 /** Number of channels in single DMA */
292 int dma_chan_count;
293 };
294
295 /**
296 * @brief DAI timestamp data
297 */
298 struct dai_ts_data {
299 /** Wall clock */
300 uint64_t walclk;
301 /** Sample count */
302 uint64_t sample;
303 /** Rate in Hz, e.g. 19200000 */
304 uint32_t walclk_rate;
305 };
306
307 /**
308 * @cond INTERNAL_HIDDEN
309 *
310 * For internal use only, skip these in public documentation.
311 */
312 __subsystem struct dai_driver_api {
313 int (*probe)(const struct device *dev);
314 int (*remove)(const struct device *dev);
315 int (*config_set)(const struct device *dev, const struct dai_config *cfg,
316 const void *bespoke_cfg);
317 int (*config_get)(const struct device *dev, struct dai_config *cfg,
318 enum dai_dir dir);
319
320 const struct dai_properties *(*get_properties)(const struct device *dev,
321 enum dai_dir dir,
322 int stream_id);
323
324 int (*trigger)(const struct device *dev, enum dai_dir dir,
325 enum dai_trigger_cmd cmd);
326
327 /* optional methods */
328 int (*ts_config)(const struct device *dev, struct dai_ts_cfg *cfg);
329 int (*ts_start)(const struct device *dev, struct dai_ts_cfg *cfg);
330 int (*ts_stop)(const struct device *dev, struct dai_ts_cfg *cfg);
331 int (*ts_get)(const struct device *dev, struct dai_ts_cfg *cfg,
332 struct dai_ts_data *tsd);
333 int (*config_update)(const struct device *dev, const void *bespoke_cfg,
334 size_t size);
335 };
336
337 /**
338 * @endcond
339 */
340
341 /**
342 * @brief Probe operation of DAI driver.
343 *
344 * The function will be called to power up the device and update for example
345 * possible reference count of the users. It can be used also to initialize
346 * internal variables and memory allocation.
347 *
348 * @param dev Pointer to the device structure for the driver instance.
349 *
350 * @retval 0 If successful.
351 */
dai_probe(const struct device * dev)352 static inline int dai_probe(const struct device *dev)
353 {
354 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
355
356 return api->probe(dev);
357 }
358
359 /**
360 * @brief Remove operation of DAI driver.
361 *
362 * The function will be called to unregister/unbind the device, for example to
363 * power down the device or decrease the usage reference count.
364 *
365 * @param dev Pointer to the device structure for the driver instance.
366 *
367 * @retval 0 If successful.
368 */
dai_remove(const struct device * dev)369 static inline int dai_remove(const struct device *dev)
370 {
371 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
372
373 return api->remove(dev);
374 }
375
376 /**
377 * @brief Configure operation of a DAI driver.
378 *
379 * The dir parameter specifies if Transmit (TX) or Receive (RX) direction
380 * will be configured by data provided via cfg parameter.
381 *
382 * The function can be called in NOT_READY or READY state only. If executed
383 * successfully the function will change the interface state to READY.
384 *
385 * If the function is called with the parameter cfg->frame_clk_freq set to 0
386 * the interface state will be changed to NOT_READY.
387 *
388 * @param dev Pointer to the device structure for the driver instance.
389 * @param cfg Pointer to the structure containing configuration parameters.
390 * @param bespoke_cfg Pointer to the structure containing bespoke config.
391 *
392 * @retval 0 If successful.
393 * @retval -EINVAL Invalid argument.
394 * @retval -ENOSYS DAI_DIR_BOTH value is not supported.
395 */
dai_config_set(const struct device * dev,const struct dai_config * cfg,const void * bespoke_cfg)396 static inline int dai_config_set(const struct device *dev,
397 const struct dai_config *cfg,
398 const void *bespoke_cfg)
399 {
400 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
401
402 return api->config_set(dev, cfg, bespoke_cfg);
403 }
404
405 /**
406 * @brief Fetch configuration information of a DAI driver
407 *
408 * @param dev Pointer to the device structure for the driver instance
409 * @param cfg Pointer to the config structure to be filled by the instance
410 * @param dir Stream direction: RX or TX as defined by DAI_DIR_*
411 * @retval 0 if success, negative if invalid parameters or DAI un-configured
412 */
dai_config_get(const struct device * dev,struct dai_config * cfg,enum dai_dir dir)413 static inline int dai_config_get(const struct device *dev,
414 struct dai_config *cfg,
415 enum dai_dir dir)
416 {
417 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
418
419 return api->config_get(dev, cfg, dir);
420 }
421
422 /**
423 * @brief Fetch properties of a DAI driver
424 *
425 * @param dev Pointer to the device structure for the driver instance
426 * @param dir Stream direction: RX or TX as defined by DAI_DIR_*
427 * @param stream_id Stream id: some drivers may have stream specific
428 * properties, this id specifies the stream.
429 * @retval Pointer to the structure containing properties,
430 * or NULL if error or no properties
431 */
dai_get_properties(const struct device * dev,enum dai_dir dir,int stream_id)432 static inline const struct dai_properties *dai_get_properties(const struct device *dev,
433 enum dai_dir dir,
434 int stream_id)
435 {
436 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
437
438 return api->get_properties(dev, dir, stream_id);
439 }
440
441 /**
442 * @brief Send a trigger command.
443 *
444 * @param dev Pointer to the device structure for the driver instance.
445 * @param dir Stream direction: RX, TX, or both, as defined by DAI_DIR_*.
446 * The DAI_DIR_BOTH value may not be supported by some drivers.
447 * For those, triggering need to be done separately for the RX
448 * and TX streams.
449 * @param cmd Trigger command.
450 *
451 * @retval 0 If successful.
452 * @retval -EINVAL Invalid argument.
453 * @retval -EIO The trigger cannot be executed in the current state or a DMA
454 * channel cannot be allocated.
455 * @retval -ENOMEM RX/TX memory block not available.
456 * @retval -ENOSYS DAI_DIR_BOTH value is not supported.
457 */
dai_trigger(const struct device * dev,enum dai_dir dir,enum dai_trigger_cmd cmd)458 static inline int dai_trigger(const struct device *dev,
459 enum dai_dir dir,
460 enum dai_trigger_cmd cmd)
461 {
462 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
463
464 return api->trigger(dev, dir, cmd);
465 }
466
467 /**
468 * Configures timestamping in attached DAI.
469 * @param dev Component device.
470 * @param cfg Timestamp config.
471 *
472 * Optional method.
473 *
474 * @retval 0 If successful.
475 */
dai_ts_config(const struct device * dev,struct dai_ts_cfg * cfg)476 static inline int dai_ts_config(const struct device *dev, struct dai_ts_cfg *cfg)
477 {
478 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
479
480 if (!api->ts_config)
481 return -EINVAL;
482
483 return api->ts_config(dev, cfg);
484 }
485
486 /**
487 * Starts timestamping.
488 * @param dev Component device.
489 * @param cfg Timestamp config.
490 *
491 * Optional method
492 *
493 * @retval 0 If successful.
494 */
dai_ts_start(const struct device * dev,struct dai_ts_cfg * cfg)495 static inline int dai_ts_start(const struct device *dev, struct dai_ts_cfg *cfg)
496 {
497 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
498
499 if (!api->ts_start)
500 return -EINVAL;
501
502 return api->ts_start(dev, cfg);
503 }
504
505 /**
506 * Stops timestamping.
507 * @param dev Component device.
508 * @param cfg Timestamp config.
509 *
510 * Optional method.
511 *
512 * @retval 0 If successful.
513 */
dai_ts_stop(const struct device * dev,struct dai_ts_cfg * cfg)514 static inline int dai_ts_stop(const struct device *dev, struct dai_ts_cfg *cfg)
515 {
516 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
517
518 if (!api->ts_stop)
519 return -EINVAL;
520
521 return api->ts_stop(dev, cfg);
522 }
523
524 /**
525 * Gets timestamp.
526 * @param dev Component device.
527 * @param cfg Timestamp config.
528 * @param tsd Receives timestamp data.
529 *
530 * Optional method.
531 *
532 * @retval 0 If successful.
533 */
dai_ts_get(const struct device * dev,struct dai_ts_cfg * cfg,struct dai_ts_data * tsd)534 static inline int dai_ts_get(const struct device *dev, struct dai_ts_cfg *cfg,
535 struct dai_ts_data *tsd)
536 {
537 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
538
539 if (!api->ts_get)
540 return -EINVAL;
541
542 return api->ts_get(dev, cfg, tsd);
543 }
544
545 /**
546 * @brief Update DAI configuration at runtime.
547 *
548 * This function updates the configuration of a DAI interface at runtime.
549 * It allows setting bespoke configuration parameters that are specific to
550 * the DAI implementation, enabling updates outside of the regular flow with
551 * the full configuration blob. The details of the bespoke configuration are
552 * specific to each DAI implementation. This function should only be called
553 * when the DAI is in the READY state, ensuring that the configuration updates
554 * are applied before data transmission or reception begins.
555 *
556 * @param dev Pointer to the device structure for the driver instance.
557 * @param bespoke_cfg Pointer to the buffer containing bespoke configuration parameters.
558 * @param size Size of the bespoke_cfg buffer in bytes.
559 *
560 * @retval 0 If successful.
561 * @retval -ENOSYS If the configuration update operation is not implemented.
562 * @retval Negative errno code if failure.
563 */
dai_config_update(const struct device * dev,const void * bespoke_cfg,size_t size)564 static inline int dai_config_update(const struct device *dev,
565 const void *bespoke_cfg,
566 size_t size)
567 {
568 const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
569
570 if (!api->config_update) {
571 return -ENOSYS;
572 }
573
574 return api->config_update(dev, bespoke_cfg, size);
575 }
576
577 /**
578 * @}
579 */
580
581 #ifdef __cplusplus
582 }
583 #endif
584
585 #endif /* ZEPHYR_INCLUDE_DRIVERS_DAI_H_ */
586