/* * 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 #include #include #include #include #include #include #include #include #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(¶m, 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, ¶m); 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(¶m.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, ¶m); 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(¶m.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, ¶ms); 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 */