| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * drivers/amlogic/media/enhancement/amvecm/pattern_detection.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/mm.h> |
| #include <linux/init.h> |
| #include <linux/module.h> |
| |
| /* moudle headers */ |
| #include <linux/amlogic/media/vfm/vframe.h> |
| #include <linux/amlogic/media/amvecm/amvecm.h> |
| #ifdef CONFIG_AMLOGIC_MEDIA_TVIN_AFE |
| #include "../../vin/tvin/tvafe/tvafe.h" |
| #include "../../vin/tvin/tvafe/tvafe_vbi.h" |
| #endif |
| #include "arch/vpp_regs.h" |
| #include "amve.h" |
| #include "reg_helper.h" |
| #include "pattern_detection.h" |
| #ifdef CONFIG_AMLOGIC_MEDIA_TVIN_AFE |
| #include "pattern_detection_bar_settings.h" |
| #include "pattern_detection_face_settings.h" |
| #include "pattern_detection_corn_settings.h" |
| #endif |
| |
| #define PATTERN_INDEX_MAX PATTERN_MULTICAST |
| |
| int pattern_detect_debug; |
| #define pr_pattern_detect_dbg(fmt, args...)\ |
| do {\ |
| if (pattern_detect_debug > 0)\ |
| pr_info("Detect debug: [%s]: " fmt, __func__, ## args);\ |
| } while (0) |
| |
| int enable_pattern_detect = 1; |
| int detected_pattern = PATTERN_UNKNOWN; |
| int last_detected_pattern = PATTERN_UNKNOWN; |
| uint pattern_mask = PATTERN_MASK(PATTERN_75COLORBAR) | |
| PATTERN_MASK(PATTERN_SKIN_TONE_FACE) | |
| PATTERN_MASK(PATTERN_GREEN_CORN) | |
| PATTERN_MASK(PATTERN_MULTICAST); |
| |
| static uint pattern_param = PATTERN_PARAM_COUNT; |
| |
| static uint pattern0_param_info[PATTERN_PARAM_COUNT] = { |
| 5, 100, 830, 30, 110, 0x70000, 0xd0000, 0x158000, |
| 400, 40, 650, 50, 0x300000, 0x500000, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| }; |
| |
| static uint pattern1_param_info[PATTERN_PARAM_COUNT] = { |
| 850, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| }; |
| |
| static uint pattern2_param_info[PATTERN_PARAM_COUNT] = { |
| 80, 730, 180, 6, /* pal hue hist 14 ~ 17 */ |
| 80, 510, 340, 70, /* ntsc hue hist 14 ~ 17 */ |
| 59, 112, 133, 223, /* pal sat hist 0 ~ 3 */ |
| 290, 165, 16, 1, /* pal sat hist 4 ~ 7 */ |
| 83, 143, 204, 316, /* ntsc sat hist 0 ~ 3 */ |
| 221, 28, 3, 0, /* ntsc sat hist 4 ~ 7 */ |
| 50, 55, 900, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| }; |
| |
| /*saturation hist*/ |
| static uint pattern3_param_info[PATTERN_PARAM_COUNT] = { |
| 0x7e9000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| }; |
| |
| module_param_array(pattern0_param_info, uint, |
| &pattern_param, 0664); |
| MODULE_PARM_DESC(pattern0_param_info, "\n pattern0_param_info\n"); |
| |
| module_param_array(pattern1_param_info, uint, |
| &pattern_param, 0664); |
| MODULE_PARM_DESC(pattern1_param_info, "\n pattern1_param_info\n"); |
| |
| module_param_array(pattern2_param_info, uint, |
| &pattern_param, 0664); |
| MODULE_PARM_DESC(pattern2_param_info, "\n pattern2_param_info\n"); |
| |
| unsigned int mltcast_ratio1 = 9; |
| unsigned int mltcast_ratio2 = 8; |
| int mltcast_skip_en; |
| |
| static int default_pattern0_checker(struct vframe_s *vf) |
| { |
| pr_pattern_detect_dbg("check for pattern0\n"); |
| return 0; |
| } |
| |
| static int default_pattern1_checker(struct vframe_s *vf) |
| { |
| pr_pattern_detect_dbg("check for pattern1\n"); |
| return 0; |
| } |
| |
| static int default_pattern2_checker(struct vframe_s *vf) |
| { |
| pr_pattern_detect_dbg("check for pattern2\n"); |
| return 0; |
| } |
| |
| static int default_pattern3_checker(struct vframe_s *vf) |
| { |
| pr_pattern_detect_dbg("check for pattern3\n"); |
| return 0; |
| } |
| |
| static int default_pattern0_handler(struct vframe_s *vf, int flag) |
| { |
| pr_pattern_detect_dbg("pattern0 detected and handled\n"); |
| return 0; |
| } |
| |
| static int default_pattern1_handler(struct vframe_s *vf, int flag) |
| { |
| pr_pattern_detect_dbg("pattern1 detected and handled\n"); |
| return 0; |
| } |
| |
| static int default_pattern2_handler(struct vframe_s *vf, int flag) |
| { |
| pr_pattern_detect_dbg("pattern2 detected and handled\n"); |
| return 0; |
| } |
| |
| static int default_pattern3_handler(struct vframe_s *vf, int flag) |
| { |
| pr_pattern_detect_dbg("pattern3 detected and handled\n"); |
| return 0; |
| } |
| |
| static int default_pattern0_defaultloader(struct vframe_s *vf) |
| { |
| pr_pattern_detect_dbg("pattern0 load default setting\n"); |
| return 0; |
| } |
| |
| static int default_pattern1_defaultloader(struct vframe_s *vf) |
| { |
| pr_pattern_detect_dbg("pattern1 load default setting\n"); |
| return 0; |
| } |
| |
| static int default_pattern2_defaultloader(struct vframe_s *vf) |
| { |
| pr_pattern_detect_dbg("pattern2 load default setting\n"); |
| return 0; |
| } |
| |
| static int default_pattern3_defaultloader(struct vframe_s *vf) |
| { |
| pr_pattern_detect_dbg("pattern3 load default setting\n"); |
| return 0; |
| } |
| |
| static int pattern_index; |
| static struct pattern pattern_list[] = { |
| { |
| PATTERN_75COLORBAR, |
| pattern0_param_info, |
| NULL, |
| NULL, |
| default_pattern0_checker, |
| default_pattern0_defaultloader, |
| default_pattern0_handler, |
| }, |
| { |
| PATTERN_SKIN_TONE_FACE, |
| pattern1_param_info, |
| NULL, |
| NULL, |
| default_pattern1_checker, |
| default_pattern1_defaultloader, |
| default_pattern1_handler, |
| }, |
| { |
| PATTERN_GREEN_CORN, |
| pattern2_param_info, |
| NULL, |
| NULL, |
| default_pattern2_checker, |
| default_pattern2_defaultloader, |
| default_pattern2_handler, |
| }, |
| { |
| PATTERN_MULTICAST, |
| pattern3_param_info, |
| NULL, |
| NULL, |
| default_pattern3_checker, |
| default_pattern3_defaultloader, |
| default_pattern3_handler, |
| }, |
| }; |
| |
| #ifdef CONFIG_AMLOGIC_MEDIA_TVIN_AFE |
| static void tvafe_get_default_regmap(struct am_regs_s *p) |
| { |
| unsigned short i; |
| |
| for (i = 0; i < p->length; i++) { |
| switch (p->am_reg[i].type) { |
| case REG_TYPE_APB: |
| tvafe_reg_read(p->am_reg[i].addr << 2, |
| &p->am_reg[i].val); |
| p->am_reg[i].val = p->am_reg[i].val & |
| p->am_reg[i].mask; |
| break; |
| default: |
| break; |
| } |
| pr_info("Detect debug: [%s]: tvafe read default 0x%x = 0x%x\n", |
| __func__, p->am_reg[i].addr, |
| p->am_reg[i].val); |
| } |
| } |
| |
| static void am_get_default_regmap(struct am_regs_s *p) |
| { |
| unsigned short i; |
| |
| for (i = 0; i < p->length; i++) { |
| switch (p->am_reg[i].type) { |
| case REG_TYPE_CBUS: |
| p->am_reg[i].val = |
| aml_read_cbus(p->am_reg[i].addr) & |
| p->am_reg[i].mask; |
| break; |
| case REG_TYPE_INDEX_VPP_COEF: |
| WRITE_VPP_REG(VPP_CHROMA_ADDR_PORT, |
| p->am_reg[i].addr); |
| p->am_reg[i].val = |
| READ_VPP_REG(VPP_CHROMA_DATA_PORT) & |
| p->am_reg[i].mask; |
| break; |
| case REG_TYPE_OFFSET_VCBUS: |
| p->am_reg[i].val = |
| READ_VPP_REG(p->am_reg[i].addr) & |
| p->am_reg[i].mask; |
| break; |
| default: |
| break; |
| } |
| pr_info("Detect debug: [%s]: vpp read default 0x%x = 0x%x\n", |
| __func__, p->am_reg[i].addr, |
| p->am_reg[i].val); |
| } |
| } |
| |
| static void defaultsetting_get_regmap(int index) |
| { |
| struct setting_regs_s *cvd2_set = |
| pattern_list[pattern_index].cvd2_settings; |
| struct setting_regs_s *vpp_set = |
| pattern_list[pattern_index].vpp_settings; |
| struct am_regs_s *regs; |
| |
| pr_pattern_detect_dbg("enter\n"); |
| |
| if (cvd2_set && cvd2_set[index].length > 0) { |
| /* default setting is not ready, need to update it */ |
| if (cvd2_set[index].am_reg[0].val == 0xffffffff) { |
| regs = (struct am_regs_s *)(&cvd2_set[index]); |
| tvafe_get_default_regmap(regs); |
| } |
| } |
| |
| if (vpp_set && vpp_set[index].length > 0) { |
| /* default setting is not ready, need to update it */ |
| if (vpp_set[index].am_reg[0].val == 0xffffffff) { |
| regs = (struct am_regs_s *)(&vpp_set[index]); |
| am_get_default_regmap(regs); |
| } |
| } |
| pr_pattern_detect_dbg("leave\n"); |
| } |
| |
| static void setting_set_regmap(int index) |
| { |
| struct setting_regs_s *cvd2_set = |
| pattern_list[pattern_index].cvd2_settings; |
| struct setting_regs_s *vpp_set = |
| pattern_list[pattern_index].vpp_settings; |
| struct am_regs_s *regs; |
| |
| pr_pattern_detect_dbg("enter\n"); |
| |
| /* check if default setting is ready*/ |
| |
| if (cvd2_set) { |
| regs = (struct am_regs_s *)(&cvd2_set[index]); |
| tvafe_set_regmap(regs); |
| } |
| |
| if (vpp_set) { |
| regs = (struct am_regs_s *)(&vpp_set[index]); |
| am_set_regmap(regs); |
| } |
| |
| pr_pattern_detect_dbg("leave\n"); |
| } |
| |
| static int colorbar_hist_checker(struct vframe_s *vf) |
| { |
| /* check cm2 hist and dnlp apl value */ |
| int flag = -1; |
| int hue_hist[32], y_hist_total; |
| int i, apl, hist_total = 0; |
| int peak_hue_bin_sum = 0, bin2021, bin2526, bin32; |
| int apl_diff, bin1, bin2, bin5, bin910, bin16; |
| int bin8, bin24, bin26, bin3132, bin17; |
| int tolerence_zero, tolerence_hue; |
| int tolerence_hue2; |
| int tolerence_hue3a, tolerence_hue3b; |
| int tolerence_hue5; |
| int tolerence_hue_all, tolerence_luma, target_apl; |
| unsigned int tvafe_val, tolerance_tvafe, max_tvafe; |
| int bin56, bin1718, bin2122; |
| int peak_hue_bin_sum4 = 0; |
| int peak_hue_bin_sum5 = 0; |
| |
| tolerence_zero = pattern_list[pattern_index].pattern_param[0]; |
| tolerence_hue = pattern_list[pattern_index].pattern_param[1]; |
| tolerence_hue_all = pattern_list[pattern_index].pattern_param[2]; |
| tolerence_luma = pattern_list[pattern_index].pattern_param[3]; |
| target_apl = pattern_list[pattern_index].pattern_param[4]; |
| tolerance_tvafe = pattern_list[pattern_index].pattern_param[5]; |
| tolerence_hue2 = pattern_list[pattern_index].pattern_param[8]; |
| tolerence_hue3a = pattern_list[pattern_index].pattern_param[9]; |
| tolerence_hue3b = pattern_list[pattern_index].pattern_param[10]; |
| tolerence_hue5 = pattern_list[pattern_index].pattern_param[11]; |
| max_tvafe = pattern_list[pattern_index].pattern_param[13]; |
| |
| if (vf->source_type == VFRAME_SOURCE_TYPE_TUNER && |
| vf->source_mode == VFRAME_SOURCE_MODE_PAL) { |
| tolerance_tvafe = pattern_list[pattern_index].pattern_param[7]; |
| } else if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_NTSC) { |
| tolerance_tvafe = pattern_list[pattern_index].pattern_param[5]; |
| max_tvafe = pattern_list[pattern_index].pattern_param[12]; |
| } else if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_PAL) { |
| tolerance_tvafe = pattern_list[pattern_index].pattern_param[6]; |
| max_tvafe = pattern_list[pattern_index].pattern_param[13]; |
| } |
| |
| if (vf->source_type != VFRAME_SOURCE_TYPE_TUNER && |
| vf->source_type != VFRAME_SOURCE_TYPE_CVBS) |
| return flag; |
| |
| if (tvafe_clk_status == 0) |
| return flag; |
| |
| flag = 0; |
| |
| /* check cm2 hue/sat hist */ |
| for (i = 0; i <= 31; i++) { |
| hue_hist[i] = vf->prop.hist.vpp_hue_gamma[i]; |
| hist_total += hue_hist[i]; |
| } |
| hist_total = hist_total / 1000; |
| for (i = 0; i <= 31; i++) { |
| hue_hist[i] = hue_hist[i] / hist_total; |
| pr_pattern_detect_dbg("read hue_hist[%d]=%d, origin=%d\n", |
| i, |
| (int)hue_hist[i], |
| vf->prop.hist.vpp_hue_gamma[i]); |
| } |
| |
| /* |
| * check hue hist bin 5, bin 9+10, bin 16 |
| * bin 20+21, bin 25+26, bin 32 and sum |
| */ |
| bin1 = hue_hist[0]; |
| bin2 = hue_hist[1]; |
| bin5 = hue_hist[3] + hue_hist[4]; |
| bin56 = hue_hist[4] + hue_hist[5]; |
| bin8 = hue_hist[7]; |
| bin910 = hue_hist[8] + hue_hist[9] + hue_hist[10]; |
| bin16 = hue_hist[15]; |
| bin17 = hue_hist[16]; |
| bin1718 = hue_hist[16] + hue_hist[17]; |
| bin2021 = hue_hist[19] + hue_hist[20]; |
| bin2122 = hue_hist[20] + hue_hist[21]; |
| bin24 = hue_hist[23]; |
| bin2526 = hue_hist[24] + hue_hist[25] + hue_hist[26]; |
| bin26 = hue_hist[25]; |
| bin32 = hue_hist[31]; |
| bin3132 = hue_hist[29] + hue_hist[30] + hue_hist[31]; |
| peak_hue_bin_sum = hue_hist[1] + hue_hist[4] + hue_hist[8] + |
| hue_hist[9] + hue_hist[15] + hue_hist[19] + |
| hue_hist[20] + hue_hist[24] + hue_hist[25] + |
| hue_hist[31] + hue_hist[3] + hue_hist[10] + |
| hue_hist[26]; |
| peak_hue_bin_sum4 = bin2 + bin56 + bin910 + |
| bin1718 + bin2122 + bin2526; |
| peak_hue_bin_sum5 = bin1 + bin5 + bin8 + |
| bin910 + bin16 + bin2021 + bin24 + |
| bin26 + bin3132 + bin17; |
| pr_pattern_detect_dbg("bin2=%d, bin5=%d, bin910=%d, bin16=%d\n", |
| bin2, bin5, bin910, bin16); |
| pr_pattern_detect_dbg("bin2021=%d, bin2526=%d, bin32=%d\n", |
| bin2021, bin2526, bin32); |
| pr_pattern_detect_dbg("peak_hue_bin_sum=%d\n", |
| peak_hue_bin_sum); |
| |
| /* check apl */ |
| apl = vf->prop.hist.luma_sum / |
| (vf->prop.hist.height * vf->prop.hist.width); |
| apl_diff = (apl - target_apl) * 1000 / target_apl; |
| pr_pattern_detect_dbg("apl=%d, target_apl=%d\n", |
| apl, target_apl); |
| |
| /* check high 4 bin sum of dnlp histogram */ |
| y_hist_total = 0; |
| for (i = 60; i <= 63; i++) |
| y_hist_total += vf->prop.hist.vpp_gamma[i]; |
| pr_pattern_detect_dbg("high 4 luma bin sum=%d\n", |
| y_hist_total); |
| |
| /* read tvafe status register to check mixed pattern*/ |
| tvafe_reg_read(0x18d << 2, &tvafe_val); |
| pr_pattern_detect_dbg("tvafe status reg val=0x%x, check value= 0x%x\n", |
| tvafe_val, tolerance_tvafe); |
| |
| /* judge type 1, normal / mixed colorbar 7 color */ |
| if (peak_hue_bin_sum >= tolerence_hue_all && |
| bin5 >= tolerence_hue && |
| bin910 >= tolerence_hue && |
| bin16 >= tolerence_hue && |
| bin2021 >= tolerence_hue && |
| bin2526 >= tolerence_hue && |
| bin32 >= tolerence_hue) { |
| if (tvafe_val <= tolerance_tvafe) { |
| pr_pattern_detect_dbg("judge1 colorbar detected\n"); |
| flag = 2; |
| } else if (tvafe_val < max_tvafe) { |
| pr_pattern_detect_dbg("judge1 mixcolorbar detected\n"); |
| flag = 1; |
| } |
| } |
| if (flag > 0) |
| goto judge_finish; |
| |
| /* judge type 2, 4 grid, pink and blue*/ |
| /* judge2 is removed */ |
| |
| /* judge type 3, ega-64blue color */ |
| pr_pattern_detect_dbg("judge3......\n"); |
| if (peak_hue_bin_sum >= tolerence_hue_all && |
| bin5 >= tolerence_hue3a && |
| bin910 >= tolerence_hue3a && |
| bin16 >= tolerence_hue3a && |
| bin2021 >= tolerence_hue3a && |
| bin2526 >= tolerence_hue3a && |
| bin32 >= tolerence_hue3b) { |
| pr_pattern_detect_dbg("judge3 ega-64blue pattern detected\n"); |
| flag = 2; |
| } |
| if (flag > 0) |
| goto judge_finish; |
| |
| /* judge type 4, BDGH pattern */ |
| pr_pattern_detect_dbg("judge4......\n"); |
| pr_pattern_detect_dbg("bin2=%d, bin56=%d, bin910=%d, bin1718=%d\n", |
| bin2, bin56, bin910, bin1718); |
| pr_pattern_detect_dbg("bin2122=%d, bin2526=%d\n", |
| bin2122, bin2526); |
| pr_pattern_detect_dbg("peak_hue_bin_sum4=%d\n", |
| peak_hue_bin_sum4); |
| if (peak_hue_bin_sum4 >= tolerence_hue_all && |
| bin56 >= tolerence_hue && |
| bin910 >= tolerence_hue && |
| bin1718 >= tolerence_hue && |
| bin2122 >= tolerence_hue && |
| bin2526 >= tolerence_hue && |
| bin2 >= tolerence_hue) { |
| pr_pattern_detect_dbg("judge4 BDGH pattern detected\n"); |
| flag = 2; |
| } |
| if (flag > 0) |
| goto judge_finish; |
| |
| /* judge type 5, color BDGH pattern */ |
| pr_pattern_detect_dbg("judge5......\n"); |
| pr_pattern_detect_dbg("bin1=%d, bin5=%d, bin8=%d, bin910=%d\n", |
| bin1, bin5, bin8, bin910); |
| pr_pattern_detect_dbg("bin16=%d, bin2021=%d, bin24=%d\n", |
| bin16, bin2021, bin24); |
| pr_pattern_detect_dbg("bin26=%d, bin3132=%d\n", |
| bin26, bin3132); |
| pr_pattern_detect_dbg("peak_hue_bin_sum5=%d\n", |
| peak_hue_bin_sum5); |
| if (peak_hue_bin_sum5 >= tolerence_hue_all && |
| bin5 >= tolerence_hue5 && |
| bin8 >= tolerence_hue5 && |
| bin910 >= tolerence_hue5 && |
| bin16 >= tolerence_hue5 && |
| bin2021 >= tolerence_hue5 && |
| bin24 >= tolerence_hue5 && |
| bin26 >= tolerence_hue5 && |
| bin3132 >= tolerence_hue5 && |
| bin1 >= tolerence_hue5) { |
| pr_pattern_detect_dbg("judge5 color BDGH pattern detected\n"); |
| flag = 2; |
| } |
| if (flag > 0) |
| goto judge_finish; |
| |
| judge_finish: |
| if (flag <= 0) |
| pr_pattern_detect_dbg("colorbar is not detected\n"); |
| |
| return flag; |
| } |
| |
| static int colorbar_default_loader(struct vframe_s *vf) |
| { |
| pr_pattern_detect_dbg("enter\n"); |
| |
| if (tvafe_clk_status == 0) |
| return 0; |
| |
| if (vf->source_type == VFRAME_SOURCE_TYPE_TUNER && |
| vf->source_mode == VFRAME_SOURCE_MODE_NTSC) |
| defaultsetting_get_regmap(12); |
| else if (vf->source_type == VFRAME_SOURCE_TYPE_TUNER && |
| vf->source_mode == VFRAME_SOURCE_MODE_PAL) |
| defaultsetting_get_regmap(13); |
| else if (vf->source_type == VFRAME_SOURCE_TYPE_TUNER && |
| vf->source_mode == VFRAME_SOURCE_MODE_SECAM) |
| defaultsetting_get_regmap(14); |
| else if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_NTSC) |
| defaultsetting_get_regmap(15); |
| else if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_PAL) |
| defaultsetting_get_regmap(16); |
| else if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_SECAM) |
| defaultsetting_get_regmap(17); |
| |
| pr_pattern_detect_dbg("leave\n"); |
| return 0; |
| } |
| |
| static int colorbar_handler(struct vframe_s *vf, int flag) |
| { |
| static int last_flag = -1, last_type, last_mode; |
| |
| pr_pattern_detect_dbg("enter\n"); |
| |
| /* |
| * flag == 0, color bar is not detected, restore |
| * the dedault setting |
| * flag == 1, mixed color bar is detected, apply |
| * the specific setting |
| * flag == 2, color bar is detected, apply |
| * the specific setting |
| */ |
| |
| /* |
| * do not keep programming register |
| * return if detection result is not chaneged |
| */ |
| if (last_type == vf->source_type && |
| last_mode == vf->source_mode && |
| flag == last_flag) |
| return 0; |
| |
| if (vf->source_type == VFRAME_SOURCE_TYPE_TUNER && |
| vf->source_mode == VFRAME_SOURCE_MODE_NTSC) { |
| if (flag <= 0) |
| setting_set_regmap(12); |
| else if (flag == 1) |
| setting_set_regmap(6); |
| else if (flag == 2) |
| setting_set_regmap(0); |
| } else if (vf->source_type == VFRAME_SOURCE_TYPE_TUNER && |
| vf->source_mode == VFRAME_SOURCE_MODE_PAL) { |
| if (flag <= 0) |
| setting_set_regmap(13); |
| else if (flag == 1) |
| setting_set_regmap(7); |
| else if (flag == 2) |
| setting_set_regmap(1); |
| } else if (vf->source_type == VFRAME_SOURCE_TYPE_TUNER && |
| vf->source_mode == VFRAME_SOURCE_MODE_SECAM) { |
| if (flag <= 0) |
| setting_set_regmap(14); |
| else if (flag == 1) |
| setting_set_regmap(8); |
| else if (flag == 2) |
| setting_set_regmap(2); |
| } else if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_NTSC) { |
| if (flag <= 0) |
| setting_set_regmap(15); |
| else if (flag == 1) |
| setting_set_regmap(9); |
| else if (flag == 2) |
| setting_set_regmap(3); |
| } else if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_PAL) { |
| if (flag <= 0) |
| setting_set_regmap(16); |
| else if (flag == 1) |
| setting_set_regmap(10); |
| else if (flag == 2) |
| setting_set_regmap(4); |
| } else if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_SECAM) { |
| if (flag <= 0) |
| setting_set_regmap(17); |
| else if (flag == 1) |
| setting_set_regmap(11); |
| else if (flag == 2) |
| setting_set_regmap(5); |
| } |
| |
| last_flag = flag; |
| last_type = vf->source_type; |
| last_mode = vf->source_mode; |
| pr_pattern_detect_dbg("leave\n"); |
| return 0; |
| } |
| |
| static int face_hist_checker(struct vframe_s *vf) |
| { |
| /* check cm2 hue hist */ |
| int flag = -1; |
| int hue_hist[32]; |
| int i, hist_total = 0; |
| int skin_tone_hue_bin_sum = 0; |
| int tolerence_face; |
| |
| tolerence_face = pattern_list[pattern_index].pattern_param[0]; |
| |
| if (vf->source_type != VFRAME_SOURCE_TYPE_TUNER && |
| vf->source_type != VFRAME_SOURCE_TYPE_CVBS) |
| return flag; |
| |
| if (tvafe_clk_status == 0) |
| return flag; |
| |
| flag = 0; |
| |
| /* check cm2 hue hist */ |
| for (i = 0; i <= 31; i++) { |
| hue_hist[i] = vf->prop.hist.vpp_hue_gamma[i]; |
| hist_total += hue_hist[i]; |
| } |
| hist_total = hist_total / 1000; |
| for (i = 0; i <= 31; i++) { |
| hue_hist[i] = hue_hist[i] / hist_total; |
| pr_pattern_detect_dbg("read hue_hist[%d]=%d, origin=%d\n", |
| i, |
| (int)hue_hist[i], |
| vf->prop.hist.vpp_hue_gamma[i]); |
| } |
| |
| /* |
| * check skin tone hue hist |
| */ |
| skin_tone_hue_bin_sum = hue_hist[9] + hue_hist[10] + |
| hue_hist[11] + hue_hist[12]; |
| pr_pattern_detect_dbg("skin_tone_hue_bin_sum=%d\n", |
| skin_tone_hue_bin_sum); |
| |
| /* judge */ |
| if (skin_tone_hue_bin_sum >= tolerence_face) { |
| pr_pattern_detect_dbg("skin tone pattern detected\n"); |
| flag = 1; |
| } |
| |
| /* patch */ |
| if (cpu_after_eq(MESON_CPU_MAJOR_ID_TM2)) { |
| if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_PAL) |
| flag = 0; |
| } |
| |
| if (flag <= 0) |
| pr_pattern_detect_dbg("skin tone pattern is not detected\n"); |
| |
| return flag; |
| } |
| |
| static int face_default_loader(struct vframe_s *vf) |
| { |
| pr_pattern_detect_dbg("enter\n"); |
| |
| if (tvafe_clk_status == 0) |
| return 0; |
| |
| if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_NTSC) |
| defaultsetting_get_regmap(2); |
| else if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_PAL) |
| defaultsetting_get_regmap(3); |
| |
| pr_pattern_detect_dbg("leave\n"); |
| return 0; |
| } |
| |
| static int face_handler(struct vframe_s *vf, int flag) |
| { |
| static int last_flag = -1, last_type, last_mode; |
| |
| pr_pattern_detect_dbg("enter\n"); |
| |
| /* |
| * flag == 0, face is not detected, restore |
| * the dedault setting |
| * flag == 1, face is detected, apply |
| * the specific setting |
| */ |
| |
| /* |
| * do not keep programming register |
| * return if detection result is not chaneged |
| */ |
| if (last_type == vf->source_type && |
| last_mode == vf->source_mode && |
| flag == last_flag) |
| return 0; |
| |
| if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_NTSC) { |
| if (flag <= 0) |
| setting_set_regmap(2); |
| else if (flag == 1) |
| setting_set_regmap(0); |
| } else if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_PAL) { |
| if (flag <= 0) |
| setting_set_regmap(3); |
| else if (flag == 1) |
| setting_set_regmap(1); |
| } |
| |
| last_flag = flag; |
| last_type = vf->source_type; |
| last_mode = vf->source_mode; |
| pr_pattern_detect_dbg("leave\n"); |
| |
| return 0; |
| } |
| |
| static int corn_hist_checker(struct vframe_s *vf) |
| { |
| /* check cm2 hue hist */ |
| int flag = -1; |
| int hue_hist[32], sat_hist[32]; |
| int i, hist_total = 0, hist_sat_total = 0; |
| int hue_bin_sum = 0, diff_hue_bin14, diff_hue_bin15; |
| int diff_hue_bin16, diff_hue_bin17, diff_sat[8]; |
| int tolerence_hue, tolerence_sat, tolerence_sum; |
| int hue_offset = 0; /* for pal default */ |
| int sat_offset = 0; /* for pal default */ |
| |
| tolerence_hue = pattern_list[pattern_index].pattern_param[24]; |
| tolerence_sat = pattern_list[pattern_index].pattern_param[25]; |
| tolerence_sum = pattern_list[pattern_index].pattern_param[26]; |
| |
| if (vf->source_mode == VFRAME_SOURCE_MODE_NTSC) { |
| hue_offset = 4; |
| sat_offset = 8; |
| } |
| |
| if (vf->source_type != VFRAME_SOURCE_TYPE_TUNER && |
| vf->source_type != VFRAME_SOURCE_TYPE_CVBS) |
| return flag; |
| |
| if (tvafe_clk_status == 0) |
| return flag; |
| |
| flag = 0; |
| |
| /* check cm2 hue hist */ |
| for (i = 0; i <= 31; i++) { |
| hue_hist[i] = vf->prop.hist.vpp_hue_gamma[i]; |
| sat_hist[i] = vf->prop.hist.vpp_sat_gamma[i]; |
| hist_total += hue_hist[i]; |
| hist_sat_total += sat_hist[i]; |
| } |
| hist_total = hist_total / 1000; |
| hist_sat_total = hist_sat_total / 1000; |
| |
| for (i = 0; i <= 31; i++) { |
| hue_hist[i] = hue_hist[i] / hist_total; |
| pr_pattern_detect_dbg("read hue_hist[%d]=%d, origin=%d\n", |
| i, |
| (int)hue_hist[i], |
| vf->prop.hist.vpp_hue_gamma[i]); |
| } |
| |
| for (i = 0; i <= 31; i++) { |
| sat_hist[i] = sat_hist[i] / hist_sat_total; |
| pr_pattern_detect_dbg("read sat_hist[%d]=%d, origin=%d\n", |
| i, |
| (int)sat_hist[i], |
| vf->prop.hist.vpp_sat_gamma[i]); |
| } |
| |
| /* |
| * check hue hist |
| */ |
| diff_hue_bin14 = hue_hist[14] - |
| pattern_list[pattern_index].pattern_param[0 + hue_offset]; |
| diff_hue_bin15 = hue_hist[15] - |
| pattern_list[pattern_index].pattern_param[1 + hue_offset]; |
| diff_hue_bin16 = hue_hist[16] - |
| pattern_list[pattern_index].pattern_param[2 + hue_offset]; |
| diff_hue_bin17 = hue_hist[17] - |
| pattern_list[pattern_index].pattern_param[3 + hue_offset]; |
| hue_bin_sum = hue_hist[14] + hue_hist[15] + |
| hue_hist[16]; |
| pr_pattern_detect_dbg("diff_hue_bin14=%d, diff_hue_bin15=%d\n", |
| diff_hue_bin14, diff_hue_bin15); |
| pr_pattern_detect_dbg("diff_hue_bin16=%d, diff_hue_bin17=%d\n", |
| diff_hue_bin16, diff_hue_bin17); |
| pr_pattern_detect_dbg("hue_bin_sum=%d\n", |
| hue_bin_sum); |
| |
| /* |
| * check sat hist |
| */ |
| for (i = 0; i <= 7; i++) { |
| diff_sat[i] = sat_hist[i] - |
| pattern_list[pattern_index].pattern_param[8 + i + sat_offset]; |
| pr_pattern_detect_dbg("diff_sat[%d]=%d\n", |
| i, diff_sat[i]); |
| } |
| |
| /* judge */ |
| if (hue_bin_sum >= tolerence_sum) { |
| pr_pattern_detect_dbg("green corn pattern detected\n"); |
| flag = 1; |
| } |
| |
| /* patch */ |
| if (cpu_after_eq(MESON_CPU_MAJOR_ID_TM2)) { |
| if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_PAL) |
| flag = 0; |
| } |
| |
| if (flag <= 0) |
| pr_pattern_detect_dbg("green corn pattern is not detected\n"); |
| |
| return flag; |
| } |
| |
| static int corn_default_loader(struct vframe_s *vf) |
| { |
| pr_pattern_detect_dbg("enter\n"); |
| |
| if (tvafe_clk_status == 0) |
| return 0; |
| |
| if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_NTSC) |
| defaultsetting_get_regmap(2); |
| else if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_PAL) |
| defaultsetting_get_regmap(3); |
| |
| pr_pattern_detect_dbg("leave\n"); |
| return 0; |
| } |
| |
| static int corn_handler(struct vframe_s *vf, int flag) |
| { |
| static int last_flag = -1, last_type, last_mode; |
| |
| pr_pattern_detect_dbg("enter\n"); |
| |
| /* |
| * flag == 0, green corn is not detected, restore |
| * the dedault setting |
| * flag == 1, green corn is detected, apply |
| * the specific setting |
| */ |
| |
| /* |
| * do not keep programming register |
| * return if detection result is not chaneged |
| */ |
| if (last_type == vf->source_type && |
| last_mode == vf->source_mode && |
| flag == last_flag) |
| return 0; |
| |
| if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_NTSC) { |
| if (flag <= 0) |
| setting_set_regmap(2); |
| else if (flag == 1) |
| setting_set_regmap(0); |
| } else if (vf->source_type == VFRAME_SOURCE_TYPE_CVBS && |
| vf->source_mode == VFRAME_SOURCE_MODE_PAL) { |
| if (flag <= 0) |
| setting_set_regmap(3); |
| else if (flag == 1) |
| setting_set_regmap(1); |
| } |
| |
| last_flag = flag; |
| last_type = vf->source_type; |
| last_mode = vf->source_mode; |
| pr_pattern_detect_dbg("leave\n"); |
| |
| return 0; |
| } |
| |
| static struct di_reg_s di_patch[MULTICAST_REG_LEV] = { |
| { |
| {0xbf201080, 0xffffffff, 0xbf201080, 0xffffffff, |
| 0xf7ffc, 0x8000}, |
| }, |
| { |
| {0xbf3f10ff, 0xffffffff, 0xbf3f10ff, 0xffffffff, |
| 0xf7ffc, 0x8000}, |
| } |
| }; |
| |
| static int multicast_detcnt; |
| static int last_detflg; |
| static bool blue_mltcast_en; |
| static int multicast_color_checker(struct vframe_s *vf) |
| { |
| int flag = -1; |
| int hue_hist[32], sat_hist[32]; |
| int total_hue_hist = 0, total_sat_hist = 0; |
| int sat_flg = 0; |
| int hue_flg = 0; |
| int i; |
| int height, width, di_size; |
| |
| if (!(vf->di_pulldown & 0x8)) |
| return flag; |
| |
| height = vf->height; |
| width = vf->width; |
| |
| di_size = height * width / 2; |
| |
| for (i = 0; i < 32; i++) { |
| sat_hist[i] = vf->prop.hist.vpp_sat_gamma[i]; |
| hue_hist[i] = vf->prop.hist.vpp_hue_gamma[i]; |
| total_sat_hist += sat_hist[i]; |
| total_hue_hist += hue_hist[i]; |
| } |
| |
| pr_pattern_detect_dbg("total_sat_hist= %d, total_hue_hist= %d, ", |
| total_sat_hist, total_hue_hist); |
| pr_pattern_detect_dbg("sat_hist[0]= %d, hue_hist[31]= %d\n", |
| sat_hist[0], hue_hist[31]); |
| |
| /*suggestion from vlsi-liuyanling, |
| *1. sat_hist[0] > 99% total_sat |
| *2. total_hue < 7/1000 * size |
| */ |
| sat_flg = sat_hist[0] * 100 - total_sat_hist * 99; |
| hue_flg = 3840 * 2160 * 7 - total_hue_hist * 1000; |
| |
| if (sat_flg > 0 && hue_flg > 0 && |
| (vf->di_gmv > di_size * mltcast_ratio1 / 16) && |
| (vf->di_gmv < di_size * 15 / 16)) |
| flag = 1; |
| else if ((sat_flg > 0) && (hue_flg > 0) && |
| (vf->di_gmv > di_size * mltcast_ratio2 / 16) && |
| (vf->di_gmv < di_size * 15 / 16)) |
| flag = 2; |
| else if ((hue_hist[31] > total_hue_hist * 9 / 10) && |
| (vf->di_cm_cnt > di_size * mltcast_ratio1 / 16) && |
| (vf->di_gmv < di_size * 15 / 16)) |
| flag = 3; |
| else |
| flag = 0; |
| |
| if (flag > 0 && flag == last_detflg) |
| multicast_detcnt++; |
| else |
| multicast_detcnt = 0; |
| |
| last_detflg = flag; |
| |
| pr_pattern_detect_dbg("flag:%d, di_gmv:%d, di_size:%d, ", |
| flag, vf->di_gmv, di_size); |
| pr_pattern_detect_dbg("di_cm_cnt:%d, sat_flg:%d, hue_flg: %d\n", |
| vf->di_cm_cnt, sat_flg, hue_flg); |
| if (multicast_detcnt > 2) |
| multicast_detcnt = 2; |
| if (multicast_detcnt < 1 && mltcast_skip_en) |
| return 0; |
| |
| return flag; |
| } |
| |
| static int multicast_color_handler(struct vframe_s *vf, int flag) |
| { |
| static int last_flag = -1; |
| |
| pr_pattern_detect_dbg("enter\n"); |
| |
| if (flag == last_flag) |
| return 0; |
| |
| if (flag == 1) |
| di_api_mov_sel(1, &di_patch[1].val[0]); |
| else if (flag == 2) |
| di_api_mov_sel(1, &di_patch[0].val[0]); |
| else if ((flag == 3) && blue_mltcast_en) |
| di_api_mov_sel(1, &di_patch[1].val[0]); |
| else |
| di_api_mov_sel(0, NULL); |
| |
| last_flag = flag; |
| |
| pr_pattern_detect_dbg("leave\n"); |
| return 0; |
| } |
| |
| #endif |
| |
| /* public api */ |
| int pattern_detect_add_checker(int id, |
| int (*checker)(struct vframe_s *vf)) |
| { |
| pattern_list[id].checker = checker; |
| |
| return 0; |
| } |
| |
| int pattern_detect_add_defaultloader(int id, |
| int (*loader)(struct vframe_s *vf)) |
| { |
| pattern_list[id].default_loader = loader; |
| |
| return 0; |
| } |
| |
| int pattern_detect_add_handler(int id, |
| int (*handler)(struct vframe_s *vf, int flag)) |
| { |
| pattern_list[id].handler = handler; |
| |
| return 0; |
| } |
| |
| int pattern_detect_add_cvd2_setting_table(int id, |
| struct setting_regs_s *new_settings) |
| { |
| pattern_list[id].cvd2_settings = new_settings; |
| |
| return 0; |
| } |
| |
| int pattern_detect_add_vpp_setting_table(int id, |
| struct setting_regs_s *new_settings) |
| { |
| pattern_list[id].vpp_settings = new_settings; |
| |
| return 0; |
| } |
| |
| int init_pattern_detect(void) |
| { |
| /* install callback for color bar */ |
| #ifdef CONFIG_AMLOGIC_MEDIA_TVIN_AFE |
| pattern_detect_add_checker(PATTERN_75COLORBAR, |
| colorbar_hist_checker); |
| pattern_detect_add_defaultloader(PATTERN_75COLORBAR, |
| colorbar_default_loader); |
| pattern_detect_add_handler(PATTERN_75COLORBAR, |
| colorbar_handler); |
| pattern_detect_add_cvd2_setting_table(PATTERN_75COLORBAR, |
| colorbar_cvd2_settings); |
| pattern_detect_add_vpp_setting_table(PATTERN_75COLORBAR, |
| colorbar_vpp_settings); |
| |
| /* install callback for face pattern */ |
| pattern_detect_add_checker(PATTERN_SKIN_TONE_FACE, |
| face_hist_checker); |
| pattern_detect_add_handler(PATTERN_SKIN_TONE_FACE, |
| face_handler); |
| pattern_detect_add_defaultloader(PATTERN_SKIN_TONE_FACE, |
| face_default_loader); |
| pattern_detect_add_cvd2_setting_table(PATTERN_SKIN_TONE_FACE, |
| face_cvd2_settings); |
| |
| /* install callback for corn */ |
| pattern_detect_add_checker(PATTERN_GREEN_CORN, |
| corn_hist_checker); |
| pattern_detect_add_handler(PATTERN_GREEN_CORN, |
| corn_handler); |
| pattern_detect_add_defaultloader(PATTERN_GREEN_CORN, |
| corn_default_loader); |
| pattern_detect_add_cvd2_setting_table(PATTERN_GREEN_CORN, |
| corn_cvd2_settings); |
| /* install callback for multicast */ |
| pattern_detect_add_checker(PATTERN_MULTICAST, |
| multicast_color_checker); |
| pattern_detect_add_handler(PATTERN_MULTICAST, |
| multicast_color_handler); |
| pattern_detect_add_defaultloader(PATTERN_MULTICAST, |
| NULL); |
| pattern_detect_add_cvd2_setting_table(PATTERN_MULTICAST, |
| NULL); |
| #endif |
| |
| return 0; |
| } |
| |
| int pattern_detect(struct vframe_s *vf) |
| { |
| int flag = 0, i, rc = PATTERN_UNKNOWN; |
| |
| if (!vf) |
| return 0; |
| |
| if (enable_pattern_detect <= 0) |
| return 0; |
| |
| for (pattern_index = PATTERN_START; |
| pattern_index <= PATTERN_INDEX_MAX; |
| pattern_index++) { |
| if (!(pattern_mask & BIT(pattern_index))) |
| continue; |
| |
| /* load the default setting */ |
| if (pattern_list[pattern_index].default_loader) { |
| pr_pattern_detect_dbg("load default setting for %d:\n", |
| pattern_index); |
| pattern_list[pattern_index].default_loader(vf); |
| } |
| } |
| |
| for (pattern_index = PATTERN_START; |
| pattern_index <= PATTERN_INDEX_MAX; |
| pattern_index++) { |
| if (!(pattern_mask & (1 << pattern_index))) |
| continue; |
| |
| /* check the pattern */ |
| if (pattern_list[pattern_index].checker) { |
| pr_pattern_detect_dbg("check the pattern %d:\n", |
| pattern_index); |
| flag = |
| pattern_list[pattern_index].checker(vf); |
| } |
| |
| detected_pattern = |
| (flag > 0) ? pattern_index : PATTERN_UNKNOWN; |
| |
| /* handle the detected pattern */ |
| if (flag >= 0) { |
| /* call the handler if present */ |
| if (pattern_list[pattern_index].handler) { |
| pr_pattern_detect_dbg("process pattern %d:\n", |
| pattern_index); |
| pattern_list[pattern_index].handler(vf, flag); |
| } else { |
| /* handler is not present, send a notify */ |
| if (detected_pattern != |
| last_detected_pattern) { |
| pr_pattern_detect_dbg("notify user\n"); |
| } |
| } |
| |
| if (flag > 0) { |
| rc = pattern_index; |
| goto finish_detect; |
| } |
| } |
| } |
| |
| finish_detect: |
| for (i = pattern_index + 1; |
| i <= PATTERN_INDEX_MAX; |
| i++) { |
| if (pattern_list[i].handler) { |
| pr_pattern_detect_dbg("load default for %d:\n", i); |
| pattern_list[i].handler(vf, 0); |
| } |
| } |
| last_detected_pattern = detected_pattern; |
| if (pattern_detect_debug > 0) |
| pattern_detect_debug--; |
| |
| return rc; |
| } |