| /* Copyright 2023 NXP |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #ifndef ZEPHYR_DRIVERS_DAI_NXP_SAI_H_ |
| #define ZEPHYR_DRIVERS_DAI_NXP_SAI_H_ |
| |
| #include <zephyr/drivers/clock_control.h> |
| #include <zephyr/drivers/pinctrl.h> |
| #include <zephyr/logging/log.h> |
| #include <fsl_sai.h> |
| |
| LOG_MODULE_REGISTER(nxp_dai_sai); |
| |
| #ifdef CONFIG_SAI_HAS_MCLK_CONFIG_OPTION |
| #define SAI_MCLK_MCR_MSEL_SHIFT 24 |
| #define SAI_MCLK_MCR_MSEL_MASK GENMASK(24, 25) |
| #endif /* CONFIG_SAI_HAS_MCLK_CONFIG_OPTION */ |
| /* workaround the fact that device_map() doesn't exist for SoCs with no MMU */ |
| #ifndef DEVICE_MMIO_IS_IN_RAM |
| #define device_map(virt, phys, size, flags) *(virt) = (phys) |
| #endif /* DEVICE_MMIO_IS_IN_RAM */ |
| |
| /* used to convert an uint to I2S_Type * */ |
| #define UINT_TO_I2S(x) ((I2S_Type *)(uintptr_t)(x)) |
| |
| /* macros used for parsing DTS data */ |
| |
| /* used instead of IDENTITY because LISTIFY expects the used macro function |
| * to also take a variable number of arguments. |
| */ |
| #define IDENTITY_VARGS(V, ...) IDENTITY(V) |
| |
| /* used to generate the list of clock indexes */ |
| #define _SAI_CLOCK_INDEX_ARRAY(inst)\ |
| LISTIFY(DT_INST_PROP_LEN_OR(inst, clocks, 0), IDENTITY_VARGS, (,)) |
| |
| /* used to retrieve a clock's ID using its index generated via _SAI_CLOCK_INDEX_ARRAY */ |
| #define _SAI_GET_CLOCK_ID(clock_idx, inst)\ |
| DT_INST_PHA_BY_IDX_OR(inst, clocks, clock_idx, name, 0x0) |
| |
| /* used to retrieve a clock's name using its index generated via _SAI_CLOCK_INDEX_ARRAY */ |
| #define _SAI_GET_CLOCK_NAME(clock_idx, inst)\ |
| DT_INST_PROP_BY_IDX(inst, clock_names, clock_idx) |
| |
| /* used to convert the clocks property into an array of clock IDs */ |
| #define _SAI_CLOCK_ID_ARRAY(inst)\ |
| FOR_EACH_FIXED_ARG(_SAI_GET_CLOCK_ID, (,), inst, _SAI_CLOCK_INDEX_ARRAY(inst)) |
| |
| /* used to convert the clock-names property into an array of clock names */ |
| #define _SAI_CLOCK_NAME_ARRAY(inst)\ |
| FOR_EACH_FIXED_ARG(_SAI_GET_CLOCK_NAME, (,), inst, _SAI_CLOCK_INDEX_ARRAY(inst)) |
| |
| /* used to convert a clocks property into an array of clock IDs. If the property |
| * is not specified then this macro will return {}. |
| */ |
| #define _SAI_GET_CLOCK_ARRAY(inst)\ |
| COND_CODE_1(DT_NODE_HAS_PROP(DT_INST(inst, nxp_dai_sai), clocks),\ |
| ({ _SAI_CLOCK_ID_ARRAY(inst) }),\ |
| ({ })) |
| |
| /* used to retrieve a const struct device *dev pointing to the clock controller. |
| * It is assumed that all SAI clocks come from a single clock provider. |
| * This macro returns a NULL if the clocks property doesn't exist. |
| */ |
| #define _SAI_GET_CLOCK_CONTROLLER(inst)\ |
| COND_CODE_1(DT_NODE_HAS_PROP(DT_INST(inst, nxp_dai_sai), clocks),\ |
| (DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(inst))),\ |
| (NULL)) |
| |
| /* used to convert a clock-names property into an array of clock names. If the |
| * property is not specified then this macro will return {}. |
| */ |
| #define _SAI_GET_CLOCK_NAMES(inst)\ |
| COND_CODE_1(DT_NODE_HAS_PROP(DT_INST(inst, nxp_dai_sai), clocks),\ |
| ({ _SAI_CLOCK_NAME_ARRAY(inst) }),\ |
| ({ })) |
| |
| /* used to declare a struct clock_data */ |
| #define SAI_CLOCK_DATA_DECLARE(inst) \ |
| { \ |
| .clocks = (uint32_t [])_SAI_GET_CLOCK_ARRAY(inst), \ |
| .clock_num = DT_INST_PROP_LEN_OR(inst, clocks, 0), \ |
| .dev = _SAI_GET_CLOCK_CONTROLLER(inst), \ |
| .clock_names = (const char *[])_SAI_GET_CLOCK_NAMES(inst), \ |
| } |
| |
| /* used to parse the tx-fifo-watermark property. If said property is not |
| * specified then this macro will return half of the number of words in the |
| * FIFO. |
| */ |
| #define SAI_TX_FIFO_WATERMARK(inst)\ |
| DT_INST_PROP_OR(inst, tx_fifo_watermark,\ |
| FSL_FEATURE_SAI_FIFO_COUNTn(UINT_TO_I2S(DT_INST_REG_ADDR(inst))) / 2) |
| |
| /* used to parse the rx-fifo-watermark property. If said property is not |
| * specified then this macro will return half of the number of words in the |
| * FIFO. |
| */ |
| #define SAI_RX_FIFO_WATERMARK(inst)\ |
| DT_INST_PROP_OR(inst, rx_fifo_watermark,\ |
| FSL_FEATURE_SAI_FIFO_COUNTn(UINT_TO_I2S(DT_INST_REG_ADDR(inst))) / 2) |
| |
| /* used to retrieve TDR0's address based on SAI's physical address */ |
| #define SAI_TX_FIFO_BASE(inst, idx)\ |
| POINTER_TO_UINT(&(UINT_TO_I2S(DT_INST_REG_ADDR(inst))->TDR[idx])) |
| |
| /* used to retrieve RDR0's address based on SAI's physical address */ |
| #define SAI_RX_FIFO_BASE(inst, idx)\ |
| POINTER_TO_UINT(&(UINT_TO_I2S(DT_INST_REG_ADDR(inst))->RDR[idx])) |
| |
| /* internal macro used to retrieve the default TX/RX FIFO's size (in FIFO words) */ |
| #define _SAI_FIFO_DEPTH(inst)\ |
| FSL_FEATURE_SAI_FIFO_COUNTn(UINT_TO_I2S(DT_INST_REG_ADDR(inst))) |
| |
| /* used to retrieve the TX/RX FIFO's size (in FIFO words) */ |
| #define SAI_FIFO_DEPTH(inst)\ |
| DT_INST_PROP_OR(inst, fifo_depth, _SAI_FIFO_DEPTH(inst)) |
| |
| /* used to retrieve the DMA MUX for transmitter */ |
| #define SAI_TX_DMA_MUX(inst)\ |
| FSL_FEATURE_SAI_TX_DMA_MUXn(UINT_TO_I2S(DT_INST_REG_ADDR(inst))) |
| |
| /* used to retrieve the DMA MUX for receiver */ |
| #define SAI_RX_DMA_MUX(inst)\ |
| FSL_FEATURE_SAI_RX_DMA_MUXn(UINT_TO_I2S(DT_INST_REG_ADDR(inst))) |
| |
| /* used to retrieve the synchronization mode of the transmitter. If this |
| * property is not specified, ASYNC mode will be used. |
| */ |
| #define SAI_TX_SYNC_MODE(inst)\ |
| DT_INST_PROP_OR(inst, tx_sync_mode, kSAI_ModeAsync) |
| |
| /* used to retrieve the synchronization mode of the receiver. If this property |
| * is not specified, ASYNC mode will be used. |
| */ |
| #define SAI_RX_SYNC_MODE(inst)\ |
| DT_INST_PROP_OR(inst, rx_sync_mode, kSAI_ModeAsync) |
| |
| /* used to retrieve the handshake value for given direction. The handshake |
| * is computed as follows: |
| * handshake = CHANNEL_ID | (MUX_VALUE << 8) |
| * The channel ID and MUX value are each encoded in 8 bits. |
| */ |
| #define SAI_TX_RX_DMA_HANDSHAKE(inst, dir)\ |
| ((DT_INST_DMAS_CELL_BY_NAME(inst, dir, channel) & GENMASK(7, 0)) |\ |
| ((DT_INST_DMAS_CELL_BY_NAME(inst, dir, mux) << 8) & GENMASK(15, 8))) |
| |
| /* used to retrieve the number of supported transmission/receive lines */ |
| #define SAI_DLINE_COUNT(inst)\ |
| FSL_FEATURE_SAI_CHANNEL_COUNTn(UINT_TO_I2S(DT_INST_REG_ADDR(inst))) |
| |
| /* used to retrieve the index of the transmission line */ |
| #define SAI_TX_DLINE_INDEX(inst) DT_INST_PROP_OR(inst, tx_dataline, 0) |
| |
| /* used to retrieve the index of the receive line */ |
| #define SAI_RX_DLINE_INDEX(inst) DT_INST_PROP_OR(inst, rx_dataline, 0) |
| |
| /* utility macros */ |
| |
| /* invert a clock's polarity. This works because a clock's polarity is expressed |
| * as a 0 or as a 1. |
| */ |
| #define SAI_INVERT_POLARITY(polarity) (polarity) = !(polarity) |
| |
| /* used to issue a software reset of the transmitter/receiver */ |
| #define SAI_TX_RX_SW_RESET(dir, regmap)\ |
| ((dir) == DAI_DIR_RX ? SAI_RxSoftwareReset(UINT_TO_I2S(regmap), kSAI_ResetTypeSoftware) :\ |
| SAI_TxSoftwareReset(UINT_TO_I2S(regmap), kSAI_ResetTypeSoftware)) |
| |
| /* used to enable/disable the transmitter/receiver. |
| * When enabling the SYNC component, the ASYNC component will also be enabled. |
| * Attempting to disable the SYNC component will fail unless the SYNC bit is |
| * cleared. It is recommended to use sai_tx_rx_force_disable() instead of this |
| * macro when disabling transmitter/receiver. |
| */ |
| #define SAI_TX_RX_ENABLE_DISABLE(dir, regmap, enable)\ |
| ((dir) == DAI_DIR_RX ? SAI_RxEnable(UINT_TO_I2S(regmap), enable) :\ |
| SAI_TxEnable(UINT_TO_I2S(regmap), enable)) |
| |
| /* used to enable/disable the DMA requests for transmitter/receiver */ |
| #define SAI_TX_RX_DMA_ENABLE_DISABLE(dir, regmap, enable)\ |
| ((dir) == DAI_DIR_RX ? SAI_RxEnableDMA(UINT_TO_I2S(regmap),\ |
| kSAI_FIFORequestDMAEnable, enable) :\ |
| SAI_TxEnableDMA(UINT_TO_I2S(regmap), kSAI_FIFORequestDMAEnable, enable)) |
| |
| /* used to check if the hardware transmitter/receiver is enabled */ |
| #define SAI_TX_RX_IS_HW_ENABLED(dir, regmap)\ |
| ((dir) == DAI_DIR_RX ? (UINT_TO_I2S(regmap)->RCSR & I2S_RCSR_RE_MASK) : \ |
| (UINT_TO_I2S(regmap)->TCSR & I2S_TCSR_TE_MASK)) |
| |
| /* used to enable various transmitter/receiver interrupts */ |
| #define _SAI_TX_RX_ENABLE_IRQ(dir, regmap, which)\ |
| ((dir) == DAI_DIR_RX ? SAI_RxEnableInterrupts(UINT_TO_I2S(regmap), which) : \ |
| SAI_TxEnableInterrupts(UINT_TO_I2S(regmap), which)) |
| |
| /* used to disable various transmitter/receiver interrupts */ |
| #define _SAI_TX_RX_DISABLE_IRQ(dir, regmap, which)\ |
| ((dir) == DAI_DIR_RX ? SAI_RxDisableInterrupts(UINT_TO_I2S(regmap), which) : \ |
| SAI_TxDisableInterrupts(UINT_TO_I2S(regmap), which)) |
| |
| /* used to enable/disable various transmitter/receiver interrupts */ |
| #define SAI_TX_RX_ENABLE_DISABLE_IRQ(dir, regmap, which, enable)\ |
| ((enable == true) ? _SAI_TX_RX_ENABLE_IRQ(dir, regmap, which) :\ |
| _SAI_TX_RX_DISABLE_IRQ(dir, regmap, which)) |
| |
| /* used to check if a status flag is set */ |
| #define SAI_TX_RX_STATUS_IS_SET(dir, regmap, which)\ |
| ((dir) == DAI_DIR_RX ? ((UINT_TO_I2S(regmap))->RCSR & (which)) : \ |
| ((UINT_TO_I2S(regmap))->TCSR & (which))) |
| |
| /* used to clear status flags */ |
| #define SAI_TX_RX_STATUS_CLEAR(dir, regmap, which) \ |
| ((dir) == DAI_DIR_RX ? SAI_RxClearStatusFlags(UINT_TO_I2S(regmap), which) \ |
| : SAI_TxClearStatusFlags(UINT_TO_I2S(regmap), which)) |
| |
| /* used to retrieve the SYNC direction. Use this macro when you know for sure |
| * you have 1 SYNC direction with 1 ASYNC direction. |
| */ |
| #define SAI_TX_RX_GET_SYNC_DIR(cfg)\ |
| ((cfg)->tx_sync_mode == kSAI_ModeSync ? DAI_DIR_TX : DAI_DIR_RX) |
| |
| /* used to retrieve the ASYNC direction. Use this macro when you know for sure |
| * you have 1 SYNC direction with 1 ASYNC direction. |
| */ |
| #define SAI_TX_RX_GET_ASYNC_DIR(cfg)\ |
| ((cfg)->tx_sync_mode == kSAI_ModeAsync ? DAI_DIR_TX : DAI_DIR_RX) |
| |
| /* used to check if transmitter/receiver is SW enabled */ |
| #define SAI_TX_RX_DIR_IS_SW_ENABLED(dir, data)\ |
| ((dir) == DAI_DIR_TX ? data->tx_enabled : data->rx_enabled) |
| |
| /* used to compute the mask for the transmission/receive lines based on |
| * the index passed from the DTS. |
| */ |
| #define SAI_TX_RX_DLINE_MASK(dir, cfg)\ |
| ((dir) == DAI_DIR_TX ? BIT((cfg)->tx_dline) : BIT((cfg)->rx_dline)) |
| |
| struct sai_clock_data { |
| uint32_t *clocks; |
| uint32_t clock_num; |
| /* assumption: all clocks belong to the same producer */ |
| const struct device *dev; |
| const char **clock_names; |
| }; |
| |
| struct sai_data { |
| mm_reg_t regmap; |
| sai_transceiver_t rx_config; |
| sai_transceiver_t tx_config; |
| bool tx_enabled; |
| bool rx_enabled; |
| enum dai_state tx_state; |
| enum dai_state rx_state; |
| struct dai_config cfg; |
| }; |
| |
| struct sai_config { |
| uint32_t regmap_phys; |
| uint32_t regmap_size; |
| uint32_t irq; |
| struct sai_clock_data clk_data; |
| bool mclk_is_output; |
| /* if the tx/rx-fifo-watermark properties are not specified, it's going |
| * to be assumed that the watermark should be set to half of the FIFO |
| * size. |
| */ |
| uint32_t rx_fifo_watermark; |
| uint32_t tx_fifo_watermark; |
| const struct dai_properties *tx_props; |
| const struct dai_properties *rx_props; |
| uint32_t dai_index; |
| /* RX synchronization mode - may be SYNC or ASYNC */ |
| sai_sync_mode_t rx_sync_mode; |
| /* TX synchronization mode - may be SYNC or ASYNC */ |
| sai_sync_mode_t tx_sync_mode; |
| void (*irq_config)(void); |
| uint32_t tx_dline; |
| uint32_t rx_dline; |
| const struct pinctrl_dev_config *pincfg; |
| }; |
| |
| /* this needs to perfectly match SOF's struct sof_ipc_dai_sai_params */ |
| struct sai_bespoke_config { |
| uint32_t reserved0; |
| |
| uint16_t reserved1; |
| uint16_t mclk_id; |
| uint32_t mclk_direction; |
| |
| /* CLOCK-related data */ |
| uint32_t mclk_rate; |
| uint32_t fsync_rate; |
| uint32_t bclk_rate; |
| |
| /* TDM-related data */ |
| uint32_t tdm_slots; |
| uint32_t rx_slots; |
| uint32_t tx_slots; |
| uint16_t tdm_slot_width; |
| uint16_t reserved2; |
| }; |
| |
| #ifdef CONFIG_SAI_HAS_MCLK_CONFIG_OPTION |
| static int get_msel(sai_bclk_source_t bclk_source, uint32_t *msel) |
| { |
| switch (bclk_source) { |
| case kSAI_BclkSourceMclkOption1: |
| *msel = 0; |
| break; |
| case kSAI_BclkSourceMclkOption2: |
| *msel = (0x2 << SAI_MCLK_MCR_MSEL_SHIFT); |
| break; |
| case kSAI_BclkSourceMclkOption3: |
| *msel = (0x3 << SAI_MCLK_MCR_MSEL_SHIFT); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static void set_msel(uint32_t regmap, int msel) |
| { |
| UINT_TO_I2S(regmap)->MCR &= ~SAI_MCLK_MCR_MSEL_MASK; |
| UINT_TO_I2S(regmap)->MCR |= msel; |
| } |
| |
| static int clk_lookup_by_name(const struct sai_clock_data *clk_data, char *name) |
| { |
| int i; |
| |
| for (i = 0; i < clk_data->clock_num; i++) { |
| if (!strcmp(name, clk_data->clock_names[i])) { |
| return i; |
| } |
| } |
| |
| return -EINVAL; |
| } |
| |
| static int get_mclk_rate(const struct sai_clock_data *clk_data, |
| sai_bclk_source_t bclk_source, |
| uint32_t *rate) |
| { |
| int clk_idx; |
| char *clk_name; |
| |
| switch (bclk_source) { |
| case kSAI_BclkSourceMclkOption1: |
| clk_name = "mclk1"; |
| break; |
| case kSAI_BclkSourceMclkOption2: |
| clk_name = "mclk2"; |
| break; |
| case kSAI_BclkSourceMclkOption3: |
| clk_name = "mclk3"; |
| break; |
| default: |
| LOG_ERR("invalid bitclock source: %d", bclk_source); |
| return -EINVAL; |
| } |
| |
| clk_idx = clk_lookup_by_name(clk_data, clk_name); |
| if (clk_idx < 0) { |
| LOG_ERR("failed to get clock index for %s", clk_name); |
| return clk_idx; |
| } |
| |
| return clock_control_get_rate(clk_data->dev, |
| UINT_TO_POINTER(clk_data->clocks[clk_idx]), |
| rate); |
| } |
| #endif /* CONFIG_SAI_HAS_MCLK_CONFIG_OPTION */ |
| |
| static inline void get_bclk_default_config(sai_bit_clock_t *cfg) |
| { |
| memset(cfg, 0, sizeof(sai_bit_clock_t)); |
| |
| /* by default, BCLK has the following properties: |
| * |
| * 1) BCLK is active HIGH. |
| * 2) BCLK uses MCLK1 source. (only applicable to master mode) |
| * 3) No source swap. |
| * 4) No input delay. |
| */ |
| cfg->bclkPolarity = kSAI_PolarityActiveHigh; |
| cfg->bclkSource = kSAI_BclkSourceMclkOption1; |
| } |
| |
| static inline void get_fsync_default_config(sai_frame_sync_t *cfg) |
| { |
| memset(cfg, 0, sizeof(sai_frame_sync_t)); |
| |
| /* by default, FSYNC has the following properties: |
| * |
| * 1) FSYNC is asserted one bit early with respect to the next |
| * frame. |
| * 2) FSYNC is active HIGH. |
| */ |
| cfg->frameSyncEarly = true; |
| cfg->frameSyncPolarity = kSAI_PolarityActiveHigh; |
| } |
| |
| static inline void get_serial_default_config(sai_serial_data_t *cfg) |
| { |
| memset(cfg, 0, sizeof(sai_serial_data_t)); |
| |
| /* by default, the serial configuration has the following quirks: |
| * |
| * 1) Data pin is not tri-stated. |
| * 2) MSB is first. |
| */ |
| /* note: this is equivalent to checking if the SAI has xCR4's CHMOD bit */ |
| #if FSL_FEATURE_SAI_HAS_CHANNEL_MODE |
| cfg->dataMode = kSAI_DataPinStateOutputZero; |
| #endif /* FSL_FEATURE_SAI_HAS_CHANNEL_MODE */ |
| cfg->dataOrder = kSAI_DataMSB; |
| } |
| |
| static inline void get_fifo_default_config(sai_fifo_t *cfg) |
| { |
| memset(cfg, 0, sizeof(sai_fifo_t)); |
| } |
| |
| static inline uint32_t sai_get_state(enum dai_dir dir, |
| struct sai_data *data) |
| { |
| if (dir == DAI_DIR_RX) { |
| return data->rx_state; |
| } else { |
| return data->tx_state; |
| } |
| } |
| |
| static int sai_update_state(enum dai_dir dir, |
| struct sai_data *data, |
| enum dai_state new_state) |
| { |
| enum dai_state old_state = sai_get_state(dir, data); |
| |
| LOG_DBG("attempting to transition from %d to %d", old_state, new_state); |
| |
| /* check if transition is possible */ |
| switch (new_state) { |
| case DAI_STATE_NOT_READY: |
| /* this shouldn't be possible */ |
| return -EPERM; |
| case DAI_STATE_READY: |
| if (old_state != DAI_STATE_NOT_READY && |
| old_state != DAI_STATE_READY && |
| old_state != DAI_STATE_STOPPING) { |
| return -EPERM; |
| } |
| break; |
| case DAI_STATE_RUNNING: |
| if (old_state != DAI_STATE_PAUSED && |
| old_state != DAI_STATE_STOPPING && |
| old_state != DAI_STATE_READY) { |
| return -EPERM; |
| } |
| break; |
| case DAI_STATE_PAUSED: |
| if (old_state != DAI_STATE_RUNNING) { |
| return -EPERM; |
| } |
| break; |
| case DAI_STATE_STOPPING: |
| if (old_state != DAI_STATE_READY && |
| old_state != DAI_STATE_RUNNING && |
| old_state != DAI_STATE_PAUSED) { |
| return -EPERM; |
| } |
| break; |
| case DAI_STATE_ERROR: |
| case DAI_STATE_PRE_RUNNING: |
| /* these states are not used so transitioning to them |
| * is considered invalid. |
| */ |
| default: |
| return -EINVAL; |
| } |
| |
| if (dir == DAI_DIR_RX) { |
| data->rx_state = new_state; |
| } else { |
| data->tx_state = new_state; |
| } |
| |
| return 0; |
| } |
| |
| static inline void sai_tx_rx_force_disable(enum dai_dir dir, |
| uint32_t regmap) |
| { |
| I2S_Type *base = UINT_TO_I2S(regmap); |
| |
| if (dir == DAI_DIR_RX) { |
| base->RCSR = ((base->RCSR & 0xFFE3FFFFU) & (~I2S_RCSR_RE_MASK)); |
| } else { |
| base->TCSR = ((base->TCSR & 0xFFE3FFFFU) & (~I2S_TCSR_TE_MASK)); |
| } |
| } |
| |
| static inline void sai_tx_rx_sw_enable_disable(enum dai_dir dir, |
| struct sai_data *data, |
| bool enable) |
| { |
| if (dir == DAI_DIR_RX) { |
| data->rx_enabled = enable; |
| } else { |
| data->tx_enabled = enable; |
| } |
| } |
| |
| static inline int count_leading_zeros(uint32_t word) |
| { |
| int num = 0; |
| |
| while (word) { |
| if (!(word & 0x1)) { |
| num++; |
| } else { |
| break; |
| } |
| |
| word = word >> 1; |
| } |
| |
| return num; |
| } |
| |
| static inline void sai_tx_rx_set_dline_mask(enum dai_dir dir, uint32_t regmap, uint32_t mask) |
| { |
| I2S_Type *base = UINT_TO_I2S(regmap); |
| |
| if (dir == DAI_DIR_RX) { |
| base->RCR3 &= ~I2S_RCR3_RCE_MASK; |
| base->RCR3 |= I2S_RCR3_RCE(mask); |
| } else { |
| base->TCR3 &= ~I2S_TCR3_TCE_MASK; |
| base->TCR3 |= I2S_TCR3_TCE(mask); |
| } |
| } |
| |
| static inline void sai_dump_register_data(uint32_t regmap) |
| { |
| I2S_Type *base = UINT_TO_I2S(regmap); |
| |
| LOG_DBG("TCSR: 0x%x", base->TCSR); |
| LOG_DBG("RCSR: 0x%x", base->RCSR); |
| |
| LOG_DBG("TCR1: 0x%x", base->TCR1); |
| LOG_DBG("RCR1: 0x%x", base->RCR1); |
| |
| LOG_DBG("TCR2: 0x%x", base->TCR2); |
| LOG_DBG("RCR2: 0x%x", base->RCR2); |
| |
| LOG_DBG("TCR3: 0x%x", base->TCR3); |
| LOG_DBG("RCR3: 0x%x", base->RCR3); |
| |
| LOG_DBG("TCR4: 0x%x", base->TCR4); |
| LOG_DBG("RCR4: 0x%x", base->RCR4); |
| |
| LOG_DBG("TCR5: 0x%x", base->TCR5); |
| LOG_DBG("RCR5: 0x%x", base->RCR5); |
| |
| LOG_DBG("TMR: 0x%x", base->TMR); |
| LOG_DBG("RMR: 0x%x", base->RMR); |
| |
| #ifdef CONFIG_SAI_HAS_MCLK_CONFIG_OPTION |
| LOG_DBG("MCR: 0x%x", base->MCR); |
| #endif /* CONFIG_SAI_HAS_MCLK_CONFIG_OPTION */ |
| } |
| |
| #endif /* ZEPHYR_DRIVERS_DAI_NXP_SAI_H_ */ |