pinebuds/apps/audioplayers/a2dp_decoder/a2dp_decoder_cp.c

594 lines
12 KiB
C

/***************************************************************************
*
* Copyright 2015-2019 BES.
* All rights reserved. All unpublished rights reserved.
*
* No part of this work may be used or reproduced in any form or by any
* means, or stored in a database or retrieval system, without prior written
* permission of BES.
*
* Use of this work is governed by a license granted by BES.
* This work contains confidential and proprietary information of
* BES. which is protected by copyright, trade secret,
* trademark and other intellectual property rights.
*
****************************************************************************/
#ifdef A2DP_CP_ACCEL
#include "a2dp_decoder_cp.h"
#include "a2dp_decoder_internal.h"
#include "cp_accel.h"
#include "hal_location.h"
#include "hal_timer.h"
#include "hal_trace.h"
#include "heap_api.h"
#include "norflash_api.h"
#define CP_IN_FRAME_CNT 100
#define CP_OUT_FRAME_CNT 3
#define CP_IN_CACHE_SIZE (1024 * 10)
#if defined(A2DP_LHDC_V3)
#define CP_HEAP_SIZE (1024 * 64)
#else
#define CP_HEAP_SIZE (1024 * 32)
#endif
enum CP_DEC_STATE_T {
CP_DEC_STATE_IDLE,
CP_DEC_STATE_WORKING,
CP_DEC_STATE_DONE,
CP_DEC_STATE_INIT_DONE,
};
struct CP_IN_FRAME_INFO_T {
uint32_t pos;
uint32_t len;
};
struct CP_OUT_FRAME_INFO_T {
enum CP_DEC_STATE_T state;
uint32_t pos;
uint32_t len;
};
static CP_BSS_LOC uint32_t cp_heap_buf[CP_HEAP_SIZE / 4];
static CP_BSS_LOC heap_handle_t cp_heap;
static CP_BSS_LOC uint16_t cp_in_widx;
static CP_BSS_LOC uint16_t cp_in_ridx;
static CP_BSS_LOC uint8_t cp_out_widx;
static CP_BSS_LOC uint8_t cp_out_ridx;
static CP_BSS_LOC struct CP_IN_FRAME_INFO_T *in_frames;
static CP_BSS_LOC struct CP_OUT_FRAME_INFO_T *out_frames;
static CP_BSS_LOC uint8_t *cp_in_cache;
static CP_BSS_LOC uint8_t *cp_out_cache;
static CP_BSS_LOC bool reset_frames;
#ifdef A2DP_TRACE_CP_DEC_TIME
static CP_BSS_LOC uint32_t cp_last_dec_time;
#endif
static uint8_t max_buffer_frames = 2;
static bool mcu_dec_inited;
static A2DP_CP_DECODE_T decode_frame;
static enum CP_PROC_DELAY_T proc_delay;
static bool cp_need_reset;
CP_TEXT_SRAM_LOC
unsigned int set_cp_reset_flag(uint8_t evt) {
cp_need_reset = true;
return 0;
}
bool is_cp_need_reset(void) {
bool ret = cp_need_reset;
cp_need_reset = false;
return ret;
}
bool is_cp_init_done(void) { return cp_accel_init_done(); }
CP_TEXT_SRAM_LOC
static void reset_frame_info(void) {
uint32_t idle_cnt;
int i;
if (proc_delay == CP_PROC_DELAY_0_FRAME) {
idle_cnt = CP_OUT_FRAME_CNT;
} else if (proc_delay == CP_PROC_DELAY_1_FRAME) {
idle_cnt = CP_OUT_FRAME_CNT - 1;
} else {
idle_cnt = CP_OUT_FRAME_CNT - 2;
}
for (i = 0; i < CP_OUT_FRAME_CNT; i++) {
if (i < idle_cnt) {
out_frames[i].state = CP_DEC_STATE_IDLE;
} else {
out_frames[i].state = CP_DEC_STATE_INIT_DONE;
}
}
cp_in_widx = 0;
cp_in_ridx = 0;
cp_out_widx = 0;
cp_out_ridx = idle_cnt;
reset_frames = false;
}
CP_TEXT_SRAM_LOC
static unsigned int cp_a2dp_main(uint8_t event) {
int ret;
#ifdef A2DP_TRACE_CP_DEC_TIME
uint32_t stime;
uint32_t etime;
#endif
if (!mcu_dec_inited) {
return 0;
}
if (decode_frame) {
do {
#ifdef A2DP_TRACE_CP_DEC_TIME
stime = hal_fast_sys_timer_get();
#endif
ret = decode_frame();
#ifdef A2DP_TRACE_CP_DEC_TIME
etime = hal_fast_sys_timer_get();
TRACE_A2DP_DECODER_I("cp_decode: %5u us in %5u us",
FAST_TICKS_TO_US(etime - stime),
FAST_TICKS_TO_US(etime - cp_last_dec_time));
cp_last_dec_time = etime;
#endif
} while (ret == 0);
}
if (reset_frames) {
reset_frame_info();
#ifdef A2DP_TRACE_CP_ACCEL
TRACE_A2DP_DECODER_I("%s: Reset frames", __func__);
#endif
}
return 0;
}
static struct cp_task_desc TASK_DESC_A2DP = {
CP_ACCEL_STATE_CLOSED, cp_a2dp_main, NULL, NULL, set_cp_reset_flag};
int a2dp_cp_init(A2DP_CP_DECODE_T decode_func, enum CP_PROC_DELAY_T delay) {
if (delay >= CP_PROC_DELAY_QTY) {
return 1;
}
mcu_dec_inited = false;
decode_frame = decode_func;
proc_delay = delay;
#ifdef A2DP_TRACE_CP_DEC_TIME
cp_last_dec_time = hal_fast_sys_timer_get();
#endif
norflash_api_flush_disable(NORFLASH_API_USER_CP,
(uint32_t)cp_accel_init_done);
cp_accel_open(CP_TASK_A2DP_DECODE, &TASK_DESC_A2DP);
while (cp_accel_init_done() == false) {
hal_sys_timer_delay_us(100);
}
norflash_api_flush_enable(NORFLASH_API_USER_CP);
return 0;
}
int a2dp_cp_deinit(void) {
cp_accel_close(CP_TASK_A2DP_DECODE);
return 0;
}
SRAM_TEXT_LOC
int a2dp_cp_decoder_init(uint32_t out_frame_len, uint8_t max_frames) {
uint32_t i;
max_buffer_frames = max_frames;
if (!mcu_dec_inited) {
#ifdef A2DP_TRACE_CP_ACCEL
TRACE_A2DP_DECODER_I("%s: Decoder init", __func__);
#endif
if (cp_accel_init_done() == false) {
TRACE_A2DP_DECODER_I("%s: CP ACCEL not init yet", __func__);
return 6;
}
cp_heap = heap_register(cp_heap_buf, sizeof(cp_heap_buf));
if (cp_heap == NULL) {
return 1;
}
in_frames = heap_malloc(cp_heap, sizeof(in_frames[0]) * CP_IN_FRAME_CNT);
if (in_frames == NULL) {
return 2;
}
out_frames = heap_malloc(cp_heap, sizeof(out_frames[0]) * CP_OUT_FRAME_CNT);
if (out_frames == NULL) {
return 3;
}
cp_in_cache = heap_malloc(cp_heap, CP_IN_CACHE_SIZE);
if (cp_in_cache == NULL) {
return 4;
}
cp_out_cache = heap_malloc(cp_heap, out_frame_len * CP_OUT_FRAME_CNT);
if (cp_out_cache == NULL) {
return 5;
}
for (i = 0; i < CP_OUT_FRAME_CNT; i++) {
out_frames[i].pos = out_frame_len * i;
out_frames[i].len = out_frame_len;
}
reset_frame_info();
mcu_dec_inited = true;
}
return 0;
}
CP_TEXT_SRAM_LOC
static uint32_t get_in_frame_cnt(uint32_t in_widx, uint32_t in_ridx) {
uint32_t cnt;
if (in_widx >= in_ridx) {
cnt = in_widx - in_ridx;
} else {
cnt = CP_IN_FRAME_CNT - in_ridx + in_widx;
}
return cnt;
}
uint32_t get_in_cp_frame_cnt(void) {
return get_in_frame_cnt(cp_in_widx, cp_in_ridx);
}
uint32_t get_in_cp_frame_delay(void) { return proc_delay; }
SRAM_TEXT_LOC
static uint32_t get_in_frame_free_cnt(uint32_t in_widx, uint32_t in_ridx) {
uint32_t free_cnt;
if (in_widx >= in_ridx) {
free_cnt = CP_IN_FRAME_CNT - in_widx + in_ridx;
} else {
free_cnt = in_ridx - in_widx;
}
free_cnt -= 1;
return free_cnt;
}
SRAM_TEXT_LOC
int a2dp_cp_put_in_frame(const void *buf1, uint32_t len1, const void *buf2,
uint32_t len2) {
uint16_t free_cnt;
uint16_t in_widx;
uint16_t in_ridx;
uint16_t prev_in_widx;
uint32_t free_len;
uint32_t in_wpos;
uint32_t in_rpos;
uint32_t aligned_len;
if (reset_frames) {
return -1;
}
in_widx = cp_in_widx;
in_ridx = cp_in_ridx;
if (max_buffer_frames < get_in_cp_frame_cnt()) {
return 1;
}
free_cnt = get_in_frame_free_cnt(in_widx, in_ridx);
if (free_cnt == 0) {
return 1;
}
if (in_widx == in_ridx) {
in_wpos = 0;
in_rpos = 0;
} else {
if (in_widx == 0) {
prev_in_widx = CP_IN_FRAME_CNT - 1;
} else {
prev_in_widx = in_widx - 1;
}
in_wpos = in_frames[prev_in_widx].pos + in_frames[prev_in_widx].len;
if (in_wpos >= CP_IN_CACHE_SIZE) {
in_wpos -= CP_IN_CACHE_SIZE;
}
in_rpos = in_frames[in_ridx].pos;
}
// Align to word boundary
in_wpos = (in_wpos + 3) & ~3;
if (in_wpos >= CP_IN_CACHE_SIZE) {
in_wpos -= CP_IN_CACHE_SIZE;
}
aligned_len = (len1 + len2 + 3) & ~3;
if (in_wpos >= in_rpos) {
free_len = CP_IN_CACHE_SIZE - in_wpos;
if (in_rpos == 0) {
free_len -= 1;
}
if (free_len < aligned_len) {
free_len = (in_rpos > 0) ? (in_rpos - 1) : 0;
if (free_len < aligned_len) {
return 2;
}
in_wpos = 0;
}
} else {
free_len = in_rpos - in_wpos - 1;
if (free_len < aligned_len) {
return 3;
}
}
in_frames[in_widx].pos = in_wpos;
in_frames[in_widx].len = len1 + len2;
if (len1) {
memcpy(&cp_in_cache[in_wpos], buf1, len1);
in_wpos += len1;
}
if (len2) {
memcpy(&cp_in_cache[in_wpos], buf2, len2);
in_wpos += len2;
}
in_widx += 1;
if (in_widx >= CP_IN_FRAME_CNT) {
in_widx -= CP_IN_FRAME_CNT;
}
cp_in_widx = in_widx;
return 0;
}
CP_TEXT_SRAM_LOC
int a2dp_cp_get_in_frame(void **p_buf, uint32_t *p_len) {
uint16_t in_widx;
uint16_t in_ridx;
uint32_t in_rpos;
if (reset_frames) {
return -1;
}
in_widx = cp_in_widx;
in_ridx = cp_in_ridx;
if (in_widx == in_ridx) {
return 1;
}
in_rpos = in_frames[in_ridx].pos;
if (p_buf) {
*p_buf = &cp_in_cache[in_rpos];
}
if (p_len) {
*p_len = in_frames[in_ridx].len;
}
return 0;
}
CP_TEXT_SRAM_LOC
int a2dp_cp_consume_in_frame(void) {
uint16_t in_widx;
uint16_t in_ridx;
if (reset_frames) {
return 0;
}
in_widx = cp_in_widx;
in_ridx = cp_in_ridx;
if (in_widx == in_ridx) {
return 1;
}
in_ridx += 1;
if (in_ridx >= CP_IN_FRAME_CNT) {
in_ridx -= CP_IN_FRAME_CNT;
}
cp_in_ridx = in_ridx;
return 0;
}
CP_TEXT_SRAM_LOC
uint32_t a2dp_cp_get_in_frame_index(void) { return cp_in_ridx; }
SRAM_TEXT_LOC
uint32_t a2dp_cp_get_in_frame_cnt_by_index(uint32_t ridx) {
return get_in_frame_cnt(cp_in_widx, ridx);
}
SRAM_TEXT_LOC
void a2dp_cp_reset_frame(void) {
#ifdef A2DP_TRACE_CP_ACCEL
TRACE_A2DP_DECODER_I("%s: Reset frames", __func__);
#endif
reset_frames = true;
// Notify CP to work again
cp_accel_send_event_mcu2cp(1);
}
SRAM_TEXT_LOC
bool a2dp_cp_get_frame_reset_status(void) { return reset_frames; }
CP_TEXT_SRAM_LOC
enum CP_EMPTY_OUT_FRM_T a2dp_cp_get_emtpy_out_frame(void **p_buf,
uint32_t *p_len) {
enum CP_EMPTY_OUT_FRM_T ret;
uint8_t out_widx;
uint32_t out_wpos;
enum CP_DEC_STATE_T state;
if (reset_frames) {
return CP_EMPTY_OUT_FRM_ERR;
}
out_widx = cp_out_widx;
state = out_frames[out_widx].state;
if (state != CP_DEC_STATE_IDLE && state != CP_DEC_STATE_WORKING) {
return CP_EMPTY_OUT_FRM_ERR;
}
if (state == CP_DEC_STATE_WORKING) {
ret = CP_EMPTY_OUT_FRM_WORKING;
} else {
out_frames[out_widx].state = CP_DEC_STATE_WORKING;
ret = CP_EMPTY_OUT_FRM_OK;
}
out_wpos = out_frames[out_widx].pos;
if (p_buf) {
*p_buf = &cp_out_cache[out_wpos];
}
if (p_len) {
*p_len = out_frames[out_widx].len;
}
return ret;
}
CP_TEXT_SRAM_LOC
int a2dp_cp_consume_emtpy_out_frame(void) {
uint8_t out_widx;
if (reset_frames) {
return 0;
}
out_widx = cp_out_widx;
if (out_frames[out_widx].state != CP_DEC_STATE_WORKING) {
return 1;
}
out_frames[out_widx].state = CP_DEC_STATE_DONE;
#ifdef A2DP_TRACE_CP_ACCEL
TRACE_A2DP_DECODER_I("AD2P-CP out frame W[%d]", out_widx);
#endif
out_widx += 1;
if (out_widx >= CP_OUT_FRAME_CNT) {
out_widx -= CP_OUT_FRAME_CNT;
}
cp_out_widx = out_widx;
return 0;
}
SRAM_TEXT_LOC
int a2dp_cp_get_full_out_frame(void **p_buf, uint32_t *p_len) {
uint8_t out_ridx;
uint32_t out_rpos;
enum CP_DEC_STATE_T state;
if (reset_frames) {
return -1;
}
out_ridx = cp_out_ridx;
state = out_frames[out_ridx].state;
if (state != CP_DEC_STATE_DONE && state != CP_DEC_STATE_INIT_DONE) {
// Notify CP to work again
cp_accel_send_event_mcu2cp(
CP_BUILD_ID(CP_TASK_A2DP_DECODE, CP_EVENT_A2DP_DECODE));
if (state == CP_DEC_STATE_WORKING) {
return CP_EMPTY_OUT_FRM_WORKING;
} else {
return (10 + state);
}
}
out_rpos = out_frames[out_ridx].pos;
if (state == CP_DEC_STATE_DONE) {
if (p_buf) {
*p_buf = &cp_out_cache[out_rpos];
}
if (p_len) {
*p_len = out_frames[out_ridx].len;
}
} else {
if (p_buf) {
*p_buf = NULL;
}
if (p_len) {
*p_len = 0;
}
}
return 0;
}
SRAM_TEXT_LOC
int a2dp_cp_consume_full_out_frame(void) {
uint8_t out_ridx;
enum CP_DEC_STATE_T state;
if (reset_frames) {
return 0;
}
out_ridx = cp_out_ridx;
state = out_frames[out_ridx].state;
if (state != CP_DEC_STATE_DONE && state != CP_DEC_STATE_INIT_DONE) {
return 1;
}
#ifdef A2DP_TRACE_CP_ACCEL
TRACE_A2DP_DECODER_I("AD2P-CP out frame R[%d]", out_ridx);
#endif
out_frames[out_ridx].state = CP_DEC_STATE_IDLE;
out_ridx += 1;
if (out_ridx >= CP_OUT_FRAME_CNT) {
out_ridx -= CP_OUT_FRAME_CNT;
}
cp_out_ridx = out_ridx;
// Notify CP to work again
cp_accel_send_event_mcu2cp(
CP_BUILD_ID(CP_TASK_A2DP_DECODE, CP_EVENT_A2DP_DECODE));
return 0;
}
#endif