| /* |
| * Copyright (c) 2022 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief Public APIs for the DAI (Digital Audio Interface) bus drivers. |
| */ |
| |
| #ifndef ZEPHYR_INCLUDE_DRIVERS_DAI_H_ |
| #define ZEPHYR_INCLUDE_DRIVERS_DAI_H_ |
| |
| /** |
| * @defgroup dai_interface DAI Interface |
| * @since 3.1 |
| * @version 0.1.0 |
| * @ingroup io_interfaces |
| * @brief DAI Interface |
| * |
| * The DAI API provides support for the standard I2S (SSP) and its common variants. |
| * It supports also DMIC, HDA and SDW backends. The API has a config function |
| * with bespoke data argument for device/vendor specific config. There are also |
| * optional timestamping functions to get device specific audio clock time. |
| * @{ |
| */ |
| |
| #include <errno.h> |
| |
| #include <zephyr/types.h> |
| #include <zephyr/device.h> |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| /** Used to extract the clock configuration from the format attribute of struct dai_config */ |
| #define DAI_FORMAT_CLOCK_PROVIDER_MASK 0xf000 |
| /** Used to extract the protocol from the format attribute of struct dai_config */ |
| #define DAI_FORMAT_PROTOCOL_MASK 0x000f |
| /** Used to extract the clock inversion from the format attribute of struct dai_config */ |
| #define DAI_FORMAT_CLOCK_INVERSION_MASK 0x0f00 |
| |
| /** @brief DAI clock configurations |
| * |
| * This is used to describe all of the possible |
| * clock-related configurations w.r.t the DAI |
| * and the codec. |
| */ |
| enum dai_clock_provider { |
| /**< codec BLCK provider, codec FSYNC provider */ |
| DAI_CBP_CFP = (0 << 12), |
| /**< codec BCLK consumer, codec FSYNC provider */ |
| DAI_CBC_CFP = (2 << 12), |
| /**< codec BCLK provider, codec FSYNC consumer */ |
| DAI_CBP_CFC = (3 << 12), |
| /**< codec BCLK consumer, codec FSYNC consumer */ |
| DAI_CBC_CFC = (4 << 12), |
| }; |
| |
| /** @brief DAI protocol |
| * |
| * The communication between the DAI and the CODEC |
| * may use different protocols depending on the scenario. |
| */ |
| enum dai_protocol { |
| DAI_PROTO_I2S = 1, /**< I2S */ |
| DAI_PROTO_RIGHT_J, /**< Right Justified */ |
| DAI_PROTO_LEFT_J, /**< Left Justified */ |
| DAI_PROTO_DSP_A, /**< TDM, FSYNC asserted 1 BCLK early */ |
| DAI_PROTO_DSP_B, /**< TDM, FSYNC asserted at the same time as MSB */ |
| DAI_PROTO_PDM, /**< Pulse Density Modulation */ |
| }; |
| |
| /** @brief DAI clock inversion |
| * |
| * Some applications may require a different |
| * clock polarity (FSYNC/BCLK) compared to |
| * the default one chosen based on the protocol. |
| */ |
| enum dai_clock_inversion { |
| /**< no BCLK inversion, no FSYNC inversion */ |
| DAI_INVERSION_NB_NF = 0, |
| /**< no BCLK inversion, FSYNC inversion */ |
| DAI_INVERSION_NB_IF = (2 << 8), |
| /**< BCLK inversion, no FSYNC inversion */ |
| DAI_INVERSION_IB_NF = (3 << 8), |
| /**< BCLK inversion, FSYNC inversion */ |
| DAI_INVERSION_IB_IF = (4 << 8), |
| }; |
| |
| /** @brief Types of DAI |
| * |
| * The type of the DAI. This ID type is used to configure bespoke DAI HW |
| * settings. |
| * |
| * DAIs have a lot of physical link feature variability and therefore need |
| * different configuration data to cater for different use cases. We |
| * usually need to pass extra bespoke configuration prior to DAI start. |
| */ |
| enum dai_type { |
| DAI_LEGACY_I2S = 0, /**< Legacy I2S compatible with i2s.h */ |
| DAI_INTEL_SSP, /**< Intel SSP */ |
| DAI_INTEL_DMIC, /**< Intel DMIC */ |
| DAI_INTEL_HDA, /**< Intel HD/A */ |
| DAI_INTEL_ALH, /**< Intel ALH */ |
| DAI_IMX_SAI, /**< i.MX SAI */ |
| DAI_IMX_ESAI, /**< i.MX ESAI */ |
| DAI_AMD_BT, /**< Amd BT */ |
| DAI_AMD_SP, /**< Amd SP */ |
| DAI_AMD_DMIC, /**< Amd DMIC */ |
| DAI_MEDIATEK_AFE, /**< Mtk AFE */ |
| DAI_INTEL_SSP_NHLT, /**< nhlt ssp */ |
| DAI_INTEL_DMIC_NHLT, /**< nhlt ssp */ |
| DAI_INTEL_HDA_NHLT, /**< nhlt Intel HD/A */ |
| DAI_INTEL_ALH_NHLT, /**< nhlt Intel ALH */ |
| }; |
| |
| /** |
| * @brief DAI Direction |
| */ |
| enum dai_dir { |
| /** Transmit data */ |
| DAI_DIR_TX = 0, |
| /** Receive data */ |
| DAI_DIR_RX, |
| /** Both receive and transmit data */ |
| DAI_DIR_BOTH, |
| }; |
| |
| /** Interface state */ |
| enum dai_state { |
| /** @brief The interface is not ready. |
| * |
| * The interface was initialized but is not yet ready to receive / |
| * transmit data. Call dai_config_set() to configure interface and change |
| * its state to READY. |
| */ |
| DAI_STATE_NOT_READY = 0, |
| /** The interface is ready to receive / transmit data. */ |
| DAI_STATE_READY, |
| /** The interface is receiving / transmitting data. */ |
| DAI_STATE_RUNNING, |
| /** The interface is clocking but not receiving / transmitting data. */ |
| DAI_STATE_PRE_RUNNING, |
| /** The interface paused */ |
| DAI_STATE_PAUSED, |
| /** The interface is draining its transmit queue. */ |
| DAI_STATE_STOPPING, |
| /** TX buffer underrun or RX buffer overrun has occurred. */ |
| DAI_STATE_ERROR, |
| }; |
| |
| /** Trigger command */ |
| enum dai_trigger_cmd { |
| /** @brief Start the transmission / reception of data. |
| * |
| * If DAI_DIR_TX is set some data has to be queued for transmission by |
| * the dai_write() function. This trigger can be used in READY state |
| * only and changes the interface state to RUNNING. |
| */ |
| DAI_TRIGGER_START = 0, |
| /** @brief Optional - Pre Start the transmission / reception of data. |
| * |
| * Allows the DAI and downstream codecs to prepare for audio Tx/Rx by |
| * starting any required clocks for downstream PLL/FLL locking. |
| */ |
| DAI_TRIGGER_PRE_START, |
| /** @brief Stop the transmission / reception of data. |
| * |
| * Stop the transmission / reception of data at the end of the current |
| * memory block. This trigger can be used in RUNNING state only and at |
| * first changes the interface state to STOPPING. When the current TX / |
| * RX block is transmitted / received the state is changed to READY. |
| * Subsequent START trigger will resume transmission / reception where |
| * it stopped. |
| */ |
| DAI_TRIGGER_STOP, |
| /** @brief Pause the transmission / reception of data. |
| * |
| * Pause the transmission / reception of data at the end of the current |
| * memory block. Behavior is implementation specific but usually this |
| * state doesn't completely stop the clocks or transmission. The DAI could |
| * be transmitting 0's (silence), but it is not consuming data from outside. |
| */ |
| DAI_TRIGGER_PAUSE, |
| /** @brief Optional - Post Stop the transmission / reception of data. |
| * |
| * Allows the DAI and downstream codecs to shutdown cleanly after audio |
| * Tx/Rx by stopping any required clocks for downstream audio completion. |
| */ |
| DAI_TRIGGER_POST_STOP, |
| /** @brief Empty the transmit queue. |
| * |
| * Send all data in the transmit queue and stop the transmission. |
| * If the trigger is applied to the RX queue it has the same effect as |
| * DAI_TRIGGER_STOP. This trigger can be used in RUNNING state only and |
| * at first changes the interface state to STOPPING. When all TX blocks |
| * are transmitted the state is changed to READY. |
| */ |
| DAI_TRIGGER_DRAIN, |
| /** @brief Discard the transmit / receive queue. |
| * |
| * Stop the transmission / reception immediately and discard the |
| * contents of the respective queue. This trigger can be used in any |
| * state other than NOT_READY and changes the interface state to READY. |
| */ |
| DAI_TRIGGER_DROP, |
| /** @brief Prepare the queues after underrun/overrun error has occurred. |
| * |
| * This trigger can be used in ERROR state only and changes the |
| * interface state to READY. |
| */ |
| DAI_TRIGGER_PREPARE, |
| /** @brief Reset |
| * |
| * This trigger frees resources and moves the driver back to initial |
| * state. |
| */ |
| DAI_TRIGGER_RESET, |
| /** @brief Copy |
| * |
| * This trigger prepares for data copying. |
| */ |
| DAI_TRIGGER_COPY, |
| }; |
| |
| /** @brief DAI properties |
| * |
| * This struct is used with APIs get_properties function to query DAI |
| * properties like fifo address and dma handshake. These are needed |
| * for example to setup dma outside the driver code. |
| */ |
| struct dai_properties { |
| /** Fifo hw address for e.g. when connecting to dma. */ |
| uint32_t fifo_address; |
| /** Fifo depth. */ |
| uint32_t fifo_depth; |
| /** DMA handshake id. */ |
| uint32_t dma_hs_id; |
| /** Delay for initializing registers. */ |
| uint32_t reg_init_delay; |
| /** Stream ID. */ |
| int stream_id; |
| }; |
| |
| /** @brief Main DAI config structure |
| * |
| * Generic DAI interface configuration options. |
| */ |
| struct dai_config { |
| /** Type of the DAI. */ |
| enum dai_type type; |
| /** Index of the DAI. */ |
| uint32_t dai_index; |
| /** Number of audio channels, words in frame. */ |
| uint8_t channels; |
| /** Frame clock (WS) frequency, sampling rate. */ |
| uint32_t rate; |
| /** DAI specific data stream format. */ |
| uint16_t format; |
| /** DAI specific configuration options. */ |
| uint8_t options; |
| /** Number of bits representing one data word. */ |
| uint8_t word_size; |
| /** Size of one RX/TX memory block (buffer) in bytes. */ |
| size_t block_size; |
| /** DAI specific link configuration. */ |
| uint16_t link_config; |
| /**< tdm slot goup number*/ |
| uint32_t tdm_slot_group; |
| }; |
| |
| /** |
| * @brief DAI timestamp configuration |
| */ |
| struct dai_ts_cfg { |
| /** Rate in Hz, e.g. 19200000 */ |
| uint32_t walclk_rate; |
| /** Type of the DAI (SSP, DMIC, HDA, etc.). */ |
| int type; |
| /** Direction (playback/capture) */ |
| int direction; |
| /** Index for SSPx to select correct timestamp register */ |
| int index; |
| /** DMA instance id */ |
| int dma_id; |
| /** Used DMA channel index */ |
| int dma_chan_index; |
| /** Number of channels in single DMA */ |
| int dma_chan_count; |
| }; |
| |
| /** |
| * @brief DAI timestamp data |
| */ |
| struct dai_ts_data { |
| /** Wall clock */ |
| uint64_t walclk; |
| /** Sample count */ |
| uint64_t sample; |
| /** Rate in Hz, e.g. 19200000 */ |
| uint32_t walclk_rate; |
| }; |
| |
| /** |
| * @cond INTERNAL_HIDDEN |
| * |
| * For internal use only, skip these in public documentation. |
| */ |
| __subsystem struct dai_driver_api { |
| int (*probe)(const struct device *dev); |
| int (*remove)(const struct device *dev); |
| int (*config_set)(const struct device *dev, const struct dai_config *cfg, |
| const void *bespoke_cfg); |
| int (*config_get)(const struct device *dev, struct dai_config *cfg, |
| enum dai_dir dir); |
| |
| const struct dai_properties *(*get_properties)(const struct device *dev, |
| enum dai_dir dir, |
| int stream_id); |
| |
| int (*trigger)(const struct device *dev, enum dai_dir dir, |
| enum dai_trigger_cmd cmd); |
| |
| /* optional methods */ |
| int (*ts_config)(const struct device *dev, struct dai_ts_cfg *cfg); |
| int (*ts_start)(const struct device *dev, struct dai_ts_cfg *cfg); |
| int (*ts_stop)(const struct device *dev, struct dai_ts_cfg *cfg); |
| int (*ts_get)(const struct device *dev, struct dai_ts_cfg *cfg, |
| struct dai_ts_data *tsd); |
| }; |
| |
| /** |
| * @endcond |
| */ |
| |
| /** |
| * @brief Probe operation of DAI driver. |
| * |
| * The function will be called to power up the device and update for example |
| * possible reference count of the users. It can be used also to initialize |
| * internal variables and memory allocation. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * |
| * @retval 0 If successful. |
| */ |
| static inline int dai_probe(const struct device *dev) |
| { |
| const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api; |
| |
| return api->probe(dev); |
| } |
| |
| /** |
| * @brief Remove operation of DAI driver. |
| * |
| * The function will be called to unregister/unbind the device, for example to |
| * power down the device or decrease the usage reference count. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * |
| * @retval 0 If successful. |
| */ |
| static inline int dai_remove(const struct device *dev) |
| { |
| const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api; |
| |
| return api->remove(dev); |
| } |
| |
| /** |
| * @brief Configure operation of a DAI driver. |
| * |
| * The dir parameter specifies if Transmit (TX) or Receive (RX) direction |
| * will be configured by data provided via cfg parameter. |
| * |
| * The function can be called in NOT_READY or READY state only. If executed |
| * successfully the function will change the interface state to READY. |
| * |
| * If the function is called with the parameter cfg->frame_clk_freq set to 0 |
| * the interface state will be changed to NOT_READY. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param cfg Pointer to the structure containing configuration parameters. |
| * @param bespoke_cfg Pointer to the structure containing bespoke config. |
| * |
| * @retval 0 If successful. |
| * @retval -EINVAL Invalid argument. |
| * @retval -ENOSYS DAI_DIR_BOTH value is not supported. |
| */ |
| static inline int dai_config_set(const struct device *dev, |
| const struct dai_config *cfg, |
| const void *bespoke_cfg) |
| { |
| const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api; |
| |
| return api->config_set(dev, cfg, bespoke_cfg); |
| } |
| |
| /** |
| * @brief Fetch configuration information of a DAI driver |
| * |
| * @param dev Pointer to the device structure for the driver instance |
| * @param cfg Pointer to the config structure to be filled by the instance |
| * @param dir Stream direction: RX or TX as defined by DAI_DIR_* |
| * @retval 0 if success, negative if invalid parameters or DAI un-configured |
| */ |
| static inline int dai_config_get(const struct device *dev, |
| struct dai_config *cfg, |
| enum dai_dir dir) |
| { |
| const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api; |
| |
| return api->config_get(dev, cfg, dir); |
| } |
| |
| /** |
| * @brief Fetch properties of a DAI driver |
| * |
| * @param dev Pointer to the device structure for the driver instance |
| * @param dir Stream direction: RX or TX as defined by DAI_DIR_* |
| * @param stream_id Stream id: some drivers may have stream specific |
| * properties, this id specifies the stream. |
| * @retval Pointer to the structure containing properties, |
| * or NULL if error or no properties |
| */ |
| static inline const struct dai_properties *dai_get_properties(const struct device *dev, |
| enum dai_dir dir, |
| int stream_id) |
| { |
| const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api; |
| |
| return api->get_properties(dev, dir, stream_id); |
| } |
| |
| /** |
| * @brief Send a trigger command. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param dir Stream direction: RX, TX, or both, as defined by DAI_DIR_*. |
| * The DAI_DIR_BOTH value may not be supported by some drivers. |
| * For those, triggering need to be done separately for the RX |
| * and TX streams. |
| * @param cmd Trigger command. |
| * |
| * @retval 0 If successful. |
| * @retval -EINVAL Invalid argument. |
| * @retval -EIO The trigger cannot be executed in the current state or a DMA |
| * channel cannot be allocated. |
| * @retval -ENOMEM RX/TX memory block not available. |
| * @retval -ENOSYS DAI_DIR_BOTH value is not supported. |
| */ |
| static inline int dai_trigger(const struct device *dev, |
| enum dai_dir dir, |
| enum dai_trigger_cmd cmd) |
| { |
| const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api; |
| |
| return api->trigger(dev, dir, cmd); |
| } |
| |
| /** |
| * Configures timestamping in attached DAI. |
| * @param dev Component device. |
| * @param cfg Timestamp config. |
| * |
| * Optional method. |
| * |
| * @retval 0 If successful. |
| */ |
| static inline int dai_ts_config(const struct device *dev, struct dai_ts_cfg *cfg) |
| { |
| const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api; |
| |
| if (!api->ts_config) |
| return -EINVAL; |
| |
| return api->ts_config(dev, cfg); |
| } |
| |
| /** |
| * Starts timestamping. |
| * @param dev Component device. |
| * @param cfg Timestamp config. |
| * |
| * Optional method |
| * |
| * @retval 0 If successful. |
| */ |
| static inline int dai_ts_start(const struct device *dev, struct dai_ts_cfg *cfg) |
| { |
| const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api; |
| |
| if (!api->ts_start) |
| return -EINVAL; |
| |
| return api->ts_start(dev, cfg); |
| } |
| |
| /** |
| * Stops timestamping. |
| * @param dev Component device. |
| * @param cfg Timestamp config. |
| * |
| * Optional method. |
| * |
| * @retval 0 If successful. |
| */ |
| static inline int dai_ts_stop(const struct device *dev, struct dai_ts_cfg *cfg) |
| { |
| const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api; |
| |
| if (!api->ts_stop) |
| return -EINVAL; |
| |
| return api->ts_stop(dev, cfg); |
| } |
| |
| /** |
| * Gets timestamp. |
| * @param dev Component device. |
| * @param cfg Timestamp config. |
| * @param tsd Receives timestamp data. |
| * |
| * Optional method. |
| * |
| * @retval 0 If successful. |
| */ |
| static inline int dai_ts_get(const struct device *dev, struct dai_ts_cfg *cfg, |
| struct dai_ts_data *tsd) |
| { |
| const struct dai_driver_api *api = (const struct dai_driver_api *)dev->api; |
| |
| if (!api->ts_get) |
| return -EINVAL; |
| |
| return api->ts_get(dev, cfg, tsd); |
| } |
| |
| /** |
| * @} |
| */ |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif /* ZEPHYR_INCLUDE_DRIVERS_DAI_H_ */ |