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 : * @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 1 : #define DAI_FORMAT_CLOCK_PROVIDER_MASK 0xf000
40 : /** Used to extract the protocol from the format attribute of struct dai_config */
41 1 : #define DAI_FORMAT_PROTOCOL_MASK 0x000f
42 : /** Used to extract the clock inversion from the format attribute of struct dai_config */
43 1 : #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 1 : enum dai_clock_provider {
52 : /** codec BCLK 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 1 : 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 1 : 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 1 : 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 1 : 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 1 : 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 1 : 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 1 : struct dai_properties {
237 : /** Fifo hw address for e.g. when connecting to dma. */
238 1 : uint32_t fifo_address;
239 : /** Fifo depth. */
240 1 : uint32_t fifo_depth;
241 : /** DMA handshake id. */
242 1 : uint32_t dma_hs_id;
243 : /** Delay for initializing registers. */
244 1 : uint32_t reg_init_delay;
245 : /** Stream ID. */
246 1 : int stream_id;
247 : };
248 :
249 : /** @brief Main DAI config structure
250 : *
251 : * Generic DAI interface configuration options.
252 : */
253 1 : struct dai_config {
254 : /** Type of the DAI. */
255 1 : enum dai_type type;
256 : /** Index of the DAI. */
257 1 : uint32_t dai_index;
258 : /** Number of audio channels, words in frame. */
259 1 : uint8_t channels;
260 : /** Frame clock (WS) frequency, sampling rate. */
261 1 : uint32_t rate;
262 : /** DAI specific data stream format. */
263 1 : uint16_t format;
264 : /** DAI specific configuration options. */
265 1 : uint8_t options;
266 : /** Number of bits representing one data word. */
267 1 : uint8_t word_size;
268 : /** Size of one RX/TX memory block (buffer) in bytes. */
269 1 : size_t block_size;
270 : /** DAI specific link configuration. */
271 1 : uint16_t link_config;
272 : /** tdm slot group number*/
273 1 : uint32_t tdm_slot_group;
274 : };
275 :
276 : /**
277 : * @brief DAI timestamp configuration
278 : */
279 1 : struct dai_ts_cfg {
280 : /** Rate in Hz, e.g. 19200000 */
281 1 : uint32_t walclk_rate;
282 : /** Type of the DAI (SSP, DMIC, HDA, etc.). */
283 1 : int type;
284 : /** Direction (playback/capture) */
285 1 : int direction;
286 : /** Index for SSPx to select correct timestamp register */
287 1 : int index;
288 : /** DMA instance id */
289 1 : int dma_id;
290 : /** Used DMA channel index */
291 1 : int dma_chan_index;
292 : /** Number of channels in single DMA */
293 1 : int dma_chan_count;
294 : };
295 :
296 : /**
297 : * @brief DAI timestamp data
298 : */
299 1 : struct dai_ts_data {
300 : /** Wall clock */
301 1 : uint64_t walclk;
302 : /** Sample count */
303 1 : uint64_t sample;
304 : /** Rate in Hz, e.g. 19200000 */
305 1 : 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 : */
353 1 : 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 : */
370 1 : 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 : */
397 1 : 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 : */
414 1 : 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 : */
433 1 : 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 : */
459 1 : 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 : */
477 1 : 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 : */
497 1 : 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 : */
517 1 : 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 : */
538 1 : 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 : */
569 1 : 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_ */
|