pinebuds/platform/hal/hal_spdif.c

422 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