Files
luban-lite-t3e-pro/bsp/artinchip/drv/i2s/drv_i2s_sound.c
刘可亮 9f7ba67007 v1.0.3
2024-01-27 08:47:24 +08:00

503 lines
13 KiB
C

/*
* Copyright (c) 2022-2023, ArtInChip Technology Co., Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Authors: dwj <weijie.ding@artinchip.com>
*/
#include <stdio.h>
#include <rtdevice.h>
#include <rtthread.h>
#include <aic_core.h>
#include <aic_drv.h>
#include <string.h>
#include <aic_osal.h>
#include <getopt.h>
#include "hal_i2s.h"
#include "codec.h"
#define TX_FIFO_PERIOD_COUNT 2
#define TX_FIFO_SIZE (RT_AUDIO_REPLAY_MP_BLOCK_SIZE *\
TX_FIFO_PERIOD_COUNT)
#define RX_FIFO_PERIOD_COUNT 2
#define RX_FIFO_SIZE (RT_AUDIO_RECORD_PIPE_SIZE *\
RX_FIFO_PERIOD_COUNT)
static rt_uint8_t i2s_tx_fifo[TX_FIFO_SIZE] __attribute__((aligned(64)));
static rt_uint8_t i2s_rx_fifo[RX_FIFO_SIZE] __attribute__((aligned(64)));
struct aic_i2s_sound
{
struct rt_audio_device audio;
aic_i2s_ctrl i2s;
struct codec *codec;
i2s_format_t format;
rt_uint8_t volume;
char *name;
uint32_t i2s_idx;
uint8_t record_idx;
};
static struct aic_i2s_sound snd_dev[] =
{
#ifdef AIC_USING_I2S0
{
.name = "i2s0_sound",
.i2s_idx = 0,
},
#endif
#ifdef AIC_USING_I2S1
{
.name = "i2s1_sound",
.i2s_idx = 1,
},
#endif
};
static void drv_i2s_sound_callback(aic_i2s_ctrl *pi2s, void *arg);
rt_err_t drv_i2s_sound_init(struct rt_audio_device *audio)
{
struct aic_i2s_sound *p_snd_dev;
aic_i2s_ctrl *pi2s;
p_snd_dev = (struct aic_i2s_sound *)audio;
pi2s = &p_snd_dev->i2s;
pi2s->tx_info.buf_info.buf = (void *)i2s_tx_fifo;
pi2s->tx_info.buf_info.buf_len = TX_FIFO_SIZE;
pi2s->tx_info.buf_info.period_len = TX_FIFO_SIZE / TX_FIFO_PERIOD_COUNT;
pi2s->rx_info.buf_info.buf = (void *)i2s_rx_fifo;
pi2s->rx_info.buf_info.buf_len = RX_FIFO_SIZE;
pi2s->rx_info.buf_info.period_len = RX_FIFO_SIZE / RX_FIFO_PERIOD_COUNT;
hal_i2s_attach_callback(pi2s, drv_i2s_sound_callback, NULL);
codec_init(p_snd_dev->codec);
return RT_EOK;
}
void drv_i2s_sound_buffer_info(struct rt_audio_device *audio,
struct rt_audio_buf_info *info)
{
struct aic_i2s_sound *p_snd_dev;
aic_i2s_ctrl *pi2s;
p_snd_dev = (struct aic_i2s_sound *)audio;
pi2s = &p_snd_dev->i2s;
info->buffer = pi2s->tx_info.buf_info.buf;
info->block_size = pi2s->tx_info.buf_info.period_len;
info->block_count = TX_FIFO_PERIOD_COUNT;
info->total_size = pi2s->tx_info.buf_info.buf_len;
}
rt_err_t drv_i2s_sound_start(struct rt_audio_device *audio, int stream)
{
struct aic_i2s_sound *p_snd_dev;
aic_i2s_ctrl *pi2s;
i2s_format_t *pformat;
p_snd_dev = (struct aic_i2s_sound *)audio;
pi2s = &p_snd_dev->i2s;
pformat = &p_snd_dev->format;
if (stream == AUDIO_STREAM_REPLAY)
{
rt_audio_tx_complete(audio);
hal_i2s_playback_start(pi2s, pformat);
codec_start(p_snd_dev->codec, I2S_STREAM_PLAYBACK);
/* for debug */
// codec_dump_reg(p_snd_dev->codec);
/* Enable PA */
#ifdef AIC_I2S_CODEC_PA_ENABLE_HIGH
codec_pa_power(p_snd_dev->codec, 1);
#else
codec_pa_power(p_snd_dev->codec, 0);
#endif
}
else if (stream == AUDIO_STREAM_RECORD)
{
/* May be need to do something for future */
}
else
{
hal_log_err("stream error\n");
return -RT_EINVAL;
}
return RT_EOK;
}
rt_err_t drv_i2s_sound_stop(struct rt_audio_device *audio, int stream)
{
struct aic_i2s_sound *p_snd_dev;
aic_i2s_ctrl *pi2s;
p_snd_dev = (struct aic_i2s_sound *)audio;
pi2s = &p_snd_dev->i2s;
if (stream == AUDIO_STREAM_REPLAY)
{
/* Disable PA first */
#ifdef AIC_I2S_CODEC_PA_ENABLE_HIGH
codec_pa_power(p_snd_dev->codec, 0);
#else
codec_pa_power(p_snd_dev->codec, 1);
#endif
hal_i2s_playback_stop(pi2s);
}
else if (stream == AUDIO_STREAM_RECORD)
{
hal_i2s_record_stop(pi2s);
}
else
{
hal_log_err("stream error\n");
return -RT_EINVAL;
}
return RT_EOK;
}
rt_err_t drv_i2s_sound_configure(struct rt_audio_device *audio,
struct rt_audio_caps *caps)
{
rt_err_t ret = RT_EOK;
struct aic_i2s_sound *p_snd_dev;
aic_i2s_ctrl *pi2s;
i2s_format_t *pformat;
struct codec *codec;
rt_uint32_t volume;
p_snd_dev = (struct aic_i2s_sound *)audio;
pi2s = &p_snd_dev->i2s;
pformat = &p_snd_dev->format;
codec = p_snd_dev->codec;
switch (caps->main_type)
{
case AUDIO_TYPE_MIXER:
{
switch (caps->sub_type)
{
case AUDIO_MIXER_VOLUME:
volume = caps->udata.value;
codec_set_volume(codec, volume);
p_snd_dev->volume = volume;
break;
default:
ret = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_OUTPUT:
case AUDIO_TYPE_INPUT:
{
if (caps->main_type == AUDIO_TYPE_OUTPUT)
pformat->stream = I2S_STREAM_PLAYBACK;
else if (caps->main_type == AUDIO_TYPE_INPUT)
pformat->stream = I2S_STREAM_RECORD;
pformat->mclk_nfs = AIC_I2S_CODEC_MCLK_NFS;
pformat->sclk_nfs = AIC_I2S_CODEC_SCLK_NFS;
pformat->protocol = I2S_PROTOCOL_I2S;
pformat->polarity = I2S_LEFT_POLARITY_LOW;
hal_i2s_protocol_select(pi2s, pformat->protocol);
hal_i2s_polarity_set(pi2s, pformat->polarity);
codec_set_protocol(codec, pformat);
codec_set_polarity(codec, pformat);
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
pformat->rate = caps->udata.config.samplerate;
pformat->channel = caps->udata.config.channels;
pformat->width = caps->udata.config.samplebits;
pformat->slot_width = caps->udata.config.samplebits;
/* Configure channel */
hal_i2s_channel_select(pi2s, pformat->channel, pformat->stream);
codec_set_channel(codec, pformat);
/* Configure samplebits */
hal_i2s_sample_width_select(pi2s, pformat->width);
hal_i2s_slot_width_select(pi2s, pformat->slot_width);
codec_set_sample_width(codec, pformat);
/* Configure sample rate */
hal_i2s_mclk_set(pi2s, pformat->rate, pformat->mclk_nfs);
hal_i2s_sclk_set(pi2s, pformat->rate, pformat->sclk_nfs);
codec_set_mclk(codec, pformat);
codec_set_sclk(codec, pformat);
LOG_D("set samplerate %d, channel: %d, samplebits: %d\n",
pformat->rate, pformat->channel, pformat->width);
break;
case AUDIO_DSP_SAMPLERATE:
// hal_i2s_set_samplerate(pi2s, caps->udata.config.samplerate);
pformat->rate = caps->udata.config.samplerate;
/* Configure sample rate */
hal_i2s_mclk_set(pi2s, pformat->rate, pformat->mclk_nfs);
hal_i2s_sclk_set(pi2s, pformat->rate, pformat->sclk_nfs);
codec_set_mclk(codec, pformat);
codec_set_sclk(codec, pformat);
LOG_D("set samplerate %d\n", pformat->rate);
break;
case AUDIO_DSP_CHANNELS:
pformat->channel = caps->udata.config.channels;
hal_i2s_channel_select(pi2s, pformat->channel, pformat->stream);
codec_set_channel(codec, pformat);
LOG_D("set channel: %d\n", pformat->channel);
break;
case AUDIO_DSP_SAMPLEBITS:
pformat->width = caps->udata.config.samplebits;
/* Configure samplebits */
hal_i2s_sample_width_select(pi2s, pformat->width);
hal_i2s_slot_width_select(pi2s, pformat->slot_width);
codec_set_sample_width(codec, pformat);
LOG_D("set samplebits: %d\n", pformat->width);
break;
default:
ret = -RT_ERROR;
}
if (caps->main_type == AUDIO_TYPE_INPUT)
{
hal_i2s_record_start(pi2s, pformat);
codec_start(codec, I2S_STREAM_RECORD);
/* for debug */
//codec_dump_reg(codec);
}
break;
}
default:
break;
}
return ret;
}
static rt_err_t drv_i2s_sound_getcaps(struct rt_audio_device *audio,
struct rt_audio_caps *caps)
{
rt_err_t ret = RT_EOK;
struct aic_i2s_sound *p_snd_dev;
i2s_format_t *pformat;
p_snd_dev = (struct aic_i2s_sound *)audio;
pformat = &p_snd_dev->format;
switch (caps->main_type)
{
case AUDIO_TYPE_QUERY:
{
switch (caps->sub_type)
{
case AUDIO_TYPE_QUERY:
caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
break;
default:
ret = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_OUTPUT:
case AUDIO_TYPE_INPUT:
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
caps->udata.config.samplerate = pformat->rate;
caps->udata.config.channels = pformat->channel;
caps->udata.config.samplebits = pformat->width;
break;
case AUDIO_DSP_SAMPLERATE:
caps->udata.config.samplerate = pformat->rate;
break;
case AUDIO_DSP_CHANNELS:
caps->udata.config.channels = pformat->channel;
break;
case AUDIO_DSP_SAMPLEBITS:
caps->udata.config.samplebits = pformat->width;
break;
default:
ret = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_MIXER:
{
switch (caps->sub_type)
{
case AUDIO_MIXER_QUERY:
caps->udata.mask = AUDIO_MIXER_VOLUME;
break;
case AUDIO_MIXER_VOLUME:
caps->udata.value = p_snd_dev->volume;
break;
default:
ret = -RT_ERROR;
break;
}
break;
}
default:
ret = -RT_ERROR;
break;
}
return ret;
}
static void drv_i2s_sound_callback(aic_i2s_ctrl *pi2s, void *arg)
{
unsigned long event = (unsigned long)arg;
struct aic_i2s_sound *p_snd_dev;
struct rt_audio_device *audio;
uint32_t period_len = 0;
p_snd_dev = rt_container_of(pi2s, struct aic_i2s_sound, i2s);
audio = (struct rt_audio_device *)p_snd_dev;
switch (event)
{
case I2S_TX_PERIOD_INT:
rt_audio_tx_complete(audio);
aicos_dcache_clean_range((void *)i2s_tx_fifo, TX_FIFO_SIZE);
break;
case I2S_RX_PERIOD_INT:
aicos_dcache_invalid_range((void *)i2s_rx_fifo, RX_FIFO_SIZE);
period_len = pi2s->rx_info.buf_info.period_len;
if (!p_snd_dev->record_idx)
{
rt_audio_rx_done(audio, &i2s_rx_fifo[0], period_len);
p_snd_dev->record_idx = 1;
}
else
{
rt_audio_rx_done(audio, &i2s_rx_fifo[period_len], period_len);
p_snd_dev->record_idx = 0;
}
break;
default:
hal_log_err("not support event\n");
break;
}
}
/* Get the queued space for playback */
static rt_size_t drv_i2s_sound_get_playback_avail(struct rt_audio_device *audio)
{
rt_uint16_t data_queue_item;
u32 dma_pos;
struct aic_i2s_sound *p_snd_dev;
aic_i2s_ctrl *pi2s;
uint32_t cache_in_tx_buf, cache_in_data_queue, cache_in_mempool;
p_snd_dev = (struct aic_i2s_sound *)audio;
pi2s = &p_snd_dev->i2s;
data_queue_item = rt_data_queue_len(&audio->replay->queue);
hal_dma_chan_tx_status(pi2s->tx_info.dma_chan, &dma_pos);
cache_in_data_queue = RT_AUDIO_REPLAY_MP_BLOCK_SIZE * data_queue_item;
cache_in_mempool = audio->replay->write_index;
cache_in_tx_buf = TX_FIFO_SIZE -
(pi2s->tx_info.buf_info.period_len - dma_pos);
return cache_in_data_queue + cache_in_mempool + cache_in_tx_buf;
}
rt_err_t drv_i2s_sound_pause(struct rt_audio_device *audio, int enable)
{
struct aic_i2s_sound *p_snd_dev;
aic_i2s_ctrl *pi2s;
p_snd_dev = (struct aic_i2s_sound *)audio;
pi2s = &p_snd_dev->i2s;
if(enable)
hal_dma_chan_pause(pi2s->tx_info.dma_chan);
else
hal_dma_chan_resume(pi2s->tx_info.dma_chan);
return RT_EOK;
}
struct rt_audio_ops aic_i2s_ops =
{
.getcaps = drv_i2s_sound_getcaps,
.configure = drv_i2s_sound_configure,
.init = drv_i2s_sound_init,
.start = drv_i2s_sound_start,
.stop = drv_i2s_sound_stop,
.transmit = NULL,
.buffer_info = drv_i2s_sound_buffer_info,
.get_avail = drv_i2s_sound_get_playback_avail,
.pause = drv_i2s_sound_pause,
};
int rt_hw_i2s_sound_init(void)
{
rt_err_t ret = RT_EOK;
int i;
for (i = 0; i < ARRAY_SIZE(snd_dev); i++)
{
hal_i2s_init(&snd_dev[i].i2s, snd_dev[i].i2s_idx);
snd_dev[i].audio.ops = &aic_i2s_ops;
snd_dev[i].record_idx = 0;
if (!register_codec) {
rt_kprintf("codec not registered!\n");
return -RT_ERROR;
}
snd_dev[i].codec = register_codec;
ret = rt_audio_register(&snd_dev[i].audio, snd_dev[i].name,
RT_DEVICE_FLAG_RDWR, &snd_dev[i]);
}
return ret;
}
INIT_DEVICE_EXPORT(rt_hw_i2s_sound_init);