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 0 : 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 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 0 : 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 : };
119 :
120 : /**
121 : * @brief DAI Direction
122 : */
123 1 : 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 1 : 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 1 : 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 1 : struct dai_properties {
236 : /** Fifo hw address for e.g. when connecting to dma. */
237 1 : uint32_t fifo_address;
238 : /** Fifo depth. */
239 1 : uint32_t fifo_depth;
240 : /** DMA handshake id. */
241 1 : uint32_t dma_hs_id;
242 : /** Delay for initializing registers. */
243 1 : uint32_t reg_init_delay;
244 : /** Stream ID. */
245 1 : int stream_id;
246 : };
247 :
248 : /** @brief Main DAI config structure
249 : *
250 : * Generic DAI interface configuration options.
251 : */
252 1 : struct dai_config {
253 : /** Type of the DAI. */
254 1 : enum dai_type type;
255 : /** Index of the DAI. */
256 1 : uint32_t dai_index;
257 : /** Number of audio channels, words in frame. */
258 1 : uint8_t channels;
259 : /** Frame clock (WS) frequency, sampling rate. */
260 1 : uint32_t rate;
261 : /** DAI specific data stream format. */
262 1 : uint16_t format;
263 : /** DAI specific configuration options. */
264 1 : uint8_t options;
265 : /** Number of bits representing one data word. */
266 1 : uint8_t word_size;
267 : /** Size of one RX/TX memory block (buffer) in bytes. */
268 1 : size_t block_size;
269 : /** DAI specific link configuration. */
270 1 : uint16_t link_config;
271 : /**< tdm slot group number*/
272 0 : uint32_t tdm_slot_group;
273 : };
274 :
275 : /**
276 : * @brief DAI timestamp configuration
277 : */
278 1 : struct dai_ts_cfg {
279 : /** Rate in Hz, e.g. 19200000 */
280 1 : uint32_t walclk_rate;
281 : /** Type of the DAI (SSP, DMIC, HDA, etc.). */
282 1 : int type;
283 : /** Direction (playback/capture) */
284 1 : int direction;
285 : /** Index for SSPx to select correct timestamp register */
286 1 : int index;
287 : /** DMA instance id */
288 1 : int dma_id;
289 : /** Used DMA channel index */
290 1 : int dma_chan_index;
291 : /** Number of channels in single DMA */
292 1 : int dma_chan_count;
293 : };
294 :
295 : /**
296 : * @brief DAI timestamp data
297 : */
298 1 : struct dai_ts_data {
299 : /** Wall clock */
300 1 : uint64_t walclk;
301 : /** Sample count */
302 1 : uint64_t sample;
303 : /** Rate in Hz, e.g. 19200000 */
304 1 : 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 : */
352 1 : 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 : */
369 1 : 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 : */
396 1 : 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 : */
413 1 : 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 : */
432 1 : 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 : */
458 1 : 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 : */
476 1 : 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 :
484 : return api->ts_config(dev, cfg);
485 : }
486 :
487 : /**
488 : * Starts timestamping.
489 : * @param dev Component device.
490 : * @param cfg Timestamp config.
491 : *
492 : * Optional method
493 : *
494 : * @retval 0 If successful.
495 : */
496 1 : static inline int dai_ts_start(const struct device *dev, struct dai_ts_cfg *cfg)
497 : {
498 : const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
499 :
500 : if (!api->ts_start) {
501 : return -EINVAL;
502 : }
503 :
504 : return api->ts_start(dev, cfg);
505 : }
506 :
507 : /**
508 : * Stops timestamping.
509 : * @param dev Component device.
510 : * @param cfg Timestamp config.
511 : *
512 : * Optional method.
513 : *
514 : * @retval 0 If successful.
515 : */
516 1 : static inline int dai_ts_stop(const struct device *dev, struct dai_ts_cfg *cfg)
517 : {
518 : const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
519 :
520 : if (!api->ts_stop) {
521 : return -EINVAL;
522 : }
523 :
524 : return api->ts_stop(dev, cfg);
525 : }
526 :
527 : /**
528 : * Gets timestamp.
529 : * @param dev Component device.
530 : * @param cfg Timestamp config.
531 : * @param tsd Receives timestamp data.
532 : *
533 : * Optional method.
534 : *
535 : * @retval 0 If successful.
536 : */
537 1 : static inline int dai_ts_get(const struct device *dev, struct dai_ts_cfg *cfg,
538 : struct dai_ts_data *tsd)
539 : {
540 : const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
541 :
542 : if (!api->ts_get) {
543 : return -EINVAL;
544 : }
545 :
546 : return api->ts_get(dev, cfg, tsd);
547 : }
548 :
549 : /**
550 : * @brief Update DAI configuration at runtime.
551 : *
552 : * This function updates the configuration of a DAI interface at runtime.
553 : * It allows setting bespoke configuration parameters that are specific to
554 : * the DAI implementation, enabling updates outside of the regular flow with
555 : * the full configuration blob. The details of the bespoke configuration are
556 : * specific to each DAI implementation. This function should only be called
557 : * when the DAI is in the READY state, ensuring that the configuration updates
558 : * are applied before data transmission or reception begins.
559 : *
560 : * @param dev Pointer to the device structure for the driver instance.
561 : * @param bespoke_cfg Pointer to the buffer containing bespoke configuration parameters.
562 : * @param size Size of the bespoke_cfg buffer in bytes.
563 : *
564 : * @retval 0 If successful.
565 : * @retval -ENOSYS If the configuration update operation is not implemented.
566 : * @retval Negative errno code if failure.
567 : */
568 1 : static inline int dai_config_update(const struct device *dev,
569 : const void *bespoke_cfg,
570 : size_t size)
571 : {
572 : const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api;
573 :
574 : if (!api->config_update) {
575 : return -ENOSYS;
576 : }
577 :
578 : return api->config_update(dev, bespoke_cfg, size);
579 : }
580 :
581 : /**
582 : * @}
583 : */
584 :
585 : #ifdef __cplusplus
586 : }
587 : #endif
588 :
589 : #endif /* ZEPHYR_INCLUDE_DRIVERS_DAI_H_ */
|