Files
BPI-CM9-Linux/aml-comp/multimedia/aml_audio_hal/audio_hal/audio_sdec.c
T

391 lines
11 KiB
C

/*
* Copyright (C) 2026 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_hw_sdec"
#include <errno.h>
#include <system/audio.h>
#include <audio_utils/primitives.h>
#include "aml_audio_log.h"
#include "aml_audio_sdec_wrapper.h"
#include "aml_malloc_debug.h"
#include "audio_hw.h"
#include "audio_hw_resource_mgr.h"
#include "audio_post_process.h"
#include "audio_sdec.h"
#include "dolby_lib_api.h"
/* match mixer MIXER_FRAME_COUNT */
#define AUDIO_SDEC_BLOCK_FRAMES 256
struct audio_sdec {
SDEC_Context *ctx;
uint32_t in_mask;
uint32_t out_mask;
void *out_buf;
size_t out_buf_sz;
};
static uint32_t audio_sdec_mask_from_buffer(const struct audio_buffer *abuf)
{
uint32_t mask;
if (!abuf)
return 0;
mask = (uint32_t)abuf->channel_mask;
if (mask != 0 && mask != (uint32_t)AUDIO_CHANNEL_INVALID)
return mask;
if (abuf->channel > 0)
return (uint32_t)audio_channel_out_mask_from_count((uint32_t)abuf->channel);
return 0;
}
static int audio_sdec_ensure_route(struct audio_sdec *sdec, uint32_t in_mask)
{
if (!sdec || !sdec->ctx)
return -1;
if (in_mask == 0)
return -1;
if (in_mask == sdec->in_mask && sdec->out_mask != 0)
return 0;
if (aml_audio_sdec_reconfigure(in_mask, sdec->out_mask, sdec->ctx) != SDEC_OK) {
AM_LOGE("sdec reconfigure failed in 0x%x out 0x%x", in_mask, sdec->out_mask);
return -1;
}
sdec->in_mask = in_mask;
AM_LOGI("sdec reconfigured in 0x%x out 0x%x", in_mask, sdec->out_mask);
return 0;
}
int audio_sdec_init(struct aml_audio_device *adev)
{
struct audio_sdec *sdec;
SDEC_Context *ctx = NULL;
uint32_t out_mask;
SDEC_Error err;
if (!adev)
return -EINVAL;
audio_sdec_deinit(adev);
if (!need_speaker_effect_path(adev)) {
AM_LOGI("sdec skip: not TV/SBR");
return 0;
}
/* Always on TV/SBR. out_mask == speaker layout. */
out_mask = (uint32_t)adev->real_spk_layout_mask;
if (out_mask == 0 || out_mask == (uint32_t)AUDIO_CHANNEL_INVALID) {
if (!adev->is_multi_channel_spk) {
out_mask = AUDIO_CHANNEL_OUT_STEREO;
AM_LOGI("sdec 2ch speaker, use STEREO out_mask");
} else {
AM_LOGE("sdec skip: real_spk_layout_mask invalid 0x%x", out_mask);
return 0;
}
}
if (!aml_audio_sdec_in_mask_is_supported(out_mask)) {
AM_LOGE("sdec skip: real_spk_layout_mask 0x%x not supported", out_mask);
return 0;
}
sdec = (struct audio_sdec *)aml_audio_calloc(1, sizeof(*sdec));
if (!sdec) {
AM_LOGE("sdec alloc failed");
return -ENOMEM;
}
err = aml_audio_sdec_create(out_mask, out_mask, AUDIO_SDEC_BLOCK_FRAMES,
SDEC_LFE_DOWNMIX_DEFAULT, &ctx);
if (err != SDEC_OK || !ctx) {
AM_LOGE("sdec create failed err=%d", err);
aml_audio_free(sdec);
return -1;
}
sdec->ctx = ctx;
sdec->in_mask = out_mask;
sdec->out_mask = out_mask;
adev->sdec = sdec;
AM_LOGI("sdec init ok out_mask 0x%x ch %d block %d",
out_mask, aml_audio_sdec_out_channels(ctx), AUDIO_SDEC_BLOCK_FRAMES);
return 0;
}
void audio_sdec_deinit(struct aml_audio_device *adev)
{
struct audio_sdec *sdec;
if (!adev || !adev->sdec)
return;
sdec = adev->sdec;
if (sdec->ctx) {
aml_audio_sdec_destroy(sdec->ctx);
sdec->ctx = NULL;
}
if (sdec->out_buf) {
aml_audio_free(sdec->out_buf);
sdec->out_buf = NULL;
}
aml_audio_free(sdec);
adev->sdec = NULL;
AM_LOGI("sdec deinit");
}
int audio_sdec_process(struct aml_audio_device *adev,
struct audio_buffer *in,
struct audio_buffer *out)
{
struct audio_sdec *sdec;
uint32_t in_mask;
int in_ch;
int out_ch;
int bps;
int total_frames;
int processed_frames;
int block_frames;
size_t need_out_bytes;
SDEC_Error err;
if (!adev || !adev->sdec || !in || !out)
return -1;
if (!check_audio_buffer_valid(in))
return -1;
sdec = adev->sdec;
if (!sdec->ctx)
return -1;
if (in->format != AUDIO_FORMAT_PCM_16_BIT &&
in->format != AUDIO_FORMAT_PCM_32_BIT) {
AM_LOGE("sdec unsupported format 0x%x", in->format);
return -1;
}
in_mask = audio_sdec_mask_from_buffer(in);
if (in_mask == 0) {
AM_LOGE("sdec invalid in_mask ch %d mask 0x%x", in->channel, in->channel_mask);
return -1;
}
if (in_mask == sdec->out_mask) {
*out = *in;
return 0;
}
if (audio_sdec_ensure_route(sdec, in_mask) != 0)
return -1;
in_ch = aml_audio_sdec_in_channels(sdec->ctx);
out_ch = aml_audio_sdec_out_channels(sdec->ctx);
if (in_ch <= 0 || out_ch <= 0)
return -1;
if (in->channel != in_ch) {
AM_LOGE("sdec in ch %d != ctx %d mask 0x%x", in->channel, in_ch, in_mask);
return -1;
}
bps = audio_bytes_per_sample(in->format);
if (bps <= 0)
return -1;
if (in->size % ((size_t)in_ch * (size_t)bps) != 0) {
AM_LOGE("sdec size %d not aligned to %dch bps %d", in->size, in_ch, bps);
return -1;
}
total_frames = in->size / (in_ch * bps);
if (total_frames <= 0)
return -1;
need_out_bytes = (size_t)total_frames * (size_t)out_ch * (size_t)bps;
if (aml_audio_check_and_realloc(&sdec->out_buf, &sdec->out_buf_sz, need_out_bytes)) {
AM_LOGE("sdec realloc out_buf %zu fail", need_out_bytes);
return -1;
}
if (in->buffer == sdec->out_buf) {
AM_LOGE("sdec in/out buffer overlap");
return -1;
}
processed_frames = 0;
block_frames = AUDIO_SDEC_BLOCK_FRAMES;
while (processed_frames < total_frames) {
int frames = total_frames - processed_frames;
if (frames > block_frames)
frames = block_frames;
if (in->format == AUDIO_FORMAT_PCM_16_BIT) {
const int16_t *in_s16 = (const int16_t *)in->buffer;
int16_t *out_s16 = (int16_t *)sdec->out_buf;
err = aml_audio_sdec_process_s16(sdec->ctx,
in_s16 + (size_t)processed_frames * (size_t)in_ch,
out_s16 + (size_t)processed_frames * (size_t)out_ch,
frames);
} else {
const int32_t *in_s32 = (const int32_t *)in->buffer;
int32_t *out_s32 = (int32_t *)sdec->out_buf;
err = aml_audio_sdec_process_s32(sdec->ctx,
in_s32 + (size_t)processed_frames * (size_t)in_ch,
out_s32 + (size_t)processed_frames * (size_t)out_ch,
frames);
}
if (err != SDEC_OK) {
AM_LOGE("sdec process failed at frame %d err=%d", processed_frames, err);
return -1;
}
processed_frames += frames;
}
*out = *in;
out->buffer = sdec->out_buf;
out->size = (int32_t)need_out_bytes;
out->channel = out_ch;
out->channel_mask = (int)sdec->out_mask;
AM_LOGV("sdec in %dch(0x%x) out %dch(0x%x) frames %d bytes %zu",
in_ch, in_mask, out_ch, sdec->out_mask, total_frames, need_out_bytes);
return 0;
}
/*
* Packed layout is fixed by MS12 lib version:
* 8ch (MS12 2.4): L R C LFE LS RS LRS RRS
* 10ch (MS12 2.8): L R C LFE LS RS LRS RRS LTF RTF
* Indices match SDEC_CH_L .. SDEC_CH_RTF. Height roles exist only on 2.8.
*/
static int dap_role_to_src_index(SDEC_ChannelRole role)
{
switch (role) {
case SDEC_ROLE_MONO:
return SDEC_CH_C;
case SDEC_ROLE_FL:
return SDEC_CH_L;
case SDEC_ROLE_FR:
return SDEC_CH_R;
case SDEC_ROLE_FC:
return SDEC_CH_C;
case SDEC_ROLE_LFE:
return SDEC_CH_LFE;
case SDEC_ROLE_SL:
return SDEC_CH_LS;
case SDEC_ROLE_SR:
return SDEC_CH_RS;
case SDEC_ROLE_RL:
return SDEC_CH_LRS;
case SDEC_ROLE_RR:
return SDEC_CH_RRS;
case SDEC_ROLE_TFL:
return SDEC_CH_LTF;
case SDEC_ROLE_TFR:
return SDEC_CH_RTF;
case SDEC_ROLE_TRL:
return SDEC_CH_LRS;
case SDEC_ROLE_TRR:
return SDEC_CH_RRS;
default:
return -1;
}
}
int audio_sdec_remap_ms12_dap_raw(struct aml_audio_device *adev,
struct audio_buffer *in,
struct audio_buffer *out)
{
struct dolby_ms12_desc *ms12;
uint32_t dst_mask;
SDEC_LayoutDesc dst_layout;
int8_t remap_table[SDEC_MAX_CHANNELS] = { 0 };
int dst_ch;
int src_ch;
int sample_size;
int frames;
size_t need_bytes;
int i;
if (!adev || !in || !out || !in->buffer || in->size <= 0)
return -1;
ms12 = &adev->ms12;
src_ch = in->channel;
if (src_ch <= 0)
return -1;
dst_mask = (uint32_t)in->channel_mask;
if (dst_mask == 0 || dst_mask == (uint32_t)AUDIO_CHANNEL_INVALID)
dst_mask = (uint32_t)audio_channel_out_mask_from_count((uint32_t)src_ch);
dst_ch = audio_channel_count_from_out_mask((audio_channel_mask_t)dst_mask);
if (dst_ch <= 0 || dst_ch > src_ch) {
AM_LOGW("dap raw remap skip src %d dst %d mask 0x%x", src_ch, dst_ch, dst_mask);
return -1;
}
if (aml_audio_sdec_layout_from_mask(dst_mask, &dst_layout) != SDEC_OK)
return -1;
if (dst_layout.count != dst_ch)
return -1;
for (i = 0; i < dst_ch; i++) {
remap_table[i] = (int8_t)dap_role_to_src_index(dst_layout.roles[i]);
if (remap_table[i] < 0 || remap_table[i] >= src_ch) {
AM_LOGW("dap raw remap invalid dst[%d] role %d src_ch %d",
i, dst_layout.roles[i], src_ch);
return -1;
}
}
if (in->format != AUDIO_FORMAT_PCM_16_BIT &&
in->format != AUDIO_FORMAT_PCM_32_BIT) {
AM_LOGW("dap raw remap expect pcm16/32, got 0x%x", in->format);
return -1;
}
sample_size = audio_bytes_per_sample((audio_format_t)in->format);
if (sample_size <= 0)
return -1;
if (in->size % (src_ch * sample_size) != 0) {
AM_LOGW("dap raw remap size %d not aligned to %dch bps %d",
in->size, src_ch, sample_size);
return -1;
}
frames = in->size / (src_ch * sample_size);
if (frames <= 0)
return -1;
need_bytes = (size_t)frames * (size_t)dst_ch * (size_t)sample_size;
if (aml_audio_check_and_realloc(&ms12->dap_raw_remap_buf, &ms12->dap_raw_remap_buf_sz, need_bytes))
return -1;
memcpy_by_index_array(ms12->dap_raw_remap_buf, (uint32_t)dst_ch, in->buffer, (uint32_t)src_ch,
remap_table, sample_size, frames);
*out = *in;
out->buffer = ms12->dap_raw_remap_buf;
out->size = (int32_t)need_bytes;
out->channel = dst_ch;
out->channel_mask = (int)dst_mask;
AM_LOGV("dap raw remap %dch -> %dch(0x%x) frames %d",
src_ch, dst_ch, dst_mask, frames);
return 0;
}