Files
99_7018_lmx/cpu/br28/audio_iis.c
2025-10-29 13:10:02 +08:00

544 lines
16 KiB
C

#define IIS_TX_MIN_PNS (JL_ALNK0->LEN / 4)
struct audio_iis_sync_node {
void *hdl;
struct list_head entry;
};
/***********************************************************
* i2s 使用接口
*
***********************************************************/
int audio_iis_buffered_frames(struct audio_iis_hdl *iis)
{
return (JL_ALNK0->LEN - *ALNK0_SHN[iis->hw_ch] - 1);
}
int audio_iis_buffered_time(struct audio_iis_hdl *iis)
{
if (!iis) {
return 0;
}
int buffered_time = ((audio_iis_buffered_frames(iis) * 1000000) / iis->sample_rate) / 1000;
return buffered_time;
}
int audio_iis_set_underrun_params(struct audio_iis_hdl *iis, int time, void *priv, void (*feedback)(void *))
{
local_irq_disable();
iis->underrun_time = time;
iis->underrun_pns = 0;
iis->underrun_data = priv;
iis->underrun_feedback = feedback;
local_irq_enable();
return 0;
}
void audio_iis_syncts_update_frame(struct audio_iis_hdl *iis)
{
struct audio_iis_sync_node *node;
list_for_each_entry(node, &iis->sync_list, entry) {
sound_pcm_enter_update_frame(node->hdl);
}
}
void audio_iis_syncts_latch_trigger(struct audio_iis_hdl *iis)
{
struct audio_iis_sync_node *node;
list_for_each_entry(node, &iis->sync_list, entry) {
sound_pcm_syncts_latch_trigger(node->hdl);
}
}
void audio_iis_dma_update_to_syncts(struct audio_iis_hdl *iis, int frames)
{
struct audio_iis_sync_node *node;
u8 have_syncts = 0;
list_for_each_entry(node, &iis->sync_list, entry) {
sound_pcm_update_frame_num(node->hdl, frames);
have_syncts = 1;
}
if (have_syncts) {
u16 free_points = *ALNK0_SHN[iis->hw_ch];
int timeout = (1000000 / iis->sample_rate) * (clk_get("sys") / 1000000);
while (free_points == *ALNK0_SHN[iis->hw_ch] && (--timeout > 0));
}
}
void audio_iis_add_syncts_handle(struct audio_iis_hdl *iis, void *syncts)
{
struct audio_iis_sync_node *node = (struct audio_iis_sync_node *)zalloc(sizeof(struct audio_iis_sync_node));
node->hdl = syncts;
list_add(&node->entry, &iis->sync_list);
if (iis->state == SOUND_PCM_STATE_RUNNING) {
sound_pcm_syncts_latch_trigger(syncts);
}
ALINK_DA2BTSRC_SEL(ALINK0, iis->hw_ch);
}
void audio_iis_remove_syncts_handle(struct audio_iis_hdl *iis, void *syncts)
{
struct audio_iis_sync_node *node;
list_for_each_entry(node, &iis->sync_list, entry) {
if (node->hdl == syncts) {
goto remove_node;
}
}
return;
remove_node:
list_del(&node->entry);
free(node);
}
static void audio_iis_tx_irq_handler(struct audio_iis_hdl *iis)
{
if (iis->irq_trigger && iis->trigger_handler) {
iis->trigger_handler(iis->trigger_data);
iis->irq_trigger = 0;
}
if (iis->underrun_pns) {
int unread_frames = JL_ALNK0->LEN - *ALNK0_SHN[iis->hw_ch] - 1;
if (unread_frames <= iis->underrun_pns) {
//TODO
} else {
ALINK_OPNS_SET(ALINK0, iis->underrun_pns);
ALINK_CLR_CHx_PND(ALINK0, iis->hw_ch);
return;
}
}
ALINK_CHx_IE(ALINK0, iis->hw_ch, 0);
ALINK_CLR_CHx_PND(ALINK0, iis->hw_ch);
}
extern ALINK_PARM alink0_platform_data;
int audio_iis_pcm_tx_open(struct audio_iis_hdl *iis, u8 ch, int sample_rate)
{
memset(iis, 0x0, sizeof(struct audio_iis_hdl));
INIT_LIST_HEAD(&iis->sync_list);
iis->state = SOUND_PCM_STATE_IDLE;
iis->sample_rate = sample_rate;
iis->alink0_param = alink_init(&alink0_platform_data);
alink_channel_init(iis->alink0_param, ch, ALINK_DIR_TX, iis, audio_iis_tx_irq_handler);
/*alink_start();*/
iis->hw_ch = ch;
}
void audio_iis_pcm_tx_close(struct audio_iis_hdl *iis)
{
alink_channel_close(iis->hw_ch);
iis->state = SOUND_PCM_STATE_IDLE;
}
int audio_iis_pcm_sample_rate(struct audio_iis_hdl *iis)
{
return iis->sample_rate;
}
int audio_iis_set_delay_time(struct audio_iis_hdl *iis, int prepared_time, int delay_time)
{
iis->prepared_time = prepared_time;
iis->delay_time = delay_time;
return 0;
}
int audio_iis_pcm_remapping(struct audio_iis_hdl *iis, u8 mapping)
{
if (!iis->input_mapping) {
iis->input_mapping = mapping;
}
if ((iis->input_mapping & SOUND_CHMAP_RL) || (iis->input_mapping & SOUND_CHMAP_RR)) {
printf("Not support this channel map : 0x%x\n", mapping);
}
return 0;
}
int audio_iis_pcm_channel_num(struct audio_iis_hdl *iis)
{
int num = 0;
for (int i = 0; i < 2; i++) {
if (iis->input_mapping & BIT(i)) {
num++;
}
}
return num;
}
#if 0
const unsigned char sin44K[88] ALIGNED(4) = {
0x00, 0x00, 0x45, 0x0E, 0x41, 0x1C, 0xAA, 0x29, 0x3B, 0x36, 0xB2, 0x41, 0xD5, 0x4B, 0x6E, 0x54,
0x51, 0x5B, 0x5A, 0x60, 0x70, 0x63, 0x82, 0x64, 0x8A, 0x63, 0x8E, 0x60, 0x9D, 0x5B, 0xD1, 0x54,
0x4D, 0x4C, 0x3D, 0x42, 0xD5, 0x36, 0x50, 0x2A, 0xF1, 0x1C, 0xFB, 0x0E, 0xB7, 0x00, 0x70, 0xF2,
0x6E, 0xE4, 0xFD, 0xD6, 0x60, 0xCA, 0xD9, 0xBE, 0xA5, 0xB4, 0xF7, 0xAB, 0xFC, 0xA4, 0xDA, 0x9F,
0xAB, 0x9C, 0x7F, 0x9B, 0x5E, 0x9C, 0x3F, 0x9F, 0x19, 0xA4, 0xCE, 0xAA, 0x3D, 0xB3, 0x3A, 0xBD,
0x92, 0xC8, 0x0A, 0xD5, 0x60, 0xE2, 0x50, 0xF0
};
int read_44k_sine_data(void *buf, int bytes, int offset, u8 channel)
{
s16 *sine = (s16 *)sin44K;
s16 *data = (s16 *)buf;
int frame_len = (bytes >> 1) / channel;
int sin44k_frame_len = sizeof(sin44K) / 2;
int i, j;
offset = offset % sin44k_frame_len;
for (i = 0; i < frame_len; i++) {
for (j = 0; j < channel; j++) {
*data++ = sine[offset];
}
if (++offset >= sin44k_frame_len) {
offset = 0;
}
}
return i * 2 * channel;
}
static int frames_offset = 0;
#endif
static int __audio_iis_pcm_write(s16 *dst, s16 *src, int frames, int remapping)
{
/* int frame_bytes = read_44k_sine_data(dst, frames * 2 * 2, frames_offset, 2); */
/* putchar('k'); */
/* frames_offset += (frame_bytes >> 1) / 2; */
/* return frame_bytes; */
if (remapping == 1) {
for (int i = 0; i < frames; i++) {
dst[i * 2] = src[i];
dst[i * 2 + 1] = src[i];
}
return frames << 1;
}
if (remapping == 2) {
memcpy(dst, src, (frames << 1) * 2);
return frames << 2;
}
return frames;
}
#define CVP_REF_SRC_ENABLE
#ifdef CVP_REF_SRC_ENABLE
#define CVP_REF_SRC_TASK_NAME "RefSrcTask"
#include "Resample_api.h"
#include "aec_user.h"
#define CVP_REF_SRC_FRAME_SIZE 512
typedef struct {
volatile u8 state;
volatile u8 busy;
RS_STUCT_API *sw_src_api;
u8 *sw_src_buf;
u16 input_rate;
u16 output_rate;
s16 ref_tmp_buf[CVP_REF_SRC_FRAME_SIZE / 2];
cbuffer_t cbuf;
u8 ref_buf[CVP_REF_SRC_FRAME_SIZE * 3];
} aec_ref_src_t;
static aec_ref_src_t *aec_ref_src = NULL;
extern void audio_aec_ref_src_get_output_rate(u16 *input_rate, u16 *output_rate);
extern u8 bt_phone_dec_is_running();
extern struct audio_iis_hdl iis_hdl;
void audio_aec_ref_src_run(s16 *data, int len)
{
u16 ref_len = 0;
if (aec_ref_src) {
if (iis_hdl.input_mapping != SOUND_CHMAP_MONO) {
/*双变单*/
for (int i = 0; i < (len >> 2); i++) {
aec_ref_src->ref_tmp_buf[i] = data[2 * i];
}
len >>= 1;
}
/* audio_aec_ref_src_get_output_rate(&aec_ref_src->input_rate, &aec_ref_src->output_rate); */
/* printf("%d %d \n",input_rate,output_rate); */
if (aec_ref_src->sw_src_api) {
/* aec_ref_src->sw_src_api->set_sr(aec_ref_src->sw_src_buf, aec_ref_src->output_rate); */
ref_len = aec_ref_src->sw_src_api->run(aec_ref_src->sw_src_buf, aec_ref_src->ref_tmp_buf, len >> 1, aec_ref_src->ref_tmp_buf);
ref_len <<= 1;
}
/* printf("ref_len %d", ref_len); */
audio_aec_refbuf(aec_ref_src->ref_tmp_buf, ref_len);
}
}
static void audio_aec_ref_src_task(void *p)
{
int res;
int msg[16];
while (1) {
res = os_taskq_pend("taskq", msg, ARRAY_SIZE(msg));
if (aec_ref_src && aec_ref_src->state) {
s16 *data = (int)msg[1];
int len = msg[2];
int rlen = 0;
aec_ref_src->busy = 1;
if (cbuf_get_data_len(&aec_ref_src->cbuf) >= CVP_REF_SRC_FRAME_SIZE) {
cbuf_read(&aec_ref_src->cbuf, aec_ref_src->ref_tmp_buf, CVP_REF_SRC_FRAME_SIZE);
audio_aec_ref_src_run(aec_ref_src->ref_tmp_buf, CVP_REF_SRC_FRAME_SIZE);
}
aec_ref_src->busy = 0;
}
}
}
int audio_aec_ref_src_data_fill(void *p, s16 *data, int len)
{
int ret = 0;
if (aec_ref_src && aec_ref_src->state) {
audio_aec_ref_start(1);
if (0 == cbuf_write(&aec_ref_src->cbuf, data, len)) {
/* cbuf_clear(&aec_ref_src->cbuf); */
printf("ref src cbuf wfail!!");
}
if (cbuf_get_data_len(&aec_ref_src->cbuf) >= CVP_REF_SRC_FRAME_SIZE) {
ret = os_taskq_post_msg(CVP_REF_SRC_TASK_NAME, 2, (int)data, len);
}
}
return ret;
}
int audio_aec_ref_src_open(u32 insr, u32 outsr)
{
if (aec_ref_src) {
printf("aec_ref_src alreadly open !!!");
return -1;
}
aec_ref_src = zalloc(sizeof(aec_ref_src_t));
if (aec_ref_src == NULL) {
printf("aec_ref_src malloc fail !!!");
return -1;
}
cbuf_init(&aec_ref_src->cbuf, aec_ref_src->ref_buf, sizeof(aec_ref_src->ref_buf));
int err = os_task_create(audio_aec_ref_src_task, NULL, 4, 256, 128, CVP_REF_SRC_TASK_NAME);
if (err != OS_NO_ERR) {
printf("task create error!");
free(aec_ref_src);
aec_ref_src = NULL;
return -1;
}
/* audio_aec_ref_src_get_output_rate(&aec_ref_src->input_rate, &aec_ref_src->output_rate); */
aec_ref_src->input_rate = insr;
aec_ref_src->output_rate = outsr;
aec_ref_src->sw_src_api = get_rs16_context();
printf("sw_src_api:0x%x\n", aec_ref_src->sw_src_api);
ASSERT(aec_ref_src->sw_src_api);
int sw_src_need_buf = aec_ref_src->sw_src_api->need_buf();
printf("sw_src_buf:%d\n", sw_src_need_buf);
aec_ref_src->sw_src_buf = zalloc(sw_src_need_buf);
ASSERT(aec_ref_src->sw_src_buf, "sw_src_buf zalloc fail");
RS_PARA_STRUCT rs_para_obj;
rs_para_obj.nch = 1;
if (insr == 44100) {
rs_para_obj.new_insample = 44117;
} else {
rs_para_obj.new_insample = insr;
}
rs_para_obj.new_outsample = outsr;
printf("sw src,ch = %d, in = %d,out = %d\n", rs_para_obj.nch, rs_para_obj.new_insample, rs_para_obj.new_outsample);
aec_ref_src->sw_src_api->open(aec_ref_src->sw_src_buf, &rs_para_obj);
aec_ref_src->state = 1;
return 0;
}
void audio_aec_ref_src_close()
{
if (aec_ref_src) {
aec_ref_src->state = 0;
while (aec_ref_src->busy) {
putchar('w');
os_time_dly(1);
}
int err = os_task_del(CVP_REF_SRC_TASK_NAME);
if (err) {
log_i("kill task %s: err=%d\n", CVP_REF_SRC_TASK_NAME, err);
}
if (aec_ref_src->sw_src_api) {
aec_ref_src->sw_src_api = NULL;
}
if (aec_ref_src->sw_src_buf) {
free(aec_ref_src->sw_src_buf);
aec_ref_src->sw_src_buf = NULL;
}
free(aec_ref_src);
aec_ref_src = NULL;
}
}
#endif/*CVP_REF_SRC_ENABLE*/
int audio_iis_pcm_write(struct audio_iis_hdl *iis, void *data, int len)
{
if (iis->state != SOUND_PCM_STATE_RUNNING) {
return 0;
}
int remapping_ch = 2;
int frames = 0;
if (iis->input_mapping == SOUND_CHMAP_MONO) {
frames = len >> 1;
remapping_ch = 1;
} else if (iis->input_mapping & SOUND_CHMAP_FR) {
frames = len >> 2;
remapping_ch = 2;
} else {
}
s16 *ref_data = (s16 *)data;
int swp = *ALNK0_SWPTR[iis->hw_ch];
int free_frames = *ALNK0_SHN[iis->hw_ch] - iis->reserved_frames;
/* printf("free : %d, %d\n", *ALNK0_SHN[iis->hw_ch], free_frames); */
if (free_frames <= 0) {
return 0;
}
if (free_frames > frames) {
free_frames = frames;
}
frames = 0;
if (swp + free_frames > JL_ALNK0->LEN) {
frames = JL_ALNK0->LEN - swp;
__audio_iis_pcm_write((s16 *)(*ALNK0_BUF_ADR[iis->hw_ch] + swp * 2 * 2), (s16 *)data, frames, remapping_ch);
free_frames -= frames;
data = (s16 *)data + frames * remapping_ch;
swp = 0;
}
__audio_iis_pcm_write((s16 *)(*ALNK0_BUF_ADR[iis->hw_ch] + swp * 2 * 2), (s16 *)data, free_frames, remapping_ch);
frames += free_frames;
audio_iis_syncts_update_frame(iis);
*ALNK0_SHN[iis->hw_ch] = frames;
__asm_csync();
audio_iis_dma_update_to_syncts(iis, frames);
/*printf("frames : %d\n", frames);*/
/*printf("CLK CON2 : %d, %d\n", JL_CLOCK->CLK_CON2 & 0x3, (JL_CLOCK->CLK_CON2 >> 2) & 0x3);*/
#ifdef CVP_REF_SRC_ENABLE
if (bt_phone_dec_is_running()) {
audio_aec_ref_src_data_fill(iis, ref_data, (frames << 1) * remapping_ch);
}
#endif
return (frames << 1) * remapping_ch;
}
static void audio_iis_dma_fifo_start(struct audio_iis_hdl *iis)
{
if (iis->state != SOUND_PCM_STATE_PREPARED) {
return;
}
if (iis->prepared_frames) {
*ALNK0_SHN[iis->hw_ch] = iis->prepared_frames;
__asm_csync();
}
local_irq_disable();
iis->state = SOUND_PCM_STATE_RUNNING;
ALINK_CHx_IE(ALINK0, iis->hw_ch, 1);
ALINK_CLR_CHx_PND(ALINK0, iis->hw_ch);
local_irq_enable();
audio_iis_syncts_latch_trigger(iis);
}
static int audio_iis_fifo_set_delay(struct audio_iis_hdl *iis)
{
iis->prepared_frames = iis->prepared_time * iis->sample_rate / 1000;
int delay_frames = (iis->delay_time * iis->sample_rate) / 1000 / 2 * 2;
if (iis->prepared_frames >= JL_ALNK0->LEN) {
iis->prepared_frames = JL_ALNK0->LEN / 2;
}
if (delay_frames < JL_ALNK0->LEN) {
iis->reserved_frames = JL_ALNK0->LEN - delay_frames;
} else {
iis->reserved_frames = 0;
}
return 0;
}
int audio_iis_trigger_interrupt(struct audio_iis_hdl *iis, int time_ms, void *priv, void (*callback)(void *))
{
if (iis->state != SOUND_PCM_STATE_RUNNING) {
return -EINVAL;
}
int irq_frames = time_ms * iis->sample_rate / 1000;
int pns = audio_iis_buffered_frames(iis) - irq_frames;
if (pns < irq_frames) {
sys_hi_timeout_add(priv, callback, time_ms);
return -EINVAL;
}
local_irq_disable();
if (pns > ALINK_OPNS(ALINK0)) {
ALINK_OPNS_SET(ALINK0, pns);
}
iis->irq_trigger = 1;
iis->trigger_handler = callback;
iis->trigger_data = priv;
ALINK_CHx_IE(ALINK0, iis->hw_ch, 1);
local_irq_enable();
return 0;
}
int audio_iis_dma_start(struct audio_iis_hdl *iis)
{
if (iis->state == SOUND_PCM_STATE_RUNNING) {
return 0;
}
audio_iis_fifo_set_delay(iis);
iis->underrun_pns = iis->underrun_time ? (iis->underrun_time * iis->sample_rate / 1000) : 0;
ALINK_OPNS_SET(ALINK0, (iis->underrun_pns ? iis->underrun_pns : IIS_TX_MIN_PNS));
iis->state = SOUND_PCM_STATE_PREPARED;
/* printf("DMA : %d, %d, %d\n", *ALNK0_SHN[iis->hw_ch], *ALNK0_SWPTR[iis->hw_ch], *ALNK0_HWPTR[iis->hw_ch]); */
audio_iis_dma_fifo_start(iis);
alink_start(iis->alink0_param);
/* printf("[iis dma start]... %d, free : %d, prepared_frames : %d, reserved_frames : %d\n", JL_ALNK0->LEN, *ALNK0_SHN[iis->hw_ch], iis->prepared_frames, iis->reserved_frames); */
}
int audio_iis_dma_stop(struct audio_iis_hdl *iis)
{
if (iis->state != SOUND_PCM_STATE_RUNNING) {
return 0;
}
/*audio_iis_buffered_frames_fade_out(iis, JL_ALNK0->LEN - *ALNK0_SHN[iis->hw_ch] - 1);*/
ALINK_CHx_IE(ALINK0, iis->hw_ch, 0);
ALINK_CLR_CHx_PND(ALINK0, iis->hw_ch);
/* alink_uninit(iis->alink0_param); */
iis->input_mapping = 0;
iis->state = SOUND_PCM_STATE_SUSPENDED;
return 0;
}