/* * 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 #include #include #include #include #include #include #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, stream_config->format == AUDIO_FORMAT_IEC61937, is_low_latency_mode(adev)); } else { get_hardware_config_parameters(config, format, channels, rate, platform_is_tv, stream_config->format == AUDIO_FORMAT_IEC61937, is_low_latency_mode(adev)); } 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\tcard:%d\n", alsa_handle->card); dprintf(fd, "\t\tpcm_index:%d\n", alsa_handle->pcm_index); dprintf(fd, "\t\talsa_port:%d\n", alsa_handle->alsa_port); dprintf(fd, "\t\tformat:%x\n", alsa_handle->format); }