| /* |
| * Copyright 2023 Amlogic Inc. All rights reserved. |
| * |
| * 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_input_tv" |
| //#define LOG_NDEBUG 0 |
| |
| #include <errno.h> |
| #include <pthread.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <fcntl.h> |
| #include <time.h> |
| #include <inttypes.h> |
| #include <sys/time.h> |
| #include <sys/stat.h> |
| #include <sys/prctl.h> |
| #include <utils/Timers.h> |
| #include <cutils/log.h> |
| #include <cutils/atomic.h> |
| #include <hardware/audio.h> |
| #include <aml_data_utils.h> |
| #include <audio_utils/channels.h> |
| #if ANDROID_PLATFORM_SDK_VERSION >= 25 // 8.0 |
| #include <system/audio-base.h> |
| #endif |
| |
| #include "audio_hw.h" |
| #include "aml_audio_stream.h" |
| #include "audio_hw_utils.h" |
| #include "aml_audio_timer.h" |
| #include "alsa_config_parameters.h" |
| #include "tv_patch_avsync.h" |
| #include "aml_ng.h" |
| #include "alsa_device_parser.h" |
| #include "tv_patch_ctrl.h" |
| #include "tv_patch.h" |
| #include "dolby_lib_api.h" |
| #include "spdif_encoder_api.h" |
| #include "audio_port.h" |
| #include "amlAudioMixer.h" |
| #include "audio_hw_ms12_common.h" |
| #include "aml_audio_ms12_bypass.h" |
| #include "aml_audio_dev2mix_process.h" |
| #include "device_patch_mgr.h" |
| #include "audio_hw_resource_mgr.h" |
| #include "component_noise_gate.h" |
| #include "tv_private_object.h" |
| #include "aml_dump_debug.h" |
| #include "aml_audio_output.h" |
| |
| void audio_digital_input_format_check(struct aml_audio_patch *patch) |
| { |
| audio_format_t cur_aformat; |
| if (IS_DIGITAL_IN_HW(patch->input_src)) { |
| cur_aformat = audio_parse_get_audio_type (patch->audio_parse_para); |
| if (cur_aformat != patch->aformat) { |
| ALOGI ("HDMI/SPDIF input format changed from %#x to %#x\n", patch->aformat, cur_aformat); |
| patch->aformat = cur_aformat; |
| if (audio_is_linear_pcm(cur_aformat)) { |
| patch->IEC61937_format = false; |
| } else { |
| patch->IEC61937_format = true; |
| } |
| patch->digital_input_fmt_change = true; |
| patch->mode_reconfig_flag = true; |
| } |
| } |
| } |
| |
| void *audio_patch_signal_detect_threadloop(void *data) |
| { |
| struct aml_audio_patch *patch = (struct aml_audio_patch *)data; |
| struct aml_audio_device *aml_dev = (struct aml_audio_device *) patch->dev; |
| bool cur_hw_stable = false; |
| bool last_hw_stable = false; |
| int cur_type = 0; |
| int lat_type = 0; |
| int cur_sr = -1; |
| int last_sr = -1; |
| int cur_channel = -1; |
| int last_channel = -1; |
| audio_format_t cur_aformat = AUDIO_FORMAT_INVALID; |
| audio_format_t last_aformat = AUDIO_FORMAT_INVALID; |
| bool cur_raw_data_change = false; |
| bool last_raw_data_change = false; |
| struct timespec mute_start_ts; |
| int mute_mdelay = 120; |
| bool mute_flag = false; |
| bool in_mute = false; |
| |
| |
| ALOGD("%s: in", __func__); |
| prctl(PR_SET_NAME, (unsigned long)"audio_signal_detect"); |
| aml_set_thread_priority("audio_signal_detect", patch->audio_signal_detect_threadID, 5); |
| |
| while (!patch->signal_detect_thread_exit) { |
| cur_hw_stable = aml_mixer_ctrl_get_int (&aml_dev->alsa_mixer, AML_MIXER_ID_HDMI_IN_AUDIO_STABLE); |
| cur_type = aml_mixer_ctrl_get_int (&aml_dev->alsa_mixer, AML_MIXER_ID_HDMIIN_AUDIO_TYPE); |
| cur_sr = aml_mixer_ctrl_get_int (&aml_dev->alsa_mixer, AML_MIXER_ID_HDMI_IN_SAMPLERATE); |
| cur_channel = aml_mixer_ctrl_get_int (&aml_dev->alsa_mixer, AML_MIXER_ID_HDMI_IN_CHANNELS); |
| cur_aformat = audio_parse_get_audio_type (patch->audio_parse_para); |
| cur_raw_data_change = patch->start_mute; |
| |
| if ((!cur_hw_stable && cur_hw_stable != last_hw_stable) || (cur_type != lat_type) |
| || (cur_sr != last_sr) || (cur_channel != last_channel) |
| || (cur_raw_data_change && cur_raw_data_change != last_raw_data_change) |
| || ((cur_aformat != last_aformat) && (cur_aformat == AUDIO_FORMAT_DTS || cur_aformat == AUDIO_FORMAT_DTS_HD) )) {// resolve dts resume pop noise |
| ALOGI("[%s:%d] hw_stable(%d)(%d) sr(%d)(%d) channel(%d)(%d) aformat(%d)(%d) raw_data_change(%d)(%d) type(%d)(%d)", __FUNCTION__, __LINE__, |
| cur_hw_stable, last_hw_stable, cur_sr, last_sr, cur_channel, last_channel, cur_aformat, last_aformat, cur_raw_data_change, |
| last_raw_data_change, cur_type, lat_type); |
| audiohal_send_msg_2_ms12(&aml_dev->ms12, MS12_MESG_TYPE_FLUSH); |
| |
| set_aed_master_volume_mute(&aml_dev->alsa_mixer, true); |
| //set_dac_digital_volume_mute(&aml_dev->alsa_mixer, true);//TBD |
| clock_gettime(CLOCK_MONOTONIC, &mute_start_ts); |
| mute_flag = true; |
| ALOGD("%s: mute", __func__); /*Complete mute*/ |
| } |
| |
| if (mute_flag) { |
| in_mute = Stop_watch(mute_start_ts, mute_mdelay); |
| if (!in_mute) { |
| set_aed_master_volume_mute(&aml_dev->alsa_mixer, false); |
| //set_dac_digital_volume_mute(&aml_dev->alsa_mixer, false);//TBD |
| ALOGD("%s: unmute", __func__); |
| mute_flag = false; |
| } |
| } |
| |
| lat_type = cur_type; |
| last_hw_stable = cur_hw_stable; |
| last_sr = cur_sr; |
| last_channel = cur_channel; |
| last_aformat = cur_aformat; |
| last_raw_data_change = cur_raw_data_change; |
| aml_audio_sleep(5*1000); |
| } |
| |
| set_aed_master_volume_mute(&aml_dev->alsa_mixer, false); |
| //set_dac_digital_volume_mute(&aml_dev->alsa_mixer, false);//TBD |
| ALOGD("%s: exit and unmute", __func__); |
| return NULL; |
| } |
| |
| void audio_patch_set_av_sync_status(struct aml_audio_patch *apatch, bool need_do_avsync) |
| { |
| if (NULL == apatch) { |
| AM_LOGE("apatch is NULL!"); |
| return; |
| } |
| //AM_LOGI("apatch(%p) set av sync status to %d", apatch, need_do_avsync); |
| apatch->need_do_avsync = need_do_avsync; |
| } |
| |
| // buffer/period ratio, bigger will add more latency |
| void *audio_patch_input_threadloop(void *data) |
| { |
| struct aml_audio_patch *patch = (struct aml_audio_patch *)data; |
| struct aml_audio_device *aml_dev = (struct aml_audio_device *) patch->dev; |
| ring_buffer_t *ringbuffer = & (patch->aml_ringbuffer); |
| struct audio_stream_in *stream_in = NULL; |
| struct aml_stream_in *in; |
| struct audio_config stream_config; |
| struct timespec ts; |
| int aux_read_bytes, read_bytes; |
| // FIXME: add calc for read_bytes; |
| read_bytes = DEFAULT_CAPTURE_PERIOD_SIZE * CAPTURE_PERIOD_COUNT; |
| int ret = 0, retry = 0; |
| audio_format_t cur_aformat; |
| int ring_buffer_size = 0; |
| bool stable_flag = false; |
| int old_chip_with_hw_det = check_chip_name("t5d", 3, &aml_dev->alsa_mixer); |
| |
| ALOGI("++%s", __FUNCTION__); |
| |
| ALOGD("%s: enter", __func__); |
| patch->chanmask = stream_config.channel_mask = patch->in_chanmask; |
| stream_config.sample_rate = patch->in_sample_rate; |
| patch->aformat = stream_config.format = patch->in_format; |
| |
| ret = aml_dev->hw_device.open_input_stream((struct audio_hw_device *)patch->dev, 0, patch->input_src, &stream_config, &stream_in, 0, NULL, 0); |
| if (ret < 0) { |
| ALOGE("%s: open input steam failed ret = %d", __func__, ret); |
| return (void *)0; |
| } |
| |
| in = (struct aml_stream_in *)stream_in; |
| patch->stream_in = in; |
| /* CVBS IN signal is stable very quickly, sometimes there is no unstable state, |
| * we need to do ease in before playing CVBS IN at in_read func. |
| */ |
| if (in->device & AUDIO_DEVICE_IN_LINE) { |
| in->mute_flag = true; |
| } |
| |
| patch->in_buf_size = read_bytes = in->config.period_size * audio_stream_in_frame_size(&in->stream); |
| patch->in_buf = aml_audio_calloc(1, patch->in_buf_size); |
| if (!patch->in_buf) { |
| AM_LOGE("patch->in_buf malloc fail, close input stream"); |
| aml_dev->hw_device.close_input_stream((struct audio_hw_device *)patch->dev, &in->stream); |
| patch->stream_in = NULL; |
| return (void *)0; |
| } |
| |
| int first_start = 1; |
| prctl(PR_SET_NAME, (unsigned long)"audio_input_patch"); |
| aml_set_thread_priority("audio_input_patch", patch->audio_input_threadID, 5); |
| /*affinity the thread to cpu 2/3 which has few IRQ*/ |
| aml_audio_set_cpu23_affinity(); |
| |
| if (ringbuffer) { |
| ring_buffer_size = ringbuffer->size; |
| } |
| |
| while (!patch->input_thread_exit) { |
| int bytes_avail = 0; |
| int period_mul = 1; |
| int read_threshold = 0; |
| |
| /* For HBR audio from hdmiin/earcin device, set read bytes size to 4 times */ |
| if (IS_DIGITAL_IN_HW(patch->input_src) && |
| (audio_parse_get_audio_packet_type(patch->audio_parse_para) == AUDIO_PACKET_HBR || in->spdif_fmt_hw == MAT)) { |
| period_mul = 4; |
| if (SRC_ARCIN != patch->patch_src) { |
| in->tv_param.read_mul_factor = EAC3_MULTIPLIER; |
| } |
| else { |
| in->tv_param.read_mul_factor = HBR_MULTIPLIER; |
| } |
| } else if ((patch->aformat == AUDIO_FORMAT_DTS || patch->aformat == AUDIO_FORMAT_DTS_HD) && audio_parse_get_audio_samplerate(patch->audio_parse_para) == 192000) { |
| period_mul = 1; |
| in->tv_param.read_mul_factor = HBR_MULTIPLIER; |
| } else if (patch->aformat == AUDIO_FORMAT_E_AC3 || patch->aformat == AUDIO_FORMAT_DTS_HD) { |
| period_mul = 1; |
| in->tv_param.read_mul_factor = EAC3_MULTIPLIER; |
| } else { |
| period_mul = 1; |
| in->tv_param.read_mul_factor = 2; |
| } |
| |
| if (patch->input_src == AUDIO_DEVICE_IN_HDMI) { |
| aml_check_pic_mode(patch); |
| } |
| |
| if (!is_low_latency_mode(aml_dev)) { |
| read_bytes = DEFAULT_CAPTURE_PERIOD_SIZE * audio_stream_in_frame_size(&in->stream) * period_mul; |
| } |
| else { |
| read_bytes = LOW_LATENCY_CAPTURE_PERIOD_SIZE * audio_stream_in_frame_size(&in->stream); |
| } |
| |
| if (patch->input_src == AUDIO_DEVICE_IN_LINE) { |
| read_threshold = 4 * read_bytes; |
| } |
| |
| // buffer size diff from allocation size, need to resize. |
| if (patch->in_buf_size < (size_t)read_bytes) { |
| ALOGI("%s: !!realloc in buf size from %zu to %d", __func__, patch->in_buf_size, read_bytes); |
| patch->in_buf = aml_audio_realloc(patch->in_buf, read_bytes); |
| if (!patch->in_buf) { |
| break; |
| } |
| patch->in_buf_size = read_bytes; |
| memset(patch->in_buf, 0, patch->in_buf_size); |
| } |
| |
| if (in->standby) { |
| ret = start_input_stream(in); |
| if (ret < 0) { |
| ALOGE("start_input_stream failed !"); |
| } |
| in->standby = 0; |
| } |
| |
| bytes_avail = read_bytes; |
| /* if audio is unstable, don't read data from hardware */ |
| stable_flag = check_tv_stream_signal(&in->stream); |
| if (!stable_flag) { |
| /* when audio is unstable, start avsync*/ |
| audio_patch_set_av_sync_status(patch, true); |
| patch->input_signal_stable = false; |
| aml_dev->mute_start = true; |
| |
| memset(patch->in_buf, 0, bytes_avail); |
| ring_buffer_clear(ringbuffer); |
| usleep(20*1000); |
| } else { |
| patch->input_signal_stable = true; |
| if (SRC_HDMIIN == patch->patch_src && in->tv_param.audio_packet_type == AUDIO_PACKET_AUDS && in->config.channels != 2) { |
| input_stream_channels_adjust(&in->stream, patch->in_buf, read_bytes); |
| } else { |
| if (is_tv_mute(aml_dev)) { |
| clear_buffer_for_avsync(patch); |
| enable_tv_mute(aml_dev, false); |
| } |
| |
| //aml_audio_trace_int("input_read_thread", read_bytes); |
| aml_alsa_input_read(&in->stream, patch->in_buf, read_bytes); |
| //aml_audio_trace_int("input_read_thread", 0); |
| if (IS_DIGITAL_IN_HW(patch->input_src) && !check_digital_in_stream_signal(patch, &in->stream)) { |
| memset(patch->in_buf, 0, bytes_avail); |
| } |
| } |
| |
| if (IS_DIGITAL_IN_HW(patch->input_src)) { |
| cur_aformat = audio_parse_get_audio_type (patch->audio_parse_para); |
| if (cur_aformat != AUDIO_FORMAT_PCM_16_BIT) { |
| audio_raw_data_continuous_check(patch, patch->audio_parse_para, patch->in_buf, read_bytes); |
| } |
| } |
| if (patch->start_mute) { |
| int flag = Stop_watch(patch->start_ts, patch->mdelay); |
| if (!flag) { |
| patch->start_mute = false; |
| } else { |
| memset(patch->in_buf, 0, read_bytes); |
| } |
| } |
| |
| if (get_debug_value(AML_DUMP_AUDIOHAL_IN)) { |
| aml_dump_audio_bitstreams_with_id("alsa_in_read.raw", patch->in_buf, read_bytes, in->unique_ID); |
| } |
| } |
| |
| audio_pcpd_format_detect(patch->audio_parse_para); |
| |
| /* For chip T5D or older version chip, there is limitation to process data type detection in hardware design.*/ |
| /* So it needs to do process below to make data detection fast than before to avoid noise happen. */ |
| if (old_chip_with_hw_det && IS_DIGITAL_IN_HW(patch->input_src)) { |
| cur_aformat = audio_parse_get_audio_type (patch->audio_parse_para); |
| if (patch && patch->audio_parse_para && audio_is_linear_pcm(cur_aformat)) { |
| audio_fmt_check(patch->audio_parse_para, patch->in_buf, read_bytes); |
| } |
| } |
| |
| #ifdef USE_EQ_DRC |
| /*noise gate is only used in Linein for 16bit audio data*/ |
| if (get_active_inport(aml_dev) == INPORT_LINEIN && is_ng_enable(aml_dev)) { |
| int ng_status = noise_gate_process(aml_dev, patch->in_buf, bytes_avail >> 1); |
| /*if (ng_status == NG_MUTE) |
| ALOGI("noise gate is working!");*/ |
| } |
| #endif |
| ALOGV("++%s in read over read_bytes = %d, in_read returns = %d, threshold %d avail data=%d", |
| __FUNCTION__, read_bytes, bytes_avail, read_threshold, get_buffer_read_space(ringbuffer)); |
| |
| if (bytes_avail > 0) { |
| //DoDumpData(patch->in_buf, bytes_avail, CC_DUMP_SRC_TYPE_INPUT); |
| do { |
| if (patch->input_src == AUDIO_DEVICE_IN_HDMI) |
| { |
| pthread_mutex_lock(&in->lock); |
| ret = reconfig_read_param_through_hdmiin(patch, in, ringbuffer, ring_buffer_size); |
| pthread_mutex_unlock(&in->lock); |
| if (ret == 0) { |
| break; |
| } |
| } |
| |
| if (get_buffer_write_space(ringbuffer) >= bytes_avail) { |
| retry = 0; |
| aml_audio_trace_int("input_thread_write2buf", bytes_avail); |
| ret = ring_buffer_write(ringbuffer, |
| (unsigned char*)patch->in_buf, |
| bytes_avail, UNCOVER_WRITE); |
| if (ret != bytes_avail) { |
| ALOGE("%s(), write buffer fails!", __func__); |
| } |
| aml_audio_trace_int("input_thread_write2buf", 0); |
| |
| if (!first_start || get_buffer_read_space(ringbuffer) >= read_threshold) { |
| pthread_cond_signal(&patch->cond); |
| if (first_start) { |
| first_start = 0; |
| } |
| } |
| //usleep(1000); |
| } else { |
| retry = 1; |
| pthread_cond_signal(&patch->cond); |
| //Fixme: if ringbuffer is full enough but no output, reset ringbuffer |
| ALOGD("%s(), ring buffer no space to write, buffer size:%d free size:%d, need write size:%d", __func__, |
| ringbuffer->size, get_buffer_write_space(ringbuffer), bytes_avail); |
| ring_buffer_reset(ringbuffer); |
| usleep(3000); |
| } |
| } while (retry && !patch->input_thread_exit); |
| } else { |
| ALOGV("%s(), read alsa pcm fails, to _read(%d), bytes_avail(%d)!", |
| __func__, read_bytes, bytes_avail); |
| if (get_buffer_read_space(ringbuffer) >= bytes_avail) { |
| pthread_cond_signal(&patch->cond); |
| } |
| usleep(3000); |
| } |
| } |
| |
| aml_dev->hw_device.close_input_stream((struct audio_hw_device *)patch->dev, &in->stream); |
| patch->stream_in = NULL; |
| |
| if (patch->in_buf) { |
| aml_audio_free(patch->in_buf); |
| patch->in_buf = NULL; |
| } |
| ALOGD("%s: exit", __func__); |
| |
| return (void *)0; |
| } |
| |
| void *audio_patch_output_threadloop(void *data) |
| { |
| struct aml_audio_patch *patch = (struct aml_audio_patch *)data; |
| struct aml_audio_device *aml_dev = (struct aml_audio_device *)patch->dev; |
| struct dolby_ms12_desc *ms12 = &(aml_dev->ms12); |
| ring_buffer_t *ringbuffer = & (patch->aml_ringbuffer); |
| struct audio_stream_out *stream_out = NULL; |
| struct aml_stream_out *out= NULL; |
| struct audio_config stream_config; |
| struct timespec ts; |
| int write_bytes = DEFAULT_PLAYBACK_PERIOD_SIZE * PLAYBACK_PERIOD_COUNT; |
| int original_write_bytes = write_bytes; |
| int ret; |
| bool find_iec_sync_word = false; |
| AM_LOGD("enter"); |
| stream_config.channel_mask = patch->out_chanmask; |
| stream_config.sample_rate = patch->out_sample_rate; |
| stream_config.format = patch->out_format; |
| |
| #ifdef DOLBY_MS12_INPUT_FORMAT_TEST |
| int format = 0; |
| format = aml_audio_property_get_int("vendor.dolby.ms12.input.format", format); |
| if (format == 1) { |
| stream_config.format = AUDIO_FORMAT_AC3; |
| } else if (format == 2) { |
| stream_config.format = AUDIO_FORMAT_E_AC3; |
| } |
| #endif |
| aml_dev->mix_init_flag = false; |
| aml_dev->mute_start = true; |
| |
| char address_buf[50] = {0}; |
| if (patch->input_src == AUDIO_DEVICE_IN_FM_TUNER) { |
| strncpy(address_buf, "AML_ATV_SOURCE", 14); |
| } else if (patch->input_src == AUDIO_DEVICE_IN_HDMI) { |
| strncpy(address_buf, "AML_HDMI_SOURCE", 15); |
| } else if (patch->input_src == AUDIO_DEVICE_IN_LINE) { |
| strncpy(address_buf, "AML_AV_SOURCE", 13); |
| } |
| ret = aml_dev->hw_device.open_output_stream((struct audio_hw_device *)patch->dev, |
| 0, |
| patch->output_src, |
| AUDIO_OUTPUT_FLAG_DIRECT, |
| &stream_config, |
| &stream_out, |
| address_buf); |
| if (ret < 0) { |
| AM_LOGE("open output stream failed"); |
| return (void *)0; |
| } |
| |
| patch->stream_out = stream_out; |
| out = (struct aml_stream_out *)stream_out; |
| patch->out_buf_size = write_bytes = original_write_bytes = out->config.period_size * audio_stream_out_frame_size(&out->stream); |
| patch->out_buf = aml_audio_calloc(1, patch->out_buf_size); |
| if (!patch->out_buf) { |
| aml_dev->hw_device.close_output_stream((struct audio_hw_device *)patch->dev, &out->stream); |
| AM_LOGE("patch->out_buf calloc fail"); |
| return (void *)0; |
| } |
| prctl(PR_SET_NAME, (unsigned long)"audio_output_patch"); |
| aml_set_thread_priority("audio_output_patch", patch->audio_output_threadID, 5); |
| /*affinity the thread to cpu 2/3 which has few IRQ*/ |
| aml_audio_set_cpu23_affinity(); |
| |
| while (!patch->output_thread_exit) { |
| int period_mul; |
| //! check hdmi input format |
| audio_digital_input_format_check(patch); |
| if (IS_DIGITAL_IN_HW(patch->input_src) && patch->digital_input_fmt_change) { |
| if (out) { |
| AM_LOGI("HDMI format change from %x to %x", out->hal_internal_format, patch->aformat); |
| } |
| |
| //! close old stream |
| if (out != NULL) { |
| aml_dev->hw_device.close_output_stream((struct audio_hw_device *)patch->dev, &out->stream); |
| stream_out = NULL; |
| out = NULL; |
| } |
| //! open new stream |
| stream_config.channel_mask = audio_parse_get_audio_channel_mask (patch->audio_parse_para); |
| stream_config.format = patch->aformat; |
| if (patch->aformat == AUDIO_FORMAT_PCM_16_BIT) {//Fix Me: |
| stream_config.sample_rate = 48000; |
| } else { |
| stream_config.sample_rate = audio_parse_get_audio_samplerate(patch->audio_parse_para); |
| } |
| |
| ret = aml_dev->hw_device.open_output_stream((struct audio_hw_device *)patch->dev, |
| 0, |
| patch->output_src, |
| AUDIO_OUTPUT_FLAG_DIRECT, |
| &stream_config, |
| &stream_out, |
| address_buf); |
| if (ret < 0) { |
| AM_LOGE("open output stream failed"); |
| stream_out = NULL; |
| out = NULL; |
| continue; |
| } |
| //! set stream format and hal rate |
| patch->stream_out = stream_out; |
| out = (struct aml_stream_out *)stream_out; |
| if (out->hal_internal_format != AUDIO_FORMAT_PCM_16_BIT && |
| out->hal_internal_format != AUDIO_FORMAT_PCM_32_BIT) { |
| out->hal_format = AUDIO_FORMAT_IEC61937; |
| find_iec_sync_word = false; |
| //Check DTS-CD |
| if (out->hal_internal_format == AUDIO_FORMAT_DTS || |
| out->hal_internal_format == AUDIO_FORMAT_DTS_HD) { |
| if (audio_parse_get_audio_type_direct(patch->audio_parse_para) == DTSCD ) { |
| out->is_dtscd = true; |
| } else { |
| out->is_dtscd = false; |
| } |
| } |
| //Reset dd and ddp sample rate |
| if (out->hal_internal_format == AUDIO_FORMAT_AC3 || |
| out->hal_internal_format == AUDIO_FORMAT_E_AC3) { |
| if (out->hal_rate == 96000 || out->hal_rate == 192000) { |
| out->hal_rate = 48000; |
| } else if (out->hal_rate == 88200 || out->hal_rate == 176400) { |
| out->hal_rate = 44100; |
| } |
| } |
| } |
| |
| /* reset audio patch ringbuffer */ |
| ring_buffer_reset(&patch->aml_ringbuffer); |
| |
| #ifdef ADD_AUDIO_DELAY_INTERFACE |
| // fixed switch between RAW and PCM noise, drop delay residual data |
| aml_audio_delay_clear(AML_DELAY_OUTPORT_SPDIF); |
| aml_audio_delay_clear(AML_DELAY_OUTPORT_SPDIF_RAW); |
| aml_audio_delay_clear(AML_DELAY_OUTPORT_SPDIF_B_RAW); |
| #endif |
| /* we need standby a2dp when switch the format, in order to prevent UNDERFLOW in a2dp stack. */ |
| if (aml_dev->active_outport == OUTPORT_A2DP) { |
| aml_dev->need_reset_a2dp = true; |
| } |
| |
| //when format change, we need do av sync again |
| audio_patch_set_av_sync_status(patch, true); |
| patch->digital_input_fmt_change = false; |
| } |
| |
| period_mul = 1; |
| write_bytes = original_write_bytes; |
| if (out == NULL) { |
| AM_LOGI("out == NULL, need check!!"); |
| } |
| if (out->hal_internal_format == AUDIO_FORMAT_AC3) { |
| write_bytes = AC3_PERIOD_SIZE; |
| } else if (out->hal_internal_format == AUDIO_FORMAT_E_AC3) { |
| write_bytes = EAC3_PERIOD_SIZE; |
| } else if (out->hal_internal_format == AUDIO_FORMAT_MAT) { |
| write_bytes = MAT_PERIOD_SIZE; |
| } else if (patch->aformat == AUDIO_FORMAT_DTS_HD){ |
| period_mul = HBR_MULTIPLIER; |
| } |
| |
| //ALOGI("%s period_mul = %d ", __func__, period_mul); |
| |
| if (is_low_latency_mode(aml_dev)) { |
| write_bytes = LOW_LATENCY_PLAYBACK_PERIOD_SIZE * audio_stream_out_frame_size(&out->stream); |
| } |
| |
| // buffer size diff from allocation size, need to resize. |
| if (patch->out_buf_size < (size_t)write_bytes * period_mul) { |
| AM_LOGI("!!realloc out buf size from %zu to %d", patch->out_buf_size, write_bytes * period_mul); |
| patch->out_buf = aml_audio_realloc(patch->out_buf, write_bytes * period_mul); |
| patch->out_buf_size = write_bytes * period_mul; |
| } |
| |
| pthread_mutex_lock(&patch->mutex); |
| ALOGV("%s(), ringbuffer level read before wait--%d write_bytes:%d, period_mul:%d", |
| __func__, get_buffer_read_space(ringbuffer), write_bytes, period_mul); |
| if (get_buffer_read_space(ringbuffer) < (write_bytes * period_mul)) { |
| // wait 300ms |
| ts_wait_time(&ts, 300000); |
| pthread_cond_timedwait(&patch->cond, &patch->mutex, &ts); |
| } |
| pthread_mutex_unlock(&patch->mutex); |
| |
| ALOGV("%s(), ringbuffer level read after wait-- %d, %d * %d", |
| __func__, get_buffer_read_space(ringbuffer), write_bytes, period_mul); |
| |
| if (is_dolby_ms12_support_compression_format(out->hal_internal_format)) { |
| int pos_sync_word = -1; |
| int need_drop_size = 0; |
| int ring_buffer_read_space = get_buffer_read_space(ringbuffer); |
| pos_sync_word = find_61937_sync_word_position_in_ringbuffer(ringbuffer); |
| |
| if (pos_sync_word >= 0) { |
| need_drop_size = pos_sync_word; |
| find_iec_sync_word = true; |
| } else if (pos_sync_word == -1 && (find_iec_sync_word == false || ring_buffer_read_space > write_bytes)) { |
| //write_bytes is frame size. |
| //We should be able to find sync word within a frame size. |
| //If we cannot find it, it is considered that there is a problem with the data |
| need_drop_size = ring_buffer_read_space; |
| } else if (pos_sync_word == -1) { |
| continue; |
| } |
| |
| if (need_drop_size >0) { |
| AM_LOGE("data error, we drop data(%d) pos_sync_word(%d) find_sync_word(%d) read_space(%d)(%d) out_buf_size(%zu), write_bytes(%d)", |
| need_drop_size, pos_sync_word, find_iec_sync_word, ring_buffer_read_space, ringbuffer->size, patch->out_buf_size, write_bytes); |
| int drop_size = ring_buffer_seek(ringbuffer, need_drop_size); |
| if (drop_size != need_drop_size) { |
| AM_LOGE("drop fail, need_drop_size(%d) actual drop_size(%d)", need_drop_size, drop_size); |
| } |
| continue; |
| } |
| } |
| if (get_buffer_read_space(ringbuffer) >= (write_bytes * period_mul)) { |
| ret = ring_buffer_read(ringbuffer, |
| (unsigned char*)patch->out_buf, write_bytes * period_mul); |
| if (ret == 0) { |
| AM_LOGE("ring_buffer read 0 data! write_bytes(%d), period_mul(%d)", write_bytes, period_mul); |
| } |
| |
| /* avsync for dev->dev patch*/ |
| //When doing AV sync, it is necessary to mute, and then unmute after AV sync ends |
| if (out->mute != patch->need_do_avsync) { |
| out->mute = patch->need_do_avsync; |
| AM_LOGI("set stream mute to %d", out->mute); |
| } |
| if (patch && (patch->need_do_avsync == true) && (patch->input_signal_stable == true) && |
| (SRC_ATV == patch->patch_src || SRC_HDMIIN == patch->patch_src || |
| SRC_LINEIN == patch->patch_src || SRC_SPDIFIN == patch->patch_src)) { |
| |
| if (!is_low_latency_mode(aml_dev)) { |
| aml_dev_try_avsync(patch); |
| if (patch->skip_frames) { |
| ALOGD("%s(), skip this period data for avsync!", __func__); |
| usleep(5); |
| continue; |
| } |
| } else { |
| audio_patch_set_av_sync_status(patch, false); |
| } |
| |
| } |
| |
| /* reconfig output in picture mode switch */ |
| if (patch && patch->input_src == AUDIO_DEVICE_IN_HDMI) { |
| stream_check_reconfig_param(stream_out); |
| } |
| stream_out->write(stream_out, patch->out_buf, ret); |
| } else { |
| ALOGV("%s(), no enough data in ring buffer, available data size:%d, need data size:%d", __func__, |
| get_buffer_read_space(ringbuffer), (write_bytes * period_mul)); |
| usleep( (DEFAULT_PLAYBACK_PERIOD_SIZE) * 1000000 / 4 / |
| stream_config.sample_rate); |
| } |
| } |
| |
| patch->stream_out = NULL; |
| aml_dev->hw_device.close_output_stream((struct audio_hw_device *)patch->dev, &out->stream); |
| if (patch->out_buf) { |
| aml_audio_free(patch->out_buf); |
| patch->out_buf = NULL; |
| } |
| AM_LOGD("exit"); |
| |
| return (void *)0; |
| } |
| |
| |
| static int create_patch_l(struct aml_audio_device *aml_dev, audio_devices_t input, struct aml_audio_patch ** apatch) |
| { |
| struct aml_audio_patch *patch; |
| int play_buffer_size = DEFAULT_PLAYBACK_PERIOD_SIZE * PLAYBACK_PERIOD_COUNT; |
| pthread_attr_t attr; |
| struct sched_param param; |
| int ret = 0; |
| struct tv_private_object *tv_obj = get_tv_object(aml_dev); |
| |
| AM_LOGD("enter"); |
| |
| patch = aml_audio_calloc(1, sizeof(*patch)); |
| if (!patch) { |
| return -ENOMEM; |
| } |
| |
| patch->dev = aml_dev; |
| patch->input_src = input; |
| patch->patch_src = get_patch_source(input, PATCH_ROUTE_DEV_DEV); |
| patch->is_dtv_src = false; |
| patch->aformat = AUDIO_FORMAT_PCM_16_BIT; |
| patch->digital_input_fmt_change = false; |
| pthread_mutex_init(&patch->mutex, NULL); |
| pthread_cond_init(&patch->cond, NULL); |
| |
| patch->in_sample_rate = 48000; |
| patch->in_chanmask = AUDIO_CHANNEL_IN_STEREO; |
| patch->output_src = AUDIO_DEVICE_OUT_SPEAKER; |
| patch->out_sample_rate = 48000; |
| patch->out_chanmask = AUDIO_CHANNEL_OUT_STEREO; |
| patch->in_format = AUDIO_FORMAT_PCM_16_BIT; |
| patch->out_format = AUDIO_FORMAT_PCM_16_BIT; |
| |
| /* when audio patch start, singal is unstale or |
| * patch signal is unstable, it need do avsync |
| */ |
| audio_patch_set_av_sync_status(patch, true); |
| |
| if (patch->out_format == AUDIO_FORMAT_PCM_16_BIT) { |
| AM_LOGD("init audio ringbuffer game %d", is_low_latency_mode(aml_dev)); |
| if (!is_low_latency_mode(aml_dev)) |
| ret = ring_buffer_init(&patch->aml_ringbuffer, 4 * 2 * play_buffer_size * PATCH_PERIOD_COUNT); |
| else |
| ret = ring_buffer_init(&patch->aml_ringbuffer, 2 * 4 * LOW_LATENCY_PLAYBACK_PERIOD_SIZE); |
| } else { |
| ret = ring_buffer_init(&patch->aml_ringbuffer, 4 * 4 * play_buffer_size * PATCH_PERIOD_COUNT); |
| } |
| |
| if (ret < 0) { |
| AM_LOGE("init audio ringbuffer failed"); |
| goto err_ring_buf; |
| } |
| |
| if (IS_DIGITAL_IN_HW(patch->input_src)) { |
| //TODO add sample rate and channel information |
| ret = creat_pthread_for_audio_type_parse(&patch->audio_parse_threadID, |
| &patch->audio_parse_para, &aml_dev->alsa_mixer, patch->input_src); |
| if (ret != 0) { |
| AM_LOGE("create format parse thread failed"); |
| goto err_parse_thread; |
| } |
| } |
| |
| ret = pthread_create(&patch->audio_input_threadID, NULL, |
| &audio_patch_input_threadloop, patch); |
| |
| if (ret != 0) { |
| AM_LOGE("Create input thread failed"); |
| goto err_in_thread; |
| } |
| ret = pthread_create(&patch->audio_output_threadID, NULL, |
| &audio_patch_output_threadloop, patch); |
| if (ret != 0) { |
| AM_LOGE("Create output thread failed"); |
| goto err_out_thread; |
| } |
| |
| if (IS_DIGITAL_IN_HW(patch->input_src)) { |
| ret = pthread_create(&patch->audio_signal_detect_threadID, NULL, |
| &audio_patch_signal_detect_threadloop, patch); |
| if (ret != 0) { |
| AM_LOGE("Create signal detect thread failed"); |
| goto err_signal_detect_thread; |
| } |
| } |
| |
| /* Use flag to indicate that patch struct is ready. TBD */ |
| //validate_dev_patch(aml_dev); |
| *apatch = patch; |
| AM_LOGD("exit"); |
| |
| return 0; |
| err_signal_detect_thread: |
| patch->signal_detect_thread_exit = 1; |
| pthread_join(patch->audio_parse_threadID, NULL); |
| err_parse_thread: |
| patch->output_thread_exit = 1; |
| pthread_join(patch->audio_output_threadID, NULL); |
| err_out_thread: |
| patch->input_thread_exit = 1; |
| pthread_join(patch->audio_input_threadID, NULL); |
| err_in_thread: |
| ring_buffer_release(&patch->aml_ringbuffer); |
| err_ring_buf: |
| aml_audio_free(patch); |
| return ret; |
| } |
| |
| int release_patch_l(struct aml_audio_device *aml_dev, struct aml_audio_patch *apatch) |
| { |
| struct aml_audio_patch *patch = apatch; |
| ALOGD("%s: enter", __func__); |
| bool is_game_mode = patch->is_game_mode; |
| /* Use flag to indicate that it will start to free patch struct. TBD */ |
| //invalidate_dev_patch(aml_dev); |
| |
| patch->output_thread_exit = 1; |
| pthread_join(patch->audio_output_threadID, NULL); |
| |
| patch->input_thread_exit = 1; |
| if (patch->stream_in) { |
| patch->stream_in->tv_param.patch_input_thread_exit = 1; |
| } else { |
| AM_LOGE("patch->stream_in is NULL"); |
| } |
| pthread_join(patch->audio_input_threadID, NULL); |
| |
| if (IS_DIGITAL_IN_HW(patch->input_src)) { |
| patch->signal_detect_thread_exit = 1; |
| pthread_join(patch->audio_signal_detect_threadID, NULL); |
| } |
| |
| if (IS_DIGITAL_IN_HW(patch->input_src)) |
| exit_pthread_for_audio_type_parse(patch->audio_parse_threadID,&patch->audio_parse_para); |
| |
| ring_buffer_release(&patch->aml_ringbuffer); |
| aml_audio_free(patch); |
| |
| //after release patch, game mode may change |
| if (is_game_mode) { |
| if (false == is_low_latency_mode(aml_dev)) { |
| AM_LOGI("game mode change to false, reconfig out"); |
| alsa_out_reconfig_params(aml_dev); |
| } |
| } |
| |
| ALOGD("%s: exit is_game_mode:%d(%d)", __func__, is_game_mode, is_low_latency_mode(aml_dev)); |
| |
| exit: |
| return 0; |
| } |
| |
| |
| int release_tv_patch(struct aml_audio_device *aml_dev, void *apatch) |
| { |
| pthread_mutex_lock(&aml_dev->patch_lock); |
| release_patch_l(aml_dev, (struct aml_audio_patch *)apatch); |
| pthread_mutex_unlock(&aml_dev->patch_lock); |
| return 0; |
| } |
| |
| int create_tv_patch(struct aml_audio_device *aml_dev, audio_devices_t input, void ** apatch) |
| { |
| int ret = 0; |
| |
| pthread_mutex_lock(&aml_dev->patch_lock); |
| ret = create_patch_l(aml_dev, input, (struct aml_audio_patch **)apatch); |
| pthread_mutex_unlock(&aml_dev->patch_lock); |
| |
| return ret; |
| } |