Files
luban-lite-t3e-pro/bsp/artinchip/hal/audio/hal_audio.c
刘可亮 0ef85b55da v1.1.0
2024-09-30 17:06:01 +08:00

536 lines
17 KiB
C

/*
* Copyright (c) 2022-2024, ArtInChip Technology Co., Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Authors: dwj <weijie.ding@artinchip.com>
*/
#include "hal_audio.h"
int hal_audio_init(aic_audio_ctrl *codec)
{
int ret;
codec->reg_base = AUDIO_BASE;
codec->irq_num = AUDIO_IRQn;
codec->clk_id = CLK_CODEC;
ret = hal_clk_set_freq(codec->clk_id, DEFAULT_AUDIO_FREQ);
if (ret)
hal_log_err("Audio init set freq error!\n");
ret = hal_clk_enable_deassertrst_iter(codec->clk_id);
if (ret)
hal_log_err("Audio init error!\n");
return ret;
}
int hal_audio_uninit(aic_audio_ctrl *codec)
{
int ret;
ret = hal_clk_disable_assertrst(codec->clk_id);
if (ret)
hal_log_err("Audio uninit error!\n");
return ret;
}
static int hal_audio_get_hw_rate(aic_audio_ctrl *codec, uint32_t samplerate)
{
switch (samplerate)
{
case 48000:
case 44100:
return 0;
case 32000:
return 1;
case 24000:
case 22050:
return 2;
case 16000:
return 3;
case 12000:
case 11025:
return 4;
case 8000:
return 5;
default:
return -EINVAL;
}
}
static uint32_t hal_audio_get_module_freq(aic_audio_ctrl *codec,
uint32_t samplerate)
{
uint32_t reg_val;
switch (samplerate)
{
case 48000:
case 32000:
case 24000:
case 16000:
case 12000:
case 8000:
/* Set playback module clock */
reg_val = readl(codec->reg_base + TX_PLAYBACK_CTRL_REG);
reg_val &= ~TX_CLK_MASK;
writel(reg_val, codec->reg_base + TX_PLAYBACK_CTRL_REG);
/* Set dmic module clock */
reg_val = readl(codec->reg_base + RX_DMIC_IF_CTRL_REG);
reg_val &= ~RX_DMIC_IF_RX_CLK_MASK;
writel(reg_val, codec->reg_base + RX_DMIC_IF_CTRL_REG);
/* Set amic module clock */
reg_val = readl(codec->reg_base + ADC_IF_CTRL_REG);
reg_val &= ~ADC_IF_CTRL_RX_CLK_FRE_MASK;
writel(reg_val, codec->reg_base + ADC_IF_CTRL_REG);
return 24576000;
case 44100:
case 22050:
case 11025:
/* Set playback module clock */
reg_val = readl(codec->reg_base + TX_PLAYBACK_CTRL_REG);
reg_val |= TX_CLK_22579KHZ;
writel(reg_val, codec->reg_base + TX_PLAYBACK_CTRL_REG);
/* set dmic module clock */
reg_val = readl(codec->reg_base + RX_DMIC_IF_CTRL_REG);
reg_val |= RX_DMIC_IF_RX_CLK_22579KHZ;
writel(reg_val, codec->reg_base + RX_DMIC_IF_CTRL_REG);
/* set amic module clock */
reg_val = readl(codec->reg_base + ADC_IF_CTRL_REG);
reg_val |= ADC_IF_CTRL_RX_CLK_22579KHZ;
writel(reg_val, codec->reg_base + ADC_IF_CTRL_REG);
return 22579200;
default:
return 0;
}
}
void hal_audio_set_samplerate(aic_audio_ctrl *codec, uint32_t samplerate)
{
int hw_rate;
uint32_t reg_val, module_freq;
unsigned int pclk_id;
hw_rate = hal_audio_get_hw_rate(codec, samplerate);
/* Set playback samplerate */
reg_val = readl(codec->reg_base + TX_PLAYBACK_CTRL_REG);
reg_val &= ~TX_FS_OUT_MASK;
reg_val |= TX_FS_OUT(hw_rate);
writel(reg_val, codec->reg_base + TX_PLAYBACK_CTRL_REG);
/* Set dmic samplerate */
reg_val = readl(codec->reg_base + RX_DMIC_IF_CTRL_REG);
reg_val &= ~RX_DMIC_IF_FS_IN_MASK;
reg_val |= RX_DMIC_IF_FS_IN(hw_rate);
writel(reg_val, codec->reg_base + RX_DMIC_IF_CTRL_REG);
/* Set amic samplerate */
reg_val = readl(codec->reg_base + ADC_IF_CTRL_REG);
reg_val &= ~ADC_IF_CTRL_FS_ADC_IN_MASK;
reg_val |= ADC_IF_CTRL_FS_ADC_IN(hw_rate);
writel(reg_val, codec->reg_base + ADC_IF_CTRL_REG);
module_freq = hal_audio_get_module_freq(codec, samplerate);
pclk_id = hal_clk_get_parent(codec->clk_id);
#ifdef AIC_AUDIO_DRV_V10
/* Set AudioCodec parent clock rate */
hal_clk_set_freq(pclk_id, module_freq * 20);
/* Set AudioCodec module clock rate */
hal_clk_set_rate(codec->clk_id, module_freq, module_freq * 20);
#elif defined(AIC_AUDIO_DRV_V11) || defined(AIC_AUDIO_DRV_V12)
/* Audio has the same frequency with AUDIO_SCLK */
hal_clk_set_freq(pclk_id, module_freq);
#endif
}
void hal_audio_set_playback_channel(aic_audio_ctrl *codec, uint32_t ch)
{
uint32_t reg_val;
if (ch == 1)
{
/* Enable TX ch0 */
reg_val = readl(codec->reg_base + TXFIFO_CTRL_REG);
reg_val &= ~TXFIFO_CH_MASK;
reg_val |= TXFIFO_CH0_EN;
writel(reg_val, codec->reg_base + TXFIFO_CTRL_REG);
}
else if (ch == 2)
{
/* Enable TX ch0 ch1 */
reg_val = readl(codec->reg_base + TXFIFO_CTRL_REG);
reg_val &= ~TXFIFO_CH_MASK;
reg_val |= (TXFIFO_CH0_EN | TXFIFO_CH1_EN);
writel(reg_val, codec->reg_base + TXFIFO_CTRL_REG);
}
}
void hal_audio_set_playback_by_spk0(aic_audio_ctrl *codec)
{
uint32_t reg_val;
/* Enable DVC3 */
reg_val = readl(codec->reg_base + TX_DVC_3_4_CTRL_REG);
reg_val &= ~TX_DVC3_MASK;
reg_val |= TX_DVC3_EN;
writel(reg_val, codec->reg_base + TX_DVC_3_4_CTRL_REG);
/* Enable IF0 */
reg_val = readl(codec->reg_base + TX_PLAYBACK_CTRL_REG);
reg_val &= ~TX_IF_CH0_MASK;
reg_val |= TX_PLAYBACK_IF_EN | TX_IF_CH0_EN;
writel(reg_val, codec->reg_base + TX_PLAYBACK_CTRL_REG);
/* Enable SDM0 */
reg_val = readl(codec->reg_base + TX_SDM_CTRL_REG);
reg_val &= ~TX_SDM_CH0_MASK;
reg_val |= TX_SDM_CH0_EN;
writel(reg_val, codec->reg_base + TX_SDM_CTRL_REG);
/* Configure MIXER audio path */
reg_val = readl(codec->reg_base + TX_MIXER_CTRL_REG);
reg_val &= ~TX_MIXER0_PATH_MASK;
reg_val |= TX_MIXER0_AUDOUTL_SEL | TX_MIXER0_AUDOUTR_SEL;
writel(reg_val, codec->reg_base + TX_MIXER_CTRL_REG);
/* Configure PWM0 single output */
reg_val = readl(codec->reg_base + TX_PWM_CTRL_REG);
reg_val &= ~TX_PWM0_MASK;
#ifdef AIC_AUDIO_PDM_MODULATION_MODE
reg_val |= TX_PWM0_EN | TX_PDM0_EN;
#else
reg_val |= TX_PWM0_EN;
#endif
writel(reg_val, codec->reg_base + TX_PWM_CTRL_REG);
}
void hal_audio_set_playback_by_spk1(aic_audio_ctrl *codec)
{
uint32_t reg_val;
/* Enable DVC4 */
reg_val = readl(codec->reg_base + TX_DVC_3_4_CTRL_REG);
reg_val &= ~TX_DVC4_MASK;
reg_val |= TX_DVC4_EN;
writel(reg_val, codec->reg_base + TX_DVC_3_4_CTRL_REG);
/* Enable IF1 */
reg_val = readl(codec->reg_base + TX_PLAYBACK_CTRL_REG);
reg_val &= ~TX_IF_CH1_MASK;
reg_val |= TX_PLAYBACK_IF_EN | TX_IF_CH1_EN;
writel(reg_val, codec->reg_base + TX_PLAYBACK_CTRL_REG);
/* Enable SDM1 */
reg_val = readl(codec->reg_base + TX_SDM_CTRL_REG);
reg_val &= ~TX_SDM_CH1_MASK;
reg_val |= TX_SDM_CH1_EN;
writel(reg_val, codec->reg_base + TX_SDM_CTRL_REG);
/* Configure MIXER audio path */
reg_val = readl(codec->reg_base + TX_MIXER_CTRL_REG);
reg_val &= ~TX_MIXER1_PATH_MASK;
reg_val |= TX_MIXER1_AUDOUTL_SEL | TX_MIXER1_AUDOUTR_SEL;
writel(reg_val, codec->reg_base + TX_MIXER_CTRL_REG);
/* Configure PWM1 single output */
reg_val = readl(codec->reg_base + TX_PWM_CTRL_REG);
reg_val &= ~TX_PWM1_MASK;
#ifdef AIC_AUDIO_PDM_MODULATION_MODE
reg_val |= TX_PWM1_EN | TX_PDM1_EN;
#else
reg_val |= TX_PWM1_EN;
#endif
writel(reg_val, codec->reg_base + TX_PWM_CTRL_REG);
}
void hal_audio_set_dmic_channel(aic_audio_ctrl *codec, uint32_t ch)
{
uint32_t reg_val;
if (ch == 1)
{
/* Enable HPF1 */
reg_val = readl(codec->reg_base + RX_HPF_1_2_CTRL_REG);
reg_val &= ~RX_HPF_MASK;
reg_val |= RX_HPF1_EN;
writel(reg_val, codec->reg_base + RX_HPF_1_2_CTRL_REG);
/* Enable DF1 and DMIC interface*/
reg_val = readl(codec->reg_base + RX_DMIC_IF_CTRL_REG);
reg_val &= ~RX_DMIC_IF_DEC_EN_MASK;
reg_val |= RX_DMIC_IF_DEC1_FLT | RX_DMIC_IF_EN;
writel(reg_val, codec->reg_base + RX_DMIC_IF_CTRL_REG);
/* Enable DVC1 */
reg_val = readl(codec->reg_base + RX_DVC_1_2_CTRL_REG);
reg_val &= ~RX_DVC_MASK;
reg_val |= RX_DVC1_EN;
writel(reg_val, codec->reg_base + RX_DVC_1_2_CTRL_REG);
/* Enable ch0 */
reg_val = readl(codec->reg_base + DMIC_RXFIFO_CTRL_REG);
reg_val &= ~DMIC_RXFIFO_CH_MASK;
reg_val |= DMIC_RXFIFO_CH0_EN;
writel(reg_val, codec->reg_base + DMIC_RXFIFO_CTRL_REG);
}
else if (ch == 2)
{
/* Enable HPF1_2 */
reg_val = readl(codec->reg_base + RX_HPF_1_2_CTRL_REG);
reg_val &= ~RX_HPF_MASK;
reg_val |= RX_HPF1_EN | RX_HPF2_EN;
writel(reg_val, codec->reg_base + RX_HPF_1_2_CTRL_REG);
/* Enable DF1_2 and DMIC interface*/
reg_val = readl(codec->reg_base + RX_DMIC_IF_CTRL_REG);
reg_val &= ~RX_DMIC_IF_DEC_EN_MASK;
reg_val |= RX_DMIC_IF_DEC1_FLT | RX_DMIC_IF_DEC2_FLT | RX_DMIC_IF_EN;
writel(reg_val, codec->reg_base + RX_DMIC_IF_CTRL_REG);
/* Enable DVC1 DVC2 */
reg_val = readl(codec->reg_base + RX_DVC_1_2_CTRL_REG);
reg_val |= RX_DVC_MASK;
writel(reg_val, codec->reg_base + RX_DVC_1_2_CTRL_REG);
/* Enable ch0 ch1 */
reg_val = readl(codec->reg_base + DMIC_RXFIFO_CTRL_REG);
reg_val |= DMIC_RXFIFO_CH_MASK;
writel(reg_val, codec->reg_base + DMIC_RXFIFO_CTRL_REG);
}
}
void hal_audio_set_amic_channel(aic_audio_ctrl *codec)
{
uint32_t reg_val;
/* Enable HPF0 */
reg_val = readl(codec->reg_base + ADC_HPF0_CTRL_REG);
reg_val |= ADC_HPF0_CTRL_HPF0_EN;
writel(reg_val, codec->reg_base + ADC_HPF0_CTRL_REG);
/* Enable MBIAS PGA ADC */
reg_val = readl(codec->reg_base + ADC_CTL1_REG);
reg_val |= ADC_CTL1_ADC_EN | ADC_CTL1_PGA_EN | ADC_CTL1_MBIAS_EN;
writel(reg_val, codec->reg_base + ADC_CTL1_REG);
/* Enable DF0 */
reg_val = readl(codec->reg_base + ADC_IF_CTRL_REG);
reg_val |= ADC_IF_CTRL_EN_DEC0_MASK;
writel(reg_val, codec->reg_base + ADC_IF_CTRL_REG);
/* Enable DVC0 */
reg_val = readl(codec->reg_base + ADC_DVC0_CTRL_REG);
reg_val |= ADC_DVC0_CTRL_DVC0_EN;
writel(reg_val, codec->reg_base + ADC_DVC0_CTRL_REG);
/* Enable amic channel */
reg_val = readl(codec->reg_base + ADC_RXFIFO_CTRL_REG);
reg_val |= ADC_RXFIFO_EN;
writel(reg_val, codec->reg_base + ADC_RXFIFO_CTRL_REG);
/* Set PGA CTRL */
reg_val = readl(codec->reg_base + ADC_CTL2_REG);
reg_val |= ADC_CTL2_PGA_GAIN_SEL;
writel(reg_val, codec->reg_base + ADC_CTL2_REG);
}
static void dma_transfer_period_callback(void *arg)
{
struct aic_audio_transfer_info *info;
aic_audio_ctrl *codec;
info = (struct aic_audio_transfer_info *)arg;
if (info->transfer_type == AUDIO_TRANSFER_TYPE_TX)
{
codec = container_of(info, aic_audio_ctrl, tx_info);
if (codec->callback)
codec->callback(codec, (void *)AUDIO_TX_PERIOD_INT);
}
else if (info->transfer_type == AUDIO_TRANSFER_TYPE_DMIC)
{
codec = container_of(info, aic_audio_ctrl, dmic_info);
if (codec->callback)
codec->callback(codec, (void *)AUDIO_RX_DMIC_PERIOD_INT);
}
else if (info->transfer_type == AUDIO_TRANSFER_TYPE_AMIC)
{
codec = container_of(info, aic_audio_ctrl, amic_info);
if (codec->callback)
codec->callback(codec, (void *)AUDIO_RX_AMIC_PERIOD_INT);
}
}
void hal_audio_playback_start(aic_audio_ctrl *codec)
{
struct dma_slave_config config;
struct aic_audio_transfer_info *info;
config.direction = DMA_MEM_TO_DEV;
config.dst_addr = codec->reg_base + TXFIFO_DATA_REG;
config.slave_id = 14;
config.src_maxburst = 1;
config.dst_maxburst = 1;
/* AudioCodec only support 16bit sample width */
if (codec->config.channel == 2)
{
config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
}
else if (codec->config.channel == 1)
{
config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
config.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
}
info = &codec->tx_info;
info->transfer_type = AUDIO_TRANSFER_TYPE_TX;
info->dma_chan = hal_request_dma_chan();
if (!info->dma_chan) {
hal_log_err("playback request dma channel error\n");
return;
}
hal_dma_chan_register_cb(info->dma_chan, dma_transfer_period_callback,
(void *)info);
hal_dma_chan_config(info->dma_chan, &config);
/* Configure DMA transfer */
hal_dma_chan_prep_cyclic(info->dma_chan, (ulong)info->buf_info.buf,
info->buf_info.buf_len, info->buf_info.period_len,
DMA_MEM_TO_DEV);
hal_dma_chan_start(info->dma_chan);
hal_audio_disable_fade(codec);
/* flush TXFIFO */
hal_audio_flush_tx_fifo(codec);
/* Enable TX global */
hal_audio_enable_tx_global(codec);
/* Enable AUDOUT DRQ */
hal_audio_tx_enable_drq(codec);
}
void hal_audio_dmic_start(aic_audio_ctrl *codec)
{
struct dma_slave_config config;
struct aic_audio_transfer_info *info;
config.direction = DMA_DEV_TO_MEM;
config.src_addr = codec->reg_base + DMIC_RXFIFO_DATA_REG;
config.slave_id = 14;
config.src_maxburst = 1;
config.dst_maxburst = 1;
/* AudioCodec only support 16bit sample width */
if (codec->config.channel == 2)
{
config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
}
else if (codec->config.channel == 1)
{
config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
config.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
}
info = &codec->dmic_info;
info->transfer_type = AUDIO_TRANSFER_TYPE_DMIC;
info->dma_chan = hal_request_dma_chan();
if (!info->dma_chan) {
hal_log_err("dmic request dma channel error\n");
return;
}
hal_dma_chan_register_cb(info->dma_chan, dma_transfer_period_callback,
(void *)info);
hal_dma_chan_config(info->dma_chan, &config);
/* Configure DMA transfer */
hal_dma_chan_prep_cyclic(info->dma_chan, (ulong)info->buf_info.buf,
info->buf_info.buf_len, info->buf_info.period_len,
DMA_DEV_TO_MEM);
hal_dma_chan_start(info->dma_chan);
/* flush DMIC FIFO */
hal_audio_flush_dmic_fifo(codec);
/* Enable RX global */
hal_audio_enable_rx_global(codec);
/* Enable DMIC ADOUT SHIFT */
hal_audio_enable_dmic_adout_shift(codec);
/* Enable DMIC DRQ */
hal_audio_dmic_enable_drq(codec);
}
void hal_audio_amic_start(aic_audio_ctrl *codec)
{
struct dma_slave_config config;
struct aic_audio_transfer_info *info;
config.direction = DMA_DEV_TO_MEM;
config.src_addr = codec->reg_base + ADC_RXFIFO_DATA_REG;
config.slave_id = 15;
config.src_maxburst = 1;
config.dst_maxburst = 1;
/* amic only support mono */
config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
config.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
info = &codec->amic_info;
info->transfer_type = AUDIO_TRANSFER_TYPE_AMIC;
info->dma_chan = hal_request_dma_chan();
if (!info->dma_chan) {
hal_log_err("amic request dma channel error\n");
return;
}
hal_dma_chan_register_cb(info->dma_chan, dma_transfer_period_callback,
(void *)info);
hal_dma_chan_config(info->dma_chan, &config);
/* Configure DMA transfer */
hal_dma_chan_prep_cyclic(info->dma_chan, (ulong)info->buf_info.buf,
info->buf_info.buf_len, info->buf_info.period_len,
DMA_DEV_TO_MEM);
hal_dma_chan_start(info->dma_chan);
/* flush AMIC FIFO */
hal_audio_flush_amic_fifo(codec);
/* Enable RX global */
hal_audio_enable_rx_global(codec);
/* Enable DMIC DRQ */
hal_audio_amic_enable_drq(codec);
}
void hal_audio_playback_stop(aic_audio_ctrl *codec)
{
struct aic_audio_transfer_info *info;
info = &codec->tx_info;
hal_audio_tx_disable_drq(codec);
hal_audio_disable_tx_global(codec);
hal_dma_chan_stop(info->dma_chan);
hal_release_dma_chan(info->dma_chan);
}
void hal_audio_dmic_stop(aic_audio_ctrl *codec)
{
struct aic_audio_transfer_info *info;
info = &codec->dmic_info;
hal_audio_dmic_disable_drq(codec);
hal_audio_disable_rx_global(codec);
hal_dma_chan_stop(info->dma_chan);
hal_release_dma_chan(info->dma_chan);
}
void hal_audio_amic_stop(aic_audio_ctrl *codec)
{
struct aic_audio_transfer_info *info;
info = &codec->amic_info;
hal_audio_amic_disable_drq(codec);
hal_audio_disable_rx_global(codec);
hal_dma_chan_stop(info->dma_chan);
hal_release_dma_chan(info->dma_chan);
}
void hal_audio_attach_callback(aic_audio_ctrl *codec, audio_callback callback,
void *arg)
{
codec->callback = callback;
codec->arg = arg;
}
void hal_audio_detach_callback(aic_audio_ctrl *codec)
{
codec->callback = NULL;
codec->arg = NULL;
}