You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

614 lines
19 KiB

/*
* Copyright (c) Hisilicon Technologies Co., Ltd. 2019-2019. All rights reserved.
* Description: MPI function file for Huanglong low latency
* Author: audio
* Create: 2019-05-30
*/
#include <sys/ioctl.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include "mpi_ao_plugin.h"
#include "mpi_ao_debug.h"
#include "securec.h"
#include "list.h"
#include "mpi_memory_ext.h"
#include "mpi_ao_ext.h"
#include "soc_errno.h"
#include "drv_ioctl_ao.h"
#include "mpi_ao_circ_buf.h"
#include "mpi_ao.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* __cplusplus */
#define MAX_BUFFER_MS 500
#define PAGE_SIZE_MASK 0xfffff000
#define WRITE_REG_OFFSET 0x10
#define READ_REG_OFFSET 0x14
#define OFFSET 0x08
#define FRAME_SAMPLES_MIN 256
#define FRAME_SAMPLES_MASK 0x1F00 /* support 256/512/1024/2048/4096 samples */
#define FRONTOUT_EXTRA_FRAME_SIZE (6 * sizeof(td_u16) * 2048)
#define FRONTOUT_WPTR_OFFSET 0x00000088
#define FRONTOUT_RPTR_OFFSET 0x00000090
#define BACKIN_WPTR_OFFSET 0x000000a4
#define BACKIN_RPTR_OFFSET 0x000000ac
#define AOE_REG_PLUGIN_ATTR_OFFSET 0x000000b4
#define FRONTOUT_FRAME_CHANNELS 6
typedef enum {
FRONT_OUT,
BACK_IN,
} frame_direction;
typedef struct {
td_bool used;
td_bool enable;
ao_snd_id snd;
pthread_mutex_t mutex;
td_s32 bit_depth;
td_u32 sample_rate;
td_u32 channels;
ext_audio_buffer aoe_reg;
ext_audio_buffer frontout_buf;
ext_audio_buffer backin_buf;
circ_buf frontout_cb;
circ_buf backin_cb;
td_u32 frontout_buf_used_size;
td_u32 frontout_buf_extra_frame_size;
td_u32 frame_samples_diff;
} snd_engine_plugin_source;
static snd_engine_plugin_source g_engine_state = {
.used = TD_FALSE,
.enable = TD_FALSE,
.snd = AO_SND_0,
.bit_depth = EXT_BIT_DEPTH_16,
.channels = FRONTOUT_FRAME_CHANNELS,
.sample_rate = EXT_SAMPLE_RATE_48K,
};
static pthread_mutex_t g_engine_plugin_mutex = PTHREAD_MUTEX_INITIALIZER;
static inline td_void engine_plugin_mutex_lock(td_void)
{
if (pthread_mutex_lock(&g_engine_plugin_mutex) != 0) {
soc_log_err("Lock mutex failed\n");
}
}
static inline td_void engine_plugin_mutex_unlock(td_void)
{
if (pthread_mutex_unlock(&g_engine_plugin_mutex) != 0) {
soc_log_err("Unlock mutex failed\n");
}
}
static td_void audio_munmap(td_void *buffer, td_u32 size)
{
td_s32 ret;
if (buffer == TD_NULL) {
return;
}
ret = munmap(buffer, size);
if (ret != TD_SUCCESS) {
soc_err_print_call_fun_err(munmap, ret);
return;
}
}
static td_void *audio_mmap(td_s32 fd, td_u32 size, td_u64 phys_addr)
{
td_void *buffer = TD_NULL;
if (fd < 0) {
return TD_NULL;
}
buffer = (td_void *)mmap64((td_void *)0, size,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off64_t)phys_addr);
if (buffer == MAP_FAILED) {
soc_log_err("mmap failed, fd = %d, size = %d\n", fd, size);
return TD_NULL;
}
return buffer;
}
static td_void audio_munmap_ion(td_void *buf, td_u32 size)
{
audio_munmap(buf, size);
}
static td_s32 buf_mmap(ext_audio_buffer *map_buf)
{
map_buf->virt_addr = audio_mmap(map_buf->fd, map_buf->size, 0);
if (map_buf->virt_addr == TD_NULL) {
return SOC_ERR_AO_MALLOC_FAILED;
}
return TD_SUCCESS;
}
static td_void buf_munmap(ext_audio_buffer *map_buf)
{
audio_munmap_ion(map_buf->virt_addr, map_buf->size);
map_buf->fd = -1;
map_buf->virt_addr = TD_NULL;
}
static inline td_void munmap_hw_aoe_reg(td_void)
{
audio_munmap(g_engine_state.aoe_reg.virt_addr, g_engine_state.aoe_reg.size);
}
static td_s32 mmap_hw_aoe_reg(td_void)
{
td_s32 fd;
ext_audio_buffer *buf = TD_NULL;
/* hw aoe use dsp dram or cpu sram as aoe reg
* we use ao fd to mmap
*/
fd = mpi_ao_get_fd();
if (fd < 0) {
soc_log_err("mpi_ao_get_fd failed\n");
return SOC_ERR_AO_DEV_NOT_OPEN;
}
buf = &g_engine_state.aoe_reg;
buf->fd = fd;
buf->virt_addr = (td_u8 *)audio_mmap(fd, buf->size, buf->phys_addr);
if (buf->virt_addr == TD_NULL) {
soc_log_err("call audio_mmap failed\n");
return SOC_ERR_AO_MALLOC_FAILED;
}
return TD_SUCCESS;
}
static td_void snd_engine_plugin_munmap_buffer(td_void)
{
snd_engine_plugin_source *engine_state = &g_engine_state;
buf_munmap(&engine_state->backin_buf);
buf_munmap(&engine_state->frontout_buf);
munmap_hw_aoe_reg();
}
static td_void get_mmap_buffer_info(const mixer_plugin_attr *attr)
{
snd_engine_plugin_source *engine_state = &g_engine_state;
engine_state->aoe_reg.phys_addr = attr->aoe_reg.phys_addr;
engine_state->aoe_reg.size = attr->aoe_reg.size;
engine_state->aoe_reg.fd = attr->aoe_reg.fd;
engine_state->frontout_buf.phys_addr = attr->frontout_buffer.phys_addr;
engine_state->frontout_buf.size = attr->frontout_buffer.size + FRONTOUT_EXTRA_FRAME_SIZE;
engine_state->frontout_buf_used_size = attr->frontout_buffer.size;
engine_state->frontout_buf_extra_frame_size = FRONTOUT_EXTRA_FRAME_SIZE;
engine_state->frontout_buf.fd = attr->frontout_buffer.fd;
engine_state->backin_buf.phys_addr = attr->backin_buffer.phys_addr;
engine_state->backin_buf.size = attr->backin_buffer.size;
engine_state->backin_buf.fd = attr->backin_buffer.fd;
}
static td_s32 snd_engine_plugin_mmap_buffer(td_void)
{
td_s32 ret;
td_u32 *write_ptr = TD_NULL;
td_u32 *read_ptr = TD_NULL;
snd_engine_plugin_source *engine_state = &g_engine_state;
ret = mmap_hw_aoe_reg();
if (ret != TD_SUCCESS) {
soc_err_print_call_fun_err(mmap_hw_aoe_reg, ret);
return ret;
}
ret = buf_mmap(&engine_state->frontout_buf);
if (ret != TD_SUCCESS) {
soc_err_print_call_fun_err(buf_mmap, ret);
munmap_hw_aoe_reg();
return ret;
}
ret = buf_mmap(&engine_state->backin_buf);
if (ret != TD_SUCCESS) {
soc_err_print_call_fun_err(buf_mmap, ret);
buf_munmap(&engine_state->frontout_buf);
munmap_hw_aoe_reg();
return ret;
}
write_ptr = (td_u32 *)(engine_state->aoe_reg.virt_addr + FRONTOUT_WPTR_OFFSET);
read_ptr = (td_u32 *)(engine_state->aoe_reg.virt_addr + FRONTOUT_RPTR_OFFSET);
soc_log_dbg("frontout v %p s %d w %p r %p\n",
engine_state->frontout_buf.virt_addr, engine_state->frontout_buf_used_size, write_ptr, read_ptr);
circ_buf_init(&engine_state->frontout_cb,
(td_u32 *)(write_ptr),
(td_u32 *)(read_ptr),
engine_state->frontout_buf.virt_addr,
engine_state->frontout_buf_used_size);
write_ptr = (td_u32 *)(engine_state->aoe_reg.virt_addr + BACKIN_WPTR_OFFSET);
read_ptr = (td_u32 *)(engine_state->aoe_reg.virt_addr + BACKIN_RPTR_OFFSET);
soc_log_dbg("backin v %p s %d w %p r %p\n",
engine_state->backin_buf.virt_addr, engine_state->backin_buf.size, write_ptr, read_ptr);
circ_buf_init(&engine_state->backin_cb,
(td_u32 *)(write_ptr),
(td_u32 *)(read_ptr),
engine_state->backin_buf.virt_addr,
engine_state->backin_buf.size);
return TD_SUCCESS;
}
static td_u32 aoe_reg_read_frontout_channel(const snd_engine_plugin_source *engine_state)
{
td_u32 ch = 0;
td_u32 engine_plugin_attr = 0;
td_u32 *engine_plugin_attr_reg = TD_NULL;
const td_u32 frontout_ch_map[] = { 0, EXT_AUDIO_CH_STEREO, EXT_AUDIO_CH_6, 0};
if (engine_state->aoe_reg.virt_addr == TD_NULL) {
soc_log_err("engine_state->aoe_reg.virt_addr null pointer!\n");
return 0;
}
engine_plugin_attr_reg = (td_u32 *)(engine_state->aoe_reg.virt_addr + AOE_REG_PLUGIN_ATTR_OFFSET);
engine_plugin_attr = *engine_plugin_attr_reg;
ch = engine_plugin_attr & 0x0003; /* aoe reg: engine_plugin_attr.bits.ch */
if (ch >= sizeof(frontout_ch_map) / sizeof(td_u32)) {
soc_log_warn("channels is error %d\n", ch);
return 0;
}
return frontout_ch_map[ch];
}
static td_s32 check_get_buffer_params_valid(td_u32 require_size, td_u32 timeout_ms)
{
if (require_size == 0) {
return SOC_ERR_AO_INVALID_PARA;
}
if (timeout_ms > MAX_BUFFER_MS) {
return SOC_ERR_AO_INVALID_PARA;
}
return TD_SUCCESS;
}
static td_s32 get_engine_plugin_frame_info(ext_audio_mixer_plugin_frame *frame, frame_direction dir)
{
snd_engine_plugin_source *engine_state = &g_engine_state;
if (dir == FRONT_OUT) {
frame->frame_addr.frame_fd.mem_handle = engine_state->frontout_buf.fd;
frame->frame_addr.frame_size = engine_state->frontout_buf_used_size;
} else if (dir == BACK_IN) {
frame->frame_addr.frame_fd.mem_handle = engine_state->backin_buf.fd;
frame->frame_addr.frame_size = engine_state->backin_buf.size;
}
frame->frame_info.bit_depth = engine_state->bit_depth;
frame->frame_info.channels = aoe_reg_read_frontout_channel(engine_state);
engine_state->channels = frame->frame_info.channels;
frame->frame_info.sample_rate = engine_state->sample_rate;
frame->frame_info.interleaved = TD_TRUE;
return TD_SUCCESS;
}
static td_s32 snd_engine_plugin_enable(ao_snd_id sound)
{
td_s32 ret, ao_fd;
engine_plugin_mutex_lock();
ao_snd_mixer_plugin_buffer_param param;
ao_snd_mixer_plugin_enable_param enable;
if (g_engine_state.used == TD_TRUE) {
soc_log_err("SND engine plugin is busy!\n");
engine_plugin_mutex_unlock();
return SOC_ERR_AO_NOTSUPPORT;
}
ao_fd = mpi_ao_get_fd();
if (ao_fd < 0) {
soc_log_err("mpi_ao_get_fd failed\n");
engine_plugin_mutex_unlock();
return SOC_ERR_AO_DEV_NOT_OPEN;
}
ret = memset_s(&param, sizeof(param), 0, sizeof(ao_snd_mixer_plugin_buffer_param));
if (ret != EOK) {
soc_err_print_call_fun_err(memset_s, ret);
engine_plugin_mutex_unlock();
return ret;
}
enable.sound = sound;
enable.plugin_enable = TD_TRUE;
ret = ioctl(ao_fd, CMD_AO_MIXER_PLUGIN_SET_ENABLE, &enable);
if (ret != TD_SUCCESS) {
soc_log_err("ioctl CMD_AO_MIXER_PLUGIN_SET_ENABLE TRUE failed(0x%x)\n", ret);
engine_plugin_mutex_unlock();
return ret;
}
param.sound = sound;
ret = ioctl(ao_fd, CMD_AO_MIXER_PLUGIN_GET_BUFFER_INFO, &param);
if (ret != TD_SUCCESS) {
soc_log_err("ioctl CMD_AO_MIXER_PLUGIN_GET_BUFFER_INFO failed(0x%x)\n", ret);
engine_plugin_mutex_unlock();
return ret;
}
get_mmap_buffer_info(&param.buffer_attr);
ret = snd_engine_plugin_mmap_buffer();
if (ret != TD_SUCCESS) {
soc_err_print_call_fun_err(snd_engine_plugin_mmap_buffer, ret);
engine_plugin_mutex_unlock();
return ret;
}
g_engine_state.enable = TD_TRUE;
g_engine_state.used = TD_TRUE;
soc_log_notice("set engine plugin enable\n");
engine_plugin_mutex_unlock();
return TD_SUCCESS;
}
static td_s32 snd_engine_plugin_disable(ao_snd_id sound)
{
td_s32 ret;
td_s32 ao_fd;
ao_snd_mixer_plugin_enable_param enable;
ao_snd_mixer_plugin_buffer_param param;
engine_plugin_mutex_lock();
ao_fd = mpi_ao_get_fd();
if (ao_fd < 0) {
soc_log_err("mpi_ao_get_fd failed\n");
engine_plugin_mutex_unlock();
return SOC_ERR_AO_DEV_NOT_OPEN;
}
param.sound = sound;
ret = ioctl(ao_fd, CMD_AO_MIXER_PLUGIN_GET_BUFFER_INFO, &param);
if (ret != TD_SUCCESS) {
soc_log_err("ioctl CMD_AO_MIXER_PLUGIN_GET_BUFFER_INFO failed(0x%x)\n", ret);
engine_plugin_mutex_unlock();
return ret;
}
g_engine_state.enable = TD_FALSE;
g_engine_state.used = TD_FALSE;
get_mmap_buffer_info(&param.buffer_attr);
snd_engine_plugin_munmap_buffer();
enable.sound = sound;
enable.plugin_enable = TD_FALSE;
ret = ioctl(ao_fd, CMD_AO_MIXER_PLUGIN_SET_ENABLE, &enable);
if (ret != TD_SUCCESS) {
soc_log_err("ioctl CMD_AO_MIXER_PLUGIN_SET_ENABLE FALSE failed(0x%x)\n", ret);
engine_plugin_mutex_unlock();
return ret;
}
soc_log_notice("set engine plugin disable\n");
engine_plugin_mutex_unlock();
return TD_SUCCESS;
}
td_s32 snd_mixer_set_engine_plugin_enable(ao_snd_id sound, td_bool enable)
{
if (enable == TD_TRUE) {
return snd_engine_plugin_enable(sound);
} else {
return snd_engine_plugin_disable(sound);
}
}
td_s32 snd_mixer_get_engine_plugin_enable(ao_snd_id sound, td_bool *enable)
{
td_s32 ret;
td_s32 ao_fd;
ao_snd_mixer_plugin_enable_param params;
if (sound != AO_SND_0) {
return SOC_ERR_AO_INVALID_ID;
}
if (enable == TD_NULL) {
return SOC_ERR_AO_NULL_PTR;
}
ao_fd = mpi_ao_get_fd();
if (ao_fd < 0) {
soc_log_err("mpi_ao_get_fd failed\n");
return SOC_ERR_AO_DEV_NOT_OPEN;
}
params.sound = sound;
ret = ioctl(ao_fd, CMD_AO_MIXER_PLUGIN_GET_ENABLE, &params);
if (ret != TD_SUCCESS) {
soc_log_err("ioctl CMD_AO_MIXER_PLUGIN_GET_ENABLE failed(0x%x)\n", ret);
return ret;
}
g_engine_state.enable = params.plugin_enable;
g_engine_state.used = params.plugin_enable;
*enable = params.plugin_enable;
return TD_SUCCESS;
}
td_s32 snd_mixer_acquire_engine_plugin_frontout_stream(ao_snd_id sound, td_u32 require_samples,
ext_audio_mixer_plugin_frame *frontout_frame, td_u32 timeout_ms)
{
td_s32 ret;
circ_buf_info info;
td_u32 wait_ms = 0;
td_u32 busy_size, dest_size, require_size, realtime_temp;
ext_audio_mixer_plugin_frame out_frame;
snd_engine_plugin_source *engine_state = &g_engine_state;
if (engine_state->enable == TD_FALSE) {
return SOC_ERR_AO_DEV_NOT_OPEN;
}
TD_UNUSED(sound);
ret = check_get_buffer_params_valid(require_samples, timeout_ms);
if (ret != TD_SUCCESS) {
soc_err_print_call_fun_err(check_get_buffer_params_valid, ret);
return ret;
}
circ_buf_query_info(&engine_state->frontout_cb, &info);
soc_log_dbg("out w %d r %d b %d f %d\n", info.write_pos, info.read_pos, info.total_data_size,
info.total_free_size);
realtime_temp = (require_samples & FRAME_SAMPLES_MASK) * sizeof(td_s16);
if (realtime_temp == 0) {
soc_log_err("query buffer samples invalid %d\n", require_samples);
return SOC_ERR_AO_INVALID_PARA;
}
require_size = realtime_temp * FRONTOUT_FRAME_CHANNELS;
do {
busy_size = circ_buf_query_busy(&engine_state->frontout_cb);
if ((require_size != 0) && (busy_size >= require_size)) {
break;
} else if (wait_ms++ >= timeout_ms) {
break;
}
usleep(1000); /* 1000 is sleep time */
} while (wait_ms <= timeout_ms);
if ((wait_ms >= timeout_ms) && (busy_size < require_size)) {
soc_log_warn("query buf_data time out! need_bytes(0x%x), avail_bytes(0x%x)\n", require_size, busy_size);
return SOC_ERR_AO_INBUF_EMPTY;
}
get_engine_plugin_frame_info(&out_frame, FRONT_OUT);
dest_size = require_size < busy_size ? require_size : busy_size;
out_frame.frame_addr.frame_size = dest_size;
out_frame.frame_addr.frame_fd.addr_offset = *(engine_state->frontout_cb.read);
out_frame.frame_info.pcm_samples = require_samples;
ret = memcpy_s(frontout_frame, sizeof(ext_audio_mixer_plugin_frame), &out_frame, sizeof(out_frame));
if (ret != EOK) {
soc_err_print_call_fun_err(memcpy_s, ret);
return ret;
}
return TD_SUCCESS;
}
td_s32 snd_mixer_release_engine_plugin_frontout_stream(ao_snd_id sound,
const ext_audio_mixer_plugin_frame *frontout_frame)
{
snd_engine_plugin_source *engine_state = &g_engine_state;
if (engine_state->enable == TD_FALSE) {
return SOC_ERR_AO_DEV_NOT_OPEN;
}
if (frontout_frame == TD_NULL) {
return SOC_ERR_AO_NULL_PTR;
}
TD_UNUSED(sound);
circ_buf_update_read_pos(&engine_state->frontout_cb, frontout_frame->frame_addr.frame_size);
engine_state->frame_samples_diff += frontout_frame->frame_info.pcm_samples;
return TD_SUCCESS;
}
td_s32 snd_mixer_get_engine_plugin_backin_buffer(ao_snd_id sound, td_u32 require_size,
ext_audio_mixer_plugin_frame *backin_frame, td_u32 timeout_ms)
{
td_s32 ret;
circ_buf_info info;
td_u32 free_size;
td_u32 dest_size;
td_u32 wait_ms = 0;
ext_audio_mixer_plugin_frame in_frame;
snd_engine_plugin_source *engine_state = &g_engine_state;
if (engine_state->enable == TD_FALSE) {
return SOC_ERR_AO_DEV_NOT_OPEN;
}
TD_UNUSED(sound);
ret = check_get_buffer_params_valid(require_size, timeout_ms);
if (ret != TD_SUCCESS) {
soc_err_print_call_fun_err(check_get_buffer_params_valid, ret);
return ret;
}
get_engine_plugin_frame_info(&in_frame, BACK_IN);
circ_buf_query_info(&engine_state->backin_cb, &info);
soc_log_dbg("in w %d r %d b %d f %d req %d\n", info.write_pos, info.read_pos,
info.total_data_size, info.total_free_size, require_size);
do {
free_size = circ_buf_query_free(&engine_state->backin_cb);
if (free_size > require_size) {
break;
} else if (wait_ms++ >= timeout_ms) {
break;
}
usleep(1000); /* 1000 is sleep time */
} while (wait_ms <= timeout_ms);
if ((wait_ms >= timeout_ms) && (free_size <= require_size)) {
soc_log_warn("query buf_data time out! need_bytes(0x%x), avail_bytes(0x%x)\n", require_size, free_size);
return SOC_ERR_AO_OUT_BUF_FULL;
}
dest_size = require_size < free_size ? require_size : free_size;
in_frame.frame_addr.frame_size = dest_size;
in_frame.frame_addr.frame_fd.addr_offset = *(engine_state->backin_cb.write);
in_frame.frame_info.pcm_samples = FRAME_SAMPLES_MIN;
ret = memcpy_s(backin_frame, sizeof(ext_audio_mixer_plugin_frame), &in_frame, sizeof(in_frame));
if (ret != EOK) {
soc_err_print_call_fun_err(memcpy_s, ret);
return ret;
}
return TD_SUCCESS;
}
td_s32 snd_mixer_put_engine_plugin_backin_buffer(ao_snd_id sound,
const ext_audio_mixer_plugin_frame *backin_frame)
{
snd_engine_plugin_source *engine_state = &g_engine_state;
if (engine_state->enable == TD_FALSE) {
return SOC_ERR_AO_DEV_NOT_OPEN;
}
if (backin_frame == TD_NULL) {
return SOC_ERR_AO_NULL_PTR;
}
TD_UNUSED(sound);
circ_buf_update_write_pos(&engine_state->backin_cb, backin_frame->frame_addr.frame_size);
engine_state->frame_samples_diff -= backin_frame->frame_info.pcm_samples;
soc_log_info("samples frame store %d \n", engine_state->frame_samples_diff);
return TD_SUCCESS;
}
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */