224 lines
7.5 KiB
C
224 lines
7.5 KiB
C
/*
|
|
* Copyright (C) 2025 Amlogic Corporation.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#define LOG_TAG "audio_llp"
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <time.h>
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "aml_audio_log.h"
|
|
#include "aml_audio_output.h"
|
|
#include "audio_buffer.h"
|
|
#ifndef NO_AUDIO_CAP
|
|
#include <IpcBuffer/IpcBuffer_c.h>
|
|
#endif
|
|
#include "audio_hw.h"
|
|
#include "audio_hw_utils.h"
|
|
#include "aml_audio_stream.h"
|
|
#include "audio_hw_ms12_v2.h"
|
|
#include "audio_llp.h"
|
|
|
|
|
|
int llp_init(struct aml_audio_device *adev, int nChannel)
|
|
{
|
|
int ret = 0;
|
|
#ifndef NO_AUDIO_CAP
|
|
do {
|
|
audio_llp_ctx_t *llp_ctx = aml_audio_calloc(1, sizeof (audio_llp_ctx_t));
|
|
if (NULL == llp_ctx) {
|
|
AM_LOGW("llp_ctx malloc error!");
|
|
ret = -1;
|
|
break;
|
|
}
|
|
|
|
char *llp_buf = IpcBuffer_create(LLP_BUFFER_NAME, LLP_BUFFER_SIZE);
|
|
if (!llp_buf) {
|
|
AM_LOGE("LLP IPC buffer create failed.");
|
|
aml_audio_free(llp_ctx);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
llp_ctx->llp_buf = llp_buf;
|
|
llp_ctx->nChannel = nChannel;
|
|
pthread_mutex_lock(&(adev->llp_lock));
|
|
adev->llp_ctx = llp_ctx;
|
|
pthread_mutex_unlock(&(adev->llp_lock));
|
|
AM_LOGI("LLP INIT llp_buf:%p, llp_ctx:%p", llp_buf, llp_ctx);
|
|
if (6 == nChannel) {
|
|
/* Allow MS12 MC output when LLP input is 6ch */
|
|
adev->sink_allow_max_channel = true;
|
|
}
|
|
aml_alsa_set_llp(true);
|
|
|
|
if (eDolbyMS12Lib == adev->dolby_lib_type) {
|
|
set_ms12_alsa_limit_frame(&adev->ms12, MS12_ALSA_LOW_LIMIT_FRAME);
|
|
struct aml_arc_hdmi_desc* hdmi_descs = NULL;
|
|
hdmi_descs = get_arc_hdmi_cap(adev);
|
|
if (((PCM == adev->hdmi_format) || (BYPASS == adev->hdmi_format))
|
|
&& (AUDIO_FORMAT_PCM_16_BIT == adev->optical_format)
|
|
&& (hdmi_descs->pcm_fmt.max_channels >= 6)) {
|
|
set_ms12_mch_enable(&(adev->ms12), true);
|
|
} else {
|
|
set_ms12_mch_enable(&(adev->ms12), false);
|
|
}
|
|
AM_LOGI("hdmi_format:%d, optical:%#x, max_channel:%d, mch_exist:%d", adev->hdmi_format, adev->optical_format,
|
|
hdmi_descs->pcm_fmt.max_channels, aml_mch_pcm_stream_exist(adev) );
|
|
}
|
|
} while (0);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
void llp_deinit(struct aml_audio_device *adev)
|
|
{
|
|
#ifndef NO_AUDIO_CAP
|
|
pthread_mutex_lock(&(adev->llp_lock));
|
|
if (adev->llp_ctx) {
|
|
audio_llp_ctx_t *llp_ctx = (audio_llp_ctx_t *)(adev->llp_ctx);
|
|
if (llp_ctx->llp_buf) {
|
|
IpcBuffer_destroy(llp_ctx->llp_buf);
|
|
}
|
|
aml_audio_free(llp_ctx);
|
|
adev->llp_ctx = NULL;
|
|
}
|
|
pthread_mutex_unlock(&(adev->llp_lock));
|
|
aml_alsa_set_llp(false);
|
|
adev->sink_allow_max_channel = false;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
void llp_injection_and_mixer(struct aml_audio_device *adev, void *main_buffer, int main_channels)
|
|
{
|
|
#ifndef NO_AUDIO_CAP
|
|
int ret = -1;
|
|
audio_llp_ctx_t *llp_ctx = (audio_llp_ctx_t *)adev->llp_ctx;
|
|
if (NULL == llp_ctx) {
|
|
return;
|
|
}
|
|
|
|
pthread_mutex_lock(&(adev->llp_lock));
|
|
if (!llp_ctx->llp_buf) {
|
|
pthread_mutex_unlock(&(adev->llp_lock));
|
|
AM_LOGW("llp_buf NULL!");
|
|
return;
|
|
}
|
|
|
|
char llp_bufin[256*6*2] = {0};
|
|
char llp_bufout[256*8*2] = {0};
|
|
int outsamples = 0;
|
|
ret = llp_input_process(adev->llp_ctx, llp_bufin, &outsamples, (int)aml_audio_pcm_out_get_delayframe(adev, true));
|
|
pthread_mutex_unlock(&(adev->llp_lock));
|
|
AM_LOGI("outsamples:%d, ret:%d, output_multipcm_ch:%d, main_channels:%d",
|
|
outsamples, ret, adev->output_multipcm_ch, main_channels);
|
|
if ((0 == ret) && (0 != outsamples)) {
|
|
/*dump llp input*/
|
|
if (get_debug_value(AML_DUMP_AUDIOHAL_IN)) {
|
|
aml_dump_audio_bitstreams_with_id("input_llp.raw", llp_bufin, 256*6*2, 0);
|
|
}
|
|
switch (main_channels) {
|
|
case 2:
|
|
extend_channel_6_2(llp_bufout, llp_bufin, 256, 2, 6);
|
|
break;
|
|
case 8:
|
|
extend_channel_6_8(llp_bufout, llp_bufin, 256, 8, 6);
|
|
break;
|
|
default:
|
|
AM_LOGW("unsupport channels:%d", main_channels);
|
|
break;
|
|
}
|
|
do_mixing_specified_channel_cnt(main_buffer, llp_bufout, 256,
|
|
AUDIO_FORMAT_PCM_16_BIT, AUDIO_FORMAT_PCM_16_BIT,
|
|
main_channels);
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
int llp_input_process(audio_llp_ctx_t *llp_ctx, void *outbuffer, int *outsamples, int alsa_level)
|
|
{
|
|
#ifndef NO_AUDIO_CAP
|
|
short *in;
|
|
unsigned offset, level;
|
|
uint64_t wp;
|
|
int nChannel;
|
|
bool fill_silence = false;
|
|
struct timespec ts;
|
|
|
|
if (NULL == outbuffer || NULL == outsamples || NULL == llp_ctx) {
|
|
AM_LOGE("outbuffer:%p, outsamples:%p, llp_ctx:%p, NULL ERR!!", outbuffer, outsamples, llp_ctx);
|
|
return -1;
|
|
}
|
|
|
|
*outsamples = 0;
|
|
|
|
wp = IpcBuffer_get_wr_pos(LLP_BUFFER_NAME);
|
|
nChannel = llp_ctx->nChannel;
|
|
AM_LOGI("get buffer, llp_ctx:%p, llp_ctx->llp_buf:%p, wp:%"PRIu64", llp_ctx->llp_rp:%"PRIu64", nChannel:%d",
|
|
llp_ctx, llp_ctx->llp_buf, wp, llp_ctx->llp_rp, nChannel);
|
|
if (0 == nChannel) {
|
|
AM_LOGE("error nChannel:0, need check!!");
|
|
return -1;
|
|
}
|
|
level = (wp - llp_ctx->llp_rp) % LLP_BUFFER_SIZE;
|
|
|
|
if (level < 256 * nChannel * 2) {
|
|
if (alsa_level > 48 * 8) {
|
|
IpcBuffer_setMeta(llp_ctx->llp_buf, wp / nChannel / 2, alsa_level + level / nChannel / 2);
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
|
AM_LOGI("[%lld.%.9ld]Holding silence, alsa_level = %d, input_level = %d",
|
|
(long long)ts.tv_sec, ts.tv_nsec, alsa_level, level / nChannel / 2);
|
|
return 0;
|
|
}
|
|
|
|
fill_silence = true;
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
|
AM_LOGI("[%lld.%.9ld]llp underrun, fill silence", (long long)ts.tv_sec, ts.tv_nsec);
|
|
|
|
if (llp_ctx->llp_underrun_wp != wp) {
|
|
IpcBuffer_inc_underrun(llp_ctx->llp_buf);
|
|
llp_ctx->llp_underrun_wp = wp;
|
|
AM_LOGI("[%lld.%.9ld] inc underrun", (long long)ts.tv_sec, ts.tv_nsec);
|
|
}
|
|
}
|
|
|
|
llp_ctx->llp_rp = wp - level; // normalize
|
|
offset = llp_ctx->llp_rp % LLP_BUFFER_SIZE;
|
|
in = (short *)(IpcBuffer_get_ptr(LLP_BUFFER_NAME) + offset);
|
|
|
|
if (!fill_silence) {
|
|
memcpy(outbuffer, in, 256*2*nChannel);
|
|
level -= 256 * nChannel * 2;
|
|
llp_ctx->llp_rp += nChannel * 2 * 256;
|
|
*outsamples = 256;
|
|
}
|
|
|
|
IpcBuffer_setMeta(llp_ctx->llp_buf, wp / nChannel / 2, alsa_level + level / nChannel / 2);
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
|
AM_LOGI("[%lld.%.9ld] llp wp = %" PRIu64 ", rp = %" PRIu64 ", alsa_level = %d, llp input level = %d, nChannel:%d",
|
|
(long long)ts.tv_sec, ts.tv_nsec, wp / 12, llp_ctx->llp_rp / 12, alsa_level, level / nChannel / 2, nChannel);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|