rt-thread-official/bsp/bluetrum/ab32vg1-ab-prougen/board/ports/audio/drv_sound.c

534 lines
14 KiB
C
Raw Normal View History

2020-12-19 10:45:03 +08:00
/*
2021-01-28 17:06:09 +08:00
* Copyright (c) 2020-2021, Bluetrum Development Team
2021-03-11 13:26:54 +08:00
*
2020-12-19 10:45:03 +08:00
* SPDX-License-Identifier: Apache-2.0
*
* Date Author Notes
* 2020-12-12 greedyhao first implementation
*/
#include <board.h>
#define DBG_TAG "drv.snd_dev"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define SAI_AUDIO_FREQUENCY_48K ((uint32_t)48000u)
2021-05-13 10:22:45 +08:00
#define SAI_AUDIO_FREQUENCY_44K ((uint32_t)44100u)
#define SAI_AUDIO_FREQUENCY_38K ((uint32_t)38000u)
2021-04-09 17:35:26 +08:00
#define TX_FIFO_SIZE (1024)
2020-12-19 10:45:03 +08:00
struct sound_device
{
struct rt_audio_device audio;
struct rt_audio_configure replay_config;
2021-04-09 17:35:26 +08:00
rt_sem_t semaphore;
2021-05-13 10:22:45 +08:00
rt_thread_t thread;
2020-12-19 10:45:03 +08:00
rt_uint8_t *tx_fifo;
rt_uint8_t *rx_fifo;
2021-04-09 17:35:26 +08:00
rt_uint8_t volume;
2021-05-13 10:22:45 +08:00
rt_uint8_t dma_to_aubuf;
2020-12-19 10:45:03 +08:00
};
static struct sound_device snd_dev = {0};
//apll = 采样率*ADPLL_DIV*512
//audio pll init
void adpll_init(uint8_t out_spr)
{
PLL1CON &= ~(BIT(16) | BIT(17)); //PLL1 refclk select xosc26m
CLKCON2 &= ~(BIT(4)| BIT(5) | BIT(6) | BIT(7));
PLL1CON &= ~(BIT(3) | BIT(4) | BIT(5));
PLL1CON |= BIT(3); //Select PLL/VCO frequency band (PLL大于206M vcos = 0x01, 否则为0)
PLL1CON |= BIT(12); //enable pll1 ldo
hal_mdelay(1);
PLL1CON |= BIT(18); //pll1 sdm enable
if (out_spr) {
CLKCON2 |= BIT(4) | BIT(7); //adpll_div = 10
2020-12-19 10:45:03 +08:00
PLL1DIV = (245.76 * 65536) / 26; //245.76Mhz for 48K
// sys.aupll_type = 1;
} else {
CLKCON2 |= BIT(5) | BIT(7); //adpll_div = 11
2020-12-19 10:45:03 +08:00
PLL1DIV = (248.3712 * 65536) / 26; //248.3712MHz for 44.1k
// sys.aupll_type = 0;
}
hal_mdelay(1);
PLL1CON |= BIT(20); //update pll1div
PLL1CON |= BIT(6); //enable analog pll1
hal_mdelay(1); //wait pll1 stable
2020-12-19 10:45:03 +08:00
}
void dac_start(void)
{
AUANGCON0 |= BIT(0) | BIT(1) | BIT(3); // bg ldoh bias enable
AUANGCON0 &= ~(BIT(6)|BIT(5)|BIT(4)); // LDOH voltage select3bit
AUANGCON0 |= (3<<4); // 2.4/2.5/2.7/2.9/3.1/3.2
AUANGCON0 |= BIT(2); // LDOL enable
AUANGCON0 |= BIT(9); //VCM enable
AUANGCON0 &= ~(BIT(13)|BIT(12)); // VCM voltage select, 2bit
AUANGCON0 |= (2<<12);
AUANGCON0 |= BIT(15) | BIT(16) | BIT(17) | BIT(18); // d2a lpf audpa audpa_dly
AUANGCON0 &= ~BIT(11); //VCM type: 0-->res divider with off-chip cap; 1-->internal VCM
//AUANGCON0 |= BIT(11);
AUANGCON0 &= ~BIT(19); // dac type: 0-->SC; 1-->SR
//AUANGCON0 |= BIT(19);
AUANGCON0 |= BIT(20); // pa type: 0-->diff; 1-->3.3V single
AUANGCON3 &= ~(0x7<<4); //BIT[6:4]=PA_GF[2:0]
AUANGCON3 |= (0<<4);
AUANGCON3 &= ~(0xf); //BIT[3:0]=PA_GX[3:0]
AUANGCON3 |= 0;
AUANGCON3 &= ~(0xF<<8); //BIT[11:8]=PA2_GX[3:0]
AUANGCON3 |= (0<<8);
AUANGCON3 &= ~(0x7<<12); //BIT[14:12]=PA2_GF[2:0]
AUANGCON3 |= (0<<12);
AUANGCON1 |= BIT(0) | BIT(1); // dac enable: BIT(0)-->right channel; BIT(1)-->left channel
//AUANGCON1 &= ~BIT(1); //disable left channel
AUANGCON1 |= BIT(12); // lpf2pa enable
AUANGCON1 &= ~BIT(29); // vcmbuf enable: 0-->disable
//AUANGCON1 |= BIT(29);
//AUANGCON1 |= BIT(30); // mirror enable
//AUANGCON2 |= BIT(29) | BIT(30); // adc mute
//AUANGCON1 |= BIT(3); // pa mute
}
rt_section(".irq.audio")
2021-04-09 17:35:26 +08:00
void audio_sem_post(void)
{
rt_sem_release(snd_dev.semaphore);
}
void audio_sem_pend(void)
{
rt_sem_take(snd_dev.semaphore, RT_WAITING_FOREVER);
}
2020-12-19 10:45:03 +08:00
void saia_frequency_set(uint32_t frequency)
{
2021-05-13 10:22:45 +08:00
DACDIGCON0 &= ~(0xf << 2);
2020-12-19 10:45:03 +08:00
if (frequency == SAI_AUDIO_FREQUENCY_48K) {
2021-05-13 10:22:45 +08:00
DACDIGCON0 |= (0 << 2);
2020-12-19 10:45:03 +08:00
} else if (frequency == SAI_AUDIO_FREQUENCY_44K) {
2021-05-13 10:22:45 +08:00
DACDIGCON0 |= (1 << 2);
} else if (frequency == SAI_AUDIO_FREQUENCY_38K) {
DACDIGCON0 |= (2 << 2);
2020-12-19 10:45:03 +08:00
}
2021-05-13 10:22:45 +08:00
DACDIGCON0 |= BIT(6);
2020-12-19 10:45:03 +08:00
}
void saia_channels_set(uint8_t channels)
{
LOG_D("saia_channels_set=%d", channels);
if (channels == 1) {
AU0LMIXCOEF = 0x00007FFF;
AU1LMIXCOEF = 0x00007FFF;
DACDIGCON0 |= BIT(7);
DACDIGCON0 |= BIT(8);
AUANGCON1 &= ~BIT(0);
} else {
AUANGCON1 |= BIT(0);
DACDIGCON0 &= ~BIT(7);
DACDIGCON0 &= ~BIT(8);
}
}
void saia_volume_set(rt_uint8_t volume)
{
if (volume > 100)
volume = 100;
2021-03-11 13:26:54 +08:00
2020-12-19 10:45:03 +08:00
uint32_t dvol = volume * 327; // max is 0x7ffff
LOG_D("dvol=0x%x", dvol);
DACVOLCON = dvol | (0x02 << 16); // dac fade in
}
uint8_t saia_volume_get(void)
{
return ((DACVOLCON & 0xffff) / 327);
}
static rt_err_t sound_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
rt_err_t result = RT_EOK;
struct sound_device *snd_dev = RT_NULL;
2021-03-11 13:26:54 +08:00
RT_ASSERT(audio != RT_NULL);
2020-12-19 10:45:03 +08:00
snd_dev = (struct sound_device *)audio->parent.user_data;
switch (caps->main_type)
{
case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */
{
switch (caps->sub_type)
{
case AUDIO_TYPE_QUERY:
caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
break;
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
caps->udata.config.samplerate = snd_dev->replay_config.samplerate;
caps->udata.config.channels = snd_dev->replay_config.channels;
caps->udata.config.samplebits = snd_dev->replay_config.samplebits;
break;
case AUDIO_DSP_SAMPLERATE:
caps->udata.config.samplerate = snd_dev->replay_config.samplerate;
break;
case AUDIO_DSP_CHANNELS:
caps->udata.config.channels = snd_dev->replay_config.channels;
break;
case AUDIO_DSP_SAMPLEBITS:
caps->udata.config.samplebits = snd_dev->replay_config.samplebits;
break;
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_MIXER: /* report the Mixer Units */
{
switch (caps->sub_type)
{
case AUDIO_MIXER_QUERY:
caps->udata.mask = AUDIO_MIXER_VOLUME;
break;
case AUDIO_MIXER_VOLUME:
caps->udata.value = saia_volume_get();
break;
default:
result = -RT_ERROR;
break;
}
break;
}
default:
result = -RT_ERROR;
break;
}
2021-03-11 13:26:54 +08:00
return RT_EOK;
2020-12-19 10:45:03 +08:00
}
static rt_err_t sound_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
rt_err_t result = RT_EOK;
struct sound_device *snd_dev = RT_NULL;
RT_ASSERT(audio != RT_NULL);
snd_dev = (struct sound_device *)audio->parent.user_data;
switch (caps->main_type)
{
case AUDIO_TYPE_MIXER:
{
switch (caps->sub_type)
{
case AUDIO_MIXER_VOLUME:
{
rt_uint8_t volume = caps->udata.value;
saia_volume_set(volume);
snd_dev->volume = volume;
LOG_D("set volume %d", volume);
break;
}
2021-05-13 10:22:45 +08:00
case AUDIO_MIXER_EXTEND:
snd_dev->dma_to_aubuf = caps->udata.value;
break;
2020-12-19 10:45:03 +08:00
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_OUTPUT:
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
{
/* set samplerate */
saia_frequency_set(caps->udata.config.samplerate);
/* set channels */
saia_channels_set(caps->udata.config.channels);
/* save configs */
snd_dev->replay_config.samplerate = caps->udata.config.samplerate;
snd_dev->replay_config.channels = caps->udata.config.channels;
snd_dev->replay_config.samplebits = caps->udata.config.samplebits;
LOG_D("set samplerate %d", snd_dev->replay_config.samplerate);
break;
}
case AUDIO_DSP_SAMPLERATE:
{
saia_frequency_set(caps->udata.config.samplerate);
snd_dev->replay_config.samplerate = caps->udata.config.samplerate;
LOG_D("set samplerate %d", snd_dev->replay_config.samplerate);
break;
}
case AUDIO_DSP_CHANNELS:
{
saia_channels_set(caps->udata.config.channels);
snd_dev->replay_config.channels = caps->udata.config.channels;
LOG_D("set channels %d", snd_dev->replay_config.channels);
break;
}
case AUDIO_DSP_SAMPLEBITS:
{
/* not support */
snd_dev->replay_config.samplebits = caps->udata.config.samplebits;
break;
}
default:
result = -RT_ERROR;
break;
}
break;
}
default:
break;
}
2021-03-11 13:26:54 +08:00
return RT_EOK;
2020-12-19 10:45:03 +08:00
}
static rt_err_t sound_init(struct rt_audio_device *audio)
{
struct sound_device *snd_dev = RT_NULL;
2021-03-11 13:26:54 +08:00
RT_ASSERT(audio != RT_NULL);
2020-12-19 10:45:03 +08:00
snd_dev = (struct sound_device *)audio->parent.user_data;
adpll_init(0);
dac_start();
/* set default params */
saia_frequency_set(snd_dev->replay_config.samplerate);
saia_channels_set(snd_dev->replay_config.channels);
2021-05-13 10:22:45 +08:00
saia_volume_set(snd_dev->volume);
2020-12-19 10:45:03 +08:00
2021-03-11 13:26:54 +08:00
return RT_EOK;
2020-12-19 10:45:03 +08:00
}
static rt_err_t sound_start(struct rt_audio_device *audio, int stream)
{
struct sound_device *snd_dev = RT_NULL;
2021-03-11 13:26:54 +08:00
RT_ASSERT(audio != RT_NULL);
2020-12-19 10:45:03 +08:00
snd_dev = (struct sound_device *)audio->parent.user_data;
if (stream == AUDIO_STREAM_REPLAY)
{
LOG_D("open sound device");
AUBUFSIZE = (TX_FIFO_SIZE / 4 - 1);
AUBUFSIZE |= (TX_FIFO_SIZE / 8) << 16;
AUBUFSTARTADDR = DMA_ADR(snd_dev->rx_fifo);
2021-05-13 10:22:45 +08:00
DACDIGCON0 |= BIT(0) | BIT(10); // (0x01<<2)
2020-12-19 10:45:03 +08:00
2021-04-09 17:35:26 +08:00
AUBUFCON |= BIT(1);
2020-12-19 10:45:03 +08:00
}
return RT_EOK;
}
static rt_err_t sound_stop(struct rt_audio_device *audio, int stream)
{
2021-03-11 13:26:54 +08:00
RT_ASSERT(audio != RT_NULL);
2020-12-19 10:45:03 +08:00
if (stream == AUDIO_STREAM_REPLAY)
{
LOG_D("close sound device");
}
return RT_EOK;
}
rt_size_t sound_transmit(struct rt_audio_device *audio, const void *writeBuf, void *readBuf, rt_size_t size)
{
struct sound_device *snd_dev = RT_NULL;
rt_size_t tmp_size = size / 4;
rt_size_t count = 0;
2021-03-11 13:26:54 +08:00
RT_ASSERT(audio != RT_NULL);
2020-12-19 10:45:03 +08:00
snd_dev = (struct sound_device *)audio->parent.user_data;
while (tmp_size-- > 0) {
2021-04-09 17:35:26 +08:00
if (AUBUFCON & BIT(8)) { // aubuf full
AUBUFCON |= BIT(1) | BIT(4);
audio_sem_pend();
}
2020-12-19 10:45:03 +08:00
AUBUFDATA = ((const uint32_t *)writeBuf)[count++];
}
2021-03-11 13:26:54 +08:00
return size;
2020-12-19 10:45:03 +08:00
}
static void sound_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
{
struct sound_device *snd_dev = RT_NULL;
2021-03-11 13:26:54 +08:00
RT_ASSERT(audio != RT_NULL);
2020-12-19 10:45:03 +08:00
snd_dev = (struct sound_device *)audio->parent.user_data;
/**
* TX_FIFO
* +----------------+----------------+
* | block1 | block2 |
* +----------------+----------------+
* \ block_size /
*/
info->buffer = snd_dev->tx_fifo;
info->total_size = TX_FIFO_SIZE;
info->block_size = TX_FIFO_SIZE / 2;
info->block_count = 2;
}
static struct rt_audio_ops ops =
{
.getcaps = sound_getcaps,
.configure = sound_configure,
.init = sound_init,
.start = sound_start,
.stop = sound_stop,
2021-03-11 13:26:54 +08:00
.transmit = sound_transmit,
2020-12-19 10:45:03 +08:00
.buffer_info = sound_buffer_info,
};
rt_section(".irq.audio")
2021-04-09 17:35:26 +08:00
static void audio_isr(int vector, void *param)
2020-12-19 10:45:03 +08:00
{
rt_interrupt_enter();
//Audio buffer pend
if (AUBUFCON & BIT(5)) {
AUBUFCON |= BIT(1); //Audio Buffer Pend Clear
2021-04-09 17:35:26 +08:00
AUBUFCON &= ~BIT(4);
audio_sem_post();
2020-12-19 10:45:03 +08:00
}
rt_interrupt_leave();
}
2021-04-09 17:35:26 +08:00
static void audio_thread_entry(void *parameter)
{
while (1)
{
2021-05-13 10:22:45 +08:00
if ((snd_dev.dma_to_aubuf == RT_FALSE) && (snd_dev.audio.replay->activated == RT_TRUE)) {
2021-04-09 17:35:26 +08:00
rt_audio_tx_complete(&snd_dev.audio);
} else {
rt_thread_mdelay(50);
}
}
}
2020-12-19 10:45:03 +08:00
static int rt_hw_sound_init(void)
{
2021-03-11 13:26:54 +08:00
rt_uint8_t *tx_fifo = RT_NULL;
rt_uint8_t *rx_fifo = RT_NULL;
2020-12-19 10:45:03 +08:00
2021-03-11 13:26:54 +08:00
/* 分配 DMA 搬运 buffer */
tx_fifo = rt_calloc(1, TX_FIFO_SIZE);
2020-12-19 10:45:03 +08:00
if(tx_fifo == RT_NULL)
{
return -RT_ENOMEM;
}
snd_dev.tx_fifo = tx_fifo;
2021-03-11 13:26:54 +08:00
/* 分配 DMA 搬运 buffer */
rx_fifo = rt_calloc(1, TX_FIFO_SIZE);
2020-12-19 10:45:03 +08:00
if(rx_fifo == RT_NULL)
{
return -RT_ENOMEM;
}
snd_dev.rx_fifo = rx_fifo;
2021-04-09 17:35:26 +08:00
snd_dev.semaphore = rt_sem_create("snd", 0, RT_IPC_FLAG_FIFO);
if (snd_dev.semaphore == RT_NULL)
{
return -RT_ENOMEM;
}
snd_dev.thread = rt_thread_create(
"audio",
audio_thread_entry,
RT_NULL,
1024,
20, // must equal or lower than tshell priority
2021-05-13 10:22:45 +08:00
1
2021-04-09 17:35:26 +08:00
);
if (snd_dev.thread != RT_NULL)
{
rt_thread_startup(snd_dev.thread);
}
2020-12-19 10:45:03 +08:00
/* init default configuration */
{
2021-05-13 10:22:45 +08:00
snd_dev.replay_config.samplerate = SAI_AUDIO_FREQUENCY_48K;
2020-12-19 10:45:03 +08:00
snd_dev.replay_config.channels = 2;
snd_dev.replay_config.samplebits = 16;
snd_dev.volume = 55;
}
/* register snd_dev device */
snd_dev.audio.ops = &ops;
rt_audio_register(&snd_dev.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &snd_dev);
rt_hw_interrupt_install(IRQ_AUBUF0_1_VECTOR, audio_isr, RT_NULL, "au_isr");
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_sound_init);