pinebuds/platform/hal/hal_i2s.c
Ben V. Brown dca92cf01f Removing FPGA dev support
As we will never get their FGPA source code. Zero loss.
2023-02-02 17:42:33 +11:00

794 lines
23 KiB
C

/***************************************************************************
*
* Copyright 2015-2019 BES.
* All rights reserved. All unpublished rights reserved.
*
* No part of this work may be used or reproduced in any form or by any
* means, or stored in a database or retrieval system, without prior written
* permission of BES.
*
* Use of this work is governed by a license granted by BES.
* This work contains confidential and proprietary information of
* BES. which is protected by copyright, trade secret,
* trademark and other intellectual property rights.
*
****************************************************************************/
#include "hal_i2s.h"
#include "hal_cmu.h"
#include "hal_i2sip.h"
#include "hal_iomux.h"
#include "plat_addr_map.h"
#include "plat_types.h"
#include "reg_i2sip.h"
#if !defined(CHIP_BEST1000) && defined(I2S_MCLK_FROM_SPDIF)
#include "hal_spdif.h"
#endif
#include "analog.h"
#include "cmsis.h"
#include "hal_timer.h"
#include "hal_trace.h"
// Trigger DMA request when TX-FIFO count <= threshold
#define HAL_I2S_TX_FIFO_TRIGGER_LEVEL (I2SIP_FIFO_DEPTH / 2)
// Trigger DMA request when RX-FIFO count >= threshold
#define HAL_I2S_RX_FIFO_TRIGGER_LEVEL (I2SIP_FIFO_DEPTH / 2)
#define HAL_I2S_YES 1
#define HAL_I2S_NO 0
#ifdef CHIP_BEST1000
#define I2S_CMU_DIV CODEC_PLL_DIV
#define I2S_CHAN_NUM 1
#else
#define I2S_CMU_DIV CODEC_CMU_DIV
#define I2S_CHAN_NUM 4
#endif
#ifndef I2S_MCLK_DIV
#define I2S_MCLK_DIV 2
#endif
enum HAL_I2S_STATUS_T {
HAL_I2S_STATUS_NULL,
HAL_I2S_STATUS_OPENED,
HAL_I2S_STATUS_STARTED,
};
struct I2S_SAMPLE_RATE_T {
enum AUD_SAMPRATE_T sample_rate;
uint32_t codec_freq;
uint8_t codec_div;
uint8_t cmu_div;
};
struct HAL_I2S_MOD_NAME_T {
enum HAL_CMU_MOD_ID_T mod;
enum HAL_CMU_MOD_ID_T apb;
};
static const struct I2S_SAMPLE_RATE_T i2s_sample_rate[] = {
{AUD_SAMPRATE_8000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_16000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_32000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_44100, CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_48000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_64000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_96000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_128000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_256000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_176400, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_192000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_352800, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_384000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_512000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_705600, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_768000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
{AUD_SAMPRATE_1024000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, I2S_CMU_DIV},
};
static const char *const invalid_id = "Invalid I2S ID: %d\n";
// static const char * const invalid_ch = "Invalid I2S CH: %d\n";
static enum HAL_I2S_STATUS_T i2s_status[HAL_I2S_ID_QTY][AUD_STREAM_NUM];
static enum HAL_I2S_MODE_T i2s_mode[HAL_I2S_ID_QTY];
static bool i2s_dma[HAL_I2S_ID_QTY][AUD_STREAM_NUM];
static bool sync_start[HAL_I2S_ID_QTY][AUD_STREAM_NUM];
#ifdef CHIP_BEST2000
static uint8_t i2s_rx_line_map[HAL_I2S_ID_QTY];
STATIC_ASSERT(sizeof(i2s_rx_line_map) * 8 >= I2S_CHAN_NUM,
"i2s_rx_line_map size too small");
#endif
static const struct HAL_I2S_MOD_NAME_T i2s_mod[HAL_I2S_ID_QTY] = {
{
.mod = HAL_CMU_MOD_O_I2S0,
.apb = HAL_CMU_MOD_P_I2S0,
},
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
{
.mod = HAL_CMU_MOD_O_I2S1,
.apb = HAL_CMU_MOD_P_I2S1,
},
#endif
};
static inline uint32_t _i2s_get_reg_base(enum HAL_I2S_ID_T id) {
ASSERT(id < HAL_I2S_ID_QTY, invalid_id, id);
switch (id) {
case HAL_I2S_ID_0:
return I2S0_BASE;
break;
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
case HAL_I2S_ID_1:
return I2S1_BASE;
break;
#endif
default:
break;
}
return 0;
}
static int hal_i2s_master_stream_closed(void) {
enum HAL_I2S_ID_T id;
for (id = HAL_I2S_ID_0; id < HAL_I2S_ID_QTY; id++) {
if (i2s_status[id][AUD_STREAM_PLAYBACK] != HAL_I2S_STATUS_NULL ||
i2s_status[id][AUD_STREAM_CAPTURE] != HAL_I2S_STATUS_NULL) {
if (i2s_mode[id] == HAL_I2S_MODE_MASTER) {
return false;
}
}
}
return true;
}
int hal_i2s_open(enum HAL_I2S_ID_T id, enum AUD_STREAM_T stream,
enum HAL_I2S_MODE_T mode) {
uint32_t reg_base;
reg_base = _i2s_get_reg_base(id);
ASSERT(mode == HAL_I2S_MODE_MASTER || mode == HAL_I2S_MODE_SLAVE,
"Invalid I2S mode for stream %d: %d", stream, mode);
ASSERT(i2s_mode[id] == HAL_I2S_MODE_NULL || i2s_mode[id] == mode,
"Incompatible I2S mode for stream %d: prev=%d cur=%d", stream,
i2s_mode[id], mode);
if (i2s_status[id][stream] != HAL_I2S_STATUS_NULL) {
TRACE(2, "Invalid I2S opening status for stream %d: %d", stream,
i2s_status[id][stream]);
return 1;
}
if (i2s_status[id][AUD_STREAM_PLAYBACK] == HAL_I2S_STATUS_NULL &&
i2s_status[id][AUD_STREAM_CAPTURE] == HAL_I2S_STATUS_NULL) {
if (mode == HAL_I2S_MODE_MASTER) {
#ifndef SIMU
if (hal_i2s_master_stream_closed()) {
analog_aud_pll_open(ANA_AUD_PLL_USER_I2S);
}
#endif
hal_cmu_i2s_clock_enable(id);
}
if (id == HAL_I2S_ID_0) {
hal_iomux_set_i2s0();
#if defined(CHIP_HAS_I2S) && (CHIP_HAS_I2S > 1)
} else {
hal_iomux_set_i2s1();
#endif
}
hal_cmu_clock_enable(i2s_mod[id].mod);
hal_cmu_clock_enable(i2s_mod[id].apb);
hal_cmu_reset_clear(i2s_mod[id].mod);
hal_cmu_reset_clear(i2s_mod[id].apb);
i2sip_w_enable_i2sip(reg_base, HAL_I2S_YES);
for (int i = 0; i < I2S_CHAN_NUM; i++) {
i2sip_w_enable_rx_channel(reg_base, i, HAL_I2S_NO);
i2sip_w_enable_tx_channel(reg_base, i, HAL_I2S_NO);
i2sip_w_rx_fifo_reset(reg_base, i);
i2sip_w_tx_fifo_reset(reg_base, i);
}
#ifndef CHIP_BEST1000
if (mode == HAL_I2S_MODE_MASTER) {
i2sip_w_enable_slave_mode(reg_base, HAL_I2S_NO);
} else {
i2sip_w_enable_slave_mode(reg_base, HAL_I2S_YES);
}
#endif
if (mode == HAL_I2S_MODE_MASTER) {
hal_cmu_i2s_set_master_mode(id);
} else {
hal_cmu_i2s_set_slave_mode(id);
}
i2s_mode[id] = mode;
}
i2s_dma[id][stream] = false;
i2s_status[id][stream] = HAL_I2S_STATUS_OPENED;
return 0;
}
int hal_i2s_close(enum HAL_I2S_ID_T id, enum AUD_STREAM_T stream) {
uint32_t reg_base;
reg_base = _i2s_get_reg_base(id);
if (i2s_status[id][stream] != HAL_I2S_STATUS_OPENED) {
TRACE(2, "Invalid I2S closing status for stream %d: %d", stream,
i2s_status[id][stream]);
return 1;
}
i2s_status[id][stream] = HAL_I2S_STATUS_NULL;
if (i2s_status[id][AUD_STREAM_PLAYBACK] == HAL_I2S_STATUS_NULL &&
i2s_status[id][AUD_STREAM_CAPTURE] == HAL_I2S_STATUS_NULL) {
i2sip_w_enable_i2sip(reg_base, HAL_I2S_NO);
hal_cmu_reset_set(i2s_mod[id].apb);
hal_cmu_reset_set(i2s_mod[id].mod);
hal_cmu_clock_disable(i2s_mod[id].apb);
hal_cmu_clock_disable(i2s_mod[id].mod);
if (i2s_mode[id] == HAL_I2S_MODE_MASTER) {
hal_cmu_i2s_clock_disable(id);
if (hal_i2s_master_stream_closed()) {
#if !defined(CHIP_BEST1000) && defined(I2S_MCLK_FROM_SPDIF)
hal_cmu_clock_out_disable();
hal_spdif_clock_out_disable(HAL_SPDIF_ID_0);
#endif
#ifndef SIMU
analog_aud_pll_close(ANA_AUD_PLL_USER_I2S);
#endif
}
}
i2s_mode[id] = HAL_I2S_MODE_NULL;
}
return 0;
}
#ifdef AF_ADC_I2S_SYNC
static bool _hal_i2s_enable_delay[HAL_I2S_ID_QTY];
void hal_i2s_enable_delay(enum HAL_I2S_ID_T id) {
_hal_i2s_enable_delay[id] = true;
}
void hal_i2s_disable_delay(enum HAL_I2S_ID_T id) {
_hal_i2s_enable_delay[id] = false;
}
void hal_i2s_enable(enum HAL_I2S_ID_T id) {
i2sip_w_enable_clk_gen(_i2s_get_reg_base(id), HAL_I2S_YES);
}
#endif
int hal_i2s_start_stream(enum HAL_I2S_ID_T id, enum AUD_STREAM_T stream) {
uint32_t reg_base;
uint32_t lock;
bool xfer_en[AUD_STREAM_NUM];
reg_base = _i2s_get_reg_base(id);
if (i2s_status[id][stream] != HAL_I2S_STATUS_OPENED) {
TRACE(2, "Invalid I2S starting status for stream %d: %d", stream,
i2s_status[id][stream]);
return 1;
}
if (i2s_mode[id] == HAL_I2S_MODE_SLAVE && sync_start[id][stream]) {
xfer_en[stream] = false;
xfer_en[!stream] = false;
} else {
xfer_en[stream] = true;
if (i2s_mode[id] == HAL_I2S_MODE_SLAVE &&
i2s_status[id][!stream] == HAL_I2S_STATUS_STARTED &&
sync_start[id][!stream]) {
xfer_en[!stream] = true;
} else {
xfer_en[!stream] = false;
}
}
if (stream == AUD_STREAM_PLAYBACK) {
lock = int_lock();
i2sip_w_enable_tx_channel(reg_base, 0, HAL_I2S_YES);
if (xfer_en[stream]) {
i2sip_w_enable_tx_block(reg_base, HAL_I2S_YES);
if (i2s_dma[id][stream]) {
i2sip_w_enable_tx_dma(reg_base, HAL_I2S_YES);
}
}
if (xfer_en[!stream]) {
i2sip_w_enable_rx_block(reg_base, HAL_I2S_YES);
}
int_unlock(lock);
if (i2s_dma[id][stream] && i2s_mode[id] == HAL_I2S_MODE_MASTER) {
// Delay a little time for DMA to fill the TX FIFO before sending
// (enabling clk gen)
for (volatile int i = 0; i < 50; i++)
;
}
} else {
if (i2s_dma[id][stream]) {
i2sip_w_enable_rx_dma(reg_base, HAL_I2S_YES);
}
#ifdef CHIP_BEST2000
int i;
for (i = 0; i < I2S_CHAN_NUM; i++) {
if (i2s_rx_line_map[id] & (1 << i)) {
i2sip_w_enable_rx_channel(reg_base, i, HAL_I2S_YES);
}
}
#else
i2sip_w_enable_rx_channel(reg_base, 0, HAL_I2S_YES);
#endif
lock = int_lock();
if (xfer_en[stream]) {
i2sip_w_enable_rx_block(reg_base, HAL_I2S_YES);
}
if (xfer_en[!stream]) {
i2sip_w_enable_tx_block(reg_base, HAL_I2S_YES);
if (i2s_dma[id][!stream]) {
i2sip_w_enable_tx_dma(reg_base, HAL_I2S_YES);
}
}
int_unlock(lock);
}
if (i2s_mode[id] == HAL_I2S_MODE_MASTER && !sync_start[id][stream]) {
if (i2sip_r_clk_gen_enabled(reg_base) == 0) {
hal_cmu_i2s_clock_out_enable(id);
#ifdef AF_ADC_I2S_SYNC
if (_hal_i2s_enable_delay[id]) {
_hal_i2s_enable_delay[id] = false;
} else {
hal_i2s_enable(id);
}
#else
i2sip_w_enable_clk_gen(reg_base, HAL_I2S_YES);
#endif
}
}
i2s_status[id][stream] = HAL_I2S_STATUS_STARTED;
return 0;
}
int hal_i2s_stop_stream(enum HAL_I2S_ID_T id, enum AUD_STREAM_T stream) {
uint32_t reg_base;
reg_base = _i2s_get_reg_base(id);
if (i2s_status[id][stream] != HAL_I2S_STATUS_STARTED) {
TRACE(2, "Invalid I2S stopping status for stream %d: %d", stream,
i2s_status[id][stream]);
return 1;
}
i2s_status[id][stream] = HAL_I2S_STATUS_OPENED;
if (i2s_status[id][AUD_STREAM_PLAYBACK] != HAL_I2S_STATUS_STARTED &&
i2s_status[id][AUD_STREAM_CAPTURE] != HAL_I2S_STATUS_STARTED) {
if (i2s_mode[id] == HAL_I2S_MODE_MASTER) {
hal_cmu_i2s_clock_out_disable(id);
i2sip_w_enable_clk_gen(reg_base, HAL_I2S_NO);
#ifdef AF_ADC_I2S_SYNC
_hal_i2s_enable_delay[id] = false;
#endif
}
}
if (stream == AUD_STREAM_PLAYBACK) {
i2sip_w_enable_tx_block(reg_base, HAL_I2S_NO);
i2sip_w_enable_tx_channel(reg_base, 0, HAL_I2S_NO);
i2sip_w_enable_tx_dma(reg_base, HAL_I2S_NO);
i2sip_w_tx_fifo_reset(reg_base, 0);
} else {
i2sip_w_enable_rx_block(reg_base, HAL_I2S_NO);
i2sip_w_enable_rx_dma(reg_base, HAL_I2S_NO);
#ifdef CHIP_BEST1000
i2sip_w_enable_rx_channel(reg_base, 0, HAL_I2S_NO);
i2sip_w_rx_fifo_reset(reg_base, 0);
#else
int i;
for (i = 0; i < I2S_CHAN_NUM; i++) {
i2sip_w_enable_rx_channel(reg_base, i, HAL_I2S_NO);
i2sip_w_rx_fifo_reset(reg_base, i);
}
#endif
}
return 0;
}
int hal_i2s_setup_stream(enum HAL_I2S_ID_T id, enum AUD_STREAM_T stream,
const struct HAL_I2S_CONFIG_T *cfg) {
uint32_t reg_base;
uint32_t div = 0;
uint8_t cycles;
uint8_t resolution = 0, word_sclk = 0;
bool valid;
int POSSIBLY_UNUSED i;
reg_base = _i2s_get_reg_base(id);
if (i2s_status[id][stream] != HAL_I2S_STATUS_OPENED) {
TRACE(2, "Invalid I2S setup status for stream %d: %d", stream,
i2s_status[id][stream]);
return 1;
}
cycles = cfg->cycles;
if (cycles == 0) {
if (cfg->bits <= AUD_BITS_16) {
cycles = 16;
} else {
cycles = 32;
}
}
ASSERT(cycles >= cfg->bits, "I2S cycles (%u) should >= bits (%u)", cycles,
cfg->bits);
switch (cycles) {
case 16:
word_sclk = I2SIP_CLK_CFG_WSS_VAL_16CYCLE;
break;
case 24:
word_sclk = I2SIP_CLK_CFG_WSS_VAL_24CYCLE;
break;
case 32:
word_sclk = I2SIP_CLK_CFG_WSS_VAL_32CYCLE;
break;
default:
ASSERT(false, "Bad I2S cycles (%u)", cycles);
return 1;
}
switch (cfg->bits) {
case AUD_BITS_8:
resolution = I2SIP_CFG_WLEN_VAL_IGNORE;
break;
case AUD_BITS_12:
resolution = I2SIP_CFG_WLEN_VAL_12BIT;
break;
case AUD_BITS_16:
resolution = I2SIP_CFG_WLEN_VAL_16BIT;
break;
case AUD_BITS_20:
resolution = I2SIP_CFG_WLEN_VAL_20BIT;
break;
case AUD_BITS_24:
resolution = I2SIP_CFG_WLEN_VAL_24BIT;
break;
case AUD_BITS_32:
resolution = I2SIP_CFG_WLEN_VAL_32BIT;
break;
default:
ASSERT(false, "Bad I2S bits (%u)", cfg->bits);
return 1;
}
/* word clock width : how many sclk work sclk count*/
/* sclk gate in word clock : how many sclk gate : fixed no gate */
i2sip_w_clk_cfg_reg(reg_base, (word_sclk << I2SIP_CLK_CFG_WSS_SHIFT) |
(I2SIP_CLK_CFG_SCLK_GATE_VAL_NO_GATE
<< I2SIP_CLK_CFG_SCLK_GATE_SHIFT));
#ifdef CHIP_BEST2000
if (stream == AUD_STREAM_PLAYBACK) {
valid = (cfg->channel_num == AUD_CHANNEL_NUM_2);
} else {
valid = (cfg->channel_num == AUD_CHANNEL_NUM_2 ||
cfg->channel_num == AUD_CHANNEL_NUM_4 ||
cfg->channel_num == AUD_CHANNEL_NUM_6 ||
cfg->channel_num == AUD_CHANNEL_NUM_8);
}
#else
valid = (cfg->channel_num == AUD_CHANNEL_NUM_2);
#endif
ASSERT(valid, "[%s] Stream %d Channel number(%d) error", __func__, stream,
cfg->channel_num);
if (i2s_mode[id] == HAL_I2S_MODE_MASTER) {
uint32_t i2s_clock;
uint32_t bit_rate;
for (i = 0; i < ARRAY_SIZE(i2s_sample_rate); i++) {
if (i2s_sample_rate[i].sample_rate == cfg->sample_rate) {
break;
}
}
ASSERT(i < ARRAY_SIZE(i2s_sample_rate), "%s: Invalid i2s sample rate: %d",
__func__, cfg->sample_rate);
TRACE(3, "[%s] stream=%d sample_rate=%d", __func__, stream,
cfg->sample_rate);
ASSERT(i2s_sample_rate[i].codec_div / i2s_sample_rate[i].cmu_div *
i2s_sample_rate[i].cmu_div ==
i2s_sample_rate[i].codec_div,
"%s: Invalid codec div for rate %u: codec_div=%u cmu_div=%u",
__func__, cfg->sample_rate, i2s_sample_rate[i].codec_div,
i2s_sample_rate[i].cmu_div);
#ifndef SIMU
analog_aud_freq_pll_config(i2s_sample_rate[i].codec_freq,
i2s_sample_rate[i].codec_div);
#ifdef CHIP_BEST2000
analog_aud_pll_set_dig_div(i2s_sample_rate[i].codec_div /
i2s_sample_rate[i].cmu_div);
#endif
#endif
i2s_clock = i2s_sample_rate[i].codec_freq * i2s_sample_rate[i].cmu_div;
bit_rate = i2s_sample_rate[i].sample_rate * AUD_CHANNEL_NUM_2 * cycles;
div = i2s_clock / bit_rate;
ASSERT(
i2s_clock == bit_rate * div,
"%s: Bad stream cfg (bad cycles?): i2s_clock=%u bit_rate=%u cycles=%u",
__func__, i2s_clock, bit_rate, cycles);
#if !defined(CHIP_BEST1000) && defined(I2S_MCLK_FROM_SPDIF)
#ifdef I2S_MCLK_PIN
hal_iomux_set_i2s_mclk();
hal_cmu_i2s_mclk_enable(HAL_CMU_I2S_MCLK_PLLSPDIF0);
#else
hal_iomux_set_clock_out();
hal_cmu_clock_out_enable(HAL_CMU_CLOCK_OUT_MCU_SPDIF0);
#endif
// By default MCLK is half of (CODEC_FREQ_24P576M or CODEC_FREQ_22P5792M)
hal_spdif_clock_out_enable(HAL_SPDIF_ID_0,
i2s_sample_rate[i].cmu_div * I2S_MCLK_DIV);
#endif
hal_cmu_i2s_set_div(id, div);
}
if ((stream == AUD_STREAM_PLAYBACK &&
i2s_status[id][AUD_STREAM_CAPTURE] == HAL_I2S_STATUS_NULL) ||
(stream == AUD_STREAM_CAPTURE &&
i2s_status[id][AUD_STREAM_PLAYBACK] == HAL_I2S_STATUS_NULL)) {
hal_cmu_reset_pulse(i2s_mod[id].mod);
}
i2s_dma[id][stream] = cfg->use_dma;
sync_start[id][stream] = cfg->sync_start;
if (stream == AUD_STREAM_PLAYBACK) {
/* resolution : valid bit width */
i2sip_w_tx_resolution(reg_base, 0, resolution);
/* fifo level to trigger empty interrupt or dma req */
i2sip_w_tx_fifo_threshold(reg_base, 0, HAL_I2S_TX_FIFO_TRIGGER_LEVEL);
} else {
#ifdef CHIP_BEST2000
uint32_t ch;
uint32_t cnt = 0;
i2s_rx_line_map[id] = 0;
for (i = 0; i < I2S_CHAN_NUM; i++) {
ch = (cfg->channel_map >> (i * 2)) & 3;
ASSERT(ch == 0 || ch == 3, "[%s] Stream %d Bad Chan Map 0x%04X", __func__,
stream, cfg->channel_map);
if (ch == 3) {
i2s_rx_line_map[id] |= (1 << i);
cnt += 2;
}
}
ASSERT(cnt == cfg->channel_num,
"[%s] Stream %d Unmatched Chan Num (%u) and Map (0x%04X)", __func__,
stream, cfg->channel_num, cfg->channel_map);
if (cfg->chan_sep_buf) {
i2sip_w_rx_dma_blk_size(reg_base, HAL_I2S_RX_FIFO_TRIGGER_LEVEL);
i2sp_w_enable_rx_dma_block(reg_base, HAL_I2S_YES);
} else {
i2sp_w_enable_rx_dma_block(reg_base, HAL_I2S_NO);
}
for (i = 0; i < I2S_CHAN_NUM; i++) {
if (i2s_rx_line_map[id] & (1 << i)) {
i2sip_w_rx_resolution(reg_base, i, resolution);
i2sip_w_rx_fifo_threshold(reg_base, i, HAL_I2S_RX_FIFO_TRIGGER_LEVEL);
}
}
#else
/* resolution : valid bit width */
i2sip_w_rx_resolution(reg_base, 0, resolution);
/* fifo level to trigger empty interrupt or dma req */
i2sip_w_rx_fifo_threshold(reg_base, 0, HAL_I2S_RX_FIFO_TRIGGER_LEVEL);
#endif
}
return 0;
}
int hal_i2s_send(enum HAL_I2S_ID_T id, const uint8_t *value,
uint32_t value_len) {
uint32_t i = 0;
uint32_t reg_base;
reg_base = _i2s_get_reg_base(id);
for (i = 0; i < value_len; i += 4) {
while (!(i2sip_r_int_status(reg_base, 0) &
I2SIP_INT_STATUS_TX_FIFO_EMPTY_MASK))
;
i2sip_w_tx_left_fifo(reg_base, 0, value[i + 1] << 8 | value[i]);
i2sip_w_tx_right_fifo(reg_base, 0, value[i + 3] << 8 | value[i + 2]);
}
return 0;
}
uint8_t hal_i2s_recv(enum HAL_I2S_ID_T id, uint8_t *value, uint32_t value_len) {
// uint32_t reg_base;
// reg_base = _i2s_get_reg_base(id);
return 0;
}
//================================================================================
// I2S Packet Mode
//================================================================================
static bool i2s_pkt_opened = false;
int hal_i2s_packet_open(void) {
uint32_t reg_base;
uint8_t resolution = 0, word_sclk = 0;
if (i2s_pkt_opened) {
return 1;
}
word_sclk = I2SIP_CLK_CFG_WSS_VAL_32CYCLE;
resolution = I2SIP_CFG_WLEN_VAL_32BIT;
reg_base = _i2s_get_reg_base(HAL_I2S_ID_0);
hal_iomux_set_i2s0();
hal_cmu_i2s_clock_enable(HAL_I2S_ID_0);
hal_cmu_clock_enable(i2s_mod[HAL_I2S_ID_0].mod);
hal_cmu_clock_enable(i2s_mod[HAL_I2S_ID_0].apb);
hal_cmu_reset_clear(i2s_mod[HAL_I2S_ID_0].mod);
hal_cmu_reset_clear(i2s_mod[HAL_I2S_ID_0].apb);
i2sip_w_enable_i2sip(reg_base, HAL_I2S_YES);
for (int i = 0; i < I2S_CHAN_NUM; i++) {
i2sip_w_enable_rx_channel(reg_base, i, HAL_I2S_NO);
i2sip_w_enable_tx_channel(reg_base, i, HAL_I2S_NO);
}
hal_cmu_i2s_set_slave_mode(HAL_I2S_ID_0);
#ifndef CHIP_BEST1000
i2sip_w_enable_slave_mode(reg_base, HAL_I2S_YES);
#endif
/* word clock width : how many sclk work sclk count*/
/* sclk gate in word clock : how many sclk gate : fixed no gate */
i2sip_w_clk_cfg_reg(reg_base, (word_sclk << I2SIP_CLK_CFG_WSS_SHIFT) |
(I2SIP_CLK_CFG_SCLK_GATE_VAL_NO_GATE
<< I2SIP_CLK_CFG_SCLK_GATE_SHIFT));
i2sip_w_tx_resolution(reg_base, 0, resolution);
i2sip_w_tx_fifo_threshold(reg_base, 0, HAL_I2S_TX_FIFO_TRIGGER_LEVEL);
i2sip_w_rx_resolution(reg_base, 0, resolution);
i2sip_w_rx_fifo_threshold(reg_base, 0, HAL_I2S_RX_FIFO_TRIGGER_LEVEL);
i2s_pkt_opened = true;
return 0;
}
int hal_i2s_packet_close(void) {
uint32_t reg_base;
if (!i2s_pkt_opened) {
return 1;
}
reg_base = _i2s_get_reg_base(HAL_I2S_ID_0);
i2sip_w_enable_i2sip(reg_base, HAL_I2S_NO);
hal_cmu_reset_set(i2s_mod[HAL_I2S_ID_0].apb);
hal_cmu_reset_set(i2s_mod[HAL_I2S_ID_0].mod);
hal_cmu_clock_disable(i2s_mod[HAL_I2S_ID_0].apb);
hal_cmu_clock_disable(i2s_mod[HAL_I2S_ID_0].mod);
hal_cmu_i2s_clock_disable(HAL_I2S_ID_0);
i2s_pkt_opened = false;
return 0;
}
int hal_i2s_start_transfer(void) {
uint32_t reg_base;
if (!i2s_pkt_opened) {
return 1;
}
hal_cmu_reset_pulse(i2s_mod[HAL_I2S_ID_0].mod);
reg_base = _i2s_get_reg_base(HAL_I2S_ID_0);
i2sip_w_enable_tx_channel(reg_base, 0, HAL_I2S_YES);
i2sip_w_enable_tx_dma(reg_base, HAL_I2S_YES);
i2sip_w_enable_tx_block(reg_base, HAL_I2S_YES);
i2sip_w_enable_rx_channel(reg_base, 0, HAL_I2S_YES);
i2sip_w_enable_rx_dma(reg_base, HAL_I2S_YES);
i2sip_w_enable_rx_block(reg_base, HAL_I2S_YES);
return 0;
}
int hal_i2s_stop_transfer(void) {
uint32_t reg_base;
if (!i2s_pkt_opened) {
return 1;
}
reg_base = _i2s_get_reg_base(HAL_I2S_ID_0);
i2sip_w_enable_tx_block(reg_base, HAL_I2S_NO);
i2sip_w_enable_tx_channel(reg_base, 0, HAL_I2S_NO);
i2sip_w_enable_tx_dma(reg_base, HAL_I2S_NO);
i2sip_w_tx_fifo_reset(reg_base, 0);
i2sip_w_enable_rx_block(reg_base, HAL_I2S_NO);
i2sip_w_enable_rx_channel(reg_base, 0, HAL_I2S_NO);
i2sip_w_enable_rx_dma(reg_base, HAL_I2S_NO);
i2sip_w_rx_fifo_reset(reg_base, 0);
return 0;
}
void hal_i2s_tx_sync_enable(enum HAL_I2S_SYNC_TYPE_T type) {
uint32_t reg_base = _i2s_get_reg_base(HAL_I2S_ID_0);
i2sip_w_tx_sync_enable(reg_base, type);
}
void hal_i2s_tx_sync_disable(void) {
uint32_t reg_base = _i2s_get_reg_base(HAL_I2S_ID_0);
i2sip_w_tx_sync_disable(reg_base);
}
void hal_i2s_rx_sync_enable(enum HAL_I2S_SYNC_TYPE_T type) {
uint32_t reg_base = _i2s_get_reg_base(HAL_I2S_ID_0);
i2sip_w_rx_sync_enable(reg_base, type);
}
void hal_i2s_rx_sync_disable(void) {
uint32_t reg_base = _i2s_get_reg_base(HAL_I2S_ID_0);
i2sip_w_rx_sync_disable(reg_base);
}
void hal_i2s_clk_sync_enable(enum HAL_I2S_SYNC_TYPE_T type) {
uint32_t reg_base = _i2s_get_reg_base(HAL_I2S_ID_0);
i2sip_w_clk_sync_enable(reg_base, type);
}
void hal_i2s_clk_sync_disable(void) {
uint32_t reg_base = _i2s_get_reg_base(HAL_I2S_ID_0);
i2sip_w_clk_sync_disable(reg_base);
}