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