2026-08-13 11:28:40 +08:00
/*
* Copyright (C) 2017 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_hal_manager"
//#define LOG_NDEBUG 0
#include "aml_audio_log.h"
#include <system/audio.h>
#include <hardware/audio.h>
#include <sound/asound.h>
#include <tinyalsa/asoundlib.h>
#include <inttypes.h>
#include <unistd.h>
#include <limits.h>
#include "audio_hw.h"
#include "alsa_manager.h"
#include "audio_hw_utils.h"
#include "alsa_device_parser.h"
#include "dolby_lib_api.h"
#include "aml_audio_stream.h"
#include "alsa_config_parameters.h"
#include "dtv_patch.h"
#include "aml_audio_timer.h"
#include "dtv_private_object.h"
#include "aml_config_data.h"
#include "aml_dump_debug.h"
#include "tv_patch_ctrl.h"
#define AML_ZERO_ADD_MIN_SIZE 1024
#define AUDIO_EAC3_FRAME_SIZE 16
#define AUDIO_AC3_FRAME_SIZE 4
#define AUDIO_TV_PCM_FRAME_SIZE 32
#define AUDIO_DEFAULT_PCM_FRAME_SIZE 4
#define MAX_AVSYNC_GAP (10*90000)
#define MAX_AVSYNC_WAIT_TIME (3*1000*1000)
#define ALSA_DELAY_THRESHOLD_MS_NORMAL (32)
#define ALSA_DELAY_THRESHOLD_MS_LLP (16)
#define ALSA_OUTPUT_PCM_FILE "alsa_pcm_write.raw"
#define ALSA_OUTPUT_SPDIF_FILE "alsa_spdif_write"
#define WAIT_COUNT_MAX 30
#define ALSA_DELAY_THRESHOLD_MS ((alsa_llp_mode) ? ALSA_DELAY_THRESHOLD_MS_LLP : ALSA_DELAY_THRESHOLD_MS_NORMAL)
static bool alsa_llp_mode ;
typedef struct alsa_handle {
unsigned int card ;
unsigned int pcm_index ; /*used for open the alsa device*/
struct pcm_config config ;
struct pcm * pcm ;
int block_mode ;
unsigned int alsa_port ; /*refer to PORT_SPDIF, PORT***/
audio_format_t format ;
uint32_t start_threshold ;
uint32_t write_cnt ;
uint64_t write_frames ;
bool alsa_running_status ; // for pcm output, av sync
bool alsa_status_changed ; // for pcm output, av sync
} alsa_handle_t ;
static int aml_audio_get_alsa_debug () {
return get_debug_value ( AML_DEBUG_AUDIOHAL_ALSA );
}
static int aml_alsa_pcm_close ( alsa_handle_t * handle )
{
struct pcm * pcm = NULL ;
if ( handle == NULL || handle -> pcm == NULL ) {
ALOGE ( "%s handle is NULL \n " , __func__ );
return - 1 ;
}
pcm = handle -> pcm ;
pcm_close ( pcm );
aml_audio_free ( handle );
return 0 ;
}
void aml_alsa_set_llp ( bool mode )
{
alsa_llp_mode = mode ;
}
int aml_alsa_input_open ( void ** handle , aml_alsa_config_t * stream_config , aml_device_config_t * device_config )
{
alsa_handle_t * alsa_handle = NULL ;
struct pcm_config * config = NULL ;
int ret = 0 ;
alsa_handle = ( alsa_handle_t * ) aml_audio_calloc ( 1 , sizeof ( alsa_handle_t ));
if ( alsa_handle == NULL ) {
AM_LOGE ( "malloc alsa_handle failed \n " );
return - 1 ;
}
config = & alsa_handle -> config ;
alsa_handle -> card = alsa_device_get_card_index ();
alsa_handle -> alsa_port = device_config -> device_port ;
if ( alsa_handle -> alsa_port == PORT_BUILTINMIC ) {
alsa_handle -> pcm_index = aml_get_json_int_value ( "Builtinmic_Alsa_Dev_Id" , alsa_device_update_pcm_index ( PORT_BUILTINMIC , CAPTURE ));
} else {
alsa_handle -> pcm_index = alsa_device_update_pcm_index ( alsa_handle -> alsa_port , CAPTURE );
}
config -> format = convert_audio_format_2_alsa_format ( stream_config -> format );
config -> channels = audio_channel_count_from_in_mask ( stream_config -> channel_mask );
config -> rate = stream_config -> sample_rate ;
config -> period_size = stream_config -> period_size ;
config -> period_count = stream_config -> period_count ;
config -> stop_threshold = config -> period_size * config -> period_count ;
config -> silence_size = LONG_MAX ;
AM_LOGI ( "open card(%d), device(%d)" , alsa_handle -> card , alsa_handle -> pcm_index );
AM_LOGI ( "ALSA open configs: channels %d format %d period_count %d period_size %d rate %d" ,
config -> channels , config -> format , config -> period_count , config -> period_size , config -> rate );
AM_LOGI ( "ALSA open configs: threshold start %lu stop %lu silence %lu silence_size %lu avail_min %lu" ,
config -> start_threshold , config -> stop_threshold , config -> silence_threshold , config -> silence_size , config -> avail_min );
/* non-blocking read prevent 10s stuck when switching TV sources */
alsa_handle -> pcm = pcm_open ( alsa_handle -> card , alsa_handle -> pcm_index , PCM_IN | PCM_NONBLOCK , config );
if ( ! alsa_handle -> pcm || ! pcm_is_ready ( alsa_handle -> pcm )) {
AM_LOGE ( "open device failed: %s" , pcm_get_error ( alsa_handle -> pcm ));
ret = - 1 ;
goto error ;
}
* handle = ( void * ) alsa_handle ;
error :
if ( ret < 0 && alsa_handle ) {
aml_alsa_pcm_close ( alsa_handle );
* handle = NULL ;
}
AM_LOGI ( "alsa_input_open handle %p, ret %d" , * handle , ret );
return ret ;
}
int aml_alsa_input_read ( void * handle , void * buffer , int bytes ) {
int ret = 0 ;
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
if ( alsa_handle == NULL || alsa_handle -> pcm == NULL ) {
AM_LOGE ( "alsa_handle or pcm is NULL!!!" );
return - 1 ;
}
ret = pcm_read ( alsa_handle -> pcm , ( unsigned char * ) buffer , bytes );
if ( ret < 0 ) {
if ( ret != - 1 ) {
AM_LOGE ( "pcm_read fail, ret:%d, error info:%s" , ret , strerror ( errno ));
memset ( buffer , 0 , bytes );
}
}
return ret ;
}
int aml_alsa_input_flush ( void * handle )
{
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
if ( alsa_handle == NULL || alsa_handle -> pcm == NULL ) {
AM_LOGE ( "alsa_handle or pcm is NULL!!!" );
return - 1 ;
}
int ret = pcm_ioctl ( alsa_handle -> pcm , SNDRV_PCM_IOCTL_RESET , 0 );
if ( ret < 0 ) {
AM_LOGE ( "cannot reset pcm!" );
}
return ret ;
}
int aml_alsa_input_get_delay ( void * handle ) {
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
int ret = 0 ;
if ( alsa_handle == NULL || alsa_handle -> pcm == NULL ) {
AM_LOGE ( "alsa_handle or pcm is NULL!!!" );
return 0 ;
}
struct pcm * pcm = alsa_handle -> pcm ;
snd_pcm_sframes_t delay = 0 ;
int delay_frames = 0 ;
ret = pcm_ioctl ( pcm , SNDRV_PCM_IOCTL_DELAY , & delay );
if ( ret < 0 ) {
AM_LOGE ( "cannot get delay!" );
} else {
delay_frames = delay ;
}
return delay_frames ;
}
uint64_t aml_alsa_input_get_timestamp ( void * handle ) {
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
if ( alsa_handle == NULL || alsa_handle -> pcm == NULL ) {
AM_LOGE ( "alsa_handle or pcm is NULL!!!" );
return 0 ;
}
return pcm_get_timestamp ( alsa_handle -> pcm , alsa_handle -> config . rate , 0 );
}
int aml_alsa_input_stop ( void * handle )
{
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
if ( alsa_handle == NULL || alsa_handle -> pcm == NULL ) {
AM_LOGE ( "alsa_handle or pcm is NULL!!!" );
return - 1 ;
}
int ret = pcm_stop ( alsa_handle -> pcm );
if ( ret < 0 ) {
AM_LOGE ( "cannot stop pcm!" );
}
return ret ;
}
int aml_alsa_input_close ( void * handle )
{
AM_LOGI ( "handle %p" , handle );
return aml_alsa_pcm_close (( alsa_handle_t * ) handle );
}
int aml_alsa_output_open ( void ** handle , aml_alsa_config_t * stream_config , aml_device_config_t * device_config )
{
struct pcm_config * config = NULL ;
int card = 0 ;
int alsa_port = 0 ;
int pcm_index = - 1 ;
struct pcm * pcm = NULL ;
alsa_handle_t * alsa_handle = NULL ;
bool platform_is_tv = 0 ;
audio_format_t format = AUDIO_FORMAT_PCM_16_BIT ;
unsigned int channels = 0 ;
unsigned int rate = 0 ;
struct aml_audio_device * adev = ( struct aml_audio_device * ) adev_get_handle ();
alsa_handle = ( alsa_handle_t * ) aml_audio_calloc ( 1 , sizeof ( alsa_handle_t ));
if ( alsa_handle == NULL ) {
AM_LOGE ( "malloc alsa_handle failed \n " );
return - 1 ;
}
config = & alsa_handle -> config ;
format = stream_config -> format ;
channels = audio_channel_count_from_out_mask ( stream_config -> channel_mask );
rate = stream_config -> sample_rate ;
if ( audio_is_linear_pcm ( format )) {
get_hardware_config_parameters ( config , format , adev -> default_alsa_ch , rate , platform_is_tv ,
2026-11-11 08:50:43 +08:00
stream_config -> format == AUDIO_FORMAT_IEC61937 , is_low_latency_mode ( adev ), adev -> hdmi_format == BYPASS );
2026-08-13 11:28:40 +08:00
} else {
get_hardware_config_parameters ( config , format , channels , rate , platform_is_tv ,
2026-11-11 08:50:43 +08:00
stream_config -> format == AUDIO_FORMAT_IEC61937 , is_low_latency_mode ( adev ), adev -> hdmi_format == BYPASS );
2026-08-13 11:28:40 +08:00
}
config -> channels = channels ;
config -> rate = rate ;
if ( config -> rate == 0 || config -> channels == 0 ) {
AM_LOGE ( "Invalid sampleate=%d channel=%d \n " , config -> rate , config -> channels );
goto exit ;
}
if ( format == AUDIO_FORMAT_PCM_16_BIT ) {
config -> format = PCM_FORMAT_S16_LE ;
} else if ( format == AUDIO_FORMAT_PCM_32_BIT ) {
config -> format = PCM_FORMAT_S32_LE ;
} else {
config -> format = PCM_FORMAT_S16_LE ;
}
config -> avail_min = 0 ;
card = alsa_device_get_card_index ();
alsa_port = device_config -> device_port ;
if ( alsa_port < 0 ) {
AM_LOGE ( "Wrong alsa_device ID \n " );
goto exit ;
}
pcm_index = alsa_device_update_pcm_index ( alsa_port , PLAYBACK );
if ( pcm_index < 0 ) {
int config_pcm_index ;
config_pcm_index = aml_get_json_int_value ( "HDMITX_HBR_PCM_INDEX" , - 1 );
if ( config_pcm_index == 2 ) {
pcm_index = config_pcm_index ;
config -> period_size = 2048 ;
config -> start_threshold = 0 ;
}
}
config -> silence_size = LONG_MAX ;
ALOGI ( "In pcm open ch=%d rate=%d \n " , config -> channels , config -> rate );
ALOGI ( "%s, audio open card(%d), device(%d) \n " , __func__ , card , pcm_index );
ALOGI ( "ALSA open configs: channels %d format %d period_count %d period_size %d rate %d \n " ,
config -> channels , config -> format , config -> period_count , config -> period_size , config -> rate );
ALOGI ( "ALSA open configs: threshold start %lu stop %lu silence %lu silence_size %lu avail_min %lu \n " ,
config -> start_threshold , config -> stop_threshold , config -> silence_threshold , config -> silence_size , config -> avail_min );
pcm = pcm_open ( card , pcm_index , PCM_OUT | PCM_MONOTONIC , config );
if ( ! pcm || ! pcm_is_ready ( pcm )) {
ALOGE ( "%s, pcm %p open [ready %d] failed. error info:%s \n " , __func__ , pcm , pcm_is_ready ( pcm ), pcm_get_error ( pcm ));
pcm_close ( pcm );
goto exit ;
}
alsa_handle -> card = card ;
alsa_handle -> pcm_index = pcm_index ;
alsa_handle -> pcm = pcm ;
alsa_handle -> alsa_port = alsa_port ;
alsa_handle -> format = format ;
alsa_handle -> write_cnt = 0 ;
alsa_handle -> write_frames = 0 ;
alsa_handle -> start_threshold = config -> start_threshold ;
* handle = ( void * ) alsa_handle ;
return 0 ;
exit :
if ( alsa_handle ) {
aml_audio_free ( alsa_handle );
}
* handle = NULL ;
return - 1 ;
}
int aml_alsa_output_close ( void * handle ) {
AM_LOGI ( "handle %p" , handle );
return aml_alsa_pcm_close (( alsa_handle_t * ) handle );
}
int aml_alsa_output_write ( void * handle , const void * buffer , int bytes ) {
int ret = - 1 ;
alsa_handle_t * alsa_handle = NULL ;
alsa_handle = ( alsa_handle_t * ) handle ;
struct aml_audio_device * adev = ( struct aml_audio_device * ) adev_get_handle ();
int debug_enable = aml_audio_get_alsa_debug ();
if ( alsa_handle == NULL || alsa_handle -> pcm == NULL || buffer == NULL || bytes == 0 ) {
//AM_LOGE("invalid param, alsa_handle:%p, pcm:%p, buffer:%p, bytes:%zu",
//alsa_handle, alsa_handle?alsa_handle->pcm:0, buffer, bytes);
return - 1 ;
}
#if 0
//ALOGD("handle=%p pcm=%p\n",alsa_handle,alsa_handle->pcm);
/*add for work around ddp dd ouput ,ddp underrun issue */
dd_pcm = alsa_handle->pcm;
ddp_pcm = adev->pcm_handle[adev->ms12.device];
if (is_sc2_chip() && eDolbyMS12Lib == adev->dolby_lib_type && dd_pcm && ddp_pcm && (alsa_handle->format != AUDIO_FORMAT_MAT)) {
snd_pcm_sframes_t delay_ddp = 0,delay_dd = 0;
ret = pcm_ioctl(dd_pcm, SNDRV_PCM_IOCTL_DELAY, &delay_dd);
if (ret < 0) {
delay_dd = alsa_handle->config.start_threshold;
}
ret = pcm_ioctl(ddp_pcm, SNDRV_PCM_IOCTL_DELAY, &delay_ddp);
if (ret < 0) {
delay_ddp = adev->ms12_config.start_threshold;
}
if (delay_dd + write_frames >= alsa_handle->config.start_threshold * 2)
overflow_flag = 1;
/* dd write blocked and ddp delay at a low level ,so skip dd data to avoid ddp underun*/
if (overflow_flag && delay_ddp <= 2 * 6144)
underrun_flag = 1;
if ( underrun_flag ) {
ALOGI("skip dd data for delay_dd frame =%ld delay_ddp frame %ld\n",delay_dd, delay_ddp);
return 0;
}
}
#endif
{
struct snd_pcm_status status ;
bool alsa_status ;
pcm_ioctl ( alsa_handle -> pcm , SNDRV_PCM_IOCTL_STATUS , & status );
if ( PORT_I2S == alsa_handle -> alsa_port ) { // for pcm output
alsa_status = ( status . state == PCM_STATE_RUNNING );
if ( alsa_status != alsa_handle -> alsa_running_status ) {
ALOGI ( "alsa_running_status change from %d to %d" , alsa_handle -> alsa_running_status , alsa_status );
alsa_handle -> alsa_running_status = alsa_status ;
alsa_handle -> alsa_status_changed = true ;
}
}
if ( status . state != PCM_STATE_RUNNING ) {
AM_LOGW ( "format =0x%x alsa underrun(%d), state(%d)" , alsa_handle -> format , status . state == PCM_STATE_XRUN , status . state );
}
}
if ( get_debug_value ( AML_DUMP_AUDIOHAL_OUT )) {
char file_name [ 128 ] = { 0 };
if ( alsa_handle -> format == AUDIO_FORMAT_AC3 ) {
snprintf ( file_name , 128 , "%s.%s" , ALSA_OUTPUT_SPDIF_FILE , "dd" );
} else if ( alsa_handle -> format == AUDIO_FORMAT_E_AC3 ) {
snprintf ( file_name , 128 , "%s.%s" , ALSA_OUTPUT_SPDIF_FILE , "ddp" );
} else if ( alsa_handle -> format == AUDIO_FORMAT_MAT ) {
snprintf ( file_name , 128 , "%s.%s" , ALSA_OUTPUT_SPDIF_FILE , "mat" );
} else if ( alsa_handle -> format == AUDIO_FORMAT_DTS ) {
snprintf ( file_name , 128 , "%s.%s" , ALSA_OUTPUT_SPDIF_FILE , "dts" );
} else if ( alsa_handle -> format == AUDIO_FORMAT_DTS_HD ) {
snprintf ( file_name , 128 , "%s.%s" , ALSA_OUTPUT_SPDIF_FILE , "dtshd" );
} else if ( alsa_handle -> format == AUDIO_FORMAT_DTS_HD_MA ) {
snprintf ( file_name , 128 , "%s.%s" , ALSA_OUTPUT_SPDIF_FILE , "dtshd_ma" );
} else if ( alsa_handle -> format == AUDIO_FORMAT_MPEGH ) {
snprintf ( file_name , 128 , "%s.%s" , ALSA_OUTPUT_SPDIF_FILE , "mpegh" );
} else {
snprintf ( file_name , 128 , "%s_%dch.%s" , ALSA_OUTPUT_SPDIF_FILE , alsa_handle -> config . channels , "pcm" );
}
aml_dump_audio_bitstreams ( file_name , buffer , bytes );
}
if ( get_debug_value ( AML_DEBUG_AUDIOHAL_LEVEL_DETECT ) && ( AUDIO_FORMAT_PCM_16_BIT == alsa_handle -> format )) {
check_audio_level ( "alsa_pcm" , buffer , bytes );
}
alsa_handle -> write_cnt ++ ;
alsa_handle -> write_frames += pcm_bytes_to_frames ( alsa_handle -> pcm , bytes );
if ( debug_enable || ( alsa_handle -> write_cnt % 1000 ) == 0 || get_debug_value ( AML_DEBUG_AUDIOHAL_GAME_MODE )) {
snd_pcm_sframes_t frames = 0 ;
ret = pcm_ioctl ( alsa_handle -> pcm , SNDRV_PCM_IOCTL_DELAY , & frames );
ALOGI ( "alsa format =0x%x delay frames =%ld total frames=%" PRIu64 " write_bytes=%d write_frame=%d" , alsa_handle -> format , frames , alsa_handle -> write_frames , bytes , pcm_bytes_to_frames ( alsa_handle -> pcm , bytes ));
}
if ( adev -> reset_hdmitx_audio && adev -> active_outport == OUTPORT_HDMI && (( alsa_handle -> format == adev -> sink_format ) || ( alsa_handle -> format == AUDIO_FORMAT_E_AC3 ))) {
pcm_stop ( alsa_handle -> pcm );
adev -> reset_hdmitx_audio = false ;
AM_LOGI ( "reset hdmitx alsa, output format=%x, sink format=%x" , alsa_handle -> format , adev -> sink_format );
}
ret = pcm_write ( alsa_handle -> pcm , buffer , bytes );
if ( ret < 0 ) {
AM_LOGE ( "write failed,pcm handle %p %s," ,
alsa_handle -> pcm , pcm_get_error ( alsa_handle -> pcm ));
}
return ret ;
}
int aml_alsa_output_getinfo ( void * handle , alsa_info_type_t type , alsa_output_info_t * info ) {
int ret = - 1 ;
alsa_handle_t * alsa_handle = NULL ;
alsa_handle = ( alsa_handle_t * ) handle ;
if ( handle == NULL ) {
return - 1 ;
}
switch ( type ) {
case OUTPUT_INFO_DELAYFRAME : {
snd_pcm_sframes_t delay = 0 ;
int rate = alsa_handle -> config . rate ;
int rate_multiply = 1 ;
ret = pcm_ioctl ( alsa_handle -> pcm , SNDRV_PCM_IOCTL_DELAY , & delay );
if ( ret < 0 ) {
info -> delay_ms = 0 ;
return - 1 ;
}
if ( alsa_handle -> format == AUDIO_FORMAT_MAT ) {
rate_multiply = 4 ;
} else if ( alsa_handle -> format == AUDIO_FORMAT_E_AC3 ) {
rate_multiply = 4 ;
}
info -> delay_ms = delay * 1000 / ( rate * rate_multiply );
return 0 ;
}
default :
return - 1 ;
}
return - 1 ;
}
void * get_internal_pcm ( void * handle ) {
if ( ! handle ) {
return NULL ;
}
return (( alsa_handle_t * ) handle ) -> pcm ;
}
int aml_alsa_output_pause ( void * handle ) {
int ret = - 1 ;
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
if ( handle == NULL ) {
return - 1 ;
}
if ( alsa_handle -> pcm ) {
ret = pcm_ioctl ( alsa_handle -> pcm , SNDRV_PCM_IOCTL_PAUSE , 1 );
if ( ret < 0 ) {
ALOGE ( "%s error %d" , __func__ , ret );
}
}
return ret ;
}
int aml_alsa_output_resume ( void * handle ) {
int ret = - 1 ;
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
if ( handle == NULL ) {
return - 1 ;
}
if ( alsa_handle -> pcm ) {
ret = pcm_ioctl ( alsa_handle -> pcm , SNDRV_PCM_IOCTL_PREPARE );
if ( ret < 0 ) {
ALOGE ( "%s error %d" , __func__ , ret );
}
}
return ret ;
}
int aml_alsa_output_stop ( void * handle ) {
int ret = - 1 ;
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
if ( handle == NULL ) {
return - 1 ;
}
if ( alsa_handle -> pcm ) {
ret = pcm_stop ( alsa_handle -> pcm );
if ( ret < 0 ) {
AM_LOGE ( "stop pcm failed,pcm handle %p %s" ,
alsa_handle -> pcm , pcm_get_error ( alsa_handle -> pcm ));
}
}
return ret ;
}
bool aml_alsa_output_get_alsa_status ( void * handle , bool * alsa_status ) {
bool ret = false ;
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
if ( handle == NULL ) {
return ret ;
}
ret = alsa_handle -> alsa_status_changed ;
* alsa_status = alsa_handle -> alsa_running_status ;
return ret ;
}
int aml_alsa_output_clear_alsa_status_changed_flag ( void * handle ) {
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
if ( handle == NULL ) {
return - 1 ;
}
alsa_handle -> alsa_status_changed = false ;
return 0 ;
}
// ignore_default == true, means that if we can not get delay frames from alsa, we will return 0 frame
// ignore_default == false, means that if we can not get delay frames from alsa, we will return start threshold
uint64_t aml_alsa_output_get_delay_frame ( void * handle , bool ignore_default )
{
uint64_t delay_frames = 0 ;
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
int ret = - 1 ;
snd_pcm_sframes_t frames = 0 ;
if ( alsa_handle ) {
if ( alsa_handle -> pcm && pcm_is_ready ( alsa_handle -> pcm )) {
ret = pcm_ioctl ( alsa_handle -> pcm , SNDRV_PCM_IOCTL_DELAY , & frames );
if ( ret < 0 ) {
AM_LOGE ( "pcm_ioctl fail, ret:%#x, error info:%s" ,
ret , strerror ( errno ));
} else if ( ignore_default && ! alsa_handle -> alsa_running_status ) {
delay_frames = 0 ;
} else if ( frames >= 0 ) {
delay_frames = frames ;
}
}
if ( ret < 0 ) {
if ( ignore_default ) {
delay_frames = 0 ;
} else {
delay_frames = alsa_handle -> config . start_threshold ;
}
}
}
return delay_frames ;
}
bool aml_alsa_output_get_start_threshold ( void * handle , uint32_t * start_threshold )
{
bool ret = false ;
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
if ( alsa_handle && start_threshold ) {
* start_threshold = alsa_handle -> start_threshold ;
ret = true ;
} else {
AM_LOGE ( "Parameter Error: alsa_handle(%p), start_threshold(%p)" , alsa_handle , start_threshold );
}
return ret ;
}
int aml_alsa_output_flush ( void * handle )
{
int ret = - 1 ;
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
if ( handle == NULL ) {
return - 1 ;
}
if ( alsa_handle -> pcm && pcm_is_ready ( alsa_handle -> pcm )) {
ret = pcm_ioctl ( alsa_handle -> pcm , SNDRV_PCM_IOCTL_RESET , 1 );
if ( ret < 0 ) {
AM_LOGE ( "cannot reset alsa \n " );
} else {
AM_LOGI ( "reset alsa success \n " );
}
} else {
if ( alsa_handle -> pcm ) {
AM_LOGE ( "pcm is not ready(%d) \n " , pcm_is_ready ( alsa_handle -> pcm ));
}
}
return ret ;
}
int aml_alsa_output_get_timestamp ( void * handle , unsigned int * avail , struct timespec * tstamp )
{
int ret = - 1 ;
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
if ( alsa_handle && alsa_handle -> pcm ) {
ret = pcm_get_htimestamp ( alsa_handle -> pcm , avail , tstamp );
}
return ret ;
}
enum pcm_format convert_audio_format_2_alsa_format ( audio_format_t format )
{
switch ( format ) {
case AUDIO_FORMAT_PCM_16_BIT :
return PCM_FORMAT_S16_LE ;
case AUDIO_FORMAT_PCM_32_BIT :
return PCM_FORMAT_S32_LE ;
case AUDIO_FORMAT_PCM_8_BIT :
return PCM_FORMAT_S8 ;
case AUDIO_FORMAT_PCM_8_24_BIT :
return PCM_FORMAT_S24_LE ;
case AUDIO_FORMAT_PCM_24_BIT_PACKED :
return PCM_FORMAT_S24_3LE ;
default :
AM_LOGE ( "invalid format:%#x, return 16bit format." , format );
return PCM_FORMAT_S16_LE ;
}
}
void aml_alsa_info_dump ( void * handle , int fd )
{
alsa_handle_t * alsa_handle = ( alsa_handle_t * ) handle ;
dprintf ( fd , " \t\t card:%d \n " , alsa_handle -> card );
dprintf ( fd , " \t\t pcm_index:%d \n " , alsa_handle -> pcm_index );
dprintf ( fd , " \t\t alsa_port:%d \n " , alsa_handle -> alsa_port );
dprintf ( fd , " \t\t format:%x \n " , alsa_handle -> format );
}