/* * 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 #include #include #include #include #include #include #include #include #include #include #include "aml_audio_log.h" #include #include #include #include #if ANDROID_PLATFORM_SDK_VERSION >= 25 // 8.0 #include #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 "device_patch_mgr.h" #include "audio_hw_resource_mgr.h" #include "aml_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_device)) { 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; } if (audio_is_linear_pcm(patch->aformat)) { if (patch->in_channel_count != patch->out_channel_count) { ALOGI ("HDMI/SPDIF PCM ch changed from %d to %d", patch->out_channel_count, patch->in_channel_count); patch->out_channel_count = patch->in_channel_count; patch->digital_input_fmt_change = true; } if (patch->in_sample_rate != patch->out_sample_rate) { ALOGI ("HDMI/SPDIF PCM sr changed from %d to %d", patch->out_sample_rate, patch->in_sample_rate); patch->out_sample_rate = patch->in_sample_rate; patch->digital_input_fmt_change = 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); if (get_hdmiin_audio_mode(&aml_dev->alsa_mixer) != HDMIIN_MODE_I2S) { cur_type = aml_mixer_ctrl_get_int (&aml_dev->alsa_mixer, AML_MIXER_ID_HDMIIN_AUDIO_TYPE); } else if (aml_mixer_ctrl_get_int (&aml_dev->alsa_mixer, AML_MIXER_ID_AUDIO_PCPD_MONITOR_ENABLE)) { cur_type = audio_pcpd_monitor_format_detection(&aml_dev->alsa_mixer, patch->input_device); } 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); int read_bytes = 0; 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); struct component_noise_gate *noise_gate = get_noise_gate_instance(aml_dev); audio_input_config_t * input_config = &patch->input_config; AM_LOGI("++in type: %d", patch->input_type); patch->aformat = patch->in_format; patch->in_channel_count = audio_channel_count_from_in_mask(patch->in_chanmask); AM_LOGI("++in format: %d, chan: %d, rate: %d", patch->in_format, patch->in_channel_count, patch->in_sample_rate); read_bytes = AUDIO_INPUT_DEFAULT_PERIOD_SIZE * audio_bytes_per_sample(patch->in_format) * patch->in_channel_count; /* 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 (patch->input_device & AUDIO_DEVICE_IN_LINE) { patch->mute_flag = true; } patch->in_buf_size = read_bytes; patch->in_buf = aml_audio_calloc(1, patch->in_buf_size); if (!patch->in_buf) { AM_LOGE("patch->in_buf malloc fail!!!"); 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(); input_config->channel_mask = patch->in_chanmask; input_config->sample_rate = patch->in_sample_rate; input_config->format = patch->in_format; input_config->period_count = AUDIO_INPUT_DEFAULT_PERIOD_COUNT; input_config->period_size = AUDIO_INPUT_DEFAULT_PERIOD_SIZE; AM_LOGI("open input type:%d, period_count:%d, period_size:%d, format:%d, sample_rate:%d", patch->input_type, input_config->period_count, input_config->period_size, input_config->format, input_config->sample_rate); audio_input_open(patch->input_type, input_config); patch->input_open_counter ++; if (ringbuffer) { ring_buffer_size = ringbuffer->size; } while (!patch->input_thread_exit) { int bytes_avail = 0; int period_mul = 1; int read_threshold = 0; if (patch->input_device == AUDIO_DEVICE_IN_HDMI || patch->input_device == AUDIO_DEVICE_IN_HDMI1) { update_low_latency_mode(aml_dev); if (aml_dev->low_latency_mode) { aml_dev->low_latency_param.is_signal_stable = check_tv_stream_signal(patch); if (aml_dev->low_latency_param.is_signal_stable) { mixer_enable_standby(aml_dev->audio_mixer, false); } } ret = reconfig_read_param_through_hdmiin(patch, ringbuffer, ring_buffer_size); if (-1 == ret) { AM_LOGE(" reconfig_read_param_through_hdmiin failed"); } 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_device)) { 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); } } } else if (patch->input_device == AUDIO_DEVICE_IN_HDMI_ARC) { ret = reconfig_read_param_for_arc_spdif(patch, ringbuffer, ring_buffer_size); if (ret < 0) { AM_LOGE(" reconfig_read_param_for_arc_spdif failed"); } } // when it is game mode, the alsa will be read at game_mode_data_read instead of audio_input_read // and we don't write data to ring buffer if (patch->is_game_mode) { usleep(3000); continue; } /* For HBR audio from hdmiin/earcin device, set read bytes size to 4 times */ if (IS_DIGITAL_IN_HW(patch->input_device) && (audio_parse_get_audio_packet_type(patch->audio_parse_para) == AUDIO_PACKET_HBR || patch->spdif_fmt_hw == MAT || patch->spdif_fmt_hw == DTSHD_MA)) { period_mul = 4; if (SRC_ARCIN != patch->patch_src) { patch->tv_param.read_mul_factor = EAC3_MULTIPLIER; } else { patch->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; patch->tv_param.read_mul_factor = HBR_MULTIPLIER; } else if (patch->aformat == AUDIO_FORMAT_E_AC3 || patch->aformat == AUDIO_FORMAT_DTS_HD) { period_mul = 1; patch->tv_param.read_mul_factor = EAC3_MULTIPLIER; } else { period_mul = 1; patch->tv_param.read_mul_factor = 2; } audio_input_set_read_factor(patch->input_type, patch->tv_param.read_mul_factor); read_bytes = AUDIO_INPUT_DEFAULT_PERIOD_SIZE * audio_bytes_per_sample(patch->in_format) * patch->in_channel_count * period_mul; if (patch->input_device == AUDIO_DEVICE_IN_LINE) { read_threshold = 4 * read_bytes; } else if ((patch->input_device == AUDIO_DEVICE_IN_HDMI_ARC || patch->input_device == AUDIO_DEVICE_IN_SPDIF) && patch->aformat == AUDIO_FORMAT_AC3 && patch->in_sample_rate == 44100) { read_threshold = 2 * 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); } bytes_avail = read_bytes; /* if audio is unstable, don't read data from hardware */ stable_flag = check_tv_stream_signal(patch); if (!stable_flag) { /* when audio is unstable, start avsync*/ audio_patch_set_av_sync_status(patch, true); patch->input_signal_stable = false; 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 || SRC_HDMIIN1 == patch->patch_src) && patch->tv_param.audio_packet_type == AUDIO_PACKET_AUDS && patch->in_channel_count != 2) { input_stream_channels_adjust(patch, 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); read_bytes = audio_input_read(patch->input_type, patch->in_buf, read_bytes); if (get_debug_value(AML_DUMP_AUDIOHAL_IN) && read_bytes >= 0) { aml_dump_audio_bitstreams_with_id("alsa_in_read.raw", patch->in_buf, read_bytes, patch->input_open_counter); } //aml_audio_trace_int("input_read_thread", 0); if (IS_DIGITAL_IN_HW(patch->input_device) && !check_digital_in_stream_signal(patch)) { memset(patch->in_buf, 0, bytes_avail); } } if (IS_DIGITAL_IN_HW(patch->input_device)) { cur_aformat = audio_parse_get_audio_type (patch->audio_parse_para); if (cur_aformat != AUDIO_FORMAT_PCM_16_BIT && cur_aformat != AUDIO_FORMAT_PCM_32_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 { /*coverity[NEGATIVE_RETURNS]:read_bytes won't be negative*/ memset(patch->in_buf, 0, read_bytes); } } } /*The noise gate is only used as an analog signal input*/ if ((get_active_inport(aml_dev) == INPORT_LINEIN || get_active_inport(aml_dev) == INPORT_ATV) && is_ng_enable(noise_gate)) { int sample_size = (patch->in_format == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(int32_t); aml_noise_gate_process(noise_gate, patch->in_buf, (bytes_avail/sample_size), sample_size); } else if (get_active_inport(aml_dev) == INPORT_HDMIIN && audio_is_linear_pcm(patch->aformat) && 8 == patch->in_channel_count) { // The hdmi input ch order is L R LFE C *** // The audio hal ch order is L R C LFE *** channel_layout_swap_center_lfe(patch->in_buf, bytes_avail, 8, patch->aformat); } AM_LOGI_IF((aml_dev->debug_flag > 2), "read_bytes = %d, in_read returns = %d, threshold %d avail data=%d", 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 (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->low_latency_mode = false; mixer_enable_standby(aml_dev->audio_mixer, true); audio_input_close(patch->input_type); 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; 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 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; char address_buf[50] = {0}; if (patch->input_device == AUDIO_DEVICE_IN_FM_TUNER) { strncpy(address_buf, "AML_ATV_SOURCE", sizeof(address_buf) - 1); } else if (patch->input_device == AUDIO_DEVICE_IN_HDMI) { strncpy(address_buf, "AML_HDMI_SOURCE", sizeof(address_buf) - 1); } else if ( patch->input_device == AUDIO_DEVICE_IN_HDMI1) { strncpy(address_buf, "AML_HDMI1_SOURCE", sizeof(address_buf) - 1); }else if (patch->input_device == AUDIO_DEVICE_IN_LINE) { strncpy(address_buf, "AML_AV_SOURCE", sizeof(address_buf) - 1); } ret = aml_dev->hw_device.open_output_stream((struct audio_hw_device *)patch->dev, 0, patch->output_device, 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; out->decoder_id = patch->decoder_id; patch->out_buf_size = write_bytes = out->config.period_size * audio_stream_out_frame_size(&out->stream); AM_LOGI("write_bytes:%d out->config.period_size:%d frame_size:%zu", 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; if (out->external_mute != aml_dev->patch_mute) { out_set_stream_mute((struct audio_stream_out *)out, aml_dev->patch_mute, true); } //! check hdmi input format audio_digital_input_format_check(patch); if (IS_DIGITAL_IN_HW(patch->input_device) && 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_channel_out_mask_from_count(patch->out_channel_count); stream_config.format = patch->aformat; if (AUDIO_FORMAT_PCM_16_BIT == patch->aformat || AUDIO_FORMAT_PCM_32_BIT == patch->aformat) {//Fix Me: stream_config.sample_rate = patch->out_sample_rate;; } 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_device, 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; } //hdmi avsync need update sink format and optical format before do sync; //Otherwise, it will lead to inaccurate delay calculations. get_sink_format(aml_dev); //! set stream format and hal rate patch->stream_out = stream_out; out = (struct aml_stream_out *)stream_out; write_bytes = out->config.period_size * audio_stream_out_frame_size(&out->stream); 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); // 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); /* 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; } // when it is game mode, we don't read data from ring buffer if (patch->is_game_mode) { usleep(3000); continue; } period_mul = 1; 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); // 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); if (patch->output_thread_exit) { AM_LOGI("need to exit"); pthread_mutex_unlock(&patch->mutex); break; } } 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->internal_mute != patch->need_do_avsync) && (patch->patch_src != SRC_ARCIN) && (patch->patch_src != SRC_SPDIFIN)) { out_set_stream_mute (stream_out, patch->need_do_avsync, false); AM_LOGI("set stream mute to %d", patch->need_do_avsync); } if (patch && (patch->need_do_avsync == true) && (patch->input_signal_stable == true) && (SRC_ATV == patch->patch_src || SRC_HDMIIN == patch->patch_src || SRC_HDMIIN1 == patch->patch_src || (SRC_LINEIN == patch->patch_src && !patch->av_audio_only))) { aml_dev_try_avsync(patch); if (patch->skip_frames) { ALOGD("%s(), skip this period data for avsync!", __func__); usleep(5); continue; } } stream_out->write(stream_out, patch->out_buf, ret); } else { AM_LOGV("no enough data in ring buffer, available data size:%d, need data size:%d", 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; int ret = 0; AM_LOGD("enter"); patch = aml_audio_calloc(1, sizeof(*patch)); if (!patch) { return -ENOMEM; } patch->dev = aml_dev; patch->input_device = input; patch->patch_src = get_patch_source(input, PATCH_ROUTE_DEV_DEV); patch->digital_input_fmt_change = false; patch->input_type = audio_input_get_type(input); AM_LOGI("input 0x%x patch_src=%d, input_type=%d", input, patch->patch_src, patch->input_type); 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_device = AUDIO_DEVICE_OUT_SPEAKER; patch->out_sample_rate = 48000; patch->out_channel_count = 2; patch->out_chanmask = AUDIO_CHANNEL_OUT_STEREO; if (32 == aml_dev->audio_process_bitwidth) { patch->in_format = AUDIO_FORMAT_PCM_32_BIT; patch->out_format = AUDIO_FORMAT_PCM_32_BIT; patch->aformat = AUDIO_FORMAT_PCM_32_BIT; } else { patch->in_format = AUDIO_FORMAT_PCM_16_BIT; patch->out_format = AUDIO_FORMAT_PCM_16_BIT; patch->aformat = AUDIO_FORMAT_PCM_16_BIT; } patch->last_parser_type = NOT_READY; /* when audio patch start, singal is unstale or * patch signal is unstable, it need do avsync */ audio_patch_set_av_sync_status(patch, true); ret = ring_buffer_init(&patch->aml_ringbuffer, 4 * 2 * 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_device)) { //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_device); 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_device)) { 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); clock_gettime(CLOCK_MONOTONIC, &patch->patch_start_ts); *apatch = patch; #if (_TIME_BITS == 64) AM_LOGD("exit patch_start_ts[%"PRId64".%ld]", patch->patch_start_ts.tv_sec, patch->patch_start_ts.tv_nsec/1000000); #else AM_LOGD("exit patch_start_ts[%ld.%ld]", patch->patch_start_ts.tv_sec, patch->patch_start_ts.tv_nsec/1000000); #endif return 0; 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; AM_LOGI(" enter"); /* Use flag to indicate that it will start to free patch struct. TBD */ //invalidate_dev_patch(aml_dev); patch->output_thread_exit = 1; pthread_cond_signal(&patch->cond); pthread_join(patch->audio_output_threadID, NULL); patch->input_thread_exit = 1; audio_input_set_exit_flag(patch->input_type); patch->tv_param.patch_input_thread_exit = 1; pthread_join(patch->audio_input_threadID, NULL); /* if (IS_DIGITAL_IN_HW(patch->input_device)) { patch->signal_detect_thread_exit = 1; pthread_join(patch->audio_signal_detect_threadID, NULL); } */ if (IS_DIGITAL_IN_HW(patch->input_device)) exit_pthread_for_audio_type_parse(patch->audio_parse_threadID,&patch->audio_parse_para); ring_buffer_release(&patch->aml_ringbuffer); aml_audio_free(patch); AM_LOGI(" 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; }