dca92cf01f
As we will never get their FGPA source code. Zero loss.
421 lines
12 KiB
C
421 lines
12 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.
|
|
*
|
|
****************************************************************************/
|
|
#ifdef CHIP_HAS_SPDIF
|
|
|
|
#include "hal_spdif.h"
|
|
#include "analog.h"
|
|
#include "cmsis.h"
|
|
#include "hal_iomux.h"
|
|
#include "hal_spdifip.h"
|
|
#include "hal_timer.h"
|
|
#include "hal_trace.h"
|
|
#include "plat_addr_map.h"
|
|
#include "plat_types.h"
|
|
#include "reg_spdifip.h"
|
|
|
|
// #define SPDIF_CLOCK_SOURCE 240000000
|
|
// #define SPDIF_CLOCK_SOURCE 22579200
|
|
// #define SPDIF_CLOCK_SOURCE 48000000
|
|
#define SPDIF_CLOCK_SOURCE 3072000
|
|
|
|
// #define SPDIF_CLOCK_SOURCE 76800000
|
|
// #define SPDIF_CLOCK_SOURCE 84672000
|
|
|
|
// Trigger DMA request when TX-FIFO count <= threshold
|
|
#define HAL_SPDIF_TX_FIFO_TRIGGER_LEVEL (SPDIFIP_FIFO_DEPTH / 2)
|
|
// Trigger DMA request when RX-FIFO count >= threshold
|
|
#define HAL_SPDIF_RX_FIFO_TRIGGER_LEVEL (SPDIFIP_FIFO_DEPTH / 2)
|
|
|
|
#define HAL_SPDIF_YES 1
|
|
#define HAL_SPDIF_NO 0
|
|
|
|
#ifdef CHIP_BEST2000
|
|
#define SPDIF_CMU_DIV CODEC_CMU_DIV
|
|
#else
|
|
#define SPDIF_CMU_DIV CODEC_PLL_DIV
|
|
#endif
|
|
|
|
enum HAL_SPDIF_STATUS_T {
|
|
HAL_SPDIF_STATUS_NULL,
|
|
HAL_SPDIF_STATUS_OPENED,
|
|
HAL_SPDIF_STATUS_STARTED,
|
|
};
|
|
|
|
struct HAL_SPDIF_MOD_NAME_T {
|
|
enum HAL_CMU_MOD_ID_T mod;
|
|
enum HAL_CMU_MOD_ID_T apb;
|
|
};
|
|
|
|
struct HAL_SPDIF_MOD_NAME_T spdif_mod[HAL_SPDIF_ID_QTY] = {
|
|
{
|
|
.mod = HAL_CMU_MOD_O_SPDIF0,
|
|
.apb = HAL_CMU_MOD_P_SPDIF0,
|
|
},
|
|
#if (CHIP_HAS_SPDIF > 1)
|
|
{
|
|
.mod = HAL_CMU_MOD_O_SPDIF1,
|
|
.apb = HAL_CMU_MOD_P_SPDIF1,
|
|
},
|
|
#endif
|
|
};
|
|
|
|
static const char *const invalid_id = "Invalid SPDIF ID: %d\n";
|
|
// static const char * const invalid_ch = "Invalid SPDIF CH: %d\n";
|
|
|
|
struct SPDIF_SAMPLE_RATE_T {
|
|
enum AUD_SAMPRATE_T sample_rate;
|
|
uint32_t codec_freq;
|
|
uint8_t codec_div;
|
|
uint8_t pcm_div;
|
|
uint8_t tx_ratio;
|
|
};
|
|
|
|
// SAMPLE_RATE * 128 = PLL_nominal / PCM_DIV / TX_RATIO
|
|
static const struct SPDIF_SAMPLE_RATE_T spdif_sample_rate[] = {
|
|
#if defined(CHIP_BEST1000) && defined(AUD_PLL_DOUBLE)
|
|
{AUD_SAMPRATE_96000, CODEC_FREQ_48K_SERIES * 2, CODEC_PLL_DIV,
|
|
SPDIF_CMU_DIV, 4},
|
|
{AUD_SAMPRATE_192000, CODEC_FREQ_48K_SERIES * 2, CODEC_PLL_DIV,
|
|
SPDIF_CMU_DIV, 2},
|
|
{AUD_SAMPRATE_384000, CODEC_FREQ_48K_SERIES * 2, CODEC_PLL_DIV,
|
|
SPDIF_CMU_DIV, 1},
|
|
#else
|
|
{AUD_SAMPRATE_8000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, SPDIF_CMU_DIV,
|
|
24},
|
|
{AUD_SAMPRATE_16000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, SPDIF_CMU_DIV,
|
|
12},
|
|
{AUD_SAMPRATE_22050, CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, SPDIF_CMU_DIV,
|
|
8},
|
|
{AUD_SAMPRATE_24000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, SPDIF_CMU_DIV,
|
|
8},
|
|
{AUD_SAMPRATE_44100, CODEC_FREQ_44_1K_SERIES, CODEC_PLL_DIV, SPDIF_CMU_DIV,
|
|
4},
|
|
{AUD_SAMPRATE_48000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, SPDIF_CMU_DIV,
|
|
4},
|
|
{AUD_SAMPRATE_96000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, SPDIF_CMU_DIV,
|
|
2},
|
|
{AUD_SAMPRATE_192000, CODEC_FREQ_48K_SERIES, CODEC_PLL_DIV, SPDIF_CMU_DIV,
|
|
1},
|
|
#endif
|
|
};
|
|
|
|
#if (CHIP_HAS_SPDIF > 1)
|
|
static uint8_t spdif_map;
|
|
STATIC_ASSERT(HAL_SPDIF_ID_QTY <= sizeof(spdif_map) * 8, "Too many SPDIF IDs");
|
|
#endif
|
|
|
|
static enum HAL_SPDIF_STATUS_T spdif_status[HAL_SPDIF_ID_QTY][AUD_STREAM_NUM];
|
|
static bool spdif_dma[HAL_SPDIF_ID_QTY][AUD_STREAM_NUM];
|
|
|
|
static inline uint32_t _spdif_get_reg_base(enum HAL_SPDIF_ID_T id) {
|
|
ASSERT(id < HAL_SPDIF_ID_QTY, invalid_id, id);
|
|
|
|
switch (id) {
|
|
case HAL_SPDIF_ID_0:
|
|
default:
|
|
return SPDIF0_BASE;
|
|
break;
|
|
#if (CHIP_HAS_SPDIF > 1)
|
|
case HAL_SPDIF_ID_1:
|
|
return SPDIF1_BASE;
|
|
break;
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int hal_spdif_open(enum HAL_SPDIF_ID_T id, enum AUD_STREAM_T stream) {
|
|
uint32_t reg_base;
|
|
|
|
reg_base = _spdif_get_reg_base(id);
|
|
|
|
if (spdif_status[id][stream] != HAL_SPDIF_STATUS_NULL) {
|
|
TRACE(2, "Invalid SPDIF opening status for stream %d: %d", stream,
|
|
spdif_status[id][stream]);
|
|
return 1;
|
|
}
|
|
|
|
if (spdif_status[id][AUD_STREAM_PLAYBACK] == HAL_SPDIF_STATUS_NULL &&
|
|
spdif_status[id][AUD_STREAM_CAPTURE] == HAL_SPDIF_STATUS_NULL) {
|
|
#ifndef SIMU
|
|
bool cfg_pll = true;
|
|
int i;
|
|
|
|
for (i = HAL_SPDIF_ID_0; i < HAL_SPDIF_ID_QTY; i++) {
|
|
if (spdif_status[i][AUD_STREAM_PLAYBACK] != HAL_SPDIF_STATUS_NULL ||
|
|
spdif_status[i][AUD_STREAM_CAPTURE] != HAL_SPDIF_STATUS_NULL) {
|
|
cfg_pll = false;
|
|
break;
|
|
}
|
|
}
|
|
if (cfg_pll) {
|
|
analog_aud_pll_open(ANA_AUD_PLL_USER_SPDIF);
|
|
}
|
|
#endif
|
|
|
|
#if (CHIP_HAS_SPDIF > 1)
|
|
if (id == HAL_SPDIF_ID_1) {
|
|
hal_iomux_set_spdif1();
|
|
} else
|
|
#endif
|
|
{
|
|
hal_iomux_set_spdif0();
|
|
}
|
|
hal_cmu_spdif_clock_enable(id);
|
|
hal_cmu_clock_enable(spdif_mod[id].mod);
|
|
hal_cmu_clock_enable(spdif_mod[id].apb);
|
|
hal_cmu_reset_clear(spdif_mod[id].mod);
|
|
hal_cmu_reset_clear(spdif_mod[id].apb);
|
|
|
|
spdifip_w_enable_spdifip(reg_base, HAL_SPDIF_YES);
|
|
}
|
|
|
|
spdif_dma[id][stream] = false;
|
|
spdif_status[id][stream] = HAL_SPDIF_STATUS_OPENED;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_spdif_close(enum HAL_SPDIF_ID_T id, enum AUD_STREAM_T stream) {
|
|
uint32_t reg_base;
|
|
|
|
if (id >= HAL_SPDIF_ID_QTY) {
|
|
return 1;
|
|
}
|
|
|
|
if (spdif_status[id][stream] != HAL_SPDIF_STATUS_OPENED) {
|
|
TRACE(2, "Invalid SPDIF closing status for stream %d: %d", stream,
|
|
spdif_status[id][stream]);
|
|
return 1;
|
|
}
|
|
|
|
spdif_status[id][stream] = HAL_SPDIF_STATUS_NULL;
|
|
|
|
if (spdif_status[id][AUD_STREAM_PLAYBACK] == HAL_SPDIF_STATUS_NULL &&
|
|
spdif_status[id][AUD_STREAM_CAPTURE] == HAL_SPDIF_STATUS_NULL) {
|
|
reg_base = _spdif_get_reg_base(id);
|
|
|
|
spdifip_w_enable_spdifip(reg_base, HAL_SPDIF_NO);
|
|
|
|
hal_cmu_spdif_set_div(id, 0x1FFF + 2);
|
|
hal_cmu_reset_set(spdif_mod[id].apb);
|
|
hal_cmu_reset_set(spdif_mod[id].mod);
|
|
hal_cmu_clock_disable(spdif_mod[id].apb);
|
|
hal_cmu_clock_disable(spdif_mod[id].mod);
|
|
hal_cmu_spdif_clock_disable(id);
|
|
|
|
#ifndef SIMU
|
|
bool cfg_pll = true;
|
|
int i;
|
|
|
|
for (i = HAL_SPDIF_ID_0; i < HAL_SPDIF_ID_QTY; i++) {
|
|
if (spdif_status[i][AUD_STREAM_PLAYBACK] != HAL_SPDIF_STATUS_NULL ||
|
|
spdif_status[i][AUD_STREAM_CAPTURE] != HAL_SPDIF_STATUS_NULL) {
|
|
cfg_pll = false;
|
|
break;
|
|
}
|
|
}
|
|
if (cfg_pll) {
|
|
analog_aud_pll_close(ANA_AUD_PLL_USER_SPDIF);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_spdif_start_stream(enum HAL_SPDIF_ID_T id, enum AUD_STREAM_T stream) {
|
|
uint32_t reg_base;
|
|
uint32_t lock;
|
|
|
|
reg_base = _spdif_get_reg_base(id);
|
|
|
|
if (spdif_status[id][stream] != HAL_SPDIF_STATUS_OPENED) {
|
|
TRACE(2, "Invalid SPDIF starting status for stream %d: %d", stream,
|
|
spdif_status[id][stream]);
|
|
return 1;
|
|
}
|
|
|
|
if (stream == AUD_STREAM_PLAYBACK) {
|
|
lock = int_lock();
|
|
spdifip_w_enable_tx_channel0(reg_base, HAL_SPDIF_YES);
|
|
spdifip_w_enable_tx(reg_base, HAL_SPDIF_YES);
|
|
spdifip_w_tx_valid(reg_base, HAL_SPDIF_YES);
|
|
if (spdif_dma[id][stream]) {
|
|
spdifip_w_enable_tx_dma(reg_base, HAL_SPDIF_YES);
|
|
}
|
|
int_unlock(lock);
|
|
} else {
|
|
if (spdif_dma[id][stream]) {
|
|
spdifip_w_enable_rx_dma(reg_base, HAL_SPDIF_YES);
|
|
}
|
|
spdifip_w_enable_rx_channel0(reg_base, HAL_SPDIF_YES);
|
|
spdifip_w_enable_rx(reg_base, HAL_SPDIF_YES);
|
|
spdifip_w_sample_en(reg_base, HAL_SPDIF_YES);
|
|
}
|
|
|
|
spdif_status[id][stream] = HAL_SPDIF_STATUS_STARTED;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_spdif_stop_stream(enum HAL_SPDIF_ID_T id, enum AUD_STREAM_T stream) {
|
|
uint32_t reg_base;
|
|
|
|
reg_base = _spdif_get_reg_base(id);
|
|
|
|
if (spdif_status[id][stream] != HAL_SPDIF_STATUS_STARTED) {
|
|
TRACE(2, "Invalid SPDIF stopping status for stream %d: %d", stream,
|
|
spdif_status[id][stream]);
|
|
return 1;
|
|
}
|
|
|
|
spdif_status[id][stream] = HAL_SPDIF_STATUS_OPENED;
|
|
|
|
if (stream == AUD_STREAM_PLAYBACK) {
|
|
spdifip_w_enable_tx(reg_base, HAL_SPDIF_NO);
|
|
spdifip_w_enable_tx_channel0(reg_base, HAL_SPDIF_NO);
|
|
spdifip_w_enable_tx_dma(reg_base, HAL_SPDIF_NO);
|
|
spdifip_w_tx_fifo_reset(reg_base);
|
|
} else {
|
|
spdifip_w_enable_rx(reg_base, HAL_SPDIF_NO);
|
|
spdifip_w_enable_rx_channel0(reg_base, HAL_SPDIF_NO);
|
|
spdifip_w_enable_rx_dma(reg_base, HAL_SPDIF_NO);
|
|
spdifip_w_rx_fifo_reset(reg_base);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_spdif_setup_stream(enum HAL_SPDIF_ID_T id, enum AUD_STREAM_T stream,
|
|
struct HAL_SPDIF_CONFIG_T *cfg) {
|
|
uint8_t i;
|
|
uint32_t reg_base;
|
|
uint8_t fmt;
|
|
|
|
reg_base = _spdif_get_reg_base(id);
|
|
|
|
if (spdif_status[id][stream] != HAL_SPDIF_STATUS_OPENED) {
|
|
TRACE(2, "Invalid SPDIF setup status for stream %d: %d", stream,
|
|
spdif_status[id][stream]);
|
|
return 1;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(spdif_sample_rate); i++) {
|
|
if (spdif_sample_rate[i].sample_rate == cfg->sample_rate) {
|
|
break;
|
|
}
|
|
}
|
|
ASSERT(i < ARRAY_SIZE(spdif_sample_rate), "%s: Invalid spdif sample rate: %d",
|
|
__func__, cfg->sample_rate);
|
|
|
|
TRACE(3, "[%s] stream=%d sample_rate=%d", __func__, stream, cfg->sample_rate);
|
|
|
|
#ifndef SIMU
|
|
analog_aud_freq_pll_config(spdif_sample_rate[i].codec_freq,
|
|
spdif_sample_rate[i].codec_div);
|
|
#ifdef CHIP_BEST2000
|
|
analog_aud_pll_set_dig_div(spdif_sample_rate[i].codec_div /
|
|
spdif_sample_rate[i].pcm_div);
|
|
#endif
|
|
#endif
|
|
// SPDIF module is working on 24.576M or 22.5792M
|
|
hal_cmu_spdif_set_div(id, spdif_sample_rate[i].pcm_div);
|
|
|
|
if ((stream == AUD_STREAM_PLAYBACK &&
|
|
spdif_status[id][AUD_STREAM_CAPTURE] == HAL_SPDIF_STATUS_NULL) ||
|
|
(stream == AUD_STREAM_CAPTURE &&
|
|
spdif_status[id][AUD_STREAM_PLAYBACK] == HAL_SPDIF_STATUS_NULL)) {
|
|
hal_cmu_reset_pulse(spdif_mod[id].mod);
|
|
}
|
|
|
|
spdif_dma[id][stream] = cfg->use_dma;
|
|
|
|
fmt = 0;
|
|
switch (cfg->bits) {
|
|
case AUD_BITS_16:
|
|
fmt = 0;
|
|
break;
|
|
// here, 32-bit is treated as 24-bit
|
|
case AUD_BITS_32:
|
|
case AUD_BITS_24:
|
|
fmt = 8;
|
|
break;
|
|
default:
|
|
ASSERT(0, "%s: invalid bits[%d]", __func__, cfg->bits);
|
|
}
|
|
|
|
if (stream == AUD_STREAM_PLAYBACK) {
|
|
spdifip_w_tx_ratio(reg_base, spdif_sample_rate[i].tx_ratio - 1);
|
|
spdifip_w_tx_format_cfg_reg(reg_base, fmt);
|
|
spdifip_w_tx_fifo_threshold(reg_base, HAL_SPDIF_TX_FIFO_TRIGGER_LEVEL);
|
|
} else {
|
|
spdifip_w_rx_format_cfg_reg(reg_base, fmt);
|
|
spdifip_w_rx_fifo_threshold(reg_base, HAL_SPDIF_RX_FIFO_TRIGGER_LEVEL);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_spdif_send(enum HAL_SPDIF_ID_T id, uint8_t *value, uint32_t value_len) {
|
|
uint32_t i = 0;
|
|
uint32_t reg_base;
|
|
|
|
reg_base = _spdif_get_reg_base(id);
|
|
|
|
for (i = 0; i < value_len; i += 4) {
|
|
while (!(spdifip_r_int_status(reg_base) &
|
|
SPDIFIP_INT_STATUS_TX_FIFO_EMPTY_MASK))
|
|
;
|
|
|
|
spdifip_w_tx_left_fifo(reg_base, value[i + 1] << 8 | value[i]);
|
|
spdifip_w_tx_right_fifo(reg_base, value[i + 3] << 8 | value[i + 2]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint8_t hal_spdif_recv(enum HAL_SPDIF_ID_T id, uint8_t *value,
|
|
uint32_t value_len) {
|
|
// uint32_t reg_base;
|
|
|
|
// reg_base = _spdif_get_reg_base(id);
|
|
return 0;
|
|
}
|
|
|
|
int hal_spdif_clock_out_enable(enum HAL_SPDIF_ID_T id, uint32_t div) {
|
|
if (id >= HAL_SPDIF_ID_QTY) {
|
|
return 1;
|
|
}
|
|
|
|
hal_cmu_spdif_clock_enable(id);
|
|
hal_cmu_spdif_set_div(id, div);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_spdif_clock_out_disable(enum HAL_SPDIF_ID_T id) {
|
|
if (id >= HAL_SPDIF_ID_QTY) {
|
|
return 1;
|
|
}
|
|
|
|
hal_cmu_spdif_set_div(id, 0x1FFF + 2);
|
|
hal_cmu_spdif_clock_disable(id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|