blob: faafaf0dadf632483750433db3f2fe50a34fa631 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* drivers/amlogic/media/vin/tvin/vdin/vdin_sm.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
/* Standard Linux Headers */
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/extcon.h>
#include <linux/workqueue.h>
/* Amlogic Headers */
#include <linux/amlogic/media/frame_provider/tvin/tvin.h>
/* Local Headers */
#include "../tvin_frontend.h"
#include "../tvin_format_table.h"
#include "vdin_sm.h"
#include "vdin_ctl.h"
#include "vdin_drv.h"
/* Stay in TVIN_SIG_STATE_NOSIG for some
* cycles => be sure TVIN_SIG_STATE_NOSIG
*/
#define NOSIG_MAX_CNT 8
/* Stay in TVIN_SIG_STATE_UNSTABLE for some
* cycles => be sure TVIN_SIG_STATE_UNSTABLE
*/
#define UNSTABLE_MAX_CNT 2/* 4 */
/* Have signal for some cycles => exit TVIN_SIG_STATE_NOSIG */
#define EXIT_NOSIG_MAX_CNT 2/* 1 */
/* No signal for some cycles => back to TVAFE_STATE_NOSIG */
#define BACK_NOSIG_MAX_CNT 24 /* 8 */
/* Signal unstable for some cycles => exit TVAFE_STATE_STABLE */
#define EXIT_STABLE_MAX_CNT 1
/* Signal stable for some cycles => back to TVAFE_STATE_STABLE */
/* must >=500ms,for new api function */
#define BACK_STABLE_MAX_CNT 50
#define EXIT_PRESTABLE_MAX_CNT 50
static struct tvin_sm_s sm_dev[VDIN_MAX_DEVS];
static int sm_print_nosig;
static int sm_print_notsup;
static int sm_print_unstable;
static int sm_print_fmt_nosig;
static int sm_print_fmt_chg;
static int sm_atv_prestable_fmt;
static int sm_print_prestable;
/*bit0:general debug bit;bit1:hdmirx color change*/
static unsigned int sm_debug_enable = VDIN_SM_LOG_L_1;
module_param(sm_debug_enable, uint, 0664);
MODULE_PARM_DESC(sm_debug_enable,
"enable/disable state machine debug message");
static int back_nosig_max_cnt = BACK_NOSIG_MAX_CNT;
static int atv_unstable_in_cnt = 45;
static int atv_unstable_out_cnt = 50;
static int hdmi_unstable_out_cnt = 1;
static int hdmi_stable_out_cnt = 1;/* 25; */
/* new add in gxtvbb@20160523,reason:
*gxtvbb add atv snow config,the config will affect signal detect.
*if atv_stable_out_cnt < 100,the signal state will change
*after swich source to atv or after atv search
*/
static int atv_stable_out_cnt = 100;
/* new add in gxtvbb@20160613,reason:
*gxtvbb add atv snow config,the config will affect signal detect.
*ensure after fmt change,the new fmt can be detect in time!
*/
static int atv_stable_fmt_check_cnt = 10;
/* new add in gxtvbb@20160613,reason:
* ensure vdin fmt can update when fmt is changed in menu
*/
static int atv_stable_fmt_check_enable;
/* new add in gxtvbb@20160523,reason:
*gxtvbb add atv snow config,the config will affect signal detect.
*ensure after prestable into stable,the state is really stable!
*/
static int atv_prestable_out_cnt = 50;
static int other_stable_out_cnt = EXIT_STABLE_MAX_CNT;
static int other_unstable_out_cnt = BACK_STABLE_MAX_CNT;
static int manual_unstable_out_cnt = 30;
static int other_unstable_in_cnt = UNSTABLE_MAX_CNT;
static int nosig_in_cnt = NOSIG_MAX_CNT;
static int nosig2_unstable_cnt = EXIT_NOSIG_MAX_CNT;
bool manual_flag;
u32 vdin_re_config = (RE_CONFIG_DV_EN | RE_CONFIG_HDR_EN);
module_param(vdin_re_config, int, 0664);
MODULE_PARM_DESC(vdin_re_config, "vdin_re_config");
u32 vdin_re_cfg_drop_cnt = 8;
module_param(vdin_re_cfg_drop_cnt, int, 0664);
MODULE_PARM_DESC(vdin_re_cfg_drop_cnt, "vdin_re_cfg_drop_cnt");
/*#define DEBUG_SUPPORT*/
#ifdef DEBUG_SUPPORT
module_param(back_nosig_max_cnt, int, 0664);
MODULE_PARM_DESC(back_nosig_max_cnt,
"unstable enter nosignal state max count");
module_param(atv_unstable_in_cnt, int, 0664);
MODULE_PARM_DESC(atv_unstable_in_cnt, "atv_unstable_in_cnt");
module_param(atv_unstable_out_cnt, int, 0664);
MODULE_PARM_DESC(atv_unstable_out_cnt, "atv_unstable_out_cnt");
module_param(hdmi_unstable_out_cnt, int, 0664);
MODULE_PARM_DESC(hdmi_unstable_out_cnt, "hdmi_unstable_out_cnt");
module_param(hdmi_stable_out_cnt, int, 0664);
MODULE_PARM_DESC(hdmi_stable_out_cnt, "hdmi_stable_out_cnt");
module_param(atv_stable_out_cnt, int, 0664);
MODULE_PARM_DESC(atv_stable_out_cnt, "atv_stable_out_cnt");
module_param(atv_stable_fmt_check_cnt, int, 0664);
MODULE_PARM_DESC(atv_stable_fmt_check_cnt, "atv_stable_fmt_check_cnt");
module_param(atv_prestable_out_cnt, int, 0664);
MODULE_PARM_DESC(atv_prestable_out_cnt, "atv_prestable_out_cnt");
module_param(other_stable_out_cnt, int, 0664);
MODULE_PARM_DESC(other_stable_out_cnt, "other_stable_out_cnt");
module_param(other_unstable_out_cnt, int, 0664);
MODULE_PARM_DESC(other_unstable_out_cnt, "other_unstable_out_cnt");
module_param(other_unstable_in_cnt, int, 0664);
MODULE_PARM_DESC(other_unstable_in_cnt, "other_unstable_in_cnt");
module_param(nosig_in_cnt, int, 0664);
MODULE_PARM_DESC(nosig_in_cnt, "nosig_in_cnt");
module_param(nosig2_unstable_cnt, int, 0664);
MODULE_PARM_DESC(nosig2_unstable_cnt, "nosig2_unstable_cnt");
#endif
static int signal_status = TVIN_SIG_STATUS_NULL;
module_param(signal_status, int, 0664);
MODULE_PARM_DESC(signal_status, "signal_status");
static unsigned int vdin_dv_chg_cnt = 1;
module_param(vdin_dv_chg_cnt, uint, 0664);
MODULE_PARM_DESC(vdin_dv_chg_cnt, "vdin_dv_chg_cnt");
static unsigned int vdin_hdr_chg_cnt = 1;
module_param(vdin_hdr_chg_cnt, uint, 0664);
MODULE_PARM_DESC(vdin_hdr_chg_cnt, "vdin_hdr_chg_cnt");
enum tvin_color_fmt_range_e
tvin_get_force_fmt_range(enum tvin_color_fmt_e color_fmt)
{
u32 fmt_range = TVIN_YUV_FULL;
if (color_fmt == TVIN_YUV444 ||
color_fmt == TVIN_YUV422) {
if (color_range_force == COLOR_RANGE_FULL)
fmt_range = TVIN_YUV_FULL;
else if (color_range_force == COLOR_RANGE_LIMIT)
fmt_range = TVIN_YUV_LIMIT;
else
fmt_range = TVIN_YUV_FULL;
} else if (color_fmt == TVIN_RGB444) {
if (color_range_force == COLOR_RANGE_FULL)
fmt_range = TVIN_RGB_FULL;
else if (color_range_force == COLOR_RANGE_LIMIT)
fmt_range = TVIN_RGB_LIMIT;
else
fmt_range = TVIN_RGB_FULL;
}
return fmt_range;
}
void vdin_update_prop(struct vdin_dev_s *devp)
{
/*devp->pre_prop.fps = devp->prop.fps;*/
devp->dv.dv_flag = devp->prop.dolby_vision;
/*devp->pre_prop.latency.allm_mode = devp->prop.latency.allm_mode;*/
/*devp->pre_prop.aspect_ratio = devp->prop.aspect_ratio;*/
devp->parm.info.aspect_ratio = devp->prop.aspect_ratio;
memcpy(&devp->pre_prop, &devp->prop,
sizeof(struct tvin_sig_property_s));
}
/*
* check hdmirx color format
*/
static enum tvin_sg_chg_flg vdin_hdmirx_fmt_chg_detect(struct vdin_dev_s *devp)
{
struct tvin_state_machine_ops_s *sm_ops;
enum tvin_port_e port = TVIN_PORT_NULL;
enum tvin_color_fmt_e cur_color_fmt, pre_color_fmt;
/*enum tvin_color_fmt_e cur_dest_color_fmt, pre_dest_color_fmt;*/
struct tvin_sig_property_s *prop, *pre_prop;
unsigned int vdin_hdr_flag, pre_vdin_hdr_flag;
unsigned int vdin_fmt_range, pre_vdin_fmt_range;
unsigned int cur_dv_flag, pre_dv_flag;
unsigned int temp;
enum tvin_sg_chg_flg signal_chg = TVIN_SIG_CHG_NONE;
if (!devp) {
return signal_chg;
}
if (!devp->frontend) {
sm_dev[devp->index].state = TVIN_SM_STATUS_NULL;
return signal_chg;
}
prop = &devp->prop;
pre_prop = &devp->pre_prop;
sm_ops = devp->frontend->sm_ops;
port = devp->parm.port;
if (port < TVIN_PORT_HDMI0 || port > TVIN_PORT_HDMI7)
return signal_chg;
if ((devp->flags & VDIN_FLAG_DEC_STARTED) &&
sm_ops->get_sig_property) {
/*if (!(devp->flags & VDIN_FLAG_ISR_EN))*/
/* sm_ops->get_sig_property(devp->frontend, prop);*/
cur_color_fmt = prop->color_format;
pre_color_fmt = pre_prop->color_format;
/*cur_dest_color_fmt = prop->dest_cfmt;*/
/*pre_dest_color_fmt = pre_prop->dest_cfmt;*/
vdin_hdr_flag = prop->vdin_hdr_flag;
pre_vdin_hdr_flag = pre_prop->vdin_hdr_flag;
if (vdin_hdr_flag != pre_vdin_hdr_flag) {
if (prop->hdr_info.hdr_check_cnt >=
vdin_hdr_chg_cnt) {
prop->hdr_info.hdr_check_cnt = 0;
signal_chg |= vdin_hdr_flag ?
TVIN_SIG_CHG_SDR2HDR :
TVIN_SIG_CHG_HDR2SDR;
if (signal_chg &&
(sm_debug_enable & VDIN_SM_LOG_L_2))
pr_info("%s hdr chg 0x%x:(0x%x->0x%x)\n",
__func__,
signal_chg, pre_vdin_hdr_flag,
vdin_hdr_flag);
pre_prop->vdin_hdr_flag = prop->vdin_hdr_flag;
}
}
cur_dv_flag = prop->dolby_vision;
pre_dv_flag = devp->dv.dv_flag;
if (cur_dv_flag != pre_dv_flag) {
if (devp->dv.chg_cnt > vdin_dv_chg_cnt) {
devp->dv.chg_cnt = 0;
signal_chg |= cur_dv_flag ? TVIN_SIG_CHG_NO2DV :
TVIN_SIG_CHG_DV2NO;
if (signal_chg &&
(sm_debug_enable & VDIN_SM_LOG_L_2))
pr_info("%s dv chg0x%x:(0x%x->0x%x)\n",
__func__,
signal_chg, pre_dv_flag,
cur_dv_flag);
if (pre_dv_flag == 2)
nosig2_unstable_cnt = 150;
pre_prop->dolby_vision = prop->dolby_vision;
devp->dv.dv_flag = prop->dolby_vision;
}
}
if (devp->pre_prop.latency.allm_mode !=
devp->prop.latency.allm_mode) {
if (devp->dv.allm_chg_cnt > vdin_dv_chg_cnt) {
devp->dv.allm_chg_cnt = 0;
signal_chg |= TVIN_SIG_CHG_DV_ALLM;
temp = devp->pre_prop.latency.allm_mode;
if (signal_chg)
pr_info("%s allm chg:(0x%x->0x%x)\n",
__func__,
temp,
devp->prop.latency.allm_mode);
devp->pre_prop.latency.allm_mode =
devp->prop.latency.allm_mode;
}
}
if (devp->pre_prop.latency.it_content !=
devp->prop.latency.it_content) {
if (devp->dv.allm_chg_cnt > vdin_dv_chg_cnt) {
devp->dv.allm_chg_cnt = 0;
signal_chg |= TVIN_SIG_CHG_DV_ALLM;
temp = devp->pre_prop.latency.it_content;
if (signal_chg)
pr_info("%s it_content chg:(0x%x->0x%x)\n",
__func__,
temp,
devp->prop.latency.it_content);
devp->pre_prop.latency.it_content =
devp->prop.latency.it_content;
}
}
if (devp->pre_prop.latency.cn_type !=
devp->prop.latency.cn_type) {
if (devp->dv.allm_chg_cnt > vdin_dv_chg_cnt) {
devp->dv.allm_chg_cnt = 0;
signal_chg |= TVIN_SIG_CHG_DV_ALLM;
temp = devp->pre_prop.latency.cn_type;
if (signal_chg)
pr_info("%s cn_type chg:(0x%x->0x%x)\n",
__func__,
temp,
devp->prop.latency.cn_type);
devp->pre_prop.latency.cn_type =
devp->prop.latency.cn_type;
}
}
if (devp->pre_prop.fps != devp->prop.fps) {
signal_chg |= TVIN_SIG_CHG_VS_FRQ;
pr_info("%s fps chg:(0x%x->0x%x)\n", __func__,
devp->pre_prop.fps,
devp->prop.fps);
devp->pre_prop.fps = devp->prop.fps;
devp->parm.info.fps = devp->prop.fps;
}
if (devp->pre_prop.aspect_ratio !=
devp->prop.aspect_ratio &&
IS_HDMI_SRC(devp->parm.port)) {
if (devp->sg_chg_afd_cnt > 1) {
signal_chg |= TVIN_SIG_CHG_AFD;
if (signal_chg)
pr_info("%s afd chg:(0x%x->0x%x)\n",
__func__,
devp->pre_prop.aspect_ratio,
devp->prop.aspect_ratio);
devp->pre_prop.aspect_ratio =
devp->prop.aspect_ratio;
devp->parm.info.aspect_ratio =
prop->aspect_ratio;
}
}
if (devp->pre_prop.vtem_data.vrr_en !=
devp->prop.vtem_data.vrr_en) {
signal_chg |= TVIN_SIG_CHG_VRR;
pr_info("%s vrr chg:(%d->%d)\n", __func__,
devp->pre_prop.vtem_data.vrr_en,
devp->prop.vtem_data.vrr_en);
devp->pre_prop.vtem_data.vrr_en =
devp->prop.vtem_data.vrr_en;
devp->prop.vdin_vrr_flag =
devp->prop.vtem_data.vrr_en;
}
if (color_range_force)
prop->color_fmt_range =
tvin_get_force_fmt_range(pre_prop->color_format);
vdin_fmt_range = prop->color_fmt_range;
pre_vdin_fmt_range = pre_prop->color_fmt_range;
if (cur_color_fmt != pre_color_fmt ||
/*(vdin_hdr_flag != pre_vdin_hdr_flag) ||*/
vdin_fmt_range != pre_vdin_fmt_range) {
if (sm_debug_enable & VDIN_SM_LOG_L_2)
pr_info("[smr.%d] fmt(%d->%d), hdr_flag(%d->%d), csc_cfg:0x%x\n",
devp->index,
pre_color_fmt, cur_color_fmt,
pre_vdin_hdr_flag, vdin_hdr_flag,
devp->csc_cfg);
vdin_get_format_convert(devp);
devp->csc_cfg = 1;
}
}
return signal_chg;
}
/* check auto de to adjust vdin cutwindow */
void vdin_auto_de_handler(struct vdin_dev_s *devp)
{
struct tvin_state_machine_ops_s *sm_ops;
struct tvin_sig_property_s *prop;
unsigned int cur_vs, cur_ve, pre_vs, pre_ve;
unsigned int cur_hs, cur_he, pre_hs, pre_he;
if (!devp) {
return;
} else if (!devp->frontend) {
sm_dev[devp->index].state = TVIN_SM_STATUS_NULL;
return;
}
if (devp->auto_cutwindow_en == 0)
return;
prop = &devp->prop;
sm_ops = devp->frontend->sm_ops;
if ((devp->flags & VDIN_FLAG_DEC_STARTED) &&
sm_ops->get_sig_property) {
sm_ops->get_sig_property(devp->frontend, prop);
cur_vs = prop->vs;
cur_ve = prop->ve;
cur_hs = prop->hs;
cur_he = prop->he;
pre_vs = prop->pre_vs;
pre_ve = prop->pre_ve;
pre_hs = prop->pre_hs;
pre_he = prop->pre_he;
if (pre_vs != cur_vs || pre_ve != cur_ve ||
pre_hs != cur_hs || pre_he != cur_he) {
if (sm_debug_enable & VDIN_SM_LOG_L_4)
pr_info("[smr.%d] pre_vs(%d->%d),pre_ve(%d->%d),pre_hs(%d->%d),pre_he(%d->%d),cutwindow_cfg:0x%x\n",
devp->index, pre_vs, cur_vs, pre_ve, cur_ve,
pre_hs, cur_hs, pre_he, cur_he,
devp->cutwindow_cfg);
devp->cutwindow_cfg = 1;
}
}
}
void tvin_smr_init_counter(int index)
{
sm_dev[index].state_cnt = 0;
sm_dev[index].exit_nosig_cnt = 0;
sm_dev[index].back_nosig_cnt = 0;
sm_dev[index].back_stable_cnt = 0;
sm_dev[index].exit_prestable_cnt = 0;
}
u32 tvin_hdmirx_signal_type_check(struct vdin_dev_s *devp)
{
unsigned int signal_type = devp->parm.info.signal_type;
enum tvin_sg_chg_flg signal_chg = TVIN_SIG_CHG_NONE;
struct tvin_state_machine_ops_s *sm_ops;
struct tvin_sig_property_s *prop;
/* need always polling the signal property, if isr enable,
* it be called in isr
*/
prop = &devp->prop;
if (!(devp->flags & VDIN_FLAG_ISR_EN) && devp->frontend) {
sm_ops = devp->frontend->sm_ops;
if (sm_ops && sm_ops->get_sig_property) {
if (IS_TVAFE_SRC(devp->parm.port) ||
vdin_get_prop_in_sm_en)
sm_ops->get_sig_property(devp->frontend,
&devp->prop);
/*devp->dv.dv_flag = devp->prop.dolby_vision;*/
}
}
if (prop->low_latency != devp->dv.low_latency)
devp->dv.low_latency = prop->low_latency;
memcpy(&devp->dv.dv_vsif,
&prop->dv_vsif, sizeof(struct tvin_dv_vsif_s));
if (sm_debug_enable & VDIN_SM_LOG_L_4)
pr_info("[sm.%d]dv:%d, hdr state:%d eotf:%d flag:0x%x, vrr state:%d\n",
devp->index,
devp->prop.dolby_vision,
devp->prop.hdr_info.hdr_state,
devp->prop.hdr_info.hdr_data.eotf,
devp->prop.vdin_hdr_flag,
devp->prop.vdin_vrr_flag);
if (devp->prop.dolby_vision)
signal_type |= (1 << 30);
else
signal_type &= ~(1 << 30);
/* check dv end */
/* check HDR/HLG begin */
if (prop->hdr_info.hdr_state == HDR_STATE_GET) {
if (vdin_hdr_sei_error_check(devp) == 1) {
/*devp->prop.vdin_hdr_flag = false;*/
signal_type &= ~(1 << 29);
signal_type &= ~(1 << 25);
/* default is bt709,if change need sync */
signal_type = ((1 << 16) |
(signal_type & (~0xFF0000)));
signal_type = ((1 << 8) | (signal_type & (~0xFF00)));
} else {
devp->prop.vdin_hdr_flag = true;
if (prop->hdr_info.hdr_data.eotf ==
EOTF_SMPTE_ST_2048 ||
prop->hdr_info.hdr_data.eotf == EOTF_HDR) {
signal_type |= (1 << 29);
signal_type |= (0 << 25);/* 0:limit */
signal_type = ((9 << 16) |
(signal_type & (~0xFF0000)));
signal_type = ((16 << 8) |
(signal_type & (~0xFF00)));
signal_type = ((9 << 0) |
(signal_type & (~0xFF)));
} else if (devp->prop.hdr_info.hdr_data.eotf ==
EOTF_HLG) {
signal_type |= (1 << 29);
signal_type |= (0 << 25);/* 0:limit */
signal_type = ((9 << 16) |
(signal_type & (~0xFF0000)));
signal_type = ((14 << 8) |
(signal_type & (~0xFF00)));
signal_type = ((9 << 0) |
(signal_type & (~0xFF)));
} else {
if (devp->prop.hdr_info.hdr_data.eotf ==
EOTF_SDR)
devp->prop.vdin_hdr_flag = false;
signal_type &= ~(1 << 29);
signal_type &= ~(1 << 25);
/* default is bt709,if change need sync */
signal_type = ((1 << 16) |
(signal_type & (~0xFF0000)));
signal_type = ((1 << 8) |
(signal_type & (~0xFF00)));
}
}
devp->prop.hdr_info.hdr_state = HDR_STATE_SET;
} else if (prop->hdr_info.hdr_state == HDR_STATE_NULL) {
devp->prop.vdin_hdr_flag = false;
signal_type &= ~(1 << 29);
signal_type &= ~(1 << 25);
signal_type |= (0 << 25);/* 0:limit */
/* default is bt709,if change need sync */
signal_type = ((1 << 16) | (signal_type & (~0xFF0000)));
signal_type = ((1 << 8) | (signal_type & (~0xFF00)));
}
/* check HDR/HLG end */
/* check HDR 10+ begin */
if (prop->hdr10p_info.hdr10p_on) {
devp->prop.vdin_hdr_flag = true;
signal_type |= (1 << 29);/* present_flag */
signal_type |= (0 << 25);/* 0:limited */
/* color_primaries */
signal_type = ((9 << 16) | (signal_type & (~0xFF0000)));
/*transfer_characteristic*/
signal_type = ((0x30 << 8) | (signal_type & (~0xFF00)));
/* matrix_coefficient */
signal_type = ((9 << 0) | (signal_type & (~0xFF)));
}
if (sm_debug_enable & VDIN_SM_LOG_L_4)
pr_info("[sm.%d] hdr flag:0x%x signal_type:0x%x\n",
devp->index,
devp->prop.vdin_hdr_flag, signal_type);
if (devp->prop.vdin_hdr_flag &&
devp->parm.info.signal_type != signal_type) {
signal_chg |= TVIN_SIG_CHG_SDR2HDR;
}
/* check HDR 10+ end */
/* check vrr begin */
if (devp->prop.vdin_vrr_flag)
signal_type |= (1 << 31);
else
signal_type &= ~(1 << 31);
/* check vrr end */
devp->parm.info.signal_type = signal_type;
return signal_chg;
}
void reset_tvin_smr(unsigned int index)
{
sm_dev[index].sig_status = TVIN_SIG_STATUS_NULL;
}
void tvin_sigchg_event_process(struct vdin_dev_s *devp, u32 chg)
{
/*struct tvin_sm_s *sm_p;*/
bool re_cfg = 0;
/*avoid when doing start dec, hdr or dv change re-config coming*/
if (!(devp->flags & VDIN_FLAG_DEC_STARTED))
return;
if (chg & TVIN_SIG_CHG_STS) {
devp->event_info.event_sts = TVIN_SIG_CHG_STS;
} else {
if (chg & TVIN_SIG_DV_CHG) {
devp->event_info.event_sts = (chg & TVIN_SIG_DV_CHG);
if (vdin_re_config & RE_CONFIG_DV_EN)
re_cfg = true;
} else if (chg & TVIN_SIG_HDR_CHG) {
devp->event_info.event_sts = (chg & TVIN_SIG_HDR_CHG);
if (vdin_re_config & RE_CONFIG_HDR_EN)
re_cfg = true;
} else if (chg & TVIN_SIG_CHG_COLOR_FMT) {
devp->event_info.event_sts = TVIN_SIG_CHG_COLOR_FMT;
} else if (chg & TVIN_SIG_CHG_RANGE) {
devp->event_info.event_sts = TVIN_SIG_CHG_RANGE;
} else if (chg & TVIN_SIG_CHG_BIT) {
devp->event_info.event_sts = TVIN_SIG_CHG_BIT;
} else if (chg & TVIN_SIG_CHG_VS_FRQ) {
devp->event_info.event_sts = TVIN_SIG_CHG_VS_FRQ;
} else if (chg & TVIN_SIG_CHG_DV_ALLM) {
devp->event_info.event_sts = TVIN_SIG_CHG_DV_ALLM;
if (vdin_re_config & RE_CONFIG_ALLM_EN)
re_cfg = true;
} else if (chg & TVIN_SIG_CHG_AFD) {
devp->event_info.event_sts = TVIN_SIG_CHG_AFD;
} else if (chg & TVIN_SIG_CHG_VRR) {
devp->event_info.event_sts = TVIN_SIG_CHG_VRR;
} else {
return;
}
}
if (re_cfg) {
if (sm_debug_enable & VDIN_SM_LOG_L_1)
pr_info("vdin reconfig, set unstable\n");
devp->parm.info.status = TVIN_SIG_STATUS_UNSTABLE;
devp->frame_drop_num = vdin_re_cfg_drop_cnt;
}
devp->pre_event_info.event_sts = devp->event_info.event_sts;
vdin_send_event(devp, devp->event_info.event_sts);
wake_up(&devp->queue);
}
/*
* tvin state machine routine
*
*/
void tvin_smr(struct vdin_dev_s *devp)
{
struct tvin_state_machine_ops_s *sm_ops;
struct tvin_info_s *info;
enum tvin_port_e port = TVIN_PORT_NULL;
unsigned int unstb_in;
struct tvin_sm_s *sm_p;
struct tvin_frontend_s *fe;
struct tvin_sig_property_s *prop, *pre_prop;
enum tvin_sg_chg_flg signal_chg = TVIN_SIG_CHG_NONE;
unsigned int cnt;
if (!devp) {
return;
} else if (!devp->frontend) {
sm_dev[devp->index].state = TVIN_SM_STATUS_NULL;
return;
}
if (devp->flags & VDIN_FLAG_SM_DISABLE ||
devp->flags & VDIN_FLAG_SUSPEND)
return;
if (!(devp->flags & VDIN_FLAG_DEC_OPENED))
return;
sm_p = &sm_dev[devp->index];
fe = devp->frontend;
sm_ops = devp->frontend->sm_ops;
info = &devp->parm.info;
port = devp->parm.port;
prop = &devp->prop;
pre_prop = &devp->pre_prop;
signal_chg = tvin_hdmirx_signal_type_check(devp);
switch (sm_p->state) {
case TVIN_SM_STATUS_NOSIG:
++sm_p->state_cnt;
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_AFE
if (IS_TVAFE_ATV_SRC(port) &&
(devp->flags & VDIN_FLAG_SNOW_FLAG))
tvafe_snow_config_clamp(1);
#endif
if (sm_ops->nosig(devp->frontend)) {
sm_p->exit_nosig_cnt = 0;
if (sm_p->state_cnt >= nosig_in_cnt) {
sm_p->state_cnt = nosig_in_cnt;
info->status = TVIN_SIG_STATUS_NOSIG;
if (!(IS_TVAFE_ATV_SRC(port) &&
(devp->flags & VDIN_FLAG_SNOW_FLAG)))
info->fmt = TVIN_SIG_FMT_NULL;
if (sm_debug_enable && !sm_print_nosig) {
pr_info("[smr.%d] no signal\n",
devp->index);
sm_print_nosig = 1;
}
sm_print_unstable = 0;
}
} else {
if (IS_TVAFE_SRC(port))
nosig2_unstable_cnt = 2;
++sm_p->exit_nosig_cnt;
if (sm_p->exit_nosig_cnt >= nosig2_unstable_cnt) {
tvin_smr_init_counter(devp->index);
sm_p->state = TVIN_SM_STATUS_UNSTABLE;
if (sm_debug_enable) {
pr_info("[smr.%d] no signal --> unstable\n",
devp->index);
pr_info("unstable_cnt:0x%x\n",
nosig2_unstable_cnt);
}
if (IS_HDMI_SRC(port))
nosig2_unstable_cnt = 20;
sm_print_nosig = 0;
sm_print_unstable = 0;
}
}
break;
case TVIN_SM_STATUS_UNSTABLE:
++sm_p->state_cnt;
if (sm_ops->nosig(devp->frontend)) {
sm_p->back_stable_cnt = 0;
++sm_p->back_nosig_cnt;
if (sm_p->back_nosig_cnt >= sm_p->back_nosig_max_cnt) {
tvin_smr_init_counter(devp->index);
sm_p->state = TVIN_SM_STATUS_NOSIG;
info->status = TVIN_SIG_STATUS_NOSIG;
if (!(IS_TVAFE_ATV_SRC(port) &&
(devp->flags & VDIN_FLAG_SNOW_FLAG)))
info->fmt = TVIN_SIG_FMT_NULL;
if (sm_debug_enable)
pr_info("[smr.%d] unstable --> no signal\n",
devp->index);
sm_print_nosig = 0;
sm_print_unstable = 0;
}
} else {
sm_p->back_nosig_cnt = 0;
if (sm_ops->fmt_changed(devp->frontend)) {
sm_p->back_stable_cnt = 0;
if (IS_TVAFE_ATV_SRC(port) &&
devp->unstable_flag &&
(devp->flags & VDIN_FLAG_SNOW_FLAG))
/* UNSTABLE_ATV_MAX_CNT; */
unstb_in = sm_p->atv_unstable_in_cnt;
else
unstb_in = other_unstable_in_cnt;
if (sm_p->state_cnt >= unstb_in) {
sm_p->state_cnt = unstb_in;
info->status = TVIN_SIG_STATUS_UNSTABLE;
/*info->fmt = TVIN_SIG_FMT_NULL;*/
if (sm_debug_enable &&
!sm_print_unstable) {
pr_info("[smr.%d] unstable\n",
devp->index);
sm_print_unstable = 1;
}
sm_print_nosig = 0;
}
} else {
++sm_p->back_stable_cnt;
if (IS_TVAFE_ATV_SRC(port) &&
(devp->flags & VDIN_FLAG_SNOW_FLAG))
unstb_in = sm_p->atv_unstable_out_cnt;
else if (IS_TVAFE_ATV_SRC(port) && manual_flag)
unstb_in = manual_unstable_out_cnt;
else if (port >= TVIN_PORT_HDMI0 &&
port <= TVIN_PORT_HDMI7)
unstb_in = sm_p->hdmi_unstable_out_cnt;
else
unstb_in = other_unstable_out_cnt;
cnt = sizeof(struct tvin_sig_property_s);
/* must wait enough time for cvd signal lock */
if (sm_p->back_stable_cnt >= unstb_in) {
sm_p->back_stable_cnt = 0;
sm_p->state_cnt = 0;
if (sm_ops->get_fmt &&
sm_ops->get_sig_property) {
info->fmt =
sm_ops->get_fmt(fe);
/*sm_ops->get_sig_property(fe,*/
/*prop);*/
info->cfmt = prop->color_format;
memcpy(pre_prop, prop, cnt);
devp->parm.info.trans_fmt =
prop->trans_fmt;
devp->parm.info.is_dvi =
prop->dvi_info;
devp->parm.info.fps =
prop->fps;
}
if (sm_ops->fmt_config)
sm_ops->fmt_config(fe);
tvin_smr_init_counter(devp->index);
sm_p->state = TVIN_SM_STATUS_PRESTABLE;
sm_atv_prestable_fmt = info->fmt;
if (sm_debug_enable) {
pr_info("[smr.%d]unstable-->prestable",
devp->index);
pr_info("and format is %d(%s)\n",
info->fmt,
tvin_sig_fmt_str(info->fmt));
}
sm_print_nosig = 0;
sm_print_unstable = 0;
sm_print_fmt_nosig = 0;
sm_print_fmt_chg = 0;
sm_print_prestable = 0;
} else {
info->status = TVIN_SIG_STATUS_UNSTABLE;
if (sm_debug_enable &&
!sm_print_notsup) {
pr_info("[smr.%d] unstable --> not support\n",
devp->index);
sm_print_notsup = 1;
}
}
}
}
break;
case TVIN_SM_STATUS_PRESTABLE: {
bool nosig = false, fmt_changed = false;
unsigned int prestable_out_cnt = 0;
devp->unstable_flag = true;
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_AFE
if (IS_TVAFE_ATV_SRC(port) &&
(devp->flags & VDIN_FLAG_SNOW_FLAG))
tvafe_snow_config_clamp(0);
#endif
if (sm_ops->nosig(devp->frontend)) {
nosig = true;
if (sm_debug_enable && !(sm_print_prestable & 0x1)) {
pr_info("[smr.%d] warning: no signal\n",
devp->index);
sm_print_prestable |= 1;
}
}
if (sm_ops->fmt_changed(devp->frontend)) {
fmt_changed = true;
if (sm_debug_enable && !(sm_print_prestable & 0x2)) {
pr_info("[smr.%d] warning: format changed\n",
devp->index);
sm_print_prestable |= (1 << 1);
}
}
if (nosig || fmt_changed) {
++sm_p->state_cnt;
if (IS_TVAFE_ATV_SRC(port) &&
(devp->flags & VDIN_FLAG_SNOW_FLAG))
prestable_out_cnt = atv_prestable_out_cnt;
else
prestable_out_cnt = other_stable_out_cnt;
if (sm_p->state_cnt >= prestable_out_cnt) {
tvin_smr_init_counter(devp->index);
sm_p->state = TVIN_SM_STATUS_UNSTABLE;
if (sm_debug_enable)
pr_info("[smr.%d] prestable --> unstable\n",
devp->index);
sm_print_nosig = 0;
sm_print_notsup = 0;
sm_print_unstable = 0;
sm_print_prestable = 0;
break;
}
} else {
sm_p->state_cnt = 0;
if (IS_TVAFE_ATV_SRC(port) &&
(devp->flags & VDIN_FLAG_SNOW_FLAG)) {
++sm_p->exit_prestable_cnt;
if (sm_p->exit_prestable_cnt <
atv_prestable_out_cnt)
break;
else
sm_p->exit_prestable_cnt = 0;
}
sm_p->state = TVIN_SM_STATUS_STABLE;
info->status = TVIN_SIG_STATUS_STABLE;
vdin_update_prop(devp);
if (sm_debug_enable)
pr_info("[smr.%d] %ums prestable --> stable\n",
devp->index, jiffies_to_msecs(jiffies));
sm_print_nosig = 0;
sm_print_notsup = 0;
sm_print_prestable = 0;
}
break;
}
case TVIN_SM_STATUS_STABLE: {
bool nosig = false, fmt_changed = false;
unsigned int stable_out_cnt = 0;
unsigned int stable_fmt = 0;
devp->unstable_flag = true;
if (sm_ops->nosig(devp->frontend)) {
nosig = true;
if (sm_debug_enable && !sm_print_fmt_nosig) {
pr_info("[smr.%d] warning: no signal\n",
devp->index);
sm_print_fmt_nosig = 1;
}
}
if (sm_ops->fmt_changed(devp->frontend)) {
fmt_changed = true;
if (sm_debug_enable && !sm_print_fmt_chg) {
pr_info("[smr.%d] warning: format changed\n",
devp->index);
sm_print_fmt_chg = 1;
}
}
/* dynamic adjust cutwindow for atv test */
if (IS_TVAFE_SRC(port))
vdin_auto_de_handler(devp);
if (IS_TVAFE_SRC(port) && sm_ops->get_sig_property) {
sm_ops->get_sig_property(devp->frontend, prop);
devp->parm.info.fps = prop->fps;
}
if (nosig || fmt_changed /* || !pll_lock */) {
++sm_p->state_cnt;
if (IS_TVAFE_ATV_SRC(port) &&
(devp->flags & VDIN_FLAG_SNOW_FLAG))
stable_out_cnt = sm_p->atv_stable_out_cnt;
else if ((port >= TVIN_PORT_HDMI0) &&
(port <= TVIN_PORT_HDMI7))
stable_out_cnt = hdmi_stable_out_cnt;
else
stable_out_cnt = other_stable_out_cnt;
/*add for atv snow*/
if (sm_p->state_cnt >= atv_stable_fmt_check_cnt &&
IS_TVAFE_ATV_SRC(port) &&
(devp->flags & VDIN_FLAG_SNOW_FLAG))
atv_stable_fmt_check_enable = 1;
if (sm_p->state_cnt >= stable_out_cnt) {
tvin_smr_init_counter(devp->index);
sm_p->state = TVIN_SM_STATUS_UNSTABLE;
if (sm_debug_enable)
pr_info("[smr.%d] stable --> unstable\n",
devp->index);
sm_print_nosig = 0;
sm_print_notsup = 0;
sm_print_unstable = 0;
sm_print_fmt_nosig = 0;
sm_print_fmt_chg = 0;
sm_print_prestable = 0;
atv_stable_fmt_check_enable = 0;
}
} else {
/*add for atv snow*/
if (IS_TVAFE_ATV_SRC(port) &&
atv_stable_fmt_check_enable &&
(devp->flags & VDIN_FLAG_SNOW_FLAG) &&
(sm_ops->get_fmt && sm_ops->get_sig_property)) {
sm_p->state_cnt = 0;
stable_fmt = sm_ops->get_fmt(fe);
if (sm_atv_prestable_fmt != stable_fmt &&
stable_fmt != TVIN_SIG_FMT_NULL) {
/*cvbs in*/
sm_ops->get_sig_property(fe, prop);
memcpy(pre_prop, prop,
sizeof(struct
tvin_sig_property_s));
devp->parm.info.trans_fmt =
prop->trans_fmt;
devp->parm.info.is_dvi =
prop->dvi_info;
devp->parm.info.fps =
prop->fps;
info->fmt = stable_fmt;
atv_stable_fmt_check_enable = 0;
if (sm_debug_enable)
pr_info("[smr.%d] stable fmt changed:0x%x-->0x%x\n",
devp->index,
sm_atv_prestable_fmt,
stable_fmt);
sm_atv_prestable_fmt = stable_fmt;
}
}
sm_p->state_cnt = 0;
signal_chg |= vdin_hdmirx_fmt_chg_detect(devp);
if (signal_chg)
tvin_sigchg_event_process(devp, signal_chg);
}
/* check unreliable vsync interrupt */
if (devp->unreliable_vs_cnt != devp->unreliable_vs_cnt_pre) {
devp->unreliable_vs_cnt_pre = devp->unreliable_vs_cnt;
if (sm_debug_enable & 0x2)
vdin_dump_vs_info(devp);
}
break;
}
case TVIN_SM_STATUS_NULL:
default:
sm_p->state = TVIN_SM_STATUS_NOSIG;
break;
}
if (devp->flags & VDIN_FLAG_DEC_OPENED) {
if (sm_p->sig_status != info->status) {
sm_p->sig_status = info->status;
devp->event_info.event_sts = TVIN_SIG_CHG_STS;
devp->pre_event_info.event_sts =
devp->event_info.event_sts;
vdin_send_event(devp, devp->event_info.event_sts);
wake_up(&devp->queue);
}
}
signal_status = sm_p->sig_status;
}
/*
* tvin state machine routine init
*
*/
void tvin_smr_init(int index)
{
sm_dev[index].sig_status = TVIN_SIG_STATUS_NULL;
sm_dev[index].state = TVIN_SM_STATUS_NULL;
sm_dev[index].atv_stable_out_cnt = atv_stable_out_cnt;
sm_dev[index].atv_unstable_in_cnt = atv_unstable_in_cnt;
sm_dev[index].back_nosig_max_cnt = back_nosig_max_cnt;
sm_dev[index].atv_unstable_out_cnt = atv_unstable_out_cnt;
sm_dev[index].hdmi_unstable_out_cnt = hdmi_unstable_out_cnt;
tvin_smr_init_counter(index);
}
enum tvin_sm_status_e tvin_get_sm_status(int index)
{
return sm_dev[index].state;
}
EXPORT_SYMBOL(tvin_get_sm_status);
int tvin_get_av_status(void)
{
if (tvin_get_sm_status(0) == TVIN_SM_STATUS_STABLE)
return true;
return false;
}
EXPORT_SYMBOL_GPL(tvin_get_av_status);
void vdin_send_event(struct vdin_dev_s *devp, enum tvin_sg_chg_flg sts)
{
/*pr_info("%s :0x%x\n", __func__, sts);*/
/*devp->extcon_event->state = sts;*/
schedule_delayed_work(&devp->event_dwork, 0);
}