| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include <linux/types.h> |
| #include <uapi/linux/dvb/frontend.h> |
| |
| #include "atvdemod_func.h" |
| #include "atvauddemod_func.h" |
| #include "atv_demod_monitor.h" |
| #include "atv_demod_ops.h" |
| #include "atv_demod_debug.h" |
| |
| static DEFINE_MUTEX(monitor_mutex); |
| |
| bool atvdemod_mixer_tune_en; |
| bool atvdemod_overmodulated_en; |
| bool atv_audio_overmodulated_en; |
| unsigned int atv_audio_overmodulated_cnt = 1; |
| bool audio_det_en; |
| bool atvdemod_det_snr_en = true; |
| bool audio_thd_en; |
| bool atvdemod_det_nonstd_en; |
| bool atvaudio_det_outputmode_en = true; |
| bool audio_carrier_offset_det_en; |
| bool atvdemod_horiz_freq_det_en = true; |
| |
| unsigned int atvdemod_timer_delay = 100; /* 1s */ |
| unsigned int atvdemod_timer_delay2 = 10; /* 100ms */ |
| |
| bool atvdemod_timer_en = true; |
| |
| |
| static void atv_demod_monitor_do_work(struct work_struct *work) |
| { |
| int vpll_lock = 0, line_lock = 0; |
| struct atv_demod_monitor *monitor = |
| container_of(work, struct atv_demod_monitor, work); |
| |
| if (monitor->state == MONI_DISABLE) |
| return; |
| |
| retrieve_vpll_carrier_lock(&vpll_lock); |
| retrieve_vpll_carrier_line_lock(&line_lock); |
| if ((vpll_lock != 0) || (line_lock != 0)) { |
| monitor->lock_cnt = 0; |
| return; |
| } |
| |
| monitor->lock_cnt++; |
| |
| if (atvdemod_mixer_tune_en) |
| atvdemod_mixer_tune(); |
| |
| if (atvdemod_overmodulated_en) |
| atvdemod_video_overmodulated(); |
| |
| if (atv_audio_overmodulated_en) { |
| if (monitor->lock_cnt > atv_audio_overmodulated_cnt) |
| aml_audio_overmodulation(1); |
| } |
| |
| if (atvdemod_det_snr_en) |
| atvdemod_det_snr_serice(); |
| |
| if (audio_thd_en) |
| audio_thd_det(); |
| |
| if (atvaudio_det_outputmode_en) |
| atvauddemod_set_outputmode(); |
| |
| if (audio_carrier_offset_det_en) |
| audio_carrier_offset_det(); |
| |
| if (atvdemod_det_nonstd_en) |
| atv_dmd_non_std_set(true); |
| |
| if (atvdemod_horiz_freq_det_en) |
| atvdemod_horiz_freq_detection(); |
| } |
| |
| static void atv_demod_monitor_timer_handler(struct timer_list *timer) |
| { |
| struct atv_demod_monitor *monitor = container_of(timer, |
| struct atv_demod_monitor, timer); |
| |
| /* 100ms timer */ |
| monitor->timer.expires = jiffies + |
| ATVDEMOD_INTERVAL * atvdemod_timer_delay2; |
| add_timer(&monitor->timer); |
| |
| if (atvdemod_timer_en == 0) |
| return; |
| |
| if (monitor->state == MONI_PAUSE) |
| return; |
| |
| schedule_work(&monitor->work); |
| } |
| |
| static void atv_demod_monitor_enable(struct atv_demod_monitor *monitor) |
| { |
| mutex_lock(&monitor->mtx); |
| |
| if (atvdemod_timer_en && monitor->state == MONI_DISABLE) { |
| atv_dmd_non_std_set(false); |
| |
| timer_setup(&monitor->timer, atv_demod_monitor_timer_handler, 0); |
| monitor->timer.function = atv_demod_monitor_timer_handler; |
| /* after 1s enable demod auto detect */ |
| monitor->timer.expires = jiffies + |
| ATVDEMOD_INTERVAL * atvdemod_timer_delay; |
| add_timer(&monitor->timer); |
| monitor->state = MONI_ENABLE; |
| monitor->lock_cnt = 0; |
| } else if (atvdemod_timer_en && monitor->state == MONI_PAUSE) { |
| atv_dmd_non_std_set(false); |
| |
| monitor->state = MONI_ENABLE; |
| monitor->lock_cnt = 0; |
| } |
| |
| mutex_unlock(&monitor->mtx); |
| |
| pr_dbg("%s: state: %d.\n", __func__, monitor->state); |
| } |
| |
| static void atv_demod_monitor_disable(struct atv_demod_monitor *monitor) |
| { |
| mutex_lock(&monitor->mtx); |
| |
| if (atvdemod_timer_en && monitor->state != MONI_DISABLE) { |
| monitor->state = MONI_DISABLE; |
| del_timer_sync(&monitor->timer); |
| cancel_work_sync(&monitor->work); |
| } |
| |
| mutex_unlock(&monitor->mtx); |
| |
| pr_dbg("%s: state: %d.\n", __func__, monitor->state); |
| } |
| |
| static void atv_demod_monitor_pause(struct atv_demod_monitor *monitor) |
| { |
| mutex_lock(&monitor->mtx); |
| |
| if (monitor->state == MONI_ENABLE) { |
| monitor->state = MONI_PAUSE; |
| atv_dmd_non_std_set(false); |
| cancel_work_sync(&monitor->work); |
| } |
| |
| mutex_unlock(&monitor->mtx); |
| } |
| |
| void atv_demod_monitor_init(struct atv_demod_monitor *monitor) |
| { |
| mutex_lock(&monitor_mutex); |
| |
| mutex_init(&monitor->mtx); |
| |
| monitor->state = MONI_DISABLE; |
| monitor->lock = false; |
| monitor->lock_cnt = 0; |
| monitor->disable = atv_demod_monitor_disable; |
| monitor->enable = atv_demod_monitor_enable; |
| monitor->pause = atv_demod_monitor_pause; |
| |
| INIT_WORK(&monitor->work, atv_demod_monitor_do_work); |
| |
| mutex_unlock(&monitor_mutex); |
| } |