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.
2463 lines
59 KiB
2463 lines
59 KiB
/*
|
|
* Copyright (c) Hisilicon Technologies Co., Ltd. 2012-2019. All rights reserved.
|
|
* Description: ai mpi function
|
|
* Author: audio
|
|
* Create: 2012-05-30
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <stdint.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/prctl.h>
|
|
|
|
#include "mpi_ai_debug.h"
|
|
|
|
#include "soc_math.h"
|
|
#include "mpi_memory_ext.h"
|
|
#include "mpi_system_ext.h"
|
|
#include "securec.h"
|
|
|
|
#include "mpi_ai_trans.h"
|
|
#include "mpi_ai_ext.h"
|
|
#include "mpi_aenc_ext.h"
|
|
#include "drv_ioctl_ai.h"
|
|
#include "mpi_ao_ext.h"
|
|
|
|
#include "iec61937_parser.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif /* __cplusplus */
|
|
|
|
#define AI_MAX_DST 4
|
|
#define AI_SLEEP_TIME_MS 5
|
|
#define AI_CHECK_DELAY_SLEEP_TIME_MS 1
|
|
#define AO_TRACK_DELAY_FOR_AI_TIME 20
|
|
#define U32_MAX_VALUE 0xffffffff
|
|
#define LINE_IN_VOLUME_MAX 100
|
|
|
|
/* when you wait delay,
|
|
* there is delay between get ai port delay and track start.
|
|
* 6ms is experience value
|
|
*/
|
|
#define EXT_AI_CMD_DELAY 6
|
|
|
|
typedef enum {
|
|
AI_CHN_STATUS_STOP = 0,
|
|
AI_CHN_STATUS_START,
|
|
AI_CHN_STATUS_RESET,
|
|
AI_CHN_STATUS_MAX,
|
|
} ai_chn_status;
|
|
|
|
typedef struct {
|
|
td_handle h_ai;
|
|
|
|
td_handle h_track; /* ai->ao by aip buff */
|
|
td_bool need_start; /* start track */
|
|
td_bool track_start;
|
|
td_bool real_pcm;
|
|
|
|
td_u32 dst_num;
|
|
td_handle h_dst[AI_MAX_DST];
|
|
|
|
td_bool pass_through;
|
|
ext_parser_handle h_parser;
|
|
ext_ai_port ai_port;
|
|
ext_ai_attr attr;
|
|
td_u32 channel;
|
|
td_u32 bit_depth;
|
|
|
|
ai_proc_info *ai_proc_info;
|
|
td_u32 proc_phy_addr;
|
|
|
|
td_bool ai_thread_run;
|
|
pthread_t ai_data_thd_inst; /* run handle of ai thread */
|
|
pthread_t ai_track_thd_inst; /* run handle of start track */
|
|
pthread_t ai_adec_thd_inst;
|
|
} ai_mpi_state;
|
|
|
|
typedef struct {
|
|
ai_mpi_state *ai[AI_MAX_TOTAL_NUM];
|
|
} ai_mpi_resource;
|
|
|
|
static td_s32 g_ai_fd = -1;
|
|
static ai_mpi_resource g_ai_res;
|
|
|
|
static pthread_mutex_t g_ai_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
static pthread_mutex_t g_ai_api_mutex[AI_MAX_TOTAL_NUM] = {
|
|
[0 ...(AI_MAX_TOTAL_NUM - 1)] = PTHREAD_MUTEX_INITIALIZER,
|
|
};
|
|
static pthread_mutex_t g_ai_data_mutex[AI_MAX_TOTAL_NUM] = {
|
|
[0 ...(AI_MAX_TOTAL_NUM - 1)] = PTHREAD_MUTEX_INITIALIZER,
|
|
};
|
|
static pthread_mutex_t g_ai_io_mutex[AI_MAX_TOTAL_NUM] = {
|
|
[0 ...(AI_MAX_TOTAL_NUM - 1)] = PTHREAD_MUTEX_INITIALIZER,
|
|
};
|
|
static pthread_mutex_t g_ai_cmd_mutex[AI_MAX_TOTAL_NUM] = {
|
|
[0 ...(AI_MAX_TOTAL_NUM - 1)] = PTHREAD_MUTEX_INITIALIZER,
|
|
};
|
|
|
|
#define AI_DEV_NAME "/dev/soc_ai"
|
|
#define check_ao_handle(ao) (((ao) >> 16) == SOC_ID_AO)
|
|
#define check_aenc_id(aenc) (((aenc) >> 16) == SOC_ID_AENC)
|
|
|
|
static td_void ai_mutex_lock(pthread_mutex_t *mutex)
|
|
{
|
|
if (mutex == TD_NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pthread_mutex_lock(mutex) != 0) {
|
|
soc_log_err("Lock mutex failed\n");
|
|
}
|
|
}
|
|
|
|
static td_void ai_mutex_unlock(pthread_mutex_t *mutex)
|
|
{
|
|
if (mutex == TD_NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pthread_mutex_unlock(mutex) != 0) {
|
|
soc_log_err("Unlock mutex failed\n");
|
|
}
|
|
}
|
|
|
|
static td_void ai_lock(td_void)
|
|
{
|
|
ai_mutex_lock(&g_ai_mutex);
|
|
}
|
|
|
|
static td_void ai_unlock(td_void)
|
|
{
|
|
ai_mutex_unlock(&g_ai_mutex);
|
|
}
|
|
|
|
static td_void ai_ch_api_lock(td_handle ai)
|
|
{
|
|
td_handle ai_lock = ai & AI_CHNID_MASK;
|
|
if (ai_lock >= AI_MAX_TOTAL_NUM) {
|
|
return;
|
|
}
|
|
|
|
ai_mutex_lock(&g_ai_api_mutex[ai_lock]);
|
|
}
|
|
|
|
static td_void ai_ch_api_unlock(td_handle ai)
|
|
{
|
|
td_handle ai_unlock = ai & AI_CHNID_MASK;
|
|
if (ai_unlock >= AI_MAX_TOTAL_NUM) {
|
|
return;
|
|
}
|
|
|
|
ai_mutex_unlock(&g_ai_api_mutex[ai_unlock]);
|
|
}
|
|
|
|
static td_void ai_ch_data_lock(td_handle ai)
|
|
{
|
|
td_handle ai_lock = ai & AI_CHNID_MASK;
|
|
if (ai_lock >= AI_MAX_TOTAL_NUM) {
|
|
return;
|
|
}
|
|
|
|
ai_mutex_lock(&g_ai_data_mutex[ai_lock]);
|
|
}
|
|
|
|
static td_void ai_ch_data_unlock(td_handle ai)
|
|
{
|
|
td_handle ai_unlock = ai & AI_CHNID_MASK;
|
|
if (ai_unlock >= AI_MAX_TOTAL_NUM) {
|
|
return;
|
|
}
|
|
|
|
ai_mutex_unlock(&g_ai_data_mutex[ai_unlock]);
|
|
}
|
|
|
|
static td_void ai_ch_io_lock(td_handle ai)
|
|
{
|
|
td_handle ai_lock = ai & AI_CHNID_MASK;
|
|
if (ai_lock >= AI_MAX_TOTAL_NUM) {
|
|
return;
|
|
}
|
|
|
|
ai_mutex_lock(&g_ai_io_mutex[ai_lock]);
|
|
}
|
|
|
|
static td_void ai_ch_io_unlock(td_handle ai)
|
|
{
|
|
td_handle ai_unlock = ai & AI_CHNID_MASK;
|
|
if (ai_unlock >= AI_MAX_TOTAL_NUM) {
|
|
return;
|
|
}
|
|
|
|
ai_mutex_unlock(&g_ai_io_mutex[ai_unlock]);
|
|
}
|
|
|
|
static td_void ai_ch_cmd_lock(td_handle ai)
|
|
{
|
|
td_handle ai_lock = ai & AI_CHNID_MASK;
|
|
if (ai_lock >= AI_MAX_TOTAL_NUM) {
|
|
return;
|
|
}
|
|
|
|
ai_mutex_lock(&g_ai_cmd_mutex[ai_lock]);
|
|
}
|
|
|
|
static td_void ai_ch_cmd_unlock(td_handle ai)
|
|
{
|
|
td_handle ai_unlock = ai & AI_CHNID_MASK;
|
|
if (ai_unlock >= AI_MAX_TOTAL_NUM) {
|
|
return;
|
|
}
|
|
|
|
ai_mutex_unlock(&g_ai_cmd_mutex[ai_unlock]);
|
|
}
|
|
|
|
#define check_ai_id(handle) do { \
|
|
if (((handle) >= AI_MAX_HANDLE_ID) || ((handle) < AI_MIN_HANDLE_ID)) { \
|
|
soc_log_err("invalid ai id!\n"); \
|
|
soc_err_print_h32((handle)); \
|
|
return SOC_ERR_AI_INVALID_ID; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define TIME_RATION 1000
|
|
|
|
static td_void ai_sleep(td_slong ms)
|
|
{
|
|
struct timespec ts;
|
|
|
|
if (ms == 0) {
|
|
return;
|
|
}
|
|
|
|
ts.tv_sec = ms / TIME_RATION;
|
|
ts.tv_nsec = (ms % TIME_RATION) * TIME_RATION * TIME_RATION;
|
|
|
|
if (nanosleep(&ts, TD_NULL) != 0) {
|
|
soc_log_err("call nanosleep failed\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static ai_mpi_state *ai_get_chan(td_handle h_ai)
|
|
{
|
|
td_u32 id;
|
|
ai_mpi_state *ai_state = TD_NULL;
|
|
|
|
id = h_ai & AI_CHNID_MASK;
|
|
if (id >= AI_MAX_TOTAL_NUM) {
|
|
soc_err_print_h32(h_ai);
|
|
return TD_NULL;
|
|
}
|
|
|
|
ai_state = g_ai_res.ai[id];
|
|
if (ai_state == TD_NULL) {
|
|
return TD_NULL;
|
|
}
|
|
|
|
if (ai_state->h_ai != h_ai) {
|
|
return TD_NULL;
|
|
}
|
|
|
|
return ai_state;
|
|
}
|
|
|
|
static td_s32 ai_get_cur_delay(td_handle h_ai, td_u32 *delay);
|
|
|
|
#define ai_array_size(array) (sizeof((array)) / sizeof((array)[0]))
|
|
|
|
static td_bool ai_port_identify(ext_ai_port ai_port,
|
|
const ext_ai_port support_port[], td_u32 support_port_cnt)
|
|
{
|
|
td_u32 i;
|
|
|
|
for (i = 0; i < support_port_cnt; i++) {
|
|
if (ai_port == support_port[i]) {
|
|
return TD_TRUE;
|
|
}
|
|
}
|
|
|
|
return TD_FALSE;
|
|
}
|
|
|
|
static td_bool ai_port_is_i2s(ext_ai_port ai_port)
|
|
{
|
|
const ext_ai_port port_group[] = {
|
|
EXT_AI_I2S0,
|
|
EXT_AI_I2S1,
|
|
EXT_AI_I2S2,
|
|
EXT_AI_I2S3,
|
|
EXT_AI_I2S4,
|
|
};
|
|
|
|
return ai_port_identify(ai_port, port_group,
|
|
ai_array_size(port_group));
|
|
}
|
|
|
|
static td_bool ai_port_is_adc(ext_ai_port ai_port)
|
|
{
|
|
const ext_ai_port port_group[] = {
|
|
EXT_AI_ADC0,
|
|
EXT_AI_ADC1,
|
|
EXT_AI_ADC2,
|
|
EXT_AI_ADC3,
|
|
EXT_AI_ADC4,
|
|
};
|
|
|
|
return ai_port_identify(ai_port, port_group,
|
|
ai_array_size(port_group));
|
|
}
|
|
|
|
static td_bool ai_port_is_hdmi(ext_ai_port ai_port)
|
|
{
|
|
const ext_ai_port port_group[] = {
|
|
EXT_AI_HDMI0,
|
|
EXT_AI_HDMI1,
|
|
EXT_AI_HDMI2,
|
|
EXT_AI_HDMI3,
|
|
};
|
|
|
|
return ai_port_identify(ai_port, port_group,
|
|
ai_array_size(port_group));
|
|
}
|
|
|
|
static td_bool ai_port_is_sif(ext_ai_port ai_port)
|
|
{
|
|
const ext_ai_port port_group[] = {
|
|
EXT_AI_SIF0,
|
|
};
|
|
|
|
return ai_port_identify(ai_port, port_group,
|
|
ai_array_size(port_group));
|
|
}
|
|
|
|
static td_void ai_chan_i2s_set_attr(ai_mpi_state *ai_state, const ext_ai_attr *ai_attr)
|
|
{
|
|
ai_state->channel = ai_attr->un_attr.i2s_attr.attr.channel;
|
|
ai_state->bit_depth = ai_attr->un_attr.i2s_attr.attr.bit_depth;
|
|
}
|
|
|
|
static td_void ai_chan_adc_set_attr(ai_mpi_state *ai_state, const ext_ai_attr *ai_attr)
|
|
{
|
|
TD_UNUSED(ai_attr);
|
|
ai_state->channel = EXT_AUDIO_CH_STEREO;
|
|
ai_state->bit_depth = EXT_BIT_DEPTH_16;
|
|
}
|
|
|
|
static td_void ai_chan_hdmi_set_attr(ai_mpi_state *ai_state, const ext_ai_attr *ai_attr)
|
|
{
|
|
const ext_ai_hdmi_attr *hdmi_attr = &ai_attr->un_attr.hdmi_attr;
|
|
|
|
ai_state->channel = hdmi_attr->channel;
|
|
ai_state->bit_depth = hdmi_attr->bit_depth;
|
|
|
|
if ((hdmi_attr->hdmi_audio_data_format == EXT_AI_HDMI_FORMAT_LBR) ||
|
|
(hdmi_attr->hdmi_audio_data_format == EXT_AI_HDMI_FORMAT_HBR)) {
|
|
ai_state->pass_through = TD_TRUE;
|
|
ai_state->ai_proc_info->data_type = EXT_AUDIO_FORMAT_STREAM;
|
|
} else {
|
|
ai_state->pass_through = TD_FALSE;
|
|
ai_state->ai_proc_info->data_type = EXT_AUDIO_FORMAT_PCM;
|
|
}
|
|
}
|
|
|
|
static td_void ai_chan_sif_set_attr(ai_mpi_state *ai_state, const ext_ai_attr *ai_attr)
|
|
{
|
|
ai_state->channel = ai_attr->un_attr.sif_attr.channels;
|
|
ai_state->bit_depth = ai_attr->un_attr.sif_attr.bit_depth;
|
|
}
|
|
|
|
static td_void ai_set_chn_attr(ext_ai_port ai_port, const ext_ai_attr *ai_attr, ai_mpi_state *ai_state)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 i;
|
|
const struct {
|
|
td_bool (*identify)(ext_ai_port ai_port);
|
|
td_void (*set_attr)(ai_mpi_state *ai_state, const ext_ai_attr *ai_attr);
|
|
} ops[] = {
|
|
{ai_port_is_i2s, ai_chan_i2s_set_attr},
|
|
{ai_port_is_adc, ai_chan_adc_set_attr},
|
|
{ai_port_is_hdmi, ai_chan_hdmi_set_attr},
|
|
{ai_port_is_sif, ai_chan_sif_set_attr},
|
|
};
|
|
|
|
ret = memcpy_s(&ai_state->attr, sizeof(ext_ai_attr),
|
|
ai_attr, sizeof(*ai_attr));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memcpy_s, ret);
|
|
return;
|
|
}
|
|
|
|
ai_state->ai_port = ai_port;
|
|
|
|
for (i = 0; i < ai_array_size(ops); i++) {
|
|
if (ops[i].identify(ai_port) == TD_FALSE) {
|
|
continue;
|
|
}
|
|
ops[i].set_attr(ai_state, ai_attr);
|
|
return;
|
|
}
|
|
|
|
/* ai_port not support */
|
|
soc_fatal_print_h32(ai_port);
|
|
}
|
|
|
|
static td_u32 ai_calc_frame_size(td_u32 ch, td_u32 bit_depth)
|
|
{
|
|
td_u32 res = 0;
|
|
if (bit_depth == EXT_BIT_DEPTH_16) {
|
|
res = ch * sizeof(td_u16);
|
|
} else if (bit_depth == EXT_BIT_DEPTH_24) {
|
|
res = ch * sizeof(td_u32);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static td_u32 ai_chan_cal_acquire_size(const ai_mpi_state *ai_state)
|
|
{
|
|
if (ai_state->attr.pcm_samples_per_frame > BITS_BURSTLENGTH_MAT) {
|
|
return 0;
|
|
}
|
|
|
|
if (ai_state->channel > EXT_AUDIO_CH_16) {
|
|
return 0;
|
|
}
|
|
|
|
return ai_state->attr.pcm_samples_per_frame *
|
|
ai_calc_frame_size(ai_state->channel, ai_state->bit_depth);
|
|
}
|
|
|
|
static td_void ai_build_frame(const ai_mpi_state *ai_state, ext_ao_frame *iapi_frame)
|
|
{
|
|
td_u32 time_ms = 0;
|
|
td_u32 aidelay_ms = 0;
|
|
|
|
ext_mpi_sys_get_time_stamp_ms(&time_ms);
|
|
ai_get_cur_delay(ai_state->h_ai, &aidelay_ms);
|
|
|
|
iapi_frame->pts = (td_s64)(time_ms - aidelay_ms);
|
|
iapi_frame->bit_depth = ai_state->bit_depth;
|
|
iapi_frame->interleaved = TD_TRUE;
|
|
iapi_frame->sample_rate = ai_state->attr.sample_rate;
|
|
iapi_frame->channels = ai_state->channel;
|
|
iapi_frame->frame_index = 0;
|
|
iapi_frame->pcm_samples = ai_state->attr.pcm_samples_per_frame;
|
|
iapi_frame->bits_bytes = 0;
|
|
iapi_frame->iec_data_type = 0;
|
|
iapi_frame->bits_buffer = TD_NULL;
|
|
iapi_frame->pcm_buffer = TD_NULL;
|
|
}
|
|
|
|
static td_s32 ai_kernel_release_frame(td_handle h_ai)
|
|
{
|
|
td_s32 ret;
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_RELEASEFRAME, &h_ai);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_RELEASEFRAME failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 ai_kernel_acquire_frame(const ai_mpi_state *ai_state, td_u32 request_size, ext_ao_frame *frame)
|
|
{
|
|
td_s32 ret;
|
|
ai_buf_param ai_buf_info = {
|
|
.ai = ai_state->h_ai,
|
|
.ai_buf = {0},
|
|
};
|
|
|
|
ai_frame_param ai_get_frame = {
|
|
.ai = ai_state->h_ai,
|
|
.need_bytes = request_size,
|
|
};
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_GETBUFINFO, &ai_buf_info);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_GETBUFINFO failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_ACQUIREFRAME, &ai_get_frame);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_warn("ioctl CMD_AI_ACQUIREFRAME failed\n");
|
|
soc_warn_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
ai_build_frame(ai_state, frame);
|
|
|
|
frame->pcm_buffer = (td_s32 *)((uintptr_t)ai_buf_info.ai_buf.user_vir_addr);
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 ai_parser_send_frame(const ai_mpi_state *ai_state)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 need_bytes = 0;
|
|
ext_ao_frame ao_frame = { 0 };
|
|
iec61937_parser_stream_buf stream = {TD_NULL, 0};
|
|
|
|
ret = iec61937_parser_get_burst_len(ai_state->h_parser, &need_bytes);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(iec61937_parser_get_burst_len, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = iec61937_parser_get_buf(ai_state->h_parser, need_bytes, &stream);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(iec61937_parser_get_buf, ret);
|
|
return ret;
|
|
}
|
|
|
|
if (stream.size == 0) {
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
ret = ai_kernel_acquire_frame(ai_state, stream.size, &ao_frame);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_kernel_acquire_frame, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = memcpy_s(stream.data, stream.size,
|
|
ao_frame.pcm_buffer, stream.size * sizeof(td_u8));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memcpy_s, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ai_kernel_release_frame(ai_state->h_ai);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_kernel_release_frame, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = iec61937_parser_put_buf(ai_state->h_parser, &stream);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(iec61937_parser_put_buf, ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 ai_parser_acquire_frame(ai_mpi_state *ai_state, ext_ao_frame *frame)
|
|
{
|
|
td_s32 ret;
|
|
|
|
ret = ai_parser_send_frame(ai_state);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_warn_print_call_fun_err(ai_parser_send_frame, ret);
|
|
}
|
|
|
|
if (ret == SOC_ERR_AI_NOT_ENOUGH_DATA) {
|
|
soc_log_dbg("not enough data.\n");
|
|
return ret;
|
|
}
|
|
|
|
if (iec61937_check_is_empty(ai_state->h_parser) == TD_TRUE) {
|
|
soc_log_dbg("not enough data.\n");
|
|
return SOC_ERR_AI_NOT_ENOUGH_DATA;
|
|
}
|
|
|
|
ret = iec61937_parser_acquire_frame(ai_state->h_parser,
|
|
(td_void *)(&frame->pcm_buffer), &frame->bits_bytes);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_warn_print_call_fun_err(iec61937_parser_acquire_frame, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline td_s32 ai_parser_release_frame(ext_parser_handle h_parser, const ext_ao_frame *frame)
|
|
{
|
|
check_ai_null_ptr(h_parser);
|
|
check_ai_null_ptr(frame);
|
|
|
|
return iec61937_parser_release_frame(h_parser, frame->bits_bytes);
|
|
}
|
|
|
|
static td_s32 ai_dev_get_enable(td_handle ai, td_bool *enable)
|
|
{
|
|
td_s32 ret;
|
|
ai_enable_param ai_enable = {
|
|
.ai = ai,
|
|
.ai_enable = TD_FALSE,
|
|
};
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(enable);
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_GETENABLE, &ai_enable);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_GETENABLE failed!\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
*enable = ai_enable.ai_enable;
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static inline td_void ai_thread_lock(td_handle ai)
|
|
{
|
|
ai_ch_io_lock(ai);
|
|
ai_ch_cmd_lock(ai);
|
|
}
|
|
|
|
static inline td_void ai_thread_unlock(td_handle ai)
|
|
{
|
|
ai_ch_cmd_unlock(ai);
|
|
ai_ch_io_unlock(ai);
|
|
}
|
|
|
|
static td_bool ai_thread_idle(td_handle ai)
|
|
{
|
|
td_s32 ret;
|
|
td_bool enable = TD_FALSE;
|
|
|
|
ret = ai_dev_get_enable(ai, &enable);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_dev_get_enable, ret);
|
|
return TD_TRUE;
|
|
}
|
|
|
|
return !enable;
|
|
}
|
|
|
|
static td_bool ai_adec_thread_run_parser(ai_mpi_state *ai_state)
|
|
{
|
|
td_s32 ret;
|
|
ext_ao_frame ao_frame;
|
|
|
|
if (ai_thread_idle(ai_state->h_ai) == TD_TRUE) {
|
|
return TD_FALSE;
|
|
}
|
|
|
|
ret = ai_parser_acquire_frame(ai_state, &ao_frame);
|
|
if (ret == SOC_ERR_AI_FIND_SYNC_FAILED) {
|
|
ai_state->real_pcm = TD_TRUE;
|
|
return TD_TRUE;
|
|
}
|
|
|
|
if (ret != TD_SUCCESS) {
|
|
return TD_FALSE;
|
|
}
|
|
|
|
ret = ai_parser_release_frame(ai_state->h_parser, &ao_frame);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_parser_release_frame, ret);
|
|
return TD_TRUE;
|
|
}
|
|
|
|
ai_state->pass_through = TD_TRUE;
|
|
return TD_TRUE;
|
|
}
|
|
|
|
static td_void *ai_adec_thread(td_void *arg)
|
|
{
|
|
td_handle ai;
|
|
ai_mpi_state *ai_state = (ai_mpi_state *)arg;
|
|
|
|
if (ai_state == TD_NULL) {
|
|
return TD_NULL;
|
|
}
|
|
|
|
prctl(PR_SET_NAME, "ai_adec");
|
|
ai = ai_state->h_ai;
|
|
/* if passthrough or attach aenc, do not need to check */
|
|
if (ai_state->pass_through == TD_TRUE) {
|
|
return TD_NULL;
|
|
}
|
|
|
|
/* dtscd format only can be 44.1K */
|
|
if ((ai_state->attr.sample_rate != EXT_SAMPLE_RATE_44K) ||
|
|
(ai_port_is_hdmi(ai_state->ai_port) != TD_TRUE)) {
|
|
ai_state->real_pcm = TD_TRUE;
|
|
return TD_NULL;
|
|
}
|
|
|
|
while (ai_state->ai_thread_run == TD_TRUE) {
|
|
if (ai_state->dst_num != 0 && (ai_state->h_track == TD_INVALID_HANDLE)) {
|
|
ai_sleep(AI_CHECK_DELAY_SLEEP_TIME_MS);
|
|
continue;
|
|
}
|
|
|
|
ai_thread_lock(ai);
|
|
if (ai_adec_thread_run_parser(ai_state) == TD_TRUE) {
|
|
soc_log_notice("check datafomat success, ai_state->real_pcm = %d, ai_state->pass_through = %d\n",
|
|
ai_state->real_pcm, ai_state->pass_through);
|
|
ai_thread_unlock(ai);
|
|
break;
|
|
}
|
|
|
|
ai_thread_unlock(ai);
|
|
ai_sleep(AI_CHECK_DELAY_SLEEP_TIME_MS); /* 1000 us */
|
|
continue;
|
|
}
|
|
|
|
return TD_NULL;
|
|
}
|
|
|
|
static td_s32 ext_mpi_ai_get_status(td_handle h_ai, td_u32 *status);
|
|
|
|
enum {
|
|
STATUS_IDLE,
|
|
STATUS_CHECK,
|
|
STATUS_START,
|
|
};
|
|
|
|
typedef struct {
|
|
td_u32 thread_status;
|
|
|
|
td_u32 track_delay;
|
|
td_u32 ai_delay;
|
|
ext_ai_delay ai_delay_compensation;
|
|
|
|
td_u32 start_time;
|
|
td_u32 cur_time;
|
|
td_u32 delta_time;
|
|
} ai_track_thread_state;
|
|
|
|
static td_s32 ai_track_thread_state_init(ai_track_thread_state *state)
|
|
{
|
|
td_s32 ret;
|
|
|
|
ret = memset_s(state, sizeof(*state),
|
|
0, sizeof(ai_track_thread_state));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memset_s, ret);
|
|
return ret;
|
|
}
|
|
|
|
state->thread_status = STATUS_IDLE;
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_void ai_chan_try_self_reset(td_handle ai)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 status = AI_CHN_STATUS_MAX;
|
|
ext_ai_attr ai_attr;
|
|
|
|
ret = ext_mpi_ai_get_status(ai, &status);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_ai_get_status, ret);
|
|
return;
|
|
}
|
|
|
|
if (status != AI_CHN_STATUS_RESET) {
|
|
return;
|
|
}
|
|
|
|
ret = memset_s(&ai_attr, sizeof(ai_attr),
|
|
0, sizeof(ext_ai_attr));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memset_s, ret);
|
|
return;
|
|
}
|
|
|
|
ret = ext_mpi_ai_get_attr(ai, &ai_attr);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_ai_get_attr, ret);
|
|
return;
|
|
}
|
|
|
|
ret = ext_mpi_ai_set_enable(ai, TD_FALSE);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_ai_set_enable, ret);
|
|
return;
|
|
}
|
|
|
|
ret = ext_mpi_ai_set_attr(ai, &ai_attr);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_ai_set_attr, ret);
|
|
return;
|
|
}
|
|
|
|
ret = ext_mpi_ai_set_enable(ai, TD_TRUE);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_ai_set_enable, ret);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static td_void ai_track_thread_idle(ai_track_thread_state *state, const ai_mpi_state *ai_state)
|
|
{
|
|
td_s32 ret;
|
|
td_bool enable = TD_FALSE;
|
|
|
|
if (ai_state->h_track == TD_INVALID_HANDLE) {
|
|
return;
|
|
}
|
|
|
|
if (ai_state->real_pcm != TD_TRUE) {
|
|
return;
|
|
}
|
|
|
|
ret = ai_dev_get_enable(ai_state->h_ai, &enable);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_dev_get_enable, ret);
|
|
return;
|
|
}
|
|
|
|
if (enable != TD_TRUE) {
|
|
return;
|
|
}
|
|
|
|
if (ai_state->need_start != TD_TRUE) {
|
|
ai_ch_cmd_unlock(ai_state->h_ai);
|
|
ai_chan_try_self_reset(ai_state->h_ai);
|
|
ai_ch_cmd_lock(ai_state->h_ai);
|
|
return;
|
|
}
|
|
|
|
if (state->thread_status == STATUS_IDLE) {
|
|
ret = ext_mpi_sys_get_time_stamp_ms(&state->start_time);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_sys_get_time_stamp_ms, ret);
|
|
return;
|
|
}
|
|
}
|
|
|
|
state->thread_status = STATUS_CHECK;
|
|
}
|
|
|
|
static td_s32 ai_track_thread_get_info(ai_track_thread_state *state, const ai_mpi_state *ai_state)
|
|
{
|
|
td_s32 ret;
|
|
|
|
ret = ext_mpi_sys_get_time_stamp_ms(&state->cur_time);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_sys_get_time_stamp_ms, ret);
|
|
return ret;
|
|
}
|
|
|
|
if (state->cur_time >= state->start_time) {
|
|
state->delta_time = state->cur_time - state->start_time;
|
|
} else {
|
|
state->delta_time = (U32_MAX_VALUE - state->start_time) + (state->cur_time + 1);
|
|
}
|
|
|
|
ret = ext_mpi_ao_track_get_delay(ai_state->h_track, &state->track_delay);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_ao_track_get_delay, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ext_mpi_ai_get_delay(ai_state->h_ai, &state->ai_delay_compensation);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_ai_get_delay, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ai_get_cur_delay(ai_state->h_ai, &state->ai_delay);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_get_cur_delay, ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_void ai_track_thread_check(ai_track_thread_state *state, const ai_mpi_state *ai_state)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 delay_compensation;
|
|
td_u32 sys_time_check;
|
|
td_u32 track_delay_check;
|
|
|
|
if (ai_state->h_track == TD_INVALID_HANDLE) {
|
|
return;
|
|
}
|
|
|
|
if (state->thread_status != STATUS_CHECK) {
|
|
return;
|
|
}
|
|
|
|
ret = ai_track_thread_get_info(state, ai_state);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_track_thread_get_info, ret);
|
|
return;
|
|
}
|
|
|
|
delay_compensation = state->track_delay + state->ai_delay + EXT_AI_CMD_DELAY;
|
|
if (delay_compensation < state->ai_delay_compensation.delay) {
|
|
return;
|
|
}
|
|
|
|
track_delay_check = state->track_delay + state->ai_delay_compensation.delay;
|
|
sys_time_check = track_delay_check + state->delta_time;
|
|
if ((sys_time_check >= AO_TRACK_DELAY_FOR_AI_TIME) ||
|
|
(track_delay_check >= AO_TRACK_DELAY_FOR_AI_TIME)) {
|
|
state->thread_status = STATUS_START;
|
|
}
|
|
}
|
|
|
|
static td_void ai_track_thread_start_track(ai_track_thread_state *state, ai_mpi_state *ai_state)
|
|
{
|
|
td_s32 ret;
|
|
|
|
if (ai_state->h_track == TD_INVALID_HANDLE) {
|
|
return;
|
|
}
|
|
|
|
if (state->thread_status != STATUS_START) {
|
|
return;
|
|
}
|
|
|
|
ret = ext_mpi_ao_track_start(ai_state->h_track);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_ao_track_start, ret);
|
|
return;
|
|
}
|
|
|
|
ai_state->track_start = TD_TRUE;
|
|
ai_state->need_start = TD_FALSE;
|
|
state->thread_status = STATUS_IDLE;
|
|
}
|
|
|
|
static td_void *ai_track_thread(td_void *arg)
|
|
{
|
|
td_s32 ret;
|
|
td_handle ai;
|
|
ai_track_thread_state thread_state;
|
|
ai_mpi_state *ai_state = (ai_mpi_state *)arg;
|
|
if (ai_state == TD_NULL) {
|
|
return TD_NULL;
|
|
}
|
|
|
|
prctl(PR_SET_NAME, "ai_track");
|
|
ai = ai_state->h_ai;
|
|
|
|
ret = ai_track_thread_state_init(&thread_state);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_track_thread_state_init, ret);
|
|
return TD_NULL;
|
|
}
|
|
|
|
while (ai_state->ai_thread_run == TD_TRUE) {
|
|
ai_ch_cmd_lock(ai);
|
|
ai_track_thread_idle(&thread_state, ai_state);
|
|
ai_track_thread_check(&thread_state, ai_state);
|
|
ai_track_thread_start_track(&thread_state, ai_state);
|
|
ai_ch_cmd_unlock(ai);
|
|
ai_sleep(AI_CHECK_DELAY_SLEEP_TIME_MS);
|
|
}
|
|
|
|
return TD_NULL;
|
|
}
|
|
|
|
static td_s32 ai_chan_send_frame(const ai_mpi_state *ai_state, const ext_ao_frame *ao_frame)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 i;
|
|
td_handle handle;
|
|
|
|
td_s32 (*send_frame)(td_handle handle, const ext_ao_frame *frame) = TD_NULL;
|
|
|
|
for (i = 0; i < AI_MAX_DST; i++) {
|
|
handle = ai_state->h_dst[i];
|
|
if (check_ao_handle(handle)) {
|
|
send_frame = ext_mpi_ao_track_send_data;
|
|
} else if (check_aenc_id(handle)) {
|
|
send_frame = ext_mpi_aenc_send_buffer;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
ret = send_frame(handle, ao_frame);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_warn_print_call_fun_err(send_frame, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
enum {
|
|
STATUS_ACQUIRE_FRAME,
|
|
STATUS_SEND_FRAME,
|
|
STATUS_RELEASE_FRAME,
|
|
};
|
|
|
|
static td_void *ai_data_thread(td_void *arg)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 status = STATUS_ACQUIRE_FRAME;
|
|
td_u32 sleep_ms = 0;
|
|
ai_mpi_state *ai_state = (ai_mpi_state *)arg;
|
|
ext_ao_frame ai_frame = { 0 };
|
|
const td_u32 request_size = ai_chan_cal_acquire_size(ai_state);
|
|
|
|
prctl(PR_SET_NAME, "ai_data");
|
|
while (ai_state->ai_thread_run == TD_TRUE) {
|
|
ai_thread_lock(ai_state->h_ai);
|
|
if ((ai_state->dst_num == 0) || (ai_thread_idle(ai_state->h_ai) == TD_TRUE) ||
|
|
((ai_state->h_track != TD_INVALID_HANDLE) && (ai_state->track_start != TD_TRUE))) {
|
|
ai_thread_unlock(ai_state->h_ai);
|
|
ai_sleep(AI_SLEEP_TIME_MS * 2); /* 2 is a number */
|
|
status = STATUS_ACQUIRE_FRAME;
|
|
continue;
|
|
}
|
|
|
|
if (status == STATUS_ACQUIRE_FRAME) {
|
|
ret = ai_kernel_acquire_frame(ai_state, request_size, &ai_frame);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_warn("call ext_mpi_ai_acquire_frame failed!\n");
|
|
sleep_ms = AI_SLEEP_TIME_MS << 1; /* 1 is a number */
|
|
} else {
|
|
status = STATUS_SEND_FRAME;
|
|
}
|
|
} else if (status == STATUS_SEND_FRAME) {
|
|
ret = ai_chan_send_frame(ai_state, &ai_frame);
|
|
if (ret != TD_SUCCESS) {
|
|
sleep_ms = AI_SLEEP_TIME_MS;
|
|
} else {
|
|
status = STATUS_RELEASE_FRAME;
|
|
}
|
|
} else if (status == STATUS_RELEASE_FRAME) {
|
|
ret = ai_kernel_release_frame(ai_state->h_ai);
|
|
if (ret != TD_SUCCESS) {
|
|
ai_thread_unlock(ai_state->h_ai);
|
|
soc_err_print_call_fun_err(ai_kernel_release_frame, ret);
|
|
continue;
|
|
}
|
|
status = STATUS_ACQUIRE_FRAME;
|
|
}
|
|
|
|
ai_thread_unlock(ai_state->h_ai);
|
|
ai_sleep(sleep_ms);
|
|
}
|
|
|
|
return TD_NULL;
|
|
}
|
|
|
|
static td_s32 ai_detach_ao_track(ai_mpi_state *ai_state, td_handle track)
|
|
{
|
|
td_s32 ret;
|
|
|
|
if ((track & 0xff) >= AO_MAX_REAL_TRACK_NUM) {
|
|
return SOC_ERR_AI_INVALID_PARA;
|
|
}
|
|
|
|
if (ai_state->h_track != track) {
|
|
soc_log_err("this track is not attach ai, can not detach!\n");
|
|
return SOC_ERR_AI_INVALID_PARA;
|
|
}
|
|
|
|
ret = ext_mpi_ao_track_detach_ai(ai_state->h_ai, track);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_ao_track_detach_ai, ret);
|
|
return ret;
|
|
}
|
|
|
|
ai_state->h_track = TD_INVALID_HANDLE;
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 ai_attach_ao_track(ai_mpi_state *ai_state, td_handle track)
|
|
{
|
|
td_s32 ret;
|
|
|
|
if ((track & 0xff) >= AO_MAX_REAL_TRACK_NUM) {
|
|
return SOC_ERR_AI_INVALID_PARA;
|
|
}
|
|
|
|
if (ai_state->h_track == track) {
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
if (ai_state->h_track != TD_INVALID_HANDLE) {
|
|
soc_log_err("AI can not attach more than one slave/master track!\n");
|
|
return SOC_ERR_AI_NOTSUPPORT;
|
|
}
|
|
|
|
if (ai_state->dst_num != 0) {
|
|
soc_log_err("AI is already attached by virtual track or aenc!\n");
|
|
return SOC_ERR_AI_NOTSUPPORT;
|
|
}
|
|
|
|
ret = ext_mpi_ao_track_attach_ai(ai_state->h_ai, track);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_ao_track_attach_ai, ret);
|
|
return ret;
|
|
}
|
|
|
|
ai_state->h_track = track;
|
|
ai_state->need_start = TD_TRUE;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_bool ai_check_aenc_attach(const ai_mpi_state *ai_state, td_handle aenc)
|
|
{
|
|
td_u32 i;
|
|
|
|
for (i = 0; i < AI_MAX_DST; i++) {
|
|
if (ai_state->h_dst[i] == aenc) {
|
|
return TD_TRUE;
|
|
}
|
|
}
|
|
|
|
return TD_FALSE;
|
|
}
|
|
|
|
static td_s32 ai_save_aenc_handle(ai_mpi_state *ai_state, td_handle aenc)
|
|
{
|
|
td_u32 i;
|
|
|
|
for (i = 0; i < AI_MAX_DST; i++) {
|
|
if (ai_state->h_dst[i] == TD_INVALID_HANDLE) {
|
|
ai_state->h_dst[i] = aenc;
|
|
ai_state->dst_num++;
|
|
return TD_SUCCESS;
|
|
}
|
|
}
|
|
|
|
soc_log_err("AI has attached max dst.\n");
|
|
return SOC_ERR_AI_NOTSUPPORT;
|
|
}
|
|
|
|
static td_s32 ai_detach_aenc(ai_mpi_state *ai_state, td_handle aenc)
|
|
{
|
|
td_u32 i;
|
|
|
|
for (i = 0; i < AI_MAX_DST; i++) {
|
|
if (ai_state->h_dst[i] == aenc) {
|
|
ai_state->h_dst[i] = TD_INVALID_HANDLE;
|
|
ai_state->dst_num--;
|
|
return TD_SUCCESS;
|
|
}
|
|
}
|
|
|
|
soc_log_err("this source is not attached, can not detach.\n");
|
|
return SOC_ERR_AI_NOTSUPPORT;
|
|
}
|
|
|
|
static td_s32 ai_attach_aenc(ai_mpi_state *ai_state, td_handle aenc)
|
|
{
|
|
td_s32 ret;
|
|
|
|
if (ai_check_aenc_attach(ai_state, aenc) == TD_TRUE) {
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
ret = ai_save_aenc_handle(ai_state, aenc);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_save_aenc_handle, ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 ai_attach(td_handle ai, td_handle handle)
|
|
{
|
|
ai_mpi_state *ai_state = ai_get_chan(ai);
|
|
if (ai_state == TD_NULL) {
|
|
soc_log_err("ai_get_chan failed\n");
|
|
return SOC_ERR_AI_INVALID_ID;
|
|
}
|
|
|
|
if (check_ao_handle(handle)) {
|
|
return ai_attach_ao_track(ai_state, handle);
|
|
} else if (check_aenc_id(handle)) {
|
|
return ai_attach_aenc(ai_state, handle);
|
|
} else {
|
|
soc_err_print_h32(handle);
|
|
return SOC_ERR_AI_INVALID_PARA;
|
|
}
|
|
}
|
|
|
|
static td_s32 ai_detach(td_handle ai, td_handle handle)
|
|
{
|
|
ai_mpi_state *ai_state = ai_get_chan(ai);
|
|
if (ai_state == TD_NULL) {
|
|
soc_log_err("ai_get_chan failed\n");
|
|
return SOC_ERR_AI_INVALID_ID;
|
|
}
|
|
|
|
if (check_ao_handle(handle)) {
|
|
return ai_detach_ao_track(ai_state, handle);
|
|
} else if (check_aenc_id(handle)) {
|
|
return ai_detach_aenc(ai_state, handle);
|
|
} else {
|
|
soc_err_print_h32(handle);
|
|
return SOC_ERR_AI_INVALID_PARA;
|
|
}
|
|
}
|
|
|
|
static td_void ai_proc_deinit(ai_mpi_state *ai_state)
|
|
{
|
|
td_s32 ret;
|
|
|
|
if (ai_state->ai_proc_info != TD_NULL) {
|
|
(td_void)munmap(ai_state->ai_proc_info, sizeof(ai_proc_info));
|
|
ai_state->ai_proc_info = TD_NULL;
|
|
}
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_PROCDEINIT, &ai_state->h_ai);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_PROCDEINIT failed!\n");
|
|
soc_err_print_err_code(ret);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static inline errno_t ai_proc_reset(ai_proc_info *ai_proc)
|
|
{
|
|
return memset_s(ai_proc, sizeof(*ai_proc),
|
|
0, sizeof(ai_proc_info));
|
|
}
|
|
|
|
static td_s32 ai_proc_init(ai_mpi_state *ai_state)
|
|
{
|
|
td_s32 ret;
|
|
ai_proc_init_param param = {
|
|
.ai = ai_state->h_ai,
|
|
.proc_phy_addr = 0,
|
|
.proc_buf_size = 0,
|
|
.proc_buf_map_fd = -1,
|
|
};
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_PROCINIT, ¶m);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_PROCINIT failed!\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
ai_state->ai_proc_info = (ai_proc_info *)mmap((td_void *)0, sizeof(ai_proc_info),
|
|
PROT_READ | PROT_WRITE, MAP_SHARED, param.proc_buf_map_fd, 0);
|
|
if (ai_state->ai_proc_info == MAP_FAILED) {
|
|
soc_log_err("mmap ai_proc_info failed!\n");
|
|
ret = TD_FAILURE;
|
|
goto out;
|
|
}
|
|
|
|
ret = ai_proc_reset(ai_state->ai_proc_info);
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(ai_proc_reset, ret);
|
|
goto out;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
out:
|
|
ai_proc_deinit(ai_state);
|
|
return ret;
|
|
}
|
|
|
|
static td_void mpi_ai_unregister_extern_func(td_void)
|
|
{
|
|
td_s32 ret;
|
|
|
|
ret = ext_mpi_ao_unregister_extern_func(SOC_ID_AI);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_ao_unregister_extern_func, ret);
|
|
}
|
|
}
|
|
|
|
static td_void mpi_ai_register_extern_func(td_void)
|
|
{
|
|
td_s32 ret;
|
|
|
|
const ext_ao_ext_module_fn ao_func = {
|
|
ext_mpi_ai_attach,
|
|
ext_mpi_ai_detach,
|
|
};
|
|
|
|
ret = ext_mpi_ao_register_extern_func(SOC_ID_AI, &ao_func);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_ao_register_extern_func, ret);
|
|
}
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_init(td_void)
|
|
{
|
|
ai_lock();
|
|
|
|
if (g_ai_fd < 0) {
|
|
g_ai_fd = open(AI_DEV_NAME, O_RDWR, 0);
|
|
if (g_ai_fd < 0) {
|
|
soc_log_fatal("open_ai_device err\n");
|
|
g_ai_fd = -1;
|
|
ai_unlock();
|
|
return SOC_ERR_AI_NOT_INIT;
|
|
}
|
|
}
|
|
|
|
ai_unlock();
|
|
mpi_ai_register_extern_func();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_deinit(td_void)
|
|
{
|
|
td_s32 ret;
|
|
|
|
mpi_ai_unregister_extern_func();
|
|
ai_lock();
|
|
|
|
if (g_ai_fd >= 0) {
|
|
ret = close(g_ai_fd);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(close, ret);
|
|
soc_err_print_s32(g_ai_fd);
|
|
}
|
|
|
|
g_ai_fd = -1;
|
|
}
|
|
|
|
ai_unlock();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_get_default_attr(ext_ai_port ai_port, ext_ai_attr *attr)
|
|
{
|
|
td_s32 ret;
|
|
ai_get_df_attr_param ai_get_df_attr = { 0 };
|
|
|
|
check_ai_port(ai_port);
|
|
check_ai_null_ptr(attr);
|
|
|
|
ai_get_df_attr.ai_port = ai_port;
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_GETDEFAULTATTR, &ai_get_df_attr);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_GETDEFAULTATTR failed!\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = memcpy_s(attr, sizeof(*attr),
|
|
&ai_get_df_attr.attr, sizeof(ext_ai_attr));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memcpy_s, ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 ai_dev_set_attr(td_handle ai, const ext_ai_attr *attr)
|
|
{
|
|
td_s32 ret;
|
|
ai_attr_param ai_set_attr = {
|
|
.ai = ai,
|
|
};
|
|
|
|
ret = memcpy_s(&ai_set_attr.attr, sizeof(ext_ai_attr),
|
|
attr, sizeof(*attr));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memcpy_s, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_SETATTR, &ai_set_attr);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_SETATTR failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 ai_chan_reattach_track(td_handle ai)
|
|
{
|
|
td_s32 ret;
|
|
td_handle track;
|
|
ai_mpi_state *ai_state = ai_get_chan(ai);
|
|
if (ai_state == TD_NULL) {
|
|
soc_log_err("ai_get_chan failed\n");
|
|
return SOC_ERR_AI_INVALID_ID;
|
|
}
|
|
|
|
track = ai_state->h_track;
|
|
if (track == TD_INVALID_HANDLE) {
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
ret = ai_detach(ai, track);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_detach, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ext_mpi_ao_track_stop(track);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ext_mpi_ao_track_stop, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ai_attach(ai, track);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_attach, ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 mpi_ai_set_attr(td_handle h_ai, const ext_ai_attr *attr)
|
|
{
|
|
td_s32 ret;
|
|
|
|
ret = ai_dev_set_attr(h_ai, attr);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_dev_set_attr, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ai_chan_reattach_track(h_ai);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_chan_reattach_track, ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_set_attr(td_handle ai, const ext_ai_attr *attr)
|
|
{
|
|
td_s32 ret;
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(attr);
|
|
|
|
ai_ch_api_lock(ai);
|
|
ai_ch_cmd_lock(ai);
|
|
ret = mpi_ai_set_attr(ai, attr);
|
|
ai_ch_cmd_unlock(ai);
|
|
ai_ch_api_unlock(ai);
|
|
|
|
return ret;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_get_attr(td_handle ai, ext_ai_attr *attr)
|
|
{
|
|
td_s32 ret;
|
|
ai_attr_param ai_get_attr = { 0 };
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(attr);
|
|
|
|
ai_get_attr.ai = ai;
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_GETATTR, &ai_get_attr);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_GETATTR failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = memcpy_s(attr, sizeof(*attr),
|
|
&ai_get_attr.attr, sizeof(ext_ai_attr));
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(memcpy_s, ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 dev_ai_unmap_stream_buffer(td_handle ai)
|
|
{
|
|
td_s32 ret;
|
|
td_u8 *addr = TD_NULL;
|
|
ai_buf_param ai_buf_info = {
|
|
.ai = ai,
|
|
.ai_buf.user_vir_addr = 0,
|
|
.ai_buf.size = 0,
|
|
};
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_GETBUFINFO, &ai_buf_info);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_GETBUFINFO failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
addr = (td_u8 *)((uintptr_t)ai_buf_info.ai_buf.user_vir_addr);
|
|
|
|
ret = munmap(addr, ai_buf_info.ai_buf.size);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(munmap, ret);
|
|
return ret;
|
|
}
|
|
|
|
ai_buf_info.ai_buf.map_fd = TD_INVALID_HANDLE;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 dev_ai_map_stream_buffer(td_handle handle)
|
|
{
|
|
td_s32 ret;
|
|
td_void *addr = TD_NULL;
|
|
ai_buf_param ai_buf_info = {
|
|
.ai = handle,
|
|
.ai_buf.size = 0,
|
|
.ai_buf.map_fd = -1,
|
|
};
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_GETBUFINFO, &ai_buf_info);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_GETBUFINFO failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
addr = mmap((td_void *)0, ai_buf_info.ai_buf.size,
|
|
PROT_READ | PROT_WRITE, MAP_SHARED, ai_buf_info.ai_buf.map_fd, 0);
|
|
if (addr == MAP_FAILED) {
|
|
soc_log_err("mmap failed\n");
|
|
return SOC_ERR_AI_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ai_buf_info.ai_buf.user_vir_addr = (td_u8 *)addr - (td_u8 *)TD_NULL;
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_SETBUFINFO, &ai_buf_info);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_SETBUFINFO failed\n");
|
|
soc_err_print_err_code(ret);
|
|
goto out;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
out:
|
|
(td_void)munmap(addr, ai_buf_info.ai_buf.size);
|
|
return ret;
|
|
}
|
|
|
|
static td_s32 dev_ai_destroy(td_handle h_ai)
|
|
{
|
|
td_s32 ret;
|
|
|
|
ret = dev_ai_unmap_stream_buffer(h_ai);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(dev_ai_unmap_stream_buffer, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_DESTROY, &h_ai);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_DESTROY failed!\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 dev_ai_create(ext_ai_port ai_port, const ext_ai_attr *attr, td_handle *handle)
|
|
{
|
|
td_s32 ret;
|
|
ai_create_param ai_param = {
|
|
.ai_port = ai_port,
|
|
.ai = TD_INVALID_HANDLE,
|
|
};
|
|
|
|
ret = memcpy_s(&ai_param.attr, sizeof(ext_ai_attr), attr, sizeof(*attr));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memcpy_s, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_CREATE, &ai_param);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_CREATE failed!\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
*handle = ai_param.ai;
|
|
|
|
ret = dev_ai_map_stream_buffer(*handle);
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(dev_ai_map_stream_buffer, ret);
|
|
goto out;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
out:
|
|
if (ioctl(g_ai_fd, CMD_AI_DESTROY, handle) != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_DESTROY failed!\n");
|
|
}
|
|
|
|
*handle = TD_INVALID_HANDLE;
|
|
return ret;
|
|
}
|
|
|
|
static td_s32 ai_state_init(ai_mpi_state *ai_state, td_handle ai)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 i;
|
|
|
|
ret = memset_s(ai_state, sizeof(*ai_state), 0, sizeof(ai_mpi_state));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memset_s, ret);
|
|
return ret;
|
|
}
|
|
|
|
ai_state->h_ai = ai;
|
|
ai_state->h_track = TD_INVALID_HANDLE;
|
|
|
|
for (i = 0; i < AI_MAX_DST; i++) {
|
|
ai_state->h_dst[i] = TD_INVALID_HANDLE;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_void ai_state_free(ai_mpi_state *ai_state)
|
|
{
|
|
td_u32 id;
|
|
|
|
if (ai_state == TD_NULL) {
|
|
return;
|
|
}
|
|
|
|
id = ai_state->h_ai & AI_CHNID_MASK;
|
|
if (id >= AI_MAX_TOTAL_NUM) {
|
|
soc_err_print_h32(ai_state->h_ai);
|
|
return;
|
|
}
|
|
|
|
g_ai_res.ai[id] = TD_NULL;
|
|
|
|
free(ai_state);
|
|
}
|
|
|
|
static ai_mpi_state *ai_chan_alloc(td_handle ai)
|
|
{
|
|
td_s32 ret;
|
|
ai_mpi_state *ai_state = TD_NULL;
|
|
|
|
if ((ai & AI_CHNID_MASK) >= AI_MAX_TOTAL_NUM) {
|
|
return TD_NULL;
|
|
}
|
|
|
|
ai_state = (ai_mpi_state *)malloc(sizeof(ai_mpi_state));
|
|
if (ai_state == TD_NULL) {
|
|
soc_log_err("malloc( ai_mpi_state failed\n");
|
|
return TD_NULL;
|
|
}
|
|
|
|
ret = ai_state_init(ai_state, ai);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_state_init, ret);
|
|
free(ai_state);
|
|
return TD_NULL;
|
|
}
|
|
|
|
g_ai_res.ai[ai & AI_CHNID_MASK] = ai_state;
|
|
|
|
return ai_state;
|
|
}
|
|
|
|
static td_void ai_chan_stop_thread(ai_mpi_state *ai_state)
|
|
{
|
|
td_s32 ret;
|
|
|
|
ai_state->ai_thread_run = TD_FALSE;
|
|
|
|
if (ai_state->ai_adec_thd_inst != 0) {
|
|
ret = pthread_join(ai_state->ai_adec_thd_inst, TD_NULL);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(pthread_join, ret);
|
|
return;
|
|
}
|
|
ai_state->ai_adec_thd_inst = 0;
|
|
}
|
|
|
|
if (ai_state->ai_track_thd_inst != 0) {
|
|
ret = pthread_join(ai_state->ai_track_thd_inst, TD_NULL);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(pthread_join, ret);
|
|
return;
|
|
}
|
|
ai_state->ai_track_thd_inst = 0;
|
|
}
|
|
|
|
if (ai_state->ai_data_thd_inst != 0) {
|
|
ret = pthread_join(ai_state->ai_data_thd_inst, TD_NULL);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(pthread_join, ret);
|
|
return;
|
|
}
|
|
ai_state->ai_data_thd_inst = 0;
|
|
}
|
|
}
|
|
|
|
static td_s32 ai_chan_start_thread(ai_mpi_state *ai_state)
|
|
{
|
|
td_s32 ret;
|
|
|
|
ai_state->ai_thread_run = TD_TRUE;
|
|
ret = pthread_create(&ai_state->ai_data_thd_inst, TD_NULL,
|
|
ai_data_thread, (td_void *)ai_state);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(pthread_create, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = pthread_create(&ai_state->ai_track_thd_inst, TD_NULL,
|
|
ai_track_thread, (td_void *)ai_state);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(pthread_create, ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = pthread_create(&ai_state->ai_adec_thd_inst, TD_NULL,
|
|
ai_adec_thread, (td_void *)ai_state);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(pthread_create, ret);
|
|
goto out;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
|
|
out:
|
|
ai_chan_stop_thread(ai_state);
|
|
return ret;
|
|
}
|
|
|
|
static td_void ai_chan_parser_destroy(ai_mpi_state *ai_state)
|
|
{
|
|
if (ai_state->h_parser == TD_NULL) {
|
|
return;
|
|
}
|
|
|
|
iec61937_parser_destroy(ai_state->h_parser);
|
|
ai_state->h_parser = TD_NULL;
|
|
}
|
|
|
|
static td_s32 ai_chan_parser_create(ai_mpi_state *ai_state)
|
|
{
|
|
td_s32 ret;
|
|
iec61937_parser_state *parser_state = TD_NULL;
|
|
|
|
ret = iec61937_parser_create(&ai_state->h_parser);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(iec61937_parser_create, ret);
|
|
return ret;
|
|
}
|
|
|
|
parser_state = (iec61937_parser_state *)ai_state->h_parser;
|
|
parser_state->channel = ai_state->channel;
|
|
parser_state->bit_depth = ai_state->bit_depth;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_void ai_chan_destroy(ai_mpi_state *ai_state)
|
|
{
|
|
ai_chan_stop_thread(ai_state);
|
|
ai_chan_parser_destroy(ai_state);
|
|
ai_proc_deinit(ai_state);
|
|
ai_state_free(ai_state);
|
|
}
|
|
|
|
static td_s32 ai_chan_create(ext_ai_port ai_port, const ext_ai_attr *attr, td_handle ai)
|
|
{
|
|
td_s32 ret;
|
|
ai_mpi_state *ai_state = TD_NULL;
|
|
|
|
ai_state = ai_chan_alloc(ai);
|
|
if (ai_state == TD_NULL) {
|
|
soc_log_err("call ai_chan_alloc failed\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
ret = ai_proc_init(ai_state);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_proc_init, ret);
|
|
goto out0;
|
|
}
|
|
|
|
ai_set_chn_attr(ai_port, attr, ai_state);
|
|
|
|
ret = ai_chan_parser_create(ai_state);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_chan_parser_create, ret);
|
|
goto out1;
|
|
}
|
|
|
|
ret = ai_chan_start_thread(ai_state);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_chan_start_thread, ret);
|
|
goto out2;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
out2:
|
|
ai_chan_parser_destroy(ai_state);
|
|
|
|
out1:
|
|
ai_proc_deinit(ai_state);
|
|
|
|
out0:
|
|
ai_state_free(ai_state);
|
|
return ret;
|
|
}
|
|
|
|
static td_s32 mpi_ai_create(ext_ai_port ai_port, const ext_ai_attr *attr, td_handle *handle)
|
|
{
|
|
td_s32 ret;
|
|
td_handle ai = TD_INVALID_HANDLE;
|
|
|
|
ret = dev_ai_create(ai_port, attr, &ai);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(dev_ai_create, ret);
|
|
return ret;
|
|
}
|
|
|
|
ai_ch_api_lock(ai);
|
|
ai_ch_data_lock(ai);
|
|
ret = ai_chan_create(ai_port, attr, ai);
|
|
ai_ch_data_unlock(ai);
|
|
ai_ch_api_unlock(ai);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_chan_create, ret);
|
|
goto out;
|
|
}
|
|
|
|
*handle = ai;
|
|
|
|
return TD_SUCCESS;
|
|
out:
|
|
if (dev_ai_destroy(ai) != TD_SUCCESS) {
|
|
soc_log_err("call dev_ai_destroy failed\n");
|
|
}
|
|
|
|
*handle = TD_INVALID_HANDLE;
|
|
return ret;
|
|
}
|
|
|
|
static td_s32 mpi_ai_destroy(td_handle ai)
|
|
{
|
|
ai_mpi_state *ai_state = ai_get_chan(ai);
|
|
if (ai_state == TD_NULL) {
|
|
soc_log_err("ai_get_chan failed\n");
|
|
return SOC_ERR_AI_INVALID_ID;
|
|
}
|
|
|
|
ai_ch_api_lock(ai);
|
|
ai_ch_data_lock(ai);
|
|
|
|
ai_chan_destroy(ai_state);
|
|
|
|
ai_ch_data_unlock(ai);
|
|
ai_ch_api_unlock(ai);
|
|
return dev_ai_destroy(ai);
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_create(ext_ai_port ai_port, const ext_ai_attr *attr, td_handle *phandle)
|
|
{
|
|
td_s32 ret;
|
|
|
|
check_ai_port(ai_port);
|
|
check_ai_null_ptr(attr);
|
|
check_ai_null_ptr(phandle);
|
|
|
|
ai_lock();
|
|
ret = mpi_ai_create(ai_port, attr, phandle);
|
|
ai_unlock();
|
|
|
|
return ret;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_destroy(td_handle ai)
|
|
{
|
|
td_s32 ret;
|
|
|
|
ai_lock();
|
|
ret = mpi_ai_destroy(ai);
|
|
ai_unlock();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static td_s32 ai_dev_set_enable(td_handle h_ai, td_bool enable)
|
|
{
|
|
td_s32 ret;
|
|
ai_enable_param ai_enable = {
|
|
.ai = h_ai,
|
|
.ai_enable = enable,
|
|
};
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_SETENABLE, &ai_enable);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_SETENABLE failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 mpi_ai_set_enable(td_handle ai, td_bool enable)
|
|
{
|
|
td_s32 ret;
|
|
ai_mpi_state *ai_state = TD_NULL;
|
|
|
|
ret = ai_dev_set_enable(ai, enable);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_dev_set_enable, ret);
|
|
return ret;
|
|
}
|
|
|
|
ai_state = ai_get_chan(ai);
|
|
if (ai_state == TD_NULL) {
|
|
soc_log_err("ai_get_chan failed\n");
|
|
return SOC_ERR_AI_INVALID_ID;
|
|
}
|
|
|
|
if (enable == TD_TRUE) {
|
|
ai_state->need_start = TD_TRUE;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_set_enable(td_handle ai, td_bool enable)
|
|
{
|
|
td_s32 ret;
|
|
|
|
check_ai_id(ai);
|
|
|
|
ai_ch_api_lock(ai);
|
|
ai_ch_cmd_lock(ai);
|
|
ret = mpi_ai_set_enable(ai, enable);
|
|
ai_ch_cmd_unlock(ai);
|
|
ai_ch_api_unlock(ai);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(mpi_ai_set_enable, ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_get_enable(td_handle ai, td_bool *enable)
|
|
{
|
|
td_s32 ret;
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(enable);
|
|
|
|
ai_ch_api_lock(ai);
|
|
ai_ch_cmd_lock(ai);
|
|
ret = ai_dev_get_enable(ai, enable);
|
|
ai_ch_cmd_unlock(ai);
|
|
ai_ch_api_unlock(ai);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ai_mpi_state *ai_get_acquire_frame_chan(td_handle ai)
|
|
{
|
|
ai_mpi_state *ai_state = ai_get_chan(ai);
|
|
if (ai_state == TD_NULL) {
|
|
soc_log_err("ai_get_chan failed\n");
|
|
return TD_NULL;
|
|
}
|
|
|
|
if ((ai_state->h_dst[0] != TD_INVALID_HANDLE) ||
|
|
(ai_state->h_track != TD_INVALID_HANDLE)) {
|
|
soc_log_err("aenc or track attach this ai chan, can not acquire frame!\n");
|
|
return TD_NULL;
|
|
}
|
|
|
|
return ai_state;
|
|
}
|
|
|
|
static td_s32 ai_chan_acquire_frame(td_handle ai, ext_ao_frame *frame)
|
|
{
|
|
td_s32 ret;
|
|
ai_mpi_state *ai_state = ai_get_acquire_frame_chan(ai);
|
|
if (ai_state == TD_NULL) {
|
|
soc_log_err("ai_get_acquire_frame_chan failed\n");
|
|
return SOC_ERR_AI_INVALID_ID;
|
|
}
|
|
|
|
if (ai_state->pass_through == TD_TRUE) {
|
|
ret = ai_parser_acquire_frame(ai_state, frame);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ai_parser_acquire_frame failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
} else {
|
|
const td_u32 request_size = ai_chan_cal_acquire_size(ai_state);
|
|
ret = ai_kernel_acquire_frame(ai_state, request_size, frame);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_warn_print_call_fun_err(ai_kernel_acquire_frame, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_acquire_frame(td_handle ai, ext_ao_frame *frame)
|
|
{
|
|
td_s32 ret;
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(frame);
|
|
|
|
ai_ch_data_lock(ai);
|
|
ai_ch_io_lock(ai);
|
|
ret = ai_chan_acquire_frame(ai, frame);
|
|
ai_ch_io_unlock(ai);
|
|
ai_ch_data_unlock(ai);
|
|
return ret;
|
|
}
|
|
|
|
static td_s32 ai_chan_release_frame(td_handle ai, const ext_ao_frame *ao_frame)
|
|
{
|
|
td_s32 ret;
|
|
ai_mpi_state *ai_state = ai_get_acquire_frame_chan(ai);
|
|
if (ai_state == TD_NULL) {
|
|
soc_log_err("ai_get_acquire_frame_chan failed\n");
|
|
return SOC_ERR_AI_INVALID_ID;
|
|
}
|
|
|
|
check_ai_null_ptr(ao_frame);
|
|
|
|
if (ai_state->pass_through == TD_TRUE) {
|
|
check_ai_null_ptr(ai_state->h_parser);
|
|
ret = ai_parser_release_frame(ai_state->h_parser, ao_frame);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ai_parser_release_frame failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
} else {
|
|
ret = ai_kernel_release_frame(ai);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ai_kernel_release_frame failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_release_frame(td_handle ai, const ext_ao_frame *frame)
|
|
{
|
|
td_s32 ret;
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(frame);
|
|
|
|
ai_ch_data_lock(ai);
|
|
ai_ch_io_lock(ai);
|
|
ret = ai_chan_release_frame(ai, frame);
|
|
ai_ch_io_unlock(ai);
|
|
ai_ch_data_unlock(ai);
|
|
return ret;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_attach(td_handle ai, td_handle dst)
|
|
{
|
|
td_s32 ret;
|
|
|
|
check_ai_id(ai);
|
|
ai_ch_api_lock(ai);
|
|
ai_ch_cmd_lock(ai);
|
|
|
|
ret = ai_attach(ai, dst);
|
|
|
|
ai_ch_cmd_unlock(ai);
|
|
ai_ch_api_unlock(ai);
|
|
|
|
return ret;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_detach(td_handle ai, td_handle dst)
|
|
{
|
|
td_s32 ret;
|
|
|
|
check_ai_id(ai);
|
|
ai_ch_api_lock(ai);
|
|
ai_ch_cmd_lock(ai);
|
|
|
|
ret = ai_detach(ai, dst);
|
|
|
|
ai_ch_cmd_unlock(ai);
|
|
ai_ch_api_unlock(ai);
|
|
|
|
return ret;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_set_delay(td_handle ai, const ext_ai_delay *delay)
|
|
{
|
|
td_s32 ret;
|
|
ai_delay_comps_param param = {
|
|
.ai = ai,
|
|
};
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(delay);
|
|
|
|
ret = memcpy_s(¶m.delay_comps, sizeof(ext_ai_delay),
|
|
delay, sizeof(*delay));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memcpy_s, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_SETDELAYCOMPS, ¶m);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_SETDELAYCOMPS failed!\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_get_delay(td_handle ai, ext_ai_delay *delay)
|
|
{
|
|
td_s32 ret;
|
|
ai_delay_comps_param param = {
|
|
.ai = ai,
|
|
};
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(delay);
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_GETDELAYCOMPS, ¶m);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_GETDELAYCOMPS failed!\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = memcpy_s(delay, sizeof(*delay),
|
|
¶m.delay_comps, sizeof(ext_ai_delay));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memcpy_s, ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 ai_get_cur_delay(td_handle h_ai, td_u32 *delay)
|
|
{
|
|
td_s32 ret;
|
|
ai_port_delay_param param = {
|
|
.ai = h_ai,
|
|
.delay = 0,
|
|
};
|
|
|
|
check_ai_id(h_ai);
|
|
check_ai_null_ptr(delay);
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_GETPORTDELAY, ¶m);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_GETDELAYCOMPS failed!\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
*delay = param.delay;
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
#define AI_PARSER_MAX_TIMES 100
|
|
|
|
static td_s32 ai_chan_get_stream_type(td_handle ai, ext_audio_format *stream_type)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 i = 0;
|
|
iec61937_parser_stream_type iapi_audio_format;
|
|
ai_mpi_state *ai_state = ai_get_chan(ai);
|
|
if (ai_state == TD_NULL) {
|
|
soc_log_err("ai_get_chan failed\n");
|
|
return SOC_ERR_AI_INVALID_ID;
|
|
}
|
|
|
|
if (ai_state->h_dst[0] != TD_INVALID_HANDLE) {
|
|
soc_log_err("aenc attach this ai chan!\n");
|
|
return SOC_ERR_AI_NOTSUPPORT;
|
|
}
|
|
|
|
if (ai_state->pass_through == TD_FALSE) {
|
|
*stream_type = EXT_AUDIO_FORMAT_PCM;
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
check_ai_null_ptr(ai_state->h_parser);
|
|
check_ai_null_ptr(ai_state->ai_proc_info);
|
|
|
|
/* some devices send long time invalid data before playing, and the caller's
|
|
* thread interval is tens of milliseconds, the do-while code avoids the
|
|
* caller consuming multiple times and getting stream type too long.
|
|
*/
|
|
do {
|
|
if ((ai_state->ai_proc_info->data_type == EXT_AUDIO_FORMAT_STREAM) ||
|
|
(ai_state->ai_proc_info->data_type == EXT_AUDIO_FORMAT_CXT)) {
|
|
ret = ai_parser_send_frame(ai_state);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ai_parser_send_frame, ret);
|
|
}
|
|
}
|
|
|
|
/* zero data no need to parser */
|
|
if (iec61937_check_is_empty(ai_state->h_parser) == TD_TRUE) {
|
|
soc_log_dbg("not enough data.\n");
|
|
return SOC_ERR_AI_NOT_ENOUGH_DATA;
|
|
}
|
|
|
|
ret = trans_audio_format_from_mpi_to_iapi_iec61937(&iapi_audio_format, stream_type);
|
|
if (ret != TD_SUCCESS) {
|
|
return ret;
|
|
}
|
|
ret = iec61937_parser_get_stream_type(ai_state->h_parser, &iapi_audio_format);
|
|
trans_audio_format_from_iapi_iec61937_to_mpi(&iapi_audio_format, stream_type);
|
|
i++;
|
|
if (i > AI_PARSER_MAX_TIMES) {
|
|
break;
|
|
}
|
|
} while ((ret == SOC_ERR_AI_FIND_SYNC_FAILED) || (ret == SOC_ERR_AI_BITS_INVALID_DATA));
|
|
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(iec61937_parser_get_stream_type, ret);
|
|
return ret;
|
|
}
|
|
|
|
ai_state->ai_proc_info->data_type = *stream_type;
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_get_stream_type(td_handle ai, ext_audio_format *stream_type)
|
|
{
|
|
td_s32 ret;
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(stream_type);
|
|
|
|
ai_ch_api_lock(ai);
|
|
ai_ch_io_lock(ai);
|
|
ret = ai_chan_get_stream_type(ai, stream_type);
|
|
ai_ch_io_unlock(ai);
|
|
ai_ch_api_unlock(ai);
|
|
|
|
return ret;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_set_nr_attr(td_handle ai, const ext_ai_nr_attr *nr_attr)
|
|
{
|
|
td_s32 ret;
|
|
ai_nr_param nr_param = {
|
|
.ai = ai,
|
|
};
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(nr_attr);
|
|
|
|
ret = memcpy_s(&nr_param.nr, sizeof(ext_ai_nr_attr),
|
|
nr_attr, sizeof(*nr_attr));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memcpy_s, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_SETNRATTRS, &nr_param);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_SETNRATTRS failed\n");
|
|
soc_err_print_err_code(ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
td_s32 ext_mpi_ai_get_nr_attr(td_handle ai, ext_ai_nr_attr *nr_attr)
|
|
{
|
|
td_s32 ret;
|
|
ai_nr_param nr_param = {
|
|
.ai = ai,
|
|
};
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(nr_attr);
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_GETNRATTRS, &nr_param);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_GETNRATTRS failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = memcpy_s(nr_attr, sizeof(*nr_attr),
|
|
&nr_param.nr, sizeof(ext_ai_nr_attr));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memcpy_s, ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_set_line_in_volume(td_handle ai, const ext_ai_line_in_gain *line_in_gain)
|
|
{
|
|
td_s32 ret;
|
|
ai_line_in_gain_param param = {
|
|
.ai = ai,
|
|
};
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(line_in_gain);
|
|
|
|
if (line_in_gain->line_in_gain > LINE_IN_VOLUME_MAX) {
|
|
soc_log_err("line_in_gain is invalid\n");
|
|
return SOC_ERR_AI_INVALID_PARA;
|
|
}
|
|
|
|
ret = memcpy_s(¶m.line_in_gain, sizeof(ext_ai_line_in_gain),
|
|
line_in_gain, sizeof(*line_in_gain));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memcpy_s, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_SETLINEINVOLUME, ¶m);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_SETLINEINVOLUME failed\n");
|
|
soc_err_print_err_code(ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_get_line_in_volume(td_handle ai, ext_ai_line_in_gain *line_in_gain)
|
|
{
|
|
td_s32 ret;
|
|
ai_line_in_gain_param param = {
|
|
.ai = ai,
|
|
.line_in_gain.line_in_gain = 0,
|
|
};
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(line_in_gain);
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_GETLINEINVOLUME, ¶m);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_GETLINEINVOLUME failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = memcpy_s(line_in_gain, sizeof(*line_in_gain),
|
|
¶m.line_in_gain, sizeof(ext_ai_line_in_gain));
|
|
if (ret != EOK) {
|
|
soc_err_print_call_fun_err(memcpy_s, ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_set_line_in_mute(td_handle ai, td_bool mute)
|
|
{
|
|
td_s32 ret;
|
|
ai_line_in_mute_param param = {
|
|
.ai = ai,
|
|
.mute = mute,
|
|
};
|
|
|
|
check_ai_id(ai);
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_SETLINEINMUTE, ¶m);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_SETLINEINMUTE failed\n");
|
|
soc_err_print_err_code(ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
td_s32 ext_mpi_ai_get_line_in_mute(td_handle ai, td_bool *mute)
|
|
{
|
|
td_s32 ret;
|
|
ai_line_in_mute_param param = {
|
|
.ai = ai,
|
|
.mute = TD_FALSE,
|
|
};
|
|
|
|
check_ai_id(ai);
|
|
check_ai_null_ptr(mute);
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_GETLINEINMUTE, ¶m);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_GETLINEINMUTE failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
*mute = param.mute;
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 ext_mpi_ai_get_status(td_handle h_ai, td_u32 *status)
|
|
{
|
|
td_s32 ret;
|
|
ai_status_param param = {
|
|
.ai = h_ai,
|
|
.status = AI_CHN_STATUS_MAX,
|
|
};
|
|
|
|
check_ai_id(h_ai);
|
|
check_ai_null_ptr(status);
|
|
|
|
ret = ioctl(g_ai_fd, CMD_AI_GETSTATUS, ¶m);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("ioctl CMD_AI_GETSTATUS failed\n");
|
|
soc_err_print_err_code(ret);
|
|
return ret;
|
|
}
|
|
|
|
*status = param.status;
|
|
if (param.status == AI_CHN_STATUS_RESET) {
|
|
soc_log_warn("detect ai need reset\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
ext_ai_port ext_mpi_get_ai_port_by_handle(td_handle h_ai)
|
|
{
|
|
ai_mpi_state *ai_state = ai_get_chan(h_ai);
|
|
if (ai_state == TD_NULL) {
|
|
soc_log_err("ai_get_chan failed\n");
|
|
return EXT_AI_MAX;
|
|
}
|
|
|
|
return ai_state->ai_port;
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif /* __cplusplus */
|