fix spdif for s3 and remove one override

SPDIF on esp32 was partly incorrect due to word ordering and required i2s_hal override. This is not needed anymore as the "mistery" of SPDIF hack has been properly sorted out
This commit is contained in:
philippe44
2023-08-30 22:00:05 -07:00
parent e9ccd8eef7
commit 55053d5941
4 changed files with 35 additions and 296 deletions

View File

@@ -1,6 +1,6 @@
if(IDF_TARGET STREQUAL esp32 AND IDF_VERSION_MAJOR EQUAL 4 AND IDF_VERSION_MINOR LESS 4)
set(lib_dir ${build_dir}/esp-idf)
set(driver esp32/i2s.c esp32/i2s_hal.c)
set(driver esp32/i2s.c)
string(REPLACE ".c" ".c.obj" driver_obj "${driver}")
idf_component_register( SRCS ${driver}
@@ -20,5 +20,5 @@ if(IDF_TARGET STREQUAL esp32 AND IDF_VERSION_MAJOR EQUAL 4 AND IDF_VERSION_MINOR
VERBATIM
)
else()
message(STATUS "==> NO I2S LIBRARY OVERRIDE, SPDIF MIGHT NOT WORK <==")
message(STATUS "==> NO OVERRIDE <==")
endif()

View File

@@ -1,275 +0,0 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The HAL layer for I2S (common part)
#include "soc/soc.h"
#include "soc/soc_caps.h"
#include "hal/i2s_hal.h"
#define I2S_TX_PDM_FP_DEF 960 // Set to the recommended value(960) in TRM
#define I2S_RX_PDM_DSR_DEF 0
void i2s_hal_set_tx_mode(i2s_hal_context_t *hal, i2s_channel_t ch, i2s_bits_per_sample_t bits)
{
if (bits <= I2S_BITS_PER_SAMPLE_16BIT) {
i2s_ll_set_tx_fifo_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1);
} else {
i2s_ll_set_tx_fifo_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 2 : 3);
}
i2s_ll_set_tx_chan_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1);
#if SOC_I2S_SUPPORTS_DMA_EQUAL
i2s_ll_set_tx_dma_equal(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1);
#endif
}
void i2s_hal_set_rx_mode(i2s_hal_context_t *hal, i2s_channel_t ch, i2s_bits_per_sample_t bits)
{
if (bits <= I2S_BITS_PER_SAMPLE_16BIT) {
i2s_ll_set_rx_fifo_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1);
} else {
i2s_ll_set_rx_fifo_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 2 : 3);
}
i2s_ll_set_rx_chan_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1);
#if SOC_I2S_SUPPORTS_DMA_EQUAL
i2s_ll_set_rx_dma_equal(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1);
#endif
}
void i2s_hal_set_in_link(i2s_hal_context_t *hal, uint32_t bytes_num, uint32_t addr)
{
i2s_ll_set_in_link_addr(hal->dev, addr);
i2s_ll_set_rx_eof_num(hal->dev, bytes_num);
}
#if SOC_I2S_SUPPORTS_PDM
void i2s_hal_tx_pdm_cfg(i2s_hal_context_t *hal, uint32_t fp, uint32_t fs)
{
i2s_ll_tx_pdm_cfg(hal->dev, fp, fs);
}
void i2s_hal_get_tx_pdm(i2s_hal_context_t *hal, uint32_t *fp, uint32_t *fs)
{
i2s_ll_get_tx_pdm(hal->dev, fp, fs);
}
void i2s_hal_rx_pdm_cfg(i2s_hal_context_t *hal, uint32_t dsr)
{
i2s_ll_rx_pdm_cfg(hal->dev, dsr);
}
void i2s_hal_get_rx_pdm(i2s_hal_context_t *hal, uint32_t *dsr)
{
i2s_ll_get_rx_pdm(hal->dev, dsr);
}
#endif
void i2s_hal_set_clk_div(i2s_hal_context_t *hal, int div_num, int div_a, int div_b, int tx_bck_div, int rx_bck_div)
{
i2s_ll_set_clkm_div_num(hal->dev, div_num);
i2s_ll_set_clkm_div_a(hal->dev, div_a);
i2s_ll_set_clkm_div_b(hal->dev, div_b);
i2s_ll_set_tx_bck_div_num(hal->dev, tx_bck_div);
i2s_ll_set_rx_bck_div_num(hal->dev, rx_bck_div);
}
void i2s_hal_set_tx_bits_mod(i2s_hal_context_t *hal, i2s_bits_per_sample_t bits)
{
i2s_ll_set_tx_bits_mod(hal->dev, bits);
}
void i2s_hal_set_rx_bits_mod(i2s_hal_context_t *hal, i2s_bits_per_sample_t bits)
{
i2s_ll_set_rx_bits_mod(hal->dev, bits);
}
void i2s_hal_reset(i2s_hal_context_t *hal)
{
// Reset I2S TX/RX module first, and then, reset DMA and FIFO.
i2s_ll_reset_tx(hal->dev);
i2s_ll_reset_rx(hal->dev);
i2s_ll_reset_dma_in(hal->dev);
i2s_ll_reset_dma_out(hal->dev);
i2s_ll_reset_rx_fifo(hal->dev);
i2s_ll_reset_tx_fifo(hal->dev);
}
void i2s_hal_start_tx(i2s_hal_context_t *hal)
{
i2s_ll_start_out_link(hal->dev);
i2s_ll_start_tx(hal->dev);
}
void i2s_hal_start_rx(i2s_hal_context_t *hal)
{
i2s_ll_start_in_link(hal->dev);
i2s_ll_start_rx(hal->dev);
}
void i2s_hal_stop_tx(i2s_hal_context_t *hal)
{
i2s_ll_stop_out_link(hal->dev);
i2s_ll_stop_tx(hal->dev);
}
void i2s_hal_stop_rx(i2s_hal_context_t *hal)
{
i2s_ll_stop_in_link(hal->dev);
i2s_ll_stop_rx(hal->dev);
}
void i2s_hal_format_config(i2s_hal_context_t *hal, const i2s_config_t *i2s_config)
{
switch (i2s_config->communication_format) {
case I2S_COMM_FORMAT_STAND_MSB:
if (i2s_config->mode & I2S_MODE_TX) {
i2s_ll_set_tx_format_msb_align(hal->dev);
}
if (i2s_config->mode & I2S_MODE_RX) {
i2s_ll_set_rx_format_msb_align(hal->dev);
}
break;
case I2S_COMM_FORMAT_STAND_PCM_SHORT:
if (i2s_config->mode & I2S_MODE_TX) {
i2s_ll_set_tx_pcm_long(hal->dev);
}
if (i2s_config->mode & I2S_MODE_RX) {
i2s_ll_set_rx_pcm_long(hal->dev);
}
break;
case I2S_COMM_FORMAT_STAND_PCM_LONG:
if (i2s_config->mode & I2S_MODE_TX) {
i2s_ll_set_tx_pcm_short(hal->dev);
}
if (i2s_config->mode & I2S_MODE_RX) {
i2s_ll_set_rx_pcm_short(hal->dev);
}
break;
default: //I2S_COMM_FORMAT_STAND_I2S
if (i2s_config->mode & I2S_MODE_TX) {
i2s_ll_set_tx_format_philip(hal->dev);
}
if (i2s_config->mode & I2S_MODE_RX) {
i2s_ll_set_rx_format_philip(hal->dev);
}
break;
}
}
void i2s_hal_config_param(i2s_hal_context_t *hal, const i2s_config_t *i2s_config)
{
//reset i2s
i2s_ll_reset_tx(hal->dev);
i2s_ll_reset_rx(hal->dev);
//reset dma
i2s_ll_reset_dma_in(hal->dev);
i2s_ll_reset_dma_out(hal->dev);
i2s_ll_enable_dma(hal->dev);
i2s_ll_set_lcd_en(hal->dev, 0);
i2s_ll_set_camera_en(hal->dev, 0);
i2s_ll_set_dscr_en(hal->dev, 0);
i2s_ll_set_tx_chan_mod(hal->dev, i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1)); // 0-two channel;1-right;2-left;3-righ;4-left
i2s_ll_set_tx_fifo_mod(hal->dev, i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1); // 0-right&left channel;1-one channel
i2s_ll_set_tx_mono(hal->dev, 0);
i2s_ll_set_rx_chan_mod(hal->dev, i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1)); // 0-two channel;1-right;2-left;3-righ;4-left
i2s_ll_set_rx_fifo_mod(hal->dev, i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1); // 0-right&left channel;1-one channel
i2s_ll_set_rx_mono(hal->dev, 0);
i2s_ll_set_dscr_en(hal->dev, 1); //connect dma to fifo
i2s_ll_stop_tx(hal->dev);
i2s_ll_stop_rx(hal->dev);
if (i2s_config->mode & I2S_MODE_TX) {
int order = i2s_config->bits_per_sample == 32 ? 0 : 1;
i2s_ll_set_tx_msb_right(hal->dev, order);
i2s_ll_set_tx_right_first(hal->dev, ~order);
i2s_ll_set_tx_slave_mod(hal->dev, 0); // Master
i2s_ll_set_tx_fifo_mod_force_en(hal->dev, 1);
if (i2s_config->mode & I2S_MODE_SLAVE) {
i2s_ll_set_tx_slave_mod(hal->dev, 1); //TX Slave
}
}
if (i2s_config->mode & I2S_MODE_RX) {
i2s_ll_set_rx_msb_right(hal->dev, 0);
i2s_ll_set_rx_right_first(hal->dev, 0);
i2s_ll_set_rx_slave_mod(hal->dev, 0); // Master
i2s_ll_set_rx_fifo_mod_force_en(hal->dev, 1);
if (i2s_config->mode & I2S_MODE_SLAVE) {
i2s_ll_set_rx_slave_mod(hal->dev, 1); //RX Slave
}
}
#if SOC_I2S_SUPPORTS_PDM
if (!(i2s_config->mode & I2S_MODE_PDM)) {
i2s_ll_set_rx_pdm_en(hal->dev, 0);
i2s_ll_set_tx_pdm_en(hal->dev, 0);
} else {
if (i2s_config->mode & I2S_MODE_TX) {
i2s_ll_tx_pdm_cfg(hal->dev, I2S_TX_PDM_FP_DEF, i2s_config->sample_rate/100);
}
if(i2s_config->mode & I2S_MODE_RX) {
i2s_ll_rx_pdm_cfg(hal->dev, I2S_RX_PDM_DSR_DEF);
}
// PDM mode have nothing to do with communication format configuration.
return;
}
#endif
#if SOC_I2S_SUPPORTS_ADC_DAC
if (i2s_config->mode & (I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN)) {
if (i2s_config->mode & I2S_MODE_DAC_BUILT_IN) {
i2s_ll_build_in_dac_ena(hal->dev);
}
if (i2s_config->mode & I2S_MODE_ADC_BUILT_IN) {
i2s_ll_build_in_adc_ena(hal->dev);
i2s_ll_set_rx_chan_mod(hal->dev, 1);
i2s_ll_set_rx_fifo_mod(hal->dev, 1);
i2s_ll_set_rx_mono(hal->dev, 0);
}
// Buildin ADC and DAC have nothing to do with communication format configuration.
return;
}
#endif
i2s_hal_format_config(hal, i2s_config);
}
void i2s_hal_enable_master_mode(i2s_hal_context_t *hal)
{
i2s_ll_set_tx_slave_mod(hal->dev, 0); //MASTER Slave
i2s_ll_set_rx_slave_mod(hal->dev, 1); //RX Slave
}
void i2s_hal_enable_slave_mode(i2s_hal_context_t *hal)
{
i2s_ll_set_tx_slave_mod(hal->dev, 1); //TX Slave
i2s_ll_set_rx_slave_mod(hal->dev, 1); //RX Slave
}
void i2s_hal_init(i2s_hal_context_t *hal, int i2s_num)
{
//Get hardware instance.
hal->dev = I2S_LL_GET_HW(i2s_num);
}

View File

@@ -109,29 +109,36 @@ void equalizer_close(void) {
* change sample rate
*/
void equalizer_set_samplerate(uint32_t samplerate) {
#if BYTES_PER_FRAME == 4
if (equalizer.samplerate != samplerate) equalizer_close();
equalizer.samplerate = samplerate;
equalizer.update = true;
LOG_INFO("equalizer sample rate %u", samplerate);
#else
LOG_INFO("no equalizer with 32 bits samples");
#endif
}
/****************************************************************************************
* get volume update and recalculate loudness according to
*/
void equalizer_set_volume(unsigned left, unsigned right) {
#if BYTES_PER_FRAME == 4
equalizer.volume = (left + right) / 2;
// do classic dB conversion and scale it 0..100
if (equalizer.volume) equalizer.volume = log2(equalizer.volume);
equalizer.volume = equalizer.volume / 16.0 * 100.0;
calculate_loudness();
equalizer.update = true;
#endif
}
/****************************************************************************************
* change gains from LMS
*/
void equalizer_set_gain(int8_t *gain) {
#if BYTES_PER_FRAME == 4
char config[EQ_BANDS * 4 + 1] = { };
int n = 0;
@@ -145,12 +152,16 @@ void equalizer_set_gain(int8_t *gain) {
equalizer.update = true;
LOG_INFO("equalizer gain %s", config);
#else
LOG_INFO("no equalizer with 32 bits samples");
#endif
}
/****************************************************************************************
* change loudness from LMS
*/
void equalizer_set_loudness(uint8_t loudness) {
#if BYTES_PER_FRAME == 4
// update loudness gains as a factor of loudness and volume
equalizer.loudness = loudness / 100.0;
calculate_loudness();
@@ -161,12 +172,16 @@ void equalizer_set_loudness(uint8_t loudness) {
equalizer.update = true;
LOG_INFO("loudness %u", (unsigned) loudness);
#else
LOG_INFO("no equalizer with 32 bits samples");
#endif
}
/****************************************************************************************
* process equalizer
*/
void equalizer_process(uint8_t *buf, uint32_t bytes) {
#if BYTES_PER_FRAME == 4
// don't want to process with output locked, so take the small risk to miss one parametric update
if (equalizer.update) {
equalizer.update = false;
@@ -198,4 +213,5 @@ void equalizer_process(uint8_t *buf, uint32_t bytes) {
if (equalizer.handle) {
esp_equalizer_process(equalizer.handle, buf, bytes, equalizer.samplerate, 2);
}
#endif
}

View File

@@ -583,15 +583,11 @@ static void output_thread_i2s(void *arg) {
i2s_set_sample_rates(CONFIG_I2S_NUM, spdif.enabled ? i2s_config.sample_rate * 2 : i2s_config.sample_rate);
i2s_zero_dma_buffer(CONFIG_I2S_NUM);
#if BYTES_PER_FRAME == 4
equalizer_set_samplerate(output.current_sample_rate);
#endif
}
#if BYTES_PER_FRAME == 4
// run equalizer
equalizer_process(obuf, oframes * BYTES_PER_FRAME);
#endif
// we assume that here we have been able to entirely fill the DMA buffers
if (spdif.enabled) {
@@ -709,12 +705,16 @@ static const u16_t spdif_bmclookup[256] = { //biphase mark encoded values (least
/*
SPDIF is supposed to be (before BMC encoding, from LSB to MSB)
PPPP AAAA SSSS SSSS SSSS SSSS SSSS VUCP
after BMC encoding, each bits becomes 2 hence this becomes a 64 bits word. The
the trick is to start not with a PPPP sequence but with an VUCP sequence to that
the 16 bits samples are aligned with a BMC word boundary. Note that the LSB of the
audio is transmitted first (not the MSB) and that ESP32 libray sends R then L,
contrary to what seems to be usually done, so (dst) order had to be changed
0.... 1... 191.. 0
BLFMRF MLFWRF MLFWRF BLFMRF (B,M,W=preamble-4, L/R=left/Right-24, F=Flags-4)
each xLF pattern is 32 bits
PPPP AAAA SSSS SSSS SSSS SSSS SSSS VUCP (P=preamble, A=auxiliary, S=sample-20bits, V=valid, U=user data, C=channel status, P=parity)
After BMC encoding, each bit becomes 2 hence this becomes a 64 bits word. The parity
is fixed by changing AAAA bits so that VUPC does not change. Then then trick is to
start not with a PPPP sequence but with an VUCP sequence to that the 16 bits samples
are aligned with a BMC word boundary. Input buffer is left first => LRLR...
The I2S interface must output first the B/M/W preamble which means that second
32 bits words must be first and so must be marked right channel.
*/
static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count) {
register u16_t hi, lo, aux;
@@ -729,7 +729,6 @@ static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, s
// invert if last preceeding bit is 1
lo ^= ~((s16_t)hi) >> 16;
// first 16 bits
*dst++ = ((u32_t)lo << 16) | hi;
aux = 0xb333 ^ (((u32_t)((s16_t)lo)) >> 17);
#else
hi = spdif_bmclookup[(u8_t)(*src >> 24)];
@@ -738,39 +737,38 @@ static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, s
// invert if last preceeding bit is 1
lo ^= ~((s16_t)hi) >> 16;
// first 16 bits
*dst++ = ((u32_t)lo << 16) | hi;
// we use 20 bits samples as we need to force parity
aux = spdif_bmclookup[(u8_t)(*src++ >> 12)];
aux = (u8_t) (aux ^ (~((s16_t)lo) >> 16));
aux |= (0xb3 ^ (((u16_t)((s8_t)aux)) >> 9)) << 8;
#endif
// VUCP-Bits: Valid, Subcode, Channelstatus, Parity = 0
// As parity is always 0, we can use fixed preambles
// set special preamble every 192 iteration
if (++cnt > 191) {
*dst++ = VUCP | (PREAMBLE_B << 16 ) | aux; //special preamble for one of 192 frames
cnt = 0;
} else {
*dst++ = VUCP | (PREAMBLE_M << 16) | aux;
}
// now write sample's 16 low bits
*dst++ = ((u32_t)lo << 16) | hi;
// then do right channel, no need to check PREAMBLE_B
#if BYTES_PER_FRAME == 4
hi = spdif_bmclookup[(u8_t)(*src >> 8)];
lo = spdif_bmclookup[(u8_t) *src++];
lo ^= ~((s16_t)hi) >> 16;
*dst++ = ((u32_t)lo << 16) | hi;
aux = 0xb333 ^ (((u32_t)((s16_t)lo)) >> 17);
#else
hi = spdif_bmclookup[(u8_t)(*src >> 24)];
lo = spdif_bmclookup[(u8_t)(*src >> 16)];
lo ^= ~((s16_t)hi) >> 16;
*dst++ = ((u32_t)lo << 16) | hi;
aux = spdif_bmclookup[(u8_t)(*src++ >> 12)];
aux = (u8_t) (aux ^ (~((s16_t)lo) >> 16));
aux |= (0xb3 ^ (((u16_t)((s8_t)aux)) >> 9)) << 8;
#endif
*dst++ = VUCP | (PREAMBLE_W << 16) | aux;
*dst++ = ((u32_t)lo << 16) | hi;
}
*count = cnt;