This commit refactors the SSP driver to support the Intel ACE30 PTL platform. The changes include: - Adding new structures ssp_rx_dir and ssp_tx_dir to hold the TDM slot configuration for RX and TX directions - Adjusting the dai_ssp_set_config_blob functions to work with the new TDM slot configuration. Signed-off-by: Damian Nikodem <damian.nikodem@intel.com>
552 lines
16 KiB
C
552 lines
16 KiB
C
/*
|
|
* 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_ */
|