/* * Copyright (c) Hisilicon Technologies Co., Ltd.. 2019-2019. All rights reserved. * Description: hdmi driver main source file * Author: Hisilicon * Create: 2019-06-22 */ #include "drv_hdmitx.h" #include #include "soc_log.h" #include "soc_errno.h" #include "hal_hdmitx_io.h" #include "hal_hdmitx_cross.h" #include "drv_ioctl_hdmitx.h" #include "drv_base_ext.h" #include "drv_vo_dip.h" #include "drv_pdm_ext.h" #include "drv_sys_ext.h" #include "drv_hdmitx_common.h" #include "drv_hdmitx_proc.h" #include "drv_hdmitx_hdcp.h" #include "drv_hdmitx_connector.h" #include "drv_hdmitx_dsc.h" #include "drv_hdmitx_phy.h" #include "drv_hdmitx_frl.h" #include "drv_hdmitx_cec.h" #include "drv_hdmitx_tee.h" #include "drv_hdmitx_crg.h" #include "drv_hdmitx_controller.h" #include "drv_module_ext.h" #include "drv_hdmitx_fit.h" #define HDMITX_PORT_NUM 2 /* AVI Byte1 S [1:0],Scan information */ #define HDMITX_SCAN_NO_DATA 0 #define HDMITX_SCAN_OVER_SCAN 1 #define HDMITX_SCAN_UNDER_SCAN 2 /* Aspect ratio width&height define */ #define WIDTH_4 4 #define HEIGHT_3 3 #define WIDTH_16 16 #define HEIGHT_9 9 #define WIDTH_64 64 #define HEIGHT_27 27 #define WIDTH_256 256 #define HEIGHT_135 135 /* Index define */ #define INDEX_0 0 #define INDEX_1 1 #define INDEX_2 2 /* static hdr type */ #define SDR 0 #define HDR10 1 #define HLG 2 #define INFOFRAME_LENGTH 32 #define INFOFRAME_BUFFER_MAX 384 #define CUVA_INFOFRAM_PACKET_SIZE 96 /* * when we have more than one hdmi interface, vo/ao will use the id to index the * specified hdmi interface, so there is an table to maintain the mapping. */ #define MAX_HDMITX_ID 0x2 #define MAX_DTS_NAME_LEN 32 /* io ctrl cmd type define. */ #define UNKNOW_IOCTRL_CMD 0x0 #define HDMITX_IOCTRL_CMD 0x1 #define HDMITX_CEC_IOCTRL_CMD 0x2 #define HDMITX_HDCP_IOCTRL_CMD 0x3 #define HDR_OE_COMPAT_TIME 500 /* unit: ms */ #define HDMITX0_MAX_TMDS_CLOCK_RATE 600000 #define MAX_FVA_TMDS_CLK 594000u #define MAX_FVA_FRL_6G 928125u #define MAX_FVA_FRL_8G 1188000u #define MAX_FVA_FRL_10G 1485000u #define TRAINING_EVENT_LEN 20 struct hdmi_table { td_s32 hdmi_id; struct ext_hdmitx *hdmi; }; static td_s32 g_hdmi_cnt; static struct hdmi_table g_mapping[MAX_HDMITX_ID]; #undef MODULE_PARAM_PREFIX #define MODULE_PARAM_PREFIX "hdmi." #define hdmitx_init_notifier_head(name) \ do { \ (name)->head = TD_NULL; \ } while (0) static td_s32 hdmitx_lock(struct ext_hdmitx *hdmi) { td_s32 ret; ret = osal_sem_down_interruptible(&hdmi->ioctl_mutex); if (ret != TD_SUCCESS) { soc_log_err("osal_sem_down_interruptible fail!\n"); return TD_FAILURE; } return TD_SUCCESS; } static td_void hdmitx_unlock(struct ext_hdmitx *hdmi) { osal_sem_up(&hdmi->ioctl_mutex); } static td_s32 hdmitx_get_silence_flag_from_pdm(td_bool *silence) { td_s32 ret; ext_pdm_export_func *pdm_func = TD_NULL; ret = osal_exportfunc_get(SOC_ID_PDM, (td_void **)&pdm_func); if (ret != TD_SUCCESS) { soc_log_err("osal_exportfunc_get is failed"); return TD_FAILURE; } if ((pdm_func == TD_NULL) || (pdm_func->pdm_get_silence_flag == TD_NULL)) { soc_log_err("PDM function is NULL\n"); return TD_FAILURE; } ret = pdm_func->pdm_get_silence_flag(silence); if (ret != TD_SUCCESS) { soc_log_err("pdm_get_silence_flag is failed"); return TD_FAILURE; } return TD_SUCCESS; } static td_bool is_silence_mode(td_void) { td_s32 ret; td_bool silence = TD_FALSE; ext_chip_name_id chip_name_id; ext_drv_sys_get_chip_name_id(&chip_name_id); if (chip_name_id == CHIP_NAME_RESERVED17 || chip_name_id == CHIP_NAME_RESERVED19 || chip_name_id == CHIP_NAME_HI3751V811) { return TD_FALSE; } ret = hdmitx_get_silence_flag_from_pdm(&silence); if (ret != TD_SUCCESS) { hdmi_log_err("hdmitx get silence flag failed\n"); silence = TD_FALSE; } return silence; } static td_s32 hdmitx_notifier_register(struct ext_hdmitx *hdmi, struct hdmitx_notifier *notifier) { struct hdmitx_notifier **notifier_list = &hdmi->notifier_list.head; osal_mutex_lock(&hdmi->notifier_mutex); soc_log_info("hdmitx%u notifier register. priority=%d\n", hdmi->id, notifier->priority); while ((*notifier_list) != TD_NULL) { if (notifier->priority > (*notifier_list)->priority) { break; } notifier_list = &((*notifier_list)->next); } notifier->next = *notifier_list; *notifier_list = notifier; osal_mutex_unlock(&hdmi->notifier_mutex); return TD_SUCCESS; } static td_s32 hdmitx_notifier_unregister(struct ext_hdmitx *hdmi, const struct hdmitx_notifier *notifier) { struct hdmitx_notifier **notifier_list = &hdmi->notifier_list.head; osal_mutex_lock(&hdmi->notifier_mutex); soc_log_info("hdmitx%u notifier unregister.\n", hdmi->id); while ((*notifier_list) != TD_NULL) { if ((*notifier_list) == notifier) { *notifier_list = notifier->next; osal_mutex_unlock(&hdmi->notifier_mutex); return TD_SUCCESS; } notifier_list = &((*notifier_list)->next); } osal_mutex_unlock(&hdmi->notifier_mutex); return TD_FAILURE; } static td_s32 hdmitx_notifier_call(struct ext_hdmitx *hdmi, td_u32 val, const void *param) { td_s32 ret = TD_SUCCESS; struct hdmitx_notifier *notifier = TD_NULL; struct hdmitx_notifier **notifier_list = &hdmi->notifier_list.head; notifier = *notifier_list; osal_mutex_lock(&hdmi->notifier_mutex); while (notifier != TD_NULL) { if (notifier->hdmitx_notifier_call != TD_NULL) { ret = notifier->hdmitx_notifier_call(notifier, val, param); } else { soc_log_warn("notifier->hdmitx_notifier_call NULL PTR ERR\n"); } notifier = notifier->next; } osal_mutex_unlock(&hdmi->notifier_mutex); return ret; } static struct ext_hdmitx *get_hdmitx_by_id(td_u32 hdmi_id) { td_s32 i; for (i = 0; i < g_hdmi_cnt; i++) { if (g_mapping[i].hdmi_id == hdmi_id) { break; } } if (i == g_hdmi_cnt) { return TD_NULL; } return g_mapping[i].hdmi; } static td_bool hdmitx_is_dual_channel_mode(td_void) { td_bool ret0 = TD_FALSE; td_bool ret1 = TD_FALSE; td_u8 hdmitx_port = 0; ret0 = hdmitx_check_valid_by_pdm_params(DISP_INTF_HDMITX0, &hdmitx_port); ret1 = hdmitx_check_valid_by_pdm_params(DISP_INTF_HDMITX1, &hdmitx_port); return !!(ret0 && ret1); } static td_void hdmitx_sw_set_phy_switch(td_void) { struct hdmitx_phy *tmp_phy = TD_NULL; struct ext_hdmitx *hdmitx0 = get_hdmitx_by_id(DISP_INTF_HDMITX0); struct ext_hdmitx *hdmitx1 = get_hdmitx_by_id(DISP_INTF_HDMITX1); if (hdmitx0 == TD_NULL || hdmitx1 == TD_NULL || hdmitx0->controller == TD_NULL || hdmitx1->controller == TD_NULL) { soc_log_err("hdmitx0 or hdmitx1 is null!\n"); return; } /* sw phy switch */ tmp_phy = hdmitx0->phy; hdmitx0->phy = hdmitx1->phy; hdmitx1->phy = tmp_phy; /* read edid&hpd event noitfy */ drv_hdmitx_connector_enable_hpd(hdmitx0->controller, hdmitx0->controller->sub_module.connector); drv_hdmitx_connector_enable_hpd(hdmitx1->controller, hdmitx1->controller->sub_module.connector); } static td_void hdmitx_hw_update_gpio(td_void) { struct ext_hdmitx *hdmi = TD_NULL; td_u8 hdmitx_port = 0; td_u32 i; for (i = 0; i < MAX_HDMITX_ID; i++) { if (hdmitx_check_valid_by_pdm_params(i, &hdmitx_port) != TD_TRUE) { continue; } hdmi = get_hdmitx_by_id(hdmitx_port); if (hdmi == TD_NULL || hdmi->controller == TD_NULL) { soc_log_err("hdmi->gpio %d is null!\n", i); continue; } hal_ctrl_update_gpio(&hdmi->controller->reg_info); } } td_s32 hdmitx_set_phy_cross_en(td_bool enable) { td_bool support = !!(hdmitx_is_dual_channel_mode() && hal_cross_get_support()); if (support == TD_FALSE) { hdmi_log_err("cross mode not support dynamic switching.\n"); return SOC_ERR_HDMITX_FEATURE_NO_SUPPORT; } if (enable == hal_cross_get_phy_cross_en()) { hdmi_log_err("already %s mode.\n", enable ? "cross" : "no cross"); return TD_SUCCESS; } soc_log_info("cross mode enable:%s\n", enable ? "TRUE" : "FALSE"); /* hw switch set */ if (hal_cross_set_phy_cross_en(enable) != TD_SUCCESS) { hdmi_log_err("hal_cross_set_phy_cross_en failed.\n"); return TD_FAILURE; } /* gpio switch set */ hdmitx_hw_update_gpio(); /* sw switch set */ hdmitx_sw_set_phy_switch(); return TD_SUCCESS; } td_bool hdmitx_get_phy_cross(void) { td_bool enable = TD_FALSE; td_bool support = !!(hdmitx_is_dual_channel_mode() && hal_cross_get_support()); if (support == TD_FALSE) { hdmi_log_err("cross mode not support dynamic switching.\n"); return TD_FALSE; } enable = hal_cross_get_phy_cross_en(); soc_log_info("cross mode %d.\n", enable); return enable; } static td_s32 hdmitx_ioc_get_phy_switch_capability(td_void *arg) { td_bool *support = (td_bool *)arg; *support = !!(hdmitx_is_dual_channel_mode() && hal_cross_get_support()); soc_log_info("cross mode support:%s\n", *support ? "TRUE" : "FALSE"); return TD_SUCCESS; } static td_s32 hdmitx_ioc_set_phy_switch(const td_void *arg) { return hdmitx_set_phy_cross_en(*(td_bool *)arg); } /* define notifier_call_chain */ td_void hdmi_ao_notifiers(struct ext_hdmitx *hdmi, td_u32 val) { td_s32 ret; if (hdmi == TD_NULL) { hdmi_log_err("Input params is NULL pointer.\n"); return; } ret = hdmitx_notifier_call(hdmi, val, TD_NULL); if (ret) { hdmi_log_err("Ao notify fail.\n"); return; } } static td_void hdmi_clk_enable(const struct ext_hdmitx *hdmi, td_bool enable, td_bool lower_power) { #ifndef CONFIG_SOCT_FPGA_SUPPORT hal_crg_enable_clock(hdmi->crg, enable, lower_power); #endif } static td_void hdmitx_crg_reset(const struct ext_hdmitx *hdmi, td_bool reset) { #ifndef CONFIG_SOCT_FPGA_SUPPORT hal_crg_reset(hdmi->crg, reset); drv_hdmitx_phy_crg_reset(hdmi->phy, reset); #endif } static td_void hdmi_crg_mode_depth_set(const struct hdmitx_hw_config *config, td_u32 color_depth, struct crg_config_data *cfg) { if (config == TD_NULL || cfg == TD_NULL) { hdmi_log_err("mode_depth_set ptr is null !\n"); return; } switch (config->work_mode) { case HDMITX_WORK_MODE_TMDS: cfg->encoder_mode = 0; /* 0: tmds mode. */ break; case HDMITX_WORK_MODE_FRL: cfg->encoder_mode = 1; /* 1: frl mode. */ break; default: break; } if (color_depth == HDMITX_BPC_24) { cfg->color_depth = 8; /* color depth is 8bit. */ } else if (color_depth == HDMITX_BPC_30) { cfg->color_depth = 10; /* color depth is 10bit. */ } else if (color_depth == HDMITX_BPC_36) { cfg->color_depth = 12; /* color depth is 12bit. */ } else { cfg->color_depth = 8; /* the default color depth is 8bit. */ } } td_void hdmi_crg_set(const struct ext_hdmitx *hdmi) { #ifndef CONFIG_SOCT_FPGA_SUPPORT struct hdmitx_controller *controller = TD_NULL; struct hdmitx_soft_status *soft_status = TD_NULL; struct ext_display_mode *mode = TD_NULL; struct hdmitx_hw_config *config = TD_NULL; td_u32 color_depth; struct crg_config_data cfg; if (hdmi == TD_NULL || hdmi->controller == TD_NULL || hdmi->crg == TD_NULL) { soc_log_err("hdmi ptr or hdmi->controller is null!\n"); return; } controller = hdmi->controller; soft_status = &controller->soft_status; mode = &soft_status->mode; config = &soft_status->cur_hw_config; color_depth = mode->timing_data.out.color_depth; if (memset_s(&cfg, sizeof(cfg), 0x0, sizeof(cfg)) != EOK) { hdmi_log_err("memset_s fail.\n"); return; } hdmi_crg_mode_depth_set(config, color_depth, &cfg); cfg.dsc_enable = config->dsc_enable; cfg.hctotal = config->hcactive + config->hcblank; cfg.htotal = mode->timing_data.in.detail.htotal; if (mode->timing_data.in.fva_factor > 1) { cfg.pixel_rate = (td_s32)mode->timing_data.in.pixel_clock * mode->timing_data.in.fva_factor; } else { cfg.pixel_rate = (td_s32)mode->timing_data.in.pixel_clock; } cfg.prefer_ppll = soft_status->ppll_enable; cfg.yuv_420 = (mode->timing_data.out.color.color_format == HDMITX_COLOR_FORMAT_YCBCR420) ? TD_TRUE : TD_FALSE; hal_crg_config(hdmi->crg, &cfg); #endif } static td_void hdmi_phy_set(const struct ext_hdmitx *hdmi) { #ifndef CONFIG_SOCT_FPGA_SUPPORT struct hdmitx_controller *controller = hdmi->controller; struct ext_display_mode *mode = &controller->soft_status.mode; struct hdmitx_hw_config *config = &controller->soft_status.cur_hw_config; td_u32 color_format = mode->timing_data.out.color.color_format; td_u32 color_depth = mode->timing_data.out.color_depth; td_u32 pixel_clk; struct phy_tmds tmds; struct phy_frl frl; if (mode->timing_data.in.fva_factor > 1) { pixel_clk = mode->timing_data.in.pixel_clock * mode->timing_data.in.fva_factor; } else { pixel_clk = mode->timing_data.in.pixel_clock; } switch (config->work_mode) { case HDMITX_WORK_MODE_TMDS: if (color_format == HDMITX_COLOR_FORMAT_YCBCR420) { tmds.pixel_clk = pixel_clk / 2; /* pixel clock need divid by 2. */ tmds.yuv_420 = TD_TRUE; } else { tmds.pixel_clk = pixel_clk; tmds.yuv_420 = TD_FALSE; } tmds.tmds_clk = config->tmds_clock; tmds.color_depth = color_depth; tmds.pcb_len = PCB_LEN_1; tmds.tpll_enable = controller->soft_status.tpll_enable; drv_hdmitx_phy_configure_tmds(hdmi->phy, &tmds); break; case HDMITX_WORK_MODE_FRL: frl.frl_rate = config->cur_frl_rate; frl.pcb_len = PCB_LEN_1; frl.tpll_enable = controller->soft_status.tpll_enable; drv_hdmitx_phy_configure_frl(hdmi->phy, &frl); break; default: break; } #endif } td_s32 hdmi_phy_fcg_set(const struct ext_hdmitx *hdmi) { #ifndef CONFIG_SOCT_FPGA_SUPPORT struct hdmitx_controller *controller = TD_NULL; struct ext_display_mode *mode = TD_NULL; struct hdmitx_hw_config *config = TD_NULL; td_u32 color_format; td_u32 color_depth; td_u32 pixel_clk; struct phy_frl_tmds_clk frl_tmds_clk; if (hdmi == TD_NULL || hdmi->controller == TD_NULL || hdmi->phy == TD_NULL) { soc_log_err("hdmi ptr or hdmi->controller is null!\n"); return TD_FAILURE; } controller = hdmi->controller; mode = &controller->soft_status.mode; config = &controller->soft_status.cur_hw_config; color_format = mode->timing_data.out.color.color_format; color_depth = mode->timing_data.out.color_depth; if (mode->timing_data.in.fva_factor > 1) { pixel_clk = mode->timing_data.in.pixel_clock * mode->timing_data.in.fva_factor; } else { pixel_clk = mode->timing_data.in.pixel_clock; } if (color_format == HDMITX_COLOR_FORMAT_YCBCR420) { frl_tmds_clk.pixel_clk = pixel_clk / 2; /* pixel clock need divid by 2. */ frl_tmds_clk.yuv_420 = TD_TRUE; } else { frl_tmds_clk.pixel_clk = pixel_clk; frl_tmds_clk.yuv_420 = TD_FALSE; } frl_tmds_clk.dsc_enable = config->dsc_enable; frl_tmds_clk.hctotal = config->hcactive + config->hcblank; frl_tmds_clk.htotal = mode->timing_data.in.detail.htotal; frl_tmds_clk.rate = config->cur_frl_rate; frl_tmds_clk.color_depth = color_depth; drv_hdmitx_phy_configure_frl_tmds_clk(hdmi->phy, &frl_tmds_clk); #endif return TD_SUCCESS; } static td_void hdmi_prior_work_mode_select(const struct ext_hdmitx *hdmi, td_u32 vic) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_strategy_data *strategy_data = &controller->strategy_data; /* * VIC[108,219] is new define in CEA-861-G * which is apply to HDMI2.1, prior frl work mode. */ switch (strategy_data->work_mode_prior) { case HDMITX_PRIOR_FORCE_FRL: strategy_data->tmds_prior = TD_FALSE; break; case HDMITX_PRIOR_FORCE_TMDS: strategy_data->tmds_prior = TD_TRUE; break; case HDMITX_PRIOR_AUTO: default: strategy_data->tmds_prior = (vic > VIC_3840X2160P60_64_27 && vic < 0xff) ? TD_FALSE : TD_TRUE; break; } } static td_void hdmitx_mode_cfg_tmds(struct hdmitx_mode_config *mode_cfg, const struct source_capability *src_cap, const struct hdmitx_connector *connector, const struct hdmitx_display_info *display_info) { if (mode_cfg == TD_NULL || src_cap == TD_NULL || connector == TD_NULL || display_info == TD_NULL) { soc_log_err("mode_cfg_tmds ptr is null!\n"); return; } if (mode_cfg->tmds_clock > HDMITX14_MAX_TMDS_CLK) { if (connector->scdc.present && src_cap->hdmi.scdc_present) { mode_cfg->hdmi_mode = HDMITX_MODE_20; mode_cfg->tmds_scdc_en = TD_TRUE; } else { /* edid error */ mode_cfg->tmds_encode = TD_FALSE; } } else { mode_cfg->hdmi_mode = (display_info->has_hdmi_infoframe && src_cap->hdmi.hdmi_support) ? HDMITX_MODE_14 : HDMITX_MODE_DVI; if (mode_cfg->hdmi_mode == HDMITX_MODE_DVI) { hdmi_log_err("HDMITX_MODE_DVI does not support FVA.\n"); return; } mode_cfg->tmds_scdc_en = !!((mode_cfg->hdmi_mode == HDMITX_MODE_14) && connector->scdc.lte_340mcsc && src_cap->hdmi.scdc_lte_340mcsc); } } static td_void hdmitx_mode_cfg_frl(struct hdmitx_mode_config *mode_cfg, td_u32 factor) { if (mode_cfg == TD_NULL) { soc_log_err("mode_cfg_frl ptr is null!\n"); return; } if ((mode_cfg->tmds_clock > MAX_FVA_TMDS_CLK) && (mode_cfg->tmds_clock < MAX_FVA_FRL_6G)) { mode_cfg->frl.min_frl_rate = FRL_RATE_6G4L; mode_cfg->frl.frl_uncompress = TD_TRUE; mode_cfg->dsc.frl_compress = TD_FALSE; } else if ((mode_cfg->tmds_clock >= MAX_FVA_FRL_6G) && (mode_cfg->tmds_clock <= MAX_FVA_FRL_8G)) { mode_cfg->frl.min_frl_rate = FRL_RATE_8G4L; mode_cfg->frl.frl_uncompress = TD_TRUE; mode_cfg->dsc.frl_compress = TD_FALSE; } else if ((mode_cfg->tmds_clock > MAX_FVA_FRL_8G) && (mode_cfg->tmds_clock <= MAX_FVA_FRL_10G)) { mode_cfg->frl.min_frl_rate = FRL_RATE_10G4L; mode_cfg->frl.frl_uncompress = TD_TRUE; mode_cfg->dsc.frl_compress = TD_FALSE; } else { hdmi_log_err("Non-compression is not supported.FVA_factor is %u.\n", factor); } } static td_void hdmitx_get_fva_factor_config(struct hdmitx_controller *controller, struct hdmitx_mode_config *mode_cfg) { struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_connector *connector = sub_module->connector; struct hdmitx_display_info *display_info = &connector->display_info; struct source_capability *src_cap = &connector->src_cap; struct hdmitx_strategy_data *strategy_data = &controller->strategy_data; td_u32 factor = controller->soft_status.mode.timing_data.in.fva_factor; td_u64 temp; if (factor <= 1) { return; } temp = mode_cfg->tmds_clock * factor; mode_cfg->tmds_clock = (td_u32)temp; if (strategy_data->tmds_prior && mode_cfg->tmds_clock < osal_min(display_info->max_tmds_clock, src_cap->hdmi.max_tmds_clock)) { mode_cfg->tmds_encode = TD_TRUE; hdmitx_mode_cfg_tmds(mode_cfg, src_cap, connector, display_info); } else { mode_cfg->tmds_encode = TD_FALSE; mode_cfg->tmds_scdc_en = TD_FALSE; mode_cfg->hdmi_mode = HDMITX_MODE_21; strategy_data->tmds_prior = TD_FALSE; hdmitx_mode_cfg_frl(mode_cfg, factor); } } static td_void hdmitx_get_target_hw_config(struct hdmitx_controller *controller, struct hdmitx_mode_config *mode_cfg, struct hdmitx_hw_config *hw_cfg) { struct hdmitx_strategy_data *strategy_data = &controller->strategy_data; hdmitx_get_fva_factor_config(controller, mode_cfg); if (mode_cfg->tmds_encode && strategy_data->tmds_prior) { hw_cfg->work_mode = HDMITX_WORK_MODE_TMDS; } else if (mode_cfg->frl.frl_uncompress || mode_cfg->dsc.frl_compress) { hw_cfg->work_mode = HDMITX_WORK_MODE_FRL; } else { soc_log_warn("no tmds & flr cap,set default tmds mode.\n"); hw_cfg->work_mode = HDMITX_WORK_MODE_TMDS; } hw_cfg->tmds_scr_en = mode_cfg->tmds_scdc_en; hw_cfg->dsc_enable = mode_cfg->dsc.frl_compress && (!mode_cfg->frl.frl_uncompress || strategy_data->dsc_force); hw_cfg->dvi_mode = (mode_cfg->hdmi_mode == HDMITX_MODE_DVI) ? TD_TRUE : TD_FALSE; if (mode_cfg->frl.frl_uncompress && mode_cfg->dsc.frl_compress) { hw_cfg->cur_frl_rate = mode_cfg->frl.min_frl_rate; hw_cfg->max_frl_rate = mode_cfg->frl.max_frl_rate; hw_cfg->min_frl_rate = mode_cfg->frl.min_frl_rate; hw_cfg->min_dsc_frl_rate = mode_cfg->dsc.min_dsc_frl_rate; } else if (!mode_cfg->frl.frl_uncompress && mode_cfg->dsc.frl_compress) { hw_cfg->cur_frl_rate = mode_cfg->dsc.min_dsc_frl_rate; hw_cfg->max_frl_rate = mode_cfg->dsc.max_dsc_frl_rate; hw_cfg->min_frl_rate = mode_cfg->dsc.min_dsc_frl_rate; hw_cfg->min_dsc_frl_rate = mode_cfg->dsc.min_dsc_frl_rate; } else if (mode_cfg->frl.frl_uncompress && !mode_cfg->dsc.frl_compress) { hw_cfg->cur_frl_rate = mode_cfg->frl.min_frl_rate; hw_cfg->max_frl_rate = mode_cfg->frl.max_frl_rate; hw_cfg->min_frl_rate = mode_cfg->frl.min_frl_rate; hw_cfg->min_dsc_frl_rate = mode_cfg->frl.min_frl_rate; } if (strategy_data->force_frl_rate) { hw_cfg->cur_frl_rate = strategy_data->force_frl_rate; hw_cfg->max_frl_rate = strategy_data->force_frl_rate; hw_cfg->min_frl_rate = strategy_data->force_frl_rate; hw_cfg->min_dsc_frl_rate = strategy_data->force_frl_rate; } hw_cfg->tmds_clock = mode_cfg->tmds_clock; hw_cfg->bpp_target = mode_cfg->dsc.bpp_target; hw_cfg->slice_width = mode_cfg->dsc.slice_width; hw_cfg->hcactive = mode_cfg->dsc.hcactive; hw_cfg->hcblank = mode_cfg->dsc.hcblank; hw_cfg->slice_count = mode_cfg->dsc.slice_count; } /* * When the upper layer wants to set a specified video format. There are usually * more than one hw configurations which can support that video format. * For example: * 4K P30 444 ---> Both TMDS and FRL can be used. * 8K P60 420 ---> Both FRL and FRL + DSC can be used. * FRL rate 4*10G 4*12G can be used. * So we need to convert the video format to an applicable hw config. * The following decisions are made: * (1) FRL/TMDS mode. * (2) Available FRL rate range.(DSC or noDSC) * based on the FRL rate returned from the FRL training, we can decide DSC or * noDSC. */ static td_s32 hdmi_cur_hw_config_decision(const struct ext_hdmitx *hdmi) { td_u32 vic; td_u32 color_format; td_u32 color_depth; struct hdmitx_hw_config target_hw_config; struct hdmitx_mode_config mode_config; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_soft_status *soft_status = &controller->soft_status; struct hdmitx_hw_config *cur_config = &soft_status->cur_hw_config; struct hdmitx_hw_config *pre_config = &soft_status->pre_hw_config; struct hdmitx_timing_data *timing_data = &soft_status->mode.timing_data; if (memset_s(&mode_config, sizeof(mode_config), 0x0, sizeof(mode_config)) != EOK) { hdmi_log_err("memset_s fail.\n"); return TD_FAILURE; } if (memset_s(&target_hw_config, sizeof(target_hw_config), 0x0, sizeof(target_hw_config)) != EOK) { hdmi_log_err("memset_s fail.\n"); return TD_FAILURE; } vic = timing_data->in.vic; color_format = timing_data->out.color.color_format; color_depth = timing_data->out.color_depth; mode_config.band.vic = vic; mode_config.band.color_format = color_format; mode_config.band.color_depth = color_depth; if (!drv_hdmitx_connector_search_mode(controller->sub_module.connector, &mode_config)) { hdmi_log_info("hdmitx%d, mode(vic=%u,c_fmt=%d,c_depth=%d) isn't avail, please set an avail-mode\n", hdmi->id, mode_config.band.vic, mode_config.band.color_format, mode_config.band.color_depth); if (soft_status->force_output) { /* force output mode create. */ drv_hdmitx_connector_create_force_mode(controller->sub_module.connector, &mode_config); } else { return HDMI_ERR_MODE_NOT_AVAIL; } } hdmi_prior_work_mode_select(hdmi, vic); soc_log_info("tmds_prior=%d,tmds=%d,frl_u=%d,dsc=%d\n", controller->strategy_data.tmds_prior, mode_config.tmds_encode, mode_config.frl.frl_uncompress, mode_config.dsc.frl_compress); hdmitx_get_target_hw_config(controller, &mode_config, &target_hw_config); if (memcpy_s(pre_config, sizeof(*pre_config), cur_config, sizeof(*cur_config)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return TD_FAILURE; } if (memcpy_s(cur_config, sizeof(*cur_config), &target_hw_config, sizeof(target_hw_config)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return TD_FAILURE; } return TD_SUCCESS; } static td_u32 hdmi_get_switch_mode(const struct ext_hdmitx *hdmi) { td_u32 switch_mode = HDMITX_SWITCH_MODE_TMDS_2_TMDS; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_soft_status *soft_status = &controller->soft_status; struct hdmitx_hw_config *cur_config = &soft_status->cur_hw_config; struct hdmitx_hw_config *pre_config = &soft_status->pre_hw_config; if (cur_config->work_mode == HDMITX_WORK_MODE_TMDS && pre_config->work_mode == HDMITX_WORK_MODE_TMDS) { switch_mode = HDMITX_SWITCH_MODE_TMDS_2_TMDS; soft_status->frl_hdcp_event = TD_FALSE; } else if (cur_config->work_mode == HDMITX_WORK_MODE_FRL && pre_config->work_mode == HDMITX_WORK_MODE_TMDS) { switch_mode = HDMITX_SWITCH_MODE_TMDS_2_FRL; soft_status->frl_hdcp_event = TD_TRUE; } else if (cur_config->work_mode == HDMITX_WORK_MODE_TMDS && pre_config->work_mode == HDMITX_WORK_MODE_FRL) { switch_mode = HDMITX_SWITCH_MODE_FRL_2_TMDS; soft_status->frl_hdcp_event = TD_TRUE; } else if (cur_config->work_mode == HDMITX_WORK_MODE_FRL && pre_config->work_mode == HDMITX_WORK_MODE_FRL) { switch_mode = HDMITX_SWITCH_MODE_FRL_2_FRL; soft_status->frl_hdcp_event = TD_FALSE; } else { hdmi_log_err("hdmi id = %d, work_mode(switch_mode:%d->%d) is invalid, please check param input\n", hdmi->id, pre_config->work_mode, cur_config->work_mode); soft_status->frl_hdcp_event = TD_FALSE; } soc_log_info("switch_mode:%d->%d.frl_hdcp_event=%d\n", pre_config->work_mode, cur_config->work_mode, soft_status->frl_hdcp_event); return switch_mode; } static td_bool hdmi_need_frl_link_training(const struct ext_hdmitx *hdmi) { td_bool ret = TD_FALSE; struct hdmitx_controller *controller = TD_NULL; struct hdmitx_soft_status *soft_status = TD_NULL; struct hdmitx_sub_module *sub_module = TD_NULL; struct hdmitx_frl *frl = TD_NULL; struct frl_stat stat; struct frl_scdc scdc; struct hdmitx_hw_config *pre_config = TD_NULL; struct hdmitx_hw_config *cur_config = TD_NULL; if (hdmi == TD_NULL || hdmi->controller == TD_NULL) { hdmi_log_err("Input params is NULL pointer.\n"); return TD_FALSE; } controller = hdmi->controller; soft_status = &controller->soft_status; pre_config = &soft_status->pre_hw_config; cur_config = &soft_status->cur_hw_config; sub_module = &controller->sub_module; frl = sub_module->frl; if (memset_s(&stat, sizeof(stat), 0, sizeof(stat)) != EOK) { hdmi_log_err("memset_s fail.\n"); return TD_TRUE; } if (memset_s(&scdc, sizeof(scdc), 0, sizeof(scdc)) != EOK) { hdmi_log_err("memset_s fail.\n"); return TD_TRUE; } drv_hdmitx_frl_get_stat(frl, &stat); drv_hdmitx_frl_get_scdc(frl, &scdc); if (scdc.frl_rate >= cur_config->min_frl_rate && stat.event == TRAIN_EVENT_SUCCESS && cur_config->work_mode == HDMITX_WORK_MODE_FRL && pre_config->work_mode == HDMITX_WORK_MODE_FRL) { ret = TD_TRUE; } else { ret = TD_TRUE; } return ret; } static td_void hdmi_video_config(const struct ext_hdmitx *hdmi) { struct hdmitx_controller *controller = hdmi->controller; /* * Some of the infoframes should be set by vo, for example: * hdr/vrr/avi/vsif. we need to carefully check with vdp guys. */ drv_hdmitx_controller_set_infoframe(controller); drv_hdmitx_controller_set_video_path(controller); drv_hdmitx_controller_set_mode(controller); drv_hdmitx_controller_tmds_set_scramble(controller); } static td_bool hdmi_frl_start(const struct ext_hdmitx *hdmi) { td_bool ret = TD_FALSE; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_soft_status *soft_status = &controller->soft_status; struct hdmitx_strategy_data *strategy_data = &controller->strategy_data; struct hdmitx_frl *frl = sub_module->frl; struct frl_config config; struct hdmitx_hw_config *cur_config = &soft_status->cur_hw_config; if (memset_s(&config, sizeof(config), 0, sizeof(config)) != EOK) { hdmi_log_err("memset_s fail.\n"); return TD_FALSE; } drv_hdmitx_frl_get_config(frl, &config); config.frl_min_rate = cur_config->min_frl_rate; config.frl_max_rate = cur_config->max_frl_rate; config.dsc_frl_min_rate = cur_config->min_dsc_frl_rate; config.max_rate_proir = !strategy_data->min_rate_prior; config.scdc_present = drv_hdmitx_connector_is_scdc_present(sub_module->connector); drv_hdmitx_frl_set_config(frl, &config); if (drv_hdmitx_frl_start(frl) == TRAIN_EVENT_SUCCESS) { ret = TD_TRUE; } return ret; } static td_s32 hdmi_frl_mode_fast_switch(const struct ext_hdmitx *hdmi) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_soft_status *soft_status = &controller->soft_status; struct hdmitx_frl *frl = TD_NULL; struct hdmitx_hw_config *cur_config = TD_NULL; struct dsc_ctrl *dsc = TD_NULL; frl = sub_module->frl; dsc = sub_module->dsc; cur_config = &soft_status->cur_hw_config; hdmi_crg_set(hdmi); if (!soft_status->ppll_enable) { hdmi_phy_fcg_set(hdmi); } /* * Audio path is not configured here, it's expected to be done * in the ao backup(oe off callback). */ hdmi_video_config(hdmi); /* Deepcolor need disable when dsc enable in the hdmi2.1 spec. */ if (cur_config->dsc_enable) { drv_hdmitx_controller_disable_deepcolor_for_dsc(controller); } drv_hdmitx_controller_soft_reset(controller, TD_TRUE); if (cur_config->dsc_enable) { drv_hdmitx_controller_get_dsc_config(controller, &dsc->timing_info, &dsc->cfg_s); if (drv_hdmitx_dsc_config(dsc, &dsc->timing_info, &dsc->cfg_s) != TD_SUCCESS) { hdmi_log_err("hal_dsc_config fail\n"); return TD_FAILURE; } if (drv_hdmitx_dsc_enable(dsc) != TD_SUCCESS) { hdmi_log_err("hal_dsc_enable fail\n"); return TD_FAILURE; } /* worken enable */ drv_hdmitx_frl_set_worken(frl, TD_TRUE, TD_TRUE); drv_hdmitx_controller_set_dsc_emp(controller, TD_TRUE); } else { soc_log_info("Do not enable DSC\n"); /* worken enable */ drv_hdmitx_frl_set_worken(frl, TD_TRUE, TD_TRUE); } return TD_SUCCESS; } static td_void hdmi_compa_strategy_avmute(const struct ext_hdmitx *hdmi, td_bool enable) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct compat_avmute avmute; avmute.send_avmute_pkg = sub_module->connector->cur_compat_info.avmute.send_avmute_pkg; avmute.time_after_start = sub_module->connector->cur_compat_info.avmute.time_after_start; avmute.time_before_stop = sub_module->connector->cur_compat_info.avmute.time_before_stop; if (enable) { drv_hdmitx_controller_set_avmute(controller, TD_TRUE, avmute.send_avmute_pkg); if (avmute.time_before_stop > 0) { osal_msleep(avmute.time_before_stop); soc_log_info("set avmute wait %ums before stop\n", avmute.time_before_stop); } } else { if (avmute.time_after_start > 0) { osal_msleep(avmute.time_after_start); soc_log_info("wait %ums after start,then begin to clr avmute\n", avmute.time_after_start); } drv_hdmitx_controller_set_avmute(controller, TD_FALSE, avmute.send_avmute_pkg); } } static td_s32 hdmi_tmds_mode_stop(struct ext_hdmitx *hdmi) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; td_bool last_phy_is_on = TD_FALSE; last_phy_is_on = hal_phy_is_on(hdmi->phy); hdmi_ao_notifiers(hdmi, HDMI_BEFORE_OE_DISABLE); drv_hdmitx_controller_set_video_black(controller, TD_TRUE); hdmi_compa_strategy_avmute(hdmi, TD_TRUE); drv_hdmitx_hdcp_off(sub_module->hdcp); drv_hdmitx_phy_off(hdmi->phy); /* Close hdmi ouput, need to wait for one filed, maxtime = 1/24~50ms */ osal_msleep(50); if (last_phy_is_on) { osal_msleep(sub_module->connector->cur_compat_info.sink.reset_delay_time); } return TD_SUCCESS; } static td_s32 hdmi_frl_mode_stop(struct ext_hdmitx *hdmi) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_soft_status *soft_status = &controller->soft_status; struct hdmitx_frl *frl = TD_NULL; struct hdmitx_hw_config *pre_config = TD_NULL; struct compat_drv_info *cur_compat_info = &sub_module->connector->cur_compat_info; struct compat_sink_edid_info *edid_info = &cur_compat_info->edid_info; struct vendor_info *info = &edid_info->info; const td_u8 mfc_names[4] = "SHP"; /* ID Manufacturer Name size 4 */ frl = sub_module->frl; pre_config = &soft_status->pre_hw_config; hdmi_ao_notifiers(hdmi, HDMI_BEFORE_OE_DISABLE); drv_hdmitx_controller_set_video_black(controller, TD_TRUE); hdmi_compa_strategy_avmute(hdmi, TD_TRUE); drv_hdmitx_hdcp_off(sub_module->hdcp); if (pre_config->dsc_enable) { drv_hdmitx_controller_set_dsc_emp(controller, TD_FALSE); } if (osal_memncmp(mfc_names, info->mfc_name, sizeof(info->mfc_name)) == TD_SUCCESS) { if (soft_status->cur_hw_config.work_mode == HDMITX_WORK_MODE_TMDS || sub_module->connector->hotplug == HPD_PLUGOUT || hdmi->is_suspend == TD_TRUE) { drv_hdmitx_phy_off(hdmi->phy); drv_hdmitx_frl_stop(frl); } } else { drv_hdmitx_phy_off(hdmi->phy); drv_hdmitx_frl_stop(frl); } if (pre_config->dsc_enable) { if (drv_hdmitx_dsc_disable(sub_module->dsc) != TD_SUCCESS) { hdmi_log_err("hal_dsc_disable fail.\n"); return TD_FAILURE; } } /* Close hdmi ouput, need to wait for one filed, maxtime = 1/24~50ms */ osal_msleep(50); return TD_SUCCESS; } static td_s32 hdmi_tmds_mode_start(struct ext_hdmitx *hdmi) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_soft_status *soft_status = &controller->soft_status; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_timing_data *timing = TD_NULL; soc_log_info("hdmitx%u, pre work mode=%u\n", hdmi->id, soft_status->pre_hw_config.work_mode); /* * Audio path expected to be done in the * ao backup(oe off callback). */ hdmi_ao_notifiers(hdmi, HDMI_AFTER_OE_DISABLE); /* if frl mode switch tmds mode, must clear frl rate and ffe level by scdc. */ if (soft_status->pre_hw_config.work_mode == HDMITX_WORK_MODE_FRL) { drv_hdmitx_frl_clr_rate(sub_module->frl); } hdmi_crg_set(hdmi); hdmi_phy_set(hdmi); /* * Audio path is not configured here, it's expected to be done * in the ao backup(oe off callback). */ hdmi_video_config(hdmi); drv_hdmitx_controller_soft_reset(controller, TD_FALSE); drv_hdmitx_phy_on(hdmi->phy); if (soft_status->frl_hdcp_event) { soft_status->frl_hdcp_event = TD_FALSE; drv_hdmitx_hdcp_frl_event(sub_module->hdcp, soft_status->cur_hw_config.work_mode == HDMITX_WORK_MODE_FRL); } drv_hdmitx_controller_set_video_black(controller, TD_FALSE); hdmi_ao_notifiers(hdmi, HDMI_OE_ENABLE); hdmi_compa_strategy_avmute(hdmi, TD_FALSE); osal_msleep(200); /* Enable HDCP auth after phy on at least wait 200ms. */ if (soft_status->cur_hw_config.tmds_clock > 340000) { /* 340000KHz */ osal_msleep(sub_module->connector->cur_compat_info.hdcp.sink_ready_over340m); } else { osal_msleep(sub_module->connector->cur_compat_info.hdcp.sink_ready_under340m); } drv_hdmitx_hdcp_on(sub_module->hdcp); timing = &soft_status->mode.timing_data; hdmi_log_info("hdmitx%u,vic=%u(%uX%u@%u),c_fmt=%d,c_depth=%d, tmds start\n", hdmi->id, timing->in.vic, timing->in.detail.hactive, timing->in.detail.vactive, timing->in.detail.refresh_rate, timing->out.color.color_format, timing->out.color_depth); return TD_SUCCESS; } static td_void hdmi_frl_event_notify(const struct hdmitx_frl *frl) { struct hdmitx_controller *controller = TD_NULL; td_char event[TRAINING_EVENT_LEN] = "TRAIN_FAIL"; controller = frl->controller; if (controller == TD_NULL || controller->hdmitx_dev == TD_NULL) { hdmi_log_err("null pointer.\n"); return; } hdmi_sysfs_event(controller->hdmitx_dev, event, sizeof(event)); } static td_bool hdmi_frl_try_start(const struct ext_hdmitx *hdmi) { struct hdmitx_sub_module *sub_module = &hdmi->controller->sub_module; struct hdmitx_connector *connector = sub_module->connector; struct hdmitx_strategy_data *strategy_data = &hdmi->controller->strategy_data; td_bool ret = TD_FALSE; td_u32 training_time = 1; do { hdmi_log_info("training %u time\n", training_time); if (connector->hotplug == HPD_PLUGOUT) { osal_msleep(strategy_data->plugin_wait_time); hdmi_log_err("HPD out, need wait HPD in %ums\n", strategy_data->plugin_wait_time); } if (hdmi_frl_start(hdmi) == TD_TRUE) { ret = TD_TRUE; break; } training_time++; } while (training_time <= 3); /* If training fail, need 3 times */ /* If training 3 times is faill, need report event */ if (training_time > 3) { hdmi_frl_event_notify(sub_module->frl); hdmi_log_err("training failed(ret %d), time: %u\n", ret, training_time); } else { hdmi_log_info("training success, time: %u\n", training_time); } return ret; } static td_s32 hdmi_frl_mode_start(struct ext_hdmitx *hdmi) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_soft_status *soft_status = &controller->soft_status; struct hdmitx_hw_config *cur_config = TD_NULL; struct hdmitx_timing_data *timing = TD_NULL; cur_config = &soft_status->cur_hw_config; soc_log_info("hdmitx%u, pre work mode=%u\n", hdmi->id, soft_status->pre_hw_config.work_mode); /* * Audio path expected to be done in the * ao backup(oe off callback). */ hdmi_ao_notifiers(hdmi, HDMI_AFTER_OE_DISABLE); if (hdmi_need_frl_link_training(hdmi)) { if (!hdmi_frl_try_start(hdmi)) { hdmi_log_err("frl[%d] training fail.\n", hdmi->id); return TD_FAILURE; } } else { /* FRL fast switch need verify by test guys */ if (hdmi_frl_mode_fast_switch(hdmi)) { hdmi_log_err("frl fast switch fail.\n"); return TD_FAILURE; } } drv_hdmitx_controller_set_video_black(controller, TD_FALSE); hdmi_ao_notifiers(hdmi, HDMI_OE_ENABLE); hdmi_compa_strategy_avmute(hdmi, TD_FALSE); osal_msleep(200); /* Enable HDCP auth after wait 200ms. */ osal_msleep(sub_module->connector->cur_compat_info.hdcp.sink_ready_over340m); drv_hdmitx_hdcp_on(sub_module->hdcp); timing = &soft_status->mode.timing_data; hdmi_log_info("hdmitx%u,vic=%u(%uX%u@%u),c_fmt=%d,c_depth=%d, frl start\n", hdmi->id, timing->in.vic, timing->in.detail.hactive, timing->in.detail.vactive, timing->in.detail.refresh_rate, timing->out.color.color_format, timing->out.color_depth); return TD_SUCCESS; } static td_s32 hdmi_vo_timing_prepare(struct ext_hdmitx *hdmi) { td_s32 ret; td_u32 switch_mode; if (hdmi == TD_NULL) { hdmi_log_err("Input params is NULL pointer.\n"); return TD_FAILURE; } ret = hdmi_cur_hw_config_decision(hdmi); if (ret) { hdmi_log_err("This mode is not in the list!\n"); return ret; } hdmi->is_start_output = TD_FALSE; switch_mode = hdmi_get_switch_mode(hdmi); switch (switch_mode) { case HDMITX_SWITCH_MODE_TMDS_2_TMDS: case HDMITX_SWITCH_MODE_TMDS_2_FRL: ret = hdmi_tmds_mode_stop(hdmi); break; case HDMITX_SWITCH_MODE_FRL_2_TMDS: case HDMITX_SWITCH_MODE_FRL_2_FRL: ret = hdmi_frl_mode_stop(hdmi); break; default: hdmi_log_err("This switch mode is illegality!\n"); ret = HDMI_ERR_INPUT_PARAM_INVALID; break; } return ret; } td_void hdmi_sysfs_event(osal_dev *hdmitx_dev, td_char *event, td_u32 size) { td_char *envp[2] = {TD_NULL}; /* 2: second member of array, used as the end judgment */ if (size == 0) { hdmi_log_err("size is zero.\n"); return; } if (event == NULL || hdmitx_dev == TD_NULL) { hdmi_log_err("null pointer.\n"); return; } envp[0] = event; soc_log_info("event %s\n\n", event); osal_kobject_uevent_env(hdmitx_dev, OSAL_KOBJ_CHANGE, envp); } static td_bool is_mode_exceed_port20_cap(const struct ext_hdmitx *hdmi, const struct hdmitx_timing_data *timing) { td_u32 tmds_clk; struct hdmitx_controller *controller = hdmi->controller; tmds_clk = drv_hdmitx_modes_get_tmds_clk(timing->in.pixel_clock, timing->out.color_depth, timing->out.color.color_format); if ((!controller->is_hdmi21) && (tmds_clk > HDMITX0_MAX_TMDS_CLOCK_RATE * 1000)) { /* 1000 for unit conversion */ return TD_TRUE; } return TD_FALSE; } static td_void hdmi_hdr_compat_strategy(struct ext_hdmitx *hdmi, td_u32 new_hdr_mode_type) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_soft_status *soft_status = &controller->soft_status; td_bool change = TD_FALSE; if (soft_status->mode.hdr_data.hdr_mode_type == new_hdr_mode_type) { return; } if ((soft_status->mode.hdr_data.hdr_mode_type == HDMITX_HDR_MODE_STATIC_ST2084) && (new_hdr_mode_type != HDMITX_HDR_MODE_STATIC_ST2084)) { change = TD_TRUE; } if ((soft_status->mode.hdr_data.hdr_mode_type != HDMITX_HDR_MODE_STATIC_ST2084) && (new_hdr_mode_type == HDMITX_HDR_MODE_STATIC_ST2084)) { change = TD_TRUE; } if ((soft_status->mode.hdr_data.hdr_mode_type == HDMITX_HDR_MODE_STATIC_HLG) && (new_hdr_mode_type != HDMITX_HDR_MODE_STATIC_HLG)) { change = TD_TRUE; } if ((soft_status->mode.hdr_data.hdr_mode_type != HDMITX_HDR_MODE_STATIC_HLG) && (new_hdr_mode_type == HDMITX_HDR_MODE_STATIC_HLG)) { change = TD_TRUE; } if ((change) && (sub_module->connector->cur_compat_info.hdr.hdr_debug_mode == HDMI_HDR_DEBUG_MODE_OE)) { osal_delayedwork_schedule(&hdmi->work_compat_hdr, 0); } } static td_void hdmi_hdr_compat_timeout_handler(osal_delayedwork *work) { td_s32 ret; struct ext_hdmitx *hdmi = osal_container_of(work, struct ext_hdmitx, work_compat_hdr); if (hal_phy_is_on(hdmi->phy)) { ret = hdmi_tmds_mode_stop(hdmi); if (ret) { hdmi_log_err("stop tmds fail.\n"); return; } osal_delayedwork_schedule(&hdmi->work_compat_hdr, HDR_OE_COMPAT_TIME); } else { ret = hdmi_tmds_mode_start(hdmi); if (ret) { hdmi_log_err("start tmds fail.\n"); } } } static td_void hdmitx_set_vic_for_custom_timing(const struct ext_hdmitx *hdmi, struct ext_display_mode *display_mode) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_connector *connector = sub_module->connector; struct hdmi_detail detail; struct timing_detail_info *detail_info = &display_mode->timing_data.in.detail; struct hdmitx_in_data *in = &display_mode->timing_data.in; if (memset_s(&detail, sizeof(detail), 0, sizeof(detail)) != EOK) { hdmi_log_err("memset_s fail.\n"); return; } detail.field_rate = detail_info->refresh_rate / 1000; /* need divid by 1000. */ detail.h_total = detail_info->htotal; detail.h_active = detail_info->hactive; detail.h_back = detail_info->hback; detail.h_sync = detail_info->hsync; detail.h_front = detail_info->hfront; detail.v_total = detail_info->vtotal; detail.v_active = detail_info->vactive; detail.v_back = detail_info->vback; detail.v_sync = detail_info->vsync; detail.v_front = detail_info->vfront; detail.pixel_clock = in->pixel_clock; detail.h_pol = in->h_sync_pol; detail.v_pol = in->v_sync_pol; display_mode->timing_data.in.vic = drv_hdmitx_modes_get_vic_by_detail_info(connector, &detail); return; } static td_void hdmi_frl_pram_init_and_reset(const struct hdmitx_controller *controller) { const struct hdmitx_sub_module *sub_module = TD_NULL; if (controller == TD_NULL) { hdmi_log_err("Null ptr err!"); return; } sub_module = &controller->sub_module; if (sub_module == TD_NULL) { hdmi_log_err("Null ptr err!"); return; } hal_frl_init_mcu_code(&sub_module->frl->hal); hal_frl_clr_mcu_ddc_req(&sub_module->frl->hal); drv_hdmitx_controller_pwd_soft_reset(controller); drv_hdmitx_controller_aon_soft_reset(controller); } static td_void hdmi_vo_suspend(struct ext_hdmitx *hdmi, td_bool lower_power) { td_u32 work_mode; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_soft_status *soft_status = &controller->soft_status; hdmi_log_info("hdmitx%d suspend lower_power:%s\n", hdmi->id, lower_power ? "TRUE" : "FALSE"); hdmi->is_suspend = TD_TRUE; hdmi->is_start_output = TD_FALSE; work_mode = soft_status->cur_hw_config.work_mode; osal_delayedwork_cancel_sync(&hdmi->work_compat_hdr); /* * Suspend function need do follows steps to get better compatibility * step 1: Mask HPD interrupt */ drv_hdmitx_hdcp_suspend(sub_module->hdcp); drv_hdmitx_cec_suspend(sub_module->cec); drv_hdmitx_controller_set_hpd_irq_mask(controller, TD_TRUE); /* * step 2: Set black frame data * step 3: Set avmute packet and wait for three video field * step 4: Close hdmi output and wait for one video field */ if (work_mode == HDMITX_WORK_MODE_TMDS) { hdmi_tmds_mode_stop(hdmi); } else { hdmi_frl_mode_stop(hdmi); /* When the reset command is executed in frl mode on an Android * device, maybe no signal is output. it needs to cleanup frl rate. */ drv_hdmitx_frl_clr_rate(sub_module->frl); } /* step 5: Need to reset phy before disable phy clk */ drv_hdmitx_phy_reset(hdmi->phy); /* step 6: Disable hdmi clock */ hdmi_clk_enable(hdmi, TD_FALSE, lower_power); soc_log_info("hdmi%d suspend ok.\n", hdmi->id); } static td_s32 hdmi_vo_resume(struct ext_hdmitx *hdmi, td_bool lower_power) { td_s32 ret; td_u32 work_mode; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_connector *connector = sub_module->connector; struct hdmitx_timing_data *timing = TD_NULL; work_mode = controller->soft_status.cur_hw_config.work_mode; timing = &(controller->soft_status.mode.timing_data); hdmi_log_info("hdmitx%d resume lower_power:%s\n", hdmi->id, lower_power ? "TRUE" : "FALSE"); if (hal_cross_resume_phy_cross_en() != TD_SUCCESS) { hdmi_log_err("hal_cross_resume_phy_cross_en failed, hdmi_id = %u\n", hdmi->id); return TD_FAILURE; } /* * Rsesume function need do follows steps to get better compatibility * step 1: Enable hdmi clock */ hdmi_clk_enable(hdmi, TD_TRUE, lower_power); hdmitx_crg_reset(hdmi, TD_FALSE); hdmi->is_suspend = TD_FALSE; /* clear frl mcu PRAM when resume */ hdmi_frl_pram_init_and_reset(controller); hdmitx_hw_update_gpio(); drv_hdmitx_hdcp_resume(sub_module->hdcp); drv_hdmitx_cec_resume(sub_module->cec); /* step 2: Need wait more than 100ms to Read HPD status,and read edid */ drv_hdmitx_connector_enable_hpd(hdmi->controller, connector); /* step 3: Check video mode valid */ soc_log_info("hdmitx%u vic=%u,c_fmt=%d,c_depth=%d\n", hdmi->id, timing->in.vic, timing->out.color.color_format, timing->out.color_depth); if (is_mode_exceed_port20_cap(hdmi, timing) == TD_TRUE) { soc_log_warn("hdmitx%u not support 8k, pls try another one", hdmi->id); } ret = drv_hdmitx_connector_mode_validate(connector, timing); if (ret == TD_FALSE) { hdmi_log_info("hdmitx%u, mode(vic=%u,c_fmt=%d,c_depth=%d) isn't avail\n", hdmi->id, timing->in.vic, timing->out.color.color_format, timing->out.color_depth); } /* * step 4: Video mode config * step 5: Clear black data frame * step 6: Set clear avmute, clear avmute packet will be sent always */ #if (defined(CONFIG_SOCT_COMMON_KERNEL) || defined(CONFIG_SOCT_OH_KERNEL)) if (is_silence_mode()) { soc_log_info("silence mode, don't start output\n"); } else { if (work_mode == HDMITX_WORK_MODE_TMDS) { hdmi_tmds_mode_start(hdmi); } else { hdmi_frl_mode_start(hdmi); } } #endif hdmi->is_start_output = TD_TRUE; soc_log_info("hdmi%d resume ok.\n", hdmi->id); return TD_SUCCESS; } static td_s32 hdmi_vo_attach(const struct ext_hdmitx *hdmi, const td_void *data) { return TD_SUCCESS; } static td_s32 hdmi_vo_detach(const struct ext_hdmitx *hdmi, const td_void *data) { return TD_SUCCESS; } static td_s32 hdmi_vo_mode_validate(const struct ext_hdmitx *hdmi, td_u32 mode, struct ext_display_mode *display_mode) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_soft_status *soft_status = &controller->soft_status; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_connector *connector = sub_module->connector; struct hdmitx_timing_data *timing = TD_NULL; if (connector->hotplug == HPD_PLUGOUT) { hdmi_log_err("hdmitx%u, hpd is 0, please check cable whether is connected, and TV whether is powered on\n", hdmi->id); return soft_status->force_output ? DISPLAY_MODE_OK : HDMI_ERR_CABLE_PLUG_OUT; /* force output. */ } else if (connector->hotplug == HPD_DET_FAIL) { soc_log_warn("hdmitx%u, hotplug detect fail, hdmi port or cable may damaged\n", hdmi->id); } if ((td_u8)mode & TIMING_SET_MODE) { if (display_mode->timing_data.in.vic == 0) { hdmitx_set_vic_for_custom_timing(hdmi, display_mode); } timing = &display_mode->timing_data; soc_log_info("hdmitx%u, vic=%u(%ux%u),c_fmt=%d,c_depth=%d\n", hdmi->id, timing->in.vic, timing->in.detail.hactive, timing->in.detail.vactive, timing->out.color.color_format, timing->out.color_depth); if (is_mode_exceed_port20_cap(hdmi, timing)) { hdmi_log_err("hdmitx%u, current port not support 8k\n", hdmi->id); return soft_status->force_output ? DISPLAY_MODE_OK : HDMI_ERR_MODE_EXCEED_PORT20_CAP; /* force output. */ } if (!drv_hdmitx_connector_mode_validate(connector, timing)) { hdmi_log_err("hdmitx%u, mode(vic=%u(%ux%u),c_fmt=%d,c_depth=%d) isn't avail, sink may not support\n", hdmi->id, timing->in.vic, timing->in.detail.hactive, timing->in.detail.vactive, timing->out.color.color_format, timing->out.color_depth); return soft_status->force_output ? DISPLAY_MODE_OK : HDMI_ERR_MODE_NOT_AVAIL; /* force output. */ } } return DISPLAY_MODE_OK; } static td_s32 hdmi_vo_prepare_set_timing_mode(struct ext_hdmitx *hdmi, struct ext_display_mode *display_mode, struct hdmitx_soft_status *soft_status) { td_s32 ret; if (display_mode->timing_data.in.vic == 0) { hdmitx_set_vic_for_custom_timing(hdmi, display_mode); } if (memcpy_s(&soft_status->mode.timing_data, sizeof(soft_status->mode.timing_data), &display_mode->timing_data, sizeof(display_mode->timing_data)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return TD_FAILURE; } osal_delayedwork_cancel_sync(&hdmi->work_compat_hdr); ret = hdmi_vo_timing_prepare(hdmi); if (ret != TD_SUCCESS) { hdmi_log_err("hdmitx%d, vo timing set prepare fail.\n", hdmi->id); return ret; } return ret; } static td_s32 hdmi_vo_prepare_set_hdr_mode(struct ext_hdmitx *hdmi, const struct ext_display_mode *display_mode, struct hdmitx_soft_status *soft_status) { if (hal_phy_is_on(hdmi->phy) && (soft_status->cur_hw_config.work_mode == HDMITX_WORK_MODE_TMDS)) { hdmi_hdr_compat_strategy(hdmi, display_mode->hdr_data.hdr_mode_type); } if (memcpy_s(&soft_status->mode.hdr_data, sizeof(soft_status->mode.hdr_data), &display_mode->hdr_data, sizeof(display_mode->hdr_data)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return TD_FAILURE; } return TD_SUCCESS; } static td_s32 hdmi_vo_prepare_set_vrr_mode(const struct ext_display_mode *display_mode, struct hdmitx_soft_status *soft_status) { if (memcpy_s(&soft_status->mode.vrr_data, sizeof(soft_status->mode.vrr_data), &display_mode->vrr_data, sizeof(display_mode->vrr_data)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return TD_FAILURE; } return TD_SUCCESS; } static td_s32 hdmi_vo_prepare(struct ext_hdmitx *hdmi, td_u32 mode, struct ext_display_mode *display_mode) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_soft_status *soft_status = &controller->soft_status; struct hdmitx_sub_module *sub_module = &controller->sub_module; td_s32 ret = 0; hdmi_log_info("hdmitx%d, vo prepare.\n", hdmi->id); if (memset_s(&soft_status->dbg_info, sizeof(struct hdmitx_debug), HDMITX_DEBUG_INFO_INVALID, sizeof(struct hdmitx_debug)) != EOK) { hdmi_log_err("memset_s fail.\n"); return TD_FAILURE; } if (mode & TIMING_SET_MODE) { ret = hdmi_vo_prepare_set_timing_mode(hdmi, display_mode, soft_status); if (ret != TD_SUCCESS) { return ret; } } if (mode & HDR_SET_MODE) { ret = hdmi_vo_prepare_set_hdr_mode(hdmi, display_mode, soft_status); if (ret != TD_SUCCESS) { return ret; } } if (mode & VRR_SET_MODE) { ret = hdmi_vo_prepare_set_vrr_mode(display_mode, soft_status); if (ret != TD_SUCCESS) { return ret; } } if (soft_status->mode.timing_data.in.vic == sub_module->connector->cur_compat_info.force_dvi.vic) { soft_status->cur_hw_config.dvi_mode = TD_TRUE; } if (memcpy_s(&soft_status->pre_mode, sizeof(soft_status->pre_mode), &soft_status->mode, sizeof(soft_status->mode)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return TD_FAILURE; } return ret; } static td_void hdmi_fault_mode(const struct ext_hdmitx *hdmi) { td_bool ret; hdmitx_fault_color_info *color_info = TD_NULL; color_info = osal_kmalloc(sizeof(hdmitx_fault_color_info), OSAL_GFP_KERNEL); if (color_info == TD_NULL) { hdmi_log_err("osal_kmalloc fail.\n"); return; } hdmi_fault_get_color_info(hdmi, color_info); ret = hdmi_fault_check_color_info(color_info); if (ret != TD_TRUE) { goto free_fault_color_info; } hdmitx_report_color_info_fault(HDMITX_FAULT_CSC_INFO_NONMATCHING, color_info); free_fault_color_info: osal_kfree(color_info); color_info = TD_NULL; } static td_s32 hdmi_vo_mode_set(struct ext_hdmitx *hdmi, td_u32 mode) { td_s32 ret = 0; td_u32 switch_mode; if (hdmi == TD_NULL) { hdmi_log_err("Input params is NULL pointer.\n"); return TD_FAILURE; } hdmi_log_info("hdmitx%u mode set.\n", hdmi->id); hdmi->is_start_output = TD_TRUE; if (is_silence_mode()) { soc_log_info("silence mode, don't start output\n"); return TD_SUCCESS; } if (mode & TIMING_SET_MODE) { switch_mode = hdmi_get_switch_mode(hdmi); switch (switch_mode) { case HDMITX_SWITCH_MODE_TMDS_2_TMDS: case HDMITX_SWITCH_MODE_FRL_2_TMDS: ret = hdmi_tmds_mode_start(hdmi); break; case HDMITX_SWITCH_MODE_TMDS_2_FRL: case HDMITX_SWITCH_MODE_FRL_2_FRL: ret = hdmi_frl_mode_start(hdmi); break; default: ret = HDMI_ERR_INPUT_PARAM_INVALID; break; } } hdmi_fault_mode(hdmi); return ret; } static td_s32 hdmi_vo_display_on(const struct ext_hdmitx *hdmi) { return TD_SUCCESS; } static td_s32 hdmi_vo_display_off(struct ext_hdmitx *hdmi) { td_u32 work_mode; if (hdmi == TD_NULL || hdmi->controller == TD_NULL) { hdmi_log_err("Input params is NULL pointer.\n"); return TD_FAILURE; } hdmi_log_info("hdmitx%u display off.\n", hdmi->id); work_mode = hdmi->controller->soft_status.cur_hw_config.work_mode; if (work_mode == HDMITX_WORK_MODE_TMDS) { hdmi_tmds_mode_stop(hdmi); } else { hdmi_frl_mode_stop(hdmi); } hdmi->is_start_output = TD_FALSE; return TD_SUCCESS; } static struct hdmitx_vo_ops g_hdmi_vo_ops = { .suspend = hdmi_vo_suspend, .resume = hdmi_vo_resume, .attach = hdmi_vo_attach, .detach = hdmi_vo_detach, .prepare = hdmi_vo_prepare, .mode_validate = hdmi_vo_mode_validate, .mode_set = hdmi_vo_mode_set, .display_on = hdmi_vo_display_on, .display_off = hdmi_vo_display_off, }; static td_s32 hdmi_ao_get_eld(const struct ext_hdmitx *hdmi, const td_void *data, td_u8 *buf, size_t len) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_connector *connector = sub_module->connector; soc_log_info("hdmitx%u ao get eld.\n", hdmi->id); len = osal_min(sizeof(struct audio_property), len); if (memcpy_s(buf, len, &connector->audio, sizeof(connector->audio)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return TD_FAILURE; } return TD_SUCCESS; } static td_s32 hdmi_ao_hw_params(const struct ext_hdmitx *hdmi, const struct ao_attr *attr) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_soft_status *soft_status = &controller->soft_status; if (hdmi->is_suspend) { soc_log_warn("hdmitx%d is suspend!\n", hdmi->id); return TD_SUCCESS; } if (memcpy_s(&soft_status->attr, sizeof(soft_status->attr), attr, sizeof(*attr)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return TD_FAILURE; } hdmi_log_info("hdmitx%u, codec=%d sample_size=%d input_type=%d sample_rate=%d channels=%d\n", hdmi->id, attr->aud_codec, attr->aud_sample_size, attr->aud_input_type, attr->aud_sample_rate, attr->aud_channels); if (drv_hdmitx_controller_set_audio_path(controller) != TD_SUCCESS) { return TD_FAILURE; } return TD_SUCCESS; } static td_s32 hdmi_ao_hw_params_validate(const struct ext_hdmitx *hdmi, const struct ao_attr *attr) { return TD_SUCCESS; } static td_s32 hdmi_ao_hpd_detect(const struct ext_hdmitx *hdmi, td_u32 *status) { struct hdmitx_controller *controller = TD_NULL; struct hdmitx_sub_module *sub_module = TD_NULL; struct hdmitx_connector *connector = TD_NULL; controller = hdmi->controller; sub_module = &controller->sub_module; connector = sub_module->connector; if (connector->hotplug == HPD_PLUGIN || connector->hotplug == HPD_DET_FAIL) { *status = HDMI_HPD_PLUGIN; } else if (connector->hotplug == HPD_PLUGOUT) { *status = HDMI_HPD_PLUGOUT; } return TD_SUCCESS; } static td_s32 hdmi_ao_digital_mute(const struct ext_hdmitx *hdmi, const td_void *data, td_bool enable) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_connector *connector = controller->sub_module.connector; if (!drv_hdmitx_connector_audio_compat_check(connector)) { if (hdmi->is_suspend) { soc_log_warn("hdmitx%d is suspend!\n", hdmi->id); return TD_SUCCESS; } } hdmi_log_info("hdmitx%u ao digital mute. enable=%d.\n", hdmi->id, enable); drv_hdmitx_controller_audio_enable(controller, enable); return TD_SUCCESS; } static td_s32 hdmi_ao_register_notifier(struct ext_hdmitx *hdmi, struct hdmitx_notifier *notifier) { return hdmitx_notifier_register(hdmi, notifier); } static td_s32 hdmi_ao_unregister_notifier(struct ext_hdmitx *hdmi, const struct hdmitx_notifier *notifier) { return hdmitx_notifier_unregister(hdmi, notifier); } struct hdmitx_ao_ops g_hdmi_ao_ops = { .get_eld = hdmi_ao_get_eld, .hw_params_validate = hdmi_ao_hw_params_validate, .hw_params = hdmi_ao_hw_params, .digital_mute = hdmi_ao_digital_mute, .hpd_detect = hdmi_ao_hpd_detect, .register_notifier = hdmi_ao_register_notifier, .unregister_notifier = hdmi_ao_unregister_notifier, }; static td_s32 add_hdmitx_to_mapping(struct ext_hdmitx *hdmi) { if (g_hdmi_cnt >= MAX_HDMITX_ID) { hdmi_log_err("add hdmi mapping failed, hdmitx%u\n", hdmi->id); return TD_FAILURE; } g_mapping[g_hdmi_cnt].hdmi_id = (td_s32)hdmi->id; g_mapping[g_hdmi_cnt].hdmi = hdmi; g_hdmi_cnt++; return TD_SUCCESS; } static td_void delect_hdmitx_from_mapping(td_void) { if (g_hdmi_cnt >= MAX_HDMITX_ID || g_hdmi_cnt <= 0) { return; } g_hdmi_cnt--; g_mapping[g_hdmi_cnt].hdmi_id = -1; g_mapping[g_hdmi_cnt].hdmi = TD_NULL; } static td_s32 hdmitx_dev_open(td_void *private_data) { osal_dev **dev = TD_NULL; struct ext_hdmitx **priv = TD_NULL; struct ext_hdmitx *hdmi = TD_NULL; if (private_data == TD_NULL) { return TD_FAILURE; } dev = private_data; hdmi = osal_container_of(*dev, struct ext_hdmitx, hdmitx_dev); priv = private_data; *priv = hdmi; soc_log_info("id:%d\n", hdmi->id); return TD_SUCCESS; } static td_s32 hdmitx_dev_close(td_void *private_data) { struct ext_hdmitx **priv = TD_NULL; struct ext_hdmitx *hdmi = TD_NULL; struct hdmitx_controller *controller = TD_NULL; struct hdmitx_sub_module *sub_module = TD_NULL; if (private_data == TD_NULL) { return TD_FAILURE; } priv = private_data; hdmi = *priv; if (hdmi == TD_NULL) { return TD_FAILURE; } controller = hdmi->controller; if (controller == TD_NULL) { return TD_FAILURE; } sub_module = &controller->sub_module; drv_hdmitx_cec_release(sub_module->cec); drv_hdmitx_hdcp_stop_auth(sub_module->hdcp); return TD_SUCCESS; } static td_s32 hdmitx_ioc_read_edid(const struct ext_hdmitx *hdmi, td_void *arg) { hdmitx_ioctl_getedid *data = (hdmitx_ioctl_getedid *)arg; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_connector *connector = sub_module->connector; if (data->force) { drv_hdmitx_connector_edid_reset(connector); connector->edid_raw = drv_hdmitx_connector_get_edid(connector, sub_module->ddc); } if (connector->edid_raw == TD_NULL || data->edid_ptr == TD_NULL) { soc_log_info("hdmitx%u, edid ptr null err!\n", hdmi->id); return SOC_ERR_HDMITX_NULL_PTR; } if ((connector->edid_size == 0) || (data->length == 0)) { soc_log_info("hdmitx%u, edid len is 0 err!\n", hdmi->id); return SOC_ERR_HDMITX_INVALID_PARA; } if (data->length > (td_u32)connector->edid_size) { data->length = (td_u32)connector->edid_size; } if (osal_copy_to_user((td_void *)(td_uintptr_t)data->edid_ptr, connector->edid_raw, data->length)) { hdmi_log_err("hdmitx%u, osal_copy_to_user fail.\n", hdmi->id); return TD_FAILURE; } return TD_SUCCESS; } static td_void copy_vendor_info_to_manufacture_info(hdmi_manufacture_info *dst, const struct vendor_info *src) { if (memcpy_s(dst->mfrs_name, sizeof(dst->mfrs_name), src->mfc_name, sizeof(src->mfc_name)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return; } dst->product_code = src->product_code; dst->serial_number = src->serial_num; dst->week = src->mfc_week; dst->year = src->mfc_year; } static td_s32 hdmitx_ioc_get_sinkinfo(const struct ext_hdmitx *hdmi, td_void *arg) { hdmitx_ioctl_getsinkinfo *data = (hdmitx_ioctl_getsinkinfo *)arg; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_connector *connector = sub_module->connector; struct source_capability *src = &connector->src_cap; if (connector->hotplug == HPD_PLUGOUT) { hdmi_log_err("no sink connect.\n"); return TD_FAILURE; } if (memcpy_s(&data->audio, sizeof(data->audio), &connector->audio, sizeof(connector->audio)) != EOK) { goto err; } if (memcpy_s(&data->colorimetry, sizeof(data->colorimetry), &connector->color.colorimetry, sizeof(connector->color.colorimetry)) != EOK) { goto err; } data->disp_para.max_image_height = connector->display_info.height_cm; data->disp_para.max_image_width = connector->display_info.width_cm; if (memcpy_s(&data->dolby, sizeof(data->dolby), &connector->dolby, sizeof(connector->dolby)) != EOK) { goto err; } if (src->hdmi.cuva_support) { if (memcpy_s(&data->cuva, sizeof(data->cuva), &connector->cuva, sizeof(connector->cuva)) != EOK) { goto err; } } data->edid_version.revision = connector->base.revision; data->edid_version.version = connector->base.version; if (memcpy_s(&data->hdr, sizeof(data->hdr), &connector->hdr, sizeof(connector->hdr)) != EOK) { goto err; } if (memcpy_s(&data->latency, sizeof(data->latency), &connector->latency, sizeof(connector->latency)) != EOK) { goto err; } copy_vendor_info_to_manufacture_info(&data->manufacture, &connector->base.vendor); if (src->hdmi.vrr_support) { if (memcpy_s(&data->vrr, sizeof(data->vrr), &connector->vrr, sizeof(connector->vrr)) != EOK) { goto err; } } return TD_SUCCESS; err: hdmi_log_err("memcpy_s fail.\n"); return TD_FAILURE; } static td_s32 hdmitx_ioc_get_status(const struct ext_hdmitx *hdmi, td_void *arg) { hdmitx_ioctl_getstatus *data = (hdmitx_ioctl_getstatus *)arg; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_connector *connector = sub_module->connector; data->hotplug = (td_s32)connector->hotplug; data->rsen = drv_hdmitx_controller_get_rsen_status(controller); data->output_enable = hal_phy_is_on(hdmi->phy); return TD_SUCCESS; } static td_s32 hdmitx_ioc_get_availcapbility(const struct ext_hdmitx *hdmi, td_void *arg) { td_s32 ret; hdmitx_ioctl_getcapbility *data = (hdmitx_ioctl_getcapbility *)arg; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_connector *connector = sub_module->connector; data->mode_count = connector->probed_mode_cnt; data->dvi_only = !(connector->display_info.has_hdmi_infoframe && connector->src_cap.hdmi.hdmi_support); data->max_tmds_clock = osal_min(connector->display_info.max_tmds_clock, connector->src_cap.hdmi.max_tmds_clock); ret = drv_hdmitx_connector_get_native_mode(connector, &data->native_mode); if (ret != TD_SUCCESS) { hdmi_log_err("hdmitx%d get native mode fail.\n", hdmi->id); return TD_FAILURE; } ret = drv_hdmitx_connector_get_max_mode(connector, &data->max_mode); if (ret != TD_SUCCESS) { hdmi_log_err("hdmitx%d get max mode fail.\n", hdmi->id); return TD_FAILURE; } return ret; } static td_s32 hdmitx_ioc_get_availmodes(const struct ext_hdmitx *hdmi, td_void *arg) { td_s32 ret; hdmitx_ioctl_getmodes *data = (hdmitx_ioctl_getmodes *)arg; hdmi_avail_mode *mode_buf = TD_NULL; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_connector *connector = sub_module->connector; if ((connector->probed_mode_cnt == 0) || (data->mode_count == 0)) { hdmi_log_err("count is 0 err!\n"); return SOC_ERR_HDMITX_INVALID_PARA; } if (data->mode_count > connector->probed_mode_cnt) { data->mode_count = connector->probed_mode_cnt; } mode_buf = osal_kmalloc(data->mode_count * sizeof(hdmi_avail_mode), OSAL_GFP_KERNEL); if (mode_buf == TD_NULL) { hdmi_log_err("osal_kmalloc fail.\n"); return TD_FAILURE; } if (memset_s(mode_buf, sizeof(*mode_buf), 0, sizeof(*mode_buf)) != EOK) { hdmi_log_err("memset_s fail.\n"); osal_kfree(mode_buf); mode_buf = TD_NULL; return TD_FAILURE; } ret = drv_hdmitx_connector_get_availmode(connector, mode_buf, data->mode_count); if (ret != TD_SUCCESS) { hdmi_log_err("hdmitx%d get avail mode fail.\n", hdmi->id); osal_kfree(mode_buf); mode_buf = TD_NULL; return TD_FAILURE; } if (osal_copy_to_user((td_void *)(td_uintptr_t)data->mode_buf_ptr, mode_buf, data->mode_count * sizeof(hdmi_avail_mode)) != TD_SUCCESS) { osal_kfree(mode_buf); mode_buf = TD_NULL; hdmi_log_err("hdmitx%d osal_copy_to_user fail.\n", hdmi->id); return TD_FAILURE; } osal_kfree(mode_buf); mode_buf = TD_NULL; return TD_SUCCESS; } static td_s32 hdmitx_ioc_set_force_output(const struct ext_hdmitx *hdmi, const td_void *arg) { td_bool *enable = (td_bool *)arg; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_soft_status *soft_status = &controller->soft_status; soc_log_info("[hdmitx%d]set force output:%s\n", hdmi->id, *enable ? "TRUE" : "FALSE"); soft_status->force_output = *enable; return TD_SUCCESS; } static td_s32 hdmitx_ioc_get_compat_info(const struct ext_hdmitx *hdmi, struct compat_info *info) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; drv_hdmitx_connector_get_compat_info(sub_module->connector, info); return TD_SUCCESS; } static td_s32 hdmitx_ioc_set_compat_info(const struct ext_hdmitx *hdmi, const struct compat_info *info) { struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; return drv_hdmitx_connector_set_compat_info(sub_module->connector, info); } static td_u32 hdmitx_get_cmd_type(td_u32 cmd) { td_u32 cmd_type; switch (cmd) { case HDMITX_IOC_READ_EDID: case HDMITX_IOC_GET_SINKINFO: case HDMITX_IOC_GET_STATUS: case HDMITX_IOC_GET_AVAILCAPBILITY: case HDMITX_IOC_GET_AVAILMODES: case HDMITX_IOC_SET_FORCEOUTPUT: case HDMITX_IOC_GET_COMPAT_INFO: case HDMITX_IOC_SET_COMPAT_INFO: case HDMITX_IOC_SET_PHY_SWITCH: case HDMITX_IOC_GET_PHY_SWITCH_CAPABILITY: cmd_type = HDMITX_IOCTRL_CMD; break; case HDMITX_IOC_CEC_OPEN: case HDMITX_IOC_CEC_CLOSE: case HDMITX_IOC_CEC_READ_EVENTS: case HDMITX_IOC_CEC_GET_STATUS: case HDMITX_IOC_CEC_SET_DEVICE_TYPE: case HDMITX_IOC_CEC_TRANSMIT: cmd_type = HDMITX_CEC_IOCTRL_CMD; break; case HDMITX_IOC_HDCP_LOADKEY: case HDMITX_IOC_HDCP_SET_SRM: case HDMITX_IOC_HDCP_GETCAPABILITY: case HDMITX_IOC_HDCP_SET_REAUTH: case HDMITX_IOC_HDCP_START: case HDMITX_IOC_HDCP_STOP: case HDMITX_IOC_HDCP_GETSTATUS: cmd_type = HDMITX_HDCP_IOCTRL_CMD; break; default: cmd_type = UNKNOW_IOCTRL_CMD; break; } return cmd_type; } static td_s32 hdmitx_ioctl_process(const struct ext_hdmitx *hdmi, td_u32 cmd, td_void *arg) { td_s32 ret; struct compat_info *info = TD_NULL; if (arg == TD_NULL) { hdmi_log_err("null ptr err!"); return SOC_ERR_HDMITX_NULL_PTR; } switch (cmd) { case HDMITX_IOC_READ_EDID: ret = hdmitx_ioc_read_edid(hdmi, arg); break; case HDMITX_IOC_GET_SINKINFO: ret = hdmitx_ioc_get_sinkinfo(hdmi, arg); break; case HDMITX_IOC_GET_STATUS: ret = hdmitx_ioc_get_status(hdmi, arg); break; case HDMITX_IOC_GET_AVAILCAPBILITY: ret = hdmitx_ioc_get_availcapbility(hdmi, arg); break; case HDMITX_IOC_GET_AVAILMODES: ret = hdmitx_ioc_get_availmodes(hdmi, arg); break; case HDMITX_IOC_SET_FORCEOUTPUT: ret = hdmitx_ioc_set_force_output(hdmi, arg); break; case HDMITX_IOC_GET_COMPAT_INFO: info = (struct compat_info *)arg; ret = hdmitx_ioc_get_compat_info(hdmi, info); break; case HDMITX_IOC_SET_COMPAT_INFO: info = (struct compat_info *)arg; ret = hdmitx_ioc_set_compat_info(hdmi, info); break; case HDMITX_IOC_SET_PHY_SWITCH: ret = hdmitx_ioc_set_phy_switch(arg); break; case HDMITX_IOC_GET_PHY_SWITCH_CAPABILITY: ret = hdmitx_ioc_get_phy_switch_capability(arg); break; default: ret = TD_FAILURE; break; } return ret; } static td_s32 hdmitx_cec_ioctl_process(const struct ext_hdmitx *hdmi, td_u32 cmd, td_void *arg) { td_s32 ret; cec_events *event = TD_NULL; cec_status *status = TD_NULL; td_u8 device_type; cec_msg *msg = TD_NULL; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; if (arg == TD_NULL && cmd != HDMITX_IOC_CEC_OPEN && cmd != HDMITX_IOC_CEC_CLOSE) { hdmi_log_err("null ptr err!"); return TD_FAILURE; } switch (cmd) { case HDMITX_IOC_CEC_OPEN: ret = drv_hdmitx_cec_open(sub_module->cec); break; case HDMITX_IOC_CEC_CLOSE: drv_hdmitx_cec_close(sub_module->cec); ret = TD_SUCCESS; break; case HDMITX_IOC_CEC_READ_EVENTS: event = (cec_events *)arg; ret = drv_hdmitx_cec_read_events(sub_module->cec, event); break; case HDMITX_IOC_CEC_GET_STATUS: status = (cec_status *)arg; ret = drv_hdmitx_cec_get_status(sub_module->cec, status); break; case HDMITX_IOC_CEC_SET_DEVICE_TYPE: device_type = *(td_u8 *)arg; ret = drv_hdmitx_cec_set_device_type(sub_module->cec, device_type); break; case HDMITX_IOC_CEC_TRANSMIT: msg = (cec_msg *)arg; ret = drv_hdmitx_cec_transmit(sub_module->cec, msg); break; default: ret = TD_FAILURE; break; } return ret; } static td_s32 hdmitx_hdcp_ioctl_process(const struct ext_hdmitx *hdmi, td_u32 cmd, td_void *arg) { td_s32 ret; td_u32 mode; td_u32 reauth_times; td_u32 key_ver; struct hdcp_cap *hdcp_cap = TD_NULL; struct hdcp_usr_status *hdcp_status = TD_NULL; hdcp_srm_info *drv_srm = TD_NULL; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; if (arg == TD_NULL && cmd != HDMITX_IOC_HDCP_STOP) { hdmi_log_err("null ptr err!"); return TD_FAILURE; } switch (cmd) { case HDMITX_IOC_HDCP_LOADKEY: key_ver = *(td_u32 *)arg; ret = drv_hdmitx_hdcp_loadkey(sub_module->hdcp, key_ver); break; case HDMITX_IOC_HDCP_SET_SRM: drv_srm = (hdcp_srm_info *)arg; ret = drv_hdmitx_hdcp_set_srm(sub_module->hdcp, drv_srm); break; case HDMITX_IOC_HDCP_GETCAPABILITY: hdcp_cap = (struct hdcp_cap *)arg; ret = drv_hdmitx_hdcp_get_cap(sub_module->hdcp, hdcp_cap); break; case HDMITX_IOC_HDCP_SET_REAUTH: reauth_times = *(td_u32 *)arg; ret = drv_hdmitx_hdcp_set_reauth_times(sub_module->hdcp, reauth_times); break; case HDMITX_IOC_HDCP_START: mode = *(td_u32 *)arg; ret = drv_hdmitx_hdcp_start_auth(sub_module->hdcp, mode); break; case HDMITX_IOC_HDCP_STOP: drv_hdmitx_hdcp_stop_auth(sub_module->hdcp); ret = TD_SUCCESS; break; case HDMITX_IOC_HDCP_GETSTATUS: hdcp_status = (struct hdcp_usr_status *)arg; ret = drv_hdmitx_hdcp_get_user_status(sub_module->hdcp, hdcp_status); break; default: ret = TD_FAILURE; break; } return ret; } static td_s32 hdmitx_ioctl(td_u32 cmd, td_void *arg, td_void *private_data) { td_s32 ret; td_u32 cmd_type; struct ext_hdmitx **priv = TD_NULL; struct ext_hdmitx *hdmi = TD_NULL; if (private_data == TD_NULL) { hdmi_log_err("null ptr err!"); return TD_FAILURE; } priv = private_data; if (*priv == TD_NULL) { hdmi_log_err("null ptr err!"); return TD_FAILURE; } hdmi = *priv; cmd_type = hdmitx_get_cmd_type(cmd); switch (cmd_type) { case HDMITX_IOCTRL_CMD: ret = hdmitx_lock(hdmi); if (ret != TD_SUCCESS) { soc_log_err("lock hdmitx_mutex failed"); return ret; } ret = hdmitx_ioctl_process(hdmi, cmd, arg); hdmitx_unlock(hdmi); break; case HDMITX_CEC_IOCTRL_CMD: ret = hdmitx_cec_ioctl_process(hdmi, cmd, arg); break; case HDMITX_HDCP_IOCTRL_CMD: ret = hdmitx_hdcp_ioctl_process(hdmi, cmd, arg); break; default: ret = TD_FAILURE; break; } return ret; } static osal_ioctl_cmd g_hdmitx_cmd_list[] = { { HDMITX_IOC_READ_EDID, hdmitx_ioctl }, { HDMITX_IOC_GET_SINKINFO, hdmitx_ioctl }, { HDMITX_IOC_GET_STATUS, hdmitx_ioctl }, { HDMITX_IOC_GET_AVAILCAPBILITY, hdmitx_ioctl }, { HDMITX_IOC_GET_AVAILMODES, hdmitx_ioctl }, { HDMITX_IOC_HDCP_LOADKEY, hdmitx_ioctl }, { HDMITX_IOC_HDCP_SET_SRM, hdmitx_ioctl }, { HDMITX_IOC_HDCP_GETCAPABILITY, hdmitx_ioctl }, { HDMITX_IOC_HDCP_SET_REAUTH, hdmitx_ioctl }, { HDMITX_IOC_HDCP_START, hdmitx_ioctl }, { HDMITX_IOC_HDCP_STOP, hdmitx_ioctl }, { HDMITX_IOC_HDCP_GETSTATUS, hdmitx_ioctl }, { HDMITX_IOC_CEC_OPEN, hdmitx_ioctl }, { HDMITX_IOC_CEC_CLOSE, hdmitx_ioctl }, { HDMITX_IOC_CEC_READ_EVENTS, hdmitx_ioctl }, { HDMITX_IOC_CEC_GET_STATUS, hdmitx_ioctl }, { HDMITX_IOC_CEC_SET_DEVICE_TYPE, hdmitx_ioctl }, { HDMITX_IOC_CEC_TRANSMIT, hdmitx_ioctl }, { HDMITX_IOC_SET_FORCEOUTPUT, hdmitx_ioctl }, { HDMITX_IOC_GET_COMPAT_INFO, hdmitx_ioctl }, { HDMITX_IOC_SET_COMPAT_INFO, hdmitx_ioctl }, { HDMITX_IOC_SET_PHY_SWITCH, hdmitx_ioctl }, { HDMITX_IOC_GET_PHY_SWITCH_CAPABILITY, hdmitx_ioctl }, }; static osal_fileops g_hdmi_fops = { .open = hdmitx_dev_open, .read = TD_NULL, .write = TD_NULL, .llseek = TD_NULL, .release = hdmitx_dev_close, .poll = TD_NULL, .mmap = TD_NULL, .cmd_list = g_hdmitx_cmd_list, .cmd_cnt = CMD_ID_HDMITX_MAX, }; static td_void hdmitx_device_struct_deinit(struct ext_hdmitx *hdmi) { osal_kfree(hdmi); } static td_void hdmitx_software_deinit(struct ext_hdmitx *hdmi) { osal_sem_destroy(&hdmi->ioctl_mutex); osal_delayedwork_cancel_sync(&hdmi->work_compat_hdr); osal_delayedwork_destroy(&hdmi->work_compat_hdr); } static td_void hdmitx_device_deinit(td_void) { td_u8 hdmi_cnt = g_hdmi_cnt; struct ext_hdmitx *hdmi = TD_NULL; while (hdmi_cnt > 0) { hdmi = g_mapping[hdmi_cnt - 1].hdmi; if (hdmi == TD_NULL) { hdmi_log_err("deinit hdmi null, hdmi_cnt %u", hdmi_cnt); return; } hdmitx_software_deinit(hdmi); drv_hdmitx_proc_deinit(hdmi); delect_hdmitx_from_mapping(); drv_hdmitx_controller_deinit(hdmi->controller); hdmi->controller = TD_NULL; osal_mutex_destroy(&hdmi->notifier_mutex); osal_mutex_destroy(&hdmi->disp_mutex); drv_hdmitx_phy_deinit(hdmi->phy); hdmi->phy = TD_NULL; drv_hdmitx_crg_deinit(hdmi->crg); hdmi->crg = TD_NULL; osal_dev_unregister(&hdmi->hdmitx_dev); hdmitx_device_struct_deinit(hdmi); hdmi_cnt--; } } static td_s32 hdmitx_device_register(struct ext_hdmitx *hdmi) { td_s32 ret; /* Setup misc device */ ret = snprintf_s(hdmi->hdmitx_dev.name, sizeof(hdmi->hdmitx_dev.name), sizeof(hdmi->hdmitx_dev.name) - 1, "hdmitx%u", hdmi->id); if (ret == TD_FAILURE) { hdmi_log_err("snprintf_s failed, hdmitx_id = %u\n", hdmi->id); return TD_FAILURE; } hdmi->hdmitx_dev.minor = 0; hdmi->hdmitx_dev.fops = &g_hdmi_fops; ret = osal_dev_register(&hdmi->hdmitx_dev); if (ret) { hdmi_log_err("osal_dev_register failed, hdmitx_id = %u\n", hdmi->id); return TD_FAILURE; } return TD_SUCCESS; } static td_s32 hdmitx_silence_callback(td_bool silence, td_void *private_data) { td_s32 ret = TD_FAILURE; td_u32 work_mode; td_u32 switch_mode; struct ext_hdmitx *hdmi = (struct ext_hdmitx *)private_data; if (hdmi == TD_NULL || hdmi->controller == TD_NULL || hdmi->phy == TD_NULL) { hdmi_log_err("null ptr!\n"); return SOC_ERR_HDMITX_NULL_PTR; } if (silence) { work_mode = hdmi->controller->soft_status.cur_hw_config.work_mode; if (work_mode == HDMITX_WORK_MODE_TMDS) { ret = hdmi_tmds_mode_stop(hdmi); } else { ret = hdmi_frl_mode_stop(hdmi); } } else { if (!hdmi->is_start_output) { return TD_SUCCESS; } if (hal_phy_is_on(hdmi->phy)) { return TD_SUCCESS; } switch_mode = hdmi_get_switch_mode(hdmi); switch (switch_mode) { case HDMITX_SWITCH_MODE_TMDS_2_TMDS: case HDMITX_SWITCH_MODE_FRL_2_TMDS: ret = hdmi_tmds_mode_start(hdmi); break; case HDMITX_SWITCH_MODE_TMDS_2_FRL: case HDMITX_SWITCH_MODE_FRL_2_FRL: ret = hdmi_frl_mode_start(hdmi); break; default: ret = HDMI_ERR_INPUT_PARAM_INVALID; break; } } return ret; } static td_s32 hdmitx_register_callback_from_pdm(td_u32 module_id, const ext_pdm_set_silence_callback_func fn, td_void *private_data) { td_s32 ret; ext_pdm_export_func *pdm_func = TD_NULL; if ((fn == TD_NULL) || (private_data == TD_NULL)) { return TD_FAILURE; } ret = osal_exportfunc_get(SOC_ID_PDM, (td_void **)&pdm_func); if (ret != TD_SUCCESS) { soc_log_err("osal_exportfunc_get is failed"); return TD_FAILURE; } if ((pdm_func == TD_NULL) || (pdm_func->pdm_register_callback == TD_NULL)) { soc_log_err("PDM function is NULL\n"); return TD_FAILURE; } ret = pdm_func->pdm_register_callback(module_id, fn, private_data); if (ret != TD_SUCCESS) { soc_log_err("pdm_register_callback is failed"); return TD_FAILURE; } return TD_SUCCESS; } static struct ext_hdmitx *hdmitx_device_struct_init(td_u8 hdmitx_id) { td_s32 ret; struct ext_hdmitx *hdmi = TD_NULL; hdmi = osal_kmalloc(sizeof(struct ext_hdmitx), OSAL_GFP_KERNEL); if (hdmi == TD_NULL) { hdmi_log_err("osal_kmalloc fail, hdmitx%u\n", hdmitx_id); return TD_NULL; } if (memset_s(hdmi, sizeof(*hdmi), 0, sizeof(*hdmi)) != EOK) { hdmi_log_err("memset_s fail, hdmitx%u\n", hdmitx_id); osal_kfree(hdmi); hdmi = TD_NULL; return TD_NULL; } hdmi->id = hdmitx_id; hdmi->is_suspend = TD_FALSE; hdmi->vo_ops = &g_hdmi_vo_ops; hdmi->ao_ops = &g_hdmi_ao_ops; hdmi->is_start_output = TD_TRUE; ret = hdmitx_register_callback_from_pdm(SOC_ID_HDMITX, hdmitx_silence_callback, hdmi); if (ret != TD_SUCCESS) { hdmi_log_err("register silence callback failed, hdmitx%u\n", hdmitx_id); } return hdmi; } static td_s32 hdmitx_software_init(struct ext_hdmitx *hdmi) { if (hdmi == TD_NULL || hdmi->controller == TD_NULL) { return TD_FAILURE; } /* hw_config init */ if (hdmi_cur_hw_config_decision(hdmi) != TD_SUCCESS) { soc_log_info("hw config init fail!\n"); } drv_hdmitx_controller_cur_config_check(hdmi->controller); hdmitx_init_notifier_head(&hdmi->notifier_list); if (osal_delayedwork_init(&hdmi->work_compat_hdr, hdmi_hdr_compat_timeout_handler) != TD_SUCCESS) { return TD_FAILURE; } if (osal_sem_init(&hdmi->ioctl_mutex, 1) != TD_SUCCESS) { goto sem_init_failed; } return TD_SUCCESS; sem_init_failed: osal_delayedwork_destroy(&hdmi->work_compat_hdr); return TD_FAILURE; } static td_s32 hdmitx_device_init_mutex(struct ext_hdmitx *hdmi) { if (osal_mutex_init(&hdmi->notifier_mutex) != TD_SUCCESS) { return TD_FAILURE; } if (osal_mutex_init(&hdmi->disp_mutex) != TD_SUCCESS) { goto disp_mutex_init_fail; } return TD_SUCCESS; disp_mutex_init_fail: osal_mutex_destroy(&hdmi->notifier_mutex); return TD_FAILURE; } static td_s32 hdmitx_device_init(td_u8 hdmitx_id) { struct ext_hdmitx *hdmi = hdmitx_device_struct_init(hdmitx_id); if (hdmi == TD_NULL) { return TD_FAILURE; } if (hdmitx_device_register(hdmi) != TD_SUCCESS) { goto device_register_failed; } if ((hdmi->crg = drv_hdmitx_crg_init(hdmi->id)) == TD_NULL) { goto crg_init_failed; } if ((hdmi->phy = drv_hdmitx_phy_init(hal_cross_get_phy_id(hdmi->id))) == TD_NULL) { goto phy_init_failed; } if (hdmitx_device_init_mutex(hdmi) != TD_SUCCESS) { goto mutex_init_failed; } if ((hdmi->controller = drv_hdmitx_controller_init(hdmi)) == TD_NULL) { goto controller_init_failed; } if (add_hdmitx_to_mapping(hdmi) != TD_SUCCESS) { goto add_mapping_failed; } if (drv_hdmitx_proc_init(hdmi) != TD_SUCCESS) { goto proc_init_failed; } if (hdmitx_software_init(hdmi) != TD_SUCCESS) { goto software_init_failed; } return TD_SUCCESS; software_init_failed: drv_hdmitx_proc_deinit(hdmi); proc_init_failed: delect_hdmitx_from_mapping(); add_mapping_failed: drv_hdmitx_controller_deinit(hdmi->controller); controller_init_failed: osal_mutex_destroy(&hdmi->disp_mutex); osal_mutex_destroy(&hdmi->notifier_mutex); mutex_init_failed: drv_hdmitx_phy_deinit(hdmi->phy); phy_init_failed: drv_hdmitx_crg_deinit(hdmi->crg); crg_init_failed: osal_dev_unregister(&hdmi->hdmitx_dev); device_register_failed: hdmitx_device_struct_deinit(hdmi); return TD_FAILURE; } /* * vo intf and ao intf are defined dependently with hdmi * so and convertion is needed here. */ static td_u32 intf_id_to_hdmi_id(td_s32 id) { if (id == AO_INTF_0 || id == DISP_INTF_HDMITX0) { return 0; } if (id == AO_INTF_1 || id == DISP_INTF_HDMITX1) { return 1; } return MAX_HDMITX_ID; } static td_s32 hdmi_module_vo_suspend_func(td_u32 vo_intf_id, td_bool lower_power) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; hdmi_id = intf_id_to_hdmi_id(vo_intf_id); if (hdmi_id == MAX_HDMITX_ID) { return TD_FAILURE; } soc_log_info("hdmitx%u\n", hdmi_id); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->vo_ops == TD_NULL) { return TD_FAILURE; } if (!hdmi->attached) { return TD_FAILURE; } hdmi->vo_ops->suspend(hdmi, lower_power); return TD_SUCCESS; } static td_s32 hdmi_module_vo_resume_func(td_u32 vo_intf_id, td_bool lower_power) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; hdmi_id = intf_id_to_hdmi_id(vo_intf_id); if (hdmi_id == MAX_HDMITX_ID) { return TD_FAILURE; } soc_log_info("hdmitx%u\n", hdmi_id); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->vo_ops == TD_NULL) { return TD_FAILURE; } if (!hdmi->attached) { return TD_FAILURE; } return hdmi->vo_ops->resume(hdmi, lower_power); } static td_s32 hdmi_module_vo_attach_func(td_u32 vo_intf_id, const td_void *data) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; hdmi_id = intf_id_to_hdmi_id(vo_intf_id); if (hdmi_id == MAX_HDMITX_ID) { hdmi_log_err("hdmitx%d, id is invalid, please check param input\n", hdmi_id); return HDMI_ERR_INPUT_PARAM_INVALID; } soc_log_info("hdmitx%u\n", hdmi_id); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->vo_ops == TD_NULL) { hdmi_log_err("hdmitx%d uninit, please check soc_hdmitx.ko loaded\n", hdmi_id); return HDMI_ERR_INPUT_PARAM_INVALID; } hdmi->attached = TD_TRUE; return hdmi->vo_ops->attach(hdmi, data); } static td_s32 hdmi_module_vo_detach_func(td_u32 vo_intf_id, const td_void *data) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; hdmi_id = intf_id_to_hdmi_id(vo_intf_id); if (hdmi_id == MAX_HDMITX_ID) { hdmi_log_err("hdmitx%d, id is invalid, please check param input\n", hdmi_id); return HDMI_ERR_INPUT_PARAM_INVALID; } soc_log_info("hdmitx%u\n", hdmi_id); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->vo_ops == TD_NULL) { hdmi_log_err("hdmitx%d, uninit, please check soc_hdmitx.ko loaded\n", hdmi_id); return HDMI_ERR_MODULE_UNINIT; } hdmi->attached = TD_FALSE; return hdmi->vo_ops->detach(hdmi, data); } static td_s32 hdmi_module_vo_mode_validate_func(td_u32 vo_intf_id, td_u32 mode, struct ext_display_mode *mode_data) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; hdmi_id = intf_id_to_hdmi_id(vo_intf_id); if (hdmi_id == MAX_HDMITX_ID) { hdmi_log_err("hdmitx%d, id is invalid, please check param input\n", hdmi_id); return HDMI_ERR_INPUT_PARAM_INVALID; } soc_log_info("hdmitx%u mode=%u\n", hdmi_id, mode); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->vo_ops == TD_NULL) { hdmi_log_err("hdmitx%d, uninit, please check soc_hdmitx.ko loaded\n", hdmi_id); return HDMI_ERR_MODULE_UNINIT; } return hdmi->vo_ops->mode_validate(hdmi, mode, mode_data); } static td_s32 hdmi_module_vo_prepare_func(td_u32 vo_intf_id, td_u32 mode, struct ext_display_mode *mode_data) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; hdmi_id = intf_id_to_hdmi_id(vo_intf_id); if (hdmi_id == MAX_HDMITX_ID) { hdmi_log_err("hdmitx%d, id is invalid, please check param input\n", hdmi_id); return HDMI_ERR_INPUT_PARAM_INVALID; } soc_log_info("hdmitx%u mode=%u\n", hdmi_id, mode); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->vo_ops == TD_NULL) { hdmi_log_err("hdmitx%d, uninit, please check soc_hdmitx.ko loaded\n", hdmi_id); return HDMI_ERR_MODULE_UNINIT; } return hdmi->vo_ops->prepare(hdmi, mode, mode_data); } static td_s32 hdmi_module_vo_mode_set_func(td_u32 vo_intf_id, td_u32 mode) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; hdmi_id = intf_id_to_hdmi_id(vo_intf_id); if (hdmi_id == MAX_HDMITX_ID) { hdmi_log_err("hdmitx%d, id is invalid, please check param input\n", hdmi_id); return HDMI_ERR_INPUT_PARAM_INVALID; } soc_log_info("hdmitx%u mode=%u\n", hdmi_id, mode); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->vo_ops == TD_NULL) { hdmi_log_err("hdmitx%d, uninit, please check soc_hdmitx.ko loaded\n", hdmi_id); return HDMI_ERR_MODULE_UNINIT; } if (!hdmi->attached) { return TD_FAILURE; } return hdmi->vo_ops->mode_set(hdmi, mode); } static td_s32 hdmi_module_vo_display_on_func(td_u32 vo_intf_id) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; hdmi_id = intf_id_to_hdmi_id(vo_intf_id); if (hdmi_id == MAX_HDMITX_ID) { hdmi_log_err("hdmitx%u, id is invalid, please check param input\n", hdmi_id); return HDMI_ERR_INPUT_PARAM_INVALID; } soc_log_info("hdmitx%u\n", hdmi_id); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->vo_ops == TD_NULL) { hdmi_log_err("hdmitx%u, uninit, please check soc_hdmitx.ko loaded\n", hdmi_id); return HDMI_ERR_MODULE_UNINIT; } if (!hdmi->attached) { return TD_FAILURE; } return hdmi->vo_ops->display_on(hdmi); } static td_s32 hdmi_module_vo_display_off_func(td_u32 vo_intf_id) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; hdmi_id = intf_id_to_hdmi_id(vo_intf_id); if (hdmi_id == MAX_HDMITX_ID) { return TD_FAILURE; } soc_log_info("hdmitx%u\n", hdmi_id); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->vo_ops == TD_NULL) { return TD_FAILURE; } if (!hdmi->attached) { return TD_FAILURE; } return hdmi->vo_ops->display_off(hdmi); } static td_void color_space_conv(ext_drv_color_descript color_descript, struct color_info *color) { switch (color_descript.color_primary) { case EXT_DRV_COLOR_PRIMARY_BT601_525: case EXT_DRV_COLOR_PRIMARY_BT601_625: color->colorimetry = HDMITX_COLORIMETRY_ITU601; break; case EXT_DRV_COLOR_PRIMARY_BT709: color->colorimetry = HDMITX_COLORIMETRY_ITU709; break; case EXT_DRV_COLOR_PRIMARY_BT2020: color->colorimetry = HDMITX_COLORIMETRY_2020_NON_CONST_LUMINOUS; break; default: color->colorimetry = HDMITX_COLORIMETRY_ITU709; break; } switch (color_descript.quantify_range) { case EXT_DRV_COLOR_LIMITED_RANGE: color->rgb_quantization = HDMITX_RGB_QUANTIZEION_LIMITED; color->ycc_quantization = HDMITX_YCC_QUANTIZEION_LIMITED; break; case EXT_DRV_COLOR_FULL_RANGE: color->rgb_quantization = HDMITX_RGB_QUANTIZEION_FULL; color->ycc_quantization = HDMITX_YCC_QUANTIZEION_FULL; break; default: color->rgb_quantization = HDMITX_RGB_QUANTIZEION_LIMITED; color->ycc_quantization = HDMITX_YCC_QUANTIZEION_LIMITED; break; } } static td_void disp_stereo_mode_conv(ext_dip_disp_stereo_mode stereo_mode, td_u8 *mode_3d) { switch (stereo_mode) { case EXT_DIP_DISP_STEREO_NONE: *mode_3d = HDMITX_3D_NONE; break; case EXT_DIP_DISP_STEREO_FRAME_PACKING: *mode_3d = HDMITX_3D_FRAME_PACKETING; break; case EXT_DIP_DISP_STEREO_SBS_HALF: *mode_3d = HDMITX_3D_SIDE_BY_SIDE_HALF; break; case EXT_DIP_DISP_STEREO_TAB: *mode_3d = HDMITX_3D_TOP_AND_BOTTOM; break; case EXT_DIP_DISP_STEREO_FIELD_ALT: *mode_3d = HDMITX_3D_FIELD_ALTERNATIVE; break; case EXT_DIP_DISP_STEREO_LINE_ALT: *mode_3d = HDMITX_3D_LINE_ALTERNATIVE; break; case EXT_DIP_DISP_STEREO_SBS_FULL: *mode_3d = HDMITX_3D_SIDE_BY_SIDE_FULL; break; case EXT_DIP_DISP_STEREO_L_DEPTH: *mode_3d = HDMITX_3D_L_DEPTH; break; case EXT_DIP_DISP_STEREO_L_DEPTH_G_DEPTH: *mode_3d = HDMITX_3D_L_DEPTH_GRAPHICS_DEPTH; break; default: *mode_3d = HDMITX_3D_NONE; break; } } static td_void pixel_bitwidth_conv(ext_drv_pixel_bitwidth bit_width, td_u32 *color_depth) { switch (bit_width) { case EXT_DRV_PIXEL_BITWIDTH_8BIT: *color_depth = HDMITX_BPC_24; break; case EXT_DRV_PIXEL_BITWIDTH_10BIT: *color_depth = HDMITX_BPC_30; break; case EXT_DRV_PIXEL_BITWIDTH_12BIT: *color_depth = HDMITX_BPC_36; break; default: *color_depth = HDMITX_BPC_24; break; } } static td_void disp_out_type_conv(ext_dip_disp_out_type type, td_u32 *hdr_type) { switch (type) { case EXT_DIP_DISP_TYPE_NORMAL: *hdr_type = HDMITX_HDR_MODE_SDR; break; case EXT_DIP_DISP_TYPE_SDR_CERT: *hdr_type = HDMITX_HDR_MODE_STATIC_TRD_SDR; break; case EXT_DIP_DISP_TYPE_DOLBY: *hdr_type = HDMITX_HDR_MODE_DOLBY_V1; break; case EXT_DIP_DISP_TYPE_HDR10: *hdr_type = HDMITX_HDR_MODE_STATIC_ST2084; break; case EXT_DIP_DISP_TYPE_HDR10_CERT: *hdr_type = HDMITX_HDR_MODE_STATIC_TRD_HDR; break; case EXT_DIP_DISP_TYPE_HLG: *hdr_type = HDMITX_HDR_MODE_STATIC_HLG; break; case EXT_DIP_DISP_TYPE_DOLBY_LL: *hdr_type = HDMITX_HDR_MODE_DOLBY_V2; break; default: break; } } static td_void picture_aspect_conv(ext_drv_aspect_ratio src, enum hdmitx_picture_aspect *dst) { if (dst == TD_NULL) { hdmi_log_err("Input params is NULL pointer.\n"); return; } if (src.aspect_ratio_w == WIDTH_4 && src.aspect_ratio_h == HEIGHT_3) { *dst = HDMITX_PICTURE_ASPECT_4_3; } else if (src.aspect_ratio_w == WIDTH_16 && src.aspect_ratio_h == HEIGHT_9) { *dst = HDMITX_PICTURE_ASPECT_16_9; } else if (src.aspect_ratio_w == WIDTH_64 && src.aspect_ratio_h == HEIGHT_27) { *dst = HDMITX_PICTURE_ASPECT_64_27; } else if (src.aspect_ratio_w == WIDTH_256 && src.aspect_ratio_h == HEIGHT_135) { *dst = HDMITX_PICTURE_ASPECT_256_135; } else { *dst = HDMITX_PICTURE_ASPECT_RESERVED; } } static td_void disp_timing_to_detail_info(const ext_dip_disp_timing *src, struct timing_detail_info *dst) { dst->htotal = src->hact + src->hbb + src->hfb + src->hpw; dst->hactive = src->hact; dst->hfront = src->hfb; dst->hsync = src->hpw; dst->hback = src->hbb; dst->vtotal = src->vact + src->vbb + src->vfb + src->vpw; dst->vactive = src->vact; dst->vfront = src->vfb; dst->vsync = src->vpw; dst->vback = src->vbb; dst->refresh_rate = src->refresh_rate; } static td_void disp_intf_info2ext_display_mode(const ext_dip_disp_intf_info *src, struct ext_display_mode *dst) { struct color_info color = {0}; enum hdmitx_picture_aspect pic_aspect; struct hdmitx_in_data *dst_in = &dst->timing_data.in; struct hdmitx_out_data *dst_out = &dst->timing_data.out; struct hdmitx_hdr_data *dst_hdr = &dst->hdr_data; struct hdmitx_vrr_data *dst_vrr = &dst->vrr_data; const ext_dip_disp_timing *disp_timing = &src->disp_timing.static_timing.timing; dst_in->active_aspect_ratio = HDMITX_ACT_ASP_RATIO_ATSC_SAME_PIC; dst_in->scan_info = HDMITX_SCAN_OVER_SCAN; disp_out_type_conv(src->out_type, &dst_hdr->hdr_mode_type); if (src->disp_timing.dynamic_timing.vrr_enable) { dst_vrr->vrr_mode_type = HDMITX_VRR_MODE_VRR; } else if (src->disp_timing.dynamic_timing.qft_attr.factor > 1) { dst_vrr->vrr_mode_type = HDMITX_VRR_MODE_QFT; } else if (src->disp_timing.dynamic_timing.qms_enable) { dst_vrr->vrr_mode_type = HDMITX_VRR_MODE_QMS; } else { dst_vrr->vrr_mode_type = HDMITX_VRR_MODE_ALLM; } dst_vrr->fva_factor = src->disp_timing.dynamic_timing.qft_attr.factor; dst_in->de_pol = src->disp_timing.static_timing.timing.idv; dst_in->h_sync_pol = src->disp_timing.static_timing.timing.ihs; dst_in->v_sync_pol = src->disp_timing.static_timing.timing.ivs; disp_timing_to_detail_info(disp_timing, &dst_in->detail); dst_in->pixel_clock = src->disp_timing.static_timing.timing.pix_freq; dst_in->pixel_repeat = src->disp_timing.static_timing.pix_repeat - 1; disp_stereo_mode_conv(src->disp_timing.static_timing.disp_3d_mode, &dst_in->mode_3d); picture_aspect_conv(src->disp_timing.static_timing.aspect_ratio, &pic_aspect); dst_in->picture_aspect_ratio = (td_u32)pic_aspect; color_space_conv(src->in_info.color_space, &color); dst_in->fva_factor = src->disp_timing.dynamic_timing.qft_attr.factor; dst_in->vic = src->disp_timing.static_timing.vic_num; pixel_bitwidth_conv(src->in_info.data_width, &dst_in->color_depth); dst_in->color.color_format = src->in_info.pixel_format; dst_in->color.colorimetry = color.colorimetry; dst_in->color.ycc_quantization = HDMITX_YCC_QUANTIZEION_LIMITED; dst_in->color.rgb_quantization = HDMITX_RGB_QUANTIZEION_DEFAULT; pixel_bitwidth_conv(src->out_info.data_width, &dst_out->color_depth); dst_out->color.colorimetry = color.colorimetry; dst_out->color.color_format = src->out_info.pixel_format; } static td_u8 get_checksum(const td_u8 *ptr, td_u32 size) { td_u8 ck_sum = 0; td_u32 i; td_u32 ret; for (i = 0; i < size; i++) { ck_sum += ptr[i]; } ret = (td_u8)(0x100 - ck_sum); return ret; } static td_void stereo_mode_conv(ext_dip_disp_stereo_mode stereo_mode, enum hdmitx_3d_structure *s3d_struct) { switch (stereo_mode) { case EXT_DIP_DISP_STEREO_NONE: *s3d_struct = HDMITX_3D_STRUCTURE_INVALID; break; case EXT_DIP_DISP_STEREO_FRAME_PACKING: *s3d_struct = HDMITX_3D_STRUCTURE_FRAME_PACKING; break; case EXT_DIP_DISP_STEREO_SBS_HALF: *s3d_struct = HDMITX_3D_STRUCTURE_SIDE_BY_SIDE_HALF; break; case EXT_DIP_DISP_STEREO_TAB: *s3d_struct = HDMITX_3D_STRUCTURE_TOP_AND_BOTTOM; break; case EXT_DIP_DISP_STEREO_FIELD_ALT: *s3d_struct = HDMITX_3D_STRUCTURE_FIELD_ALTERNATIVE; break; case EXT_DIP_DISP_STEREO_LINE_ALT: *s3d_struct = HDMITX_3D_STRUCTURE_LINE_ALTERNATIVE; break; case EXT_DIP_DISP_STEREO_SBS_FULL: *s3d_struct = HDMITX_3D_STRUCTURE_SIDE_BY_SIDE_FULL; break; case EXT_DIP_DISP_STEREO_L_DEPTH: *s3d_struct = HDMITX_3D_STRUCTURE_L_DEPTH; break; case EXT_DIP_DISP_STEREO_L_DEPTH_G_DEPTH: *s3d_struct = HDMITX_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH; break; default: *s3d_struct = HDMITX_3D_STRUCTURE_INVALID; break; } } static td_void disp_avi_info2hdmitx_avi_info(const format_frame_info *src, struct avi_infoframe *dst) { struct color_info color; td_u32 colorimetry; td_u32 extended_colorimetry; struct hdmitx_avi_infoframe *avi_info = &dst->avi; dst->enable = TD_TRUE; if (drv_hdmitx_avi_infoframe_init(avi_info) < 0) { hdmi_log_err("frame init fail.\n"); return; } color_space_conv(src->color_space, &color); colorimetry = color.colorimetry & 0xf; extended_colorimetry = (color.colorimetry >> 4) & 0xf; /* The upper 4 bits is extended colorimetry. */ avi_info->pixel_repeat = src->pixel_repeat - 1; avi_info->video_code = (src->vic > 255) ? 0 : (src->vic & 0xff); /* Vic > 255 is not CEA timing. */ /* * The video code must be 0 in the avi infoframe, when the timing * is HDMI_3840X2160P24_16_9(93), HDMI_3840X2160P25_16_9(94), * HDMI_3840X2160P30_16_9(95), HDMI_4096X2160P24_256_135(98) at 2D mode. */ if ((avi_info->video_code == VIC_3840X2160P24_16_9 || avi_info->video_code == VIC_3840X2160P25_16_9 || avi_info->video_code == VIC_3840X2160P30_16_9 || avi_info->video_code == VIC_4096X2160P24_256_135) && src->frame_3d_mode == EXT_DIP_DISP_STEREO_NONE) { avi_info->video_code = 0; } picture_aspect_conv(src->picture_aspect, &avi_info->picture_aspect); avi_info->content_type = (enum hdmitx_content_type)src->content_type; avi_info->itc = TD_FALSE; avi_info->active_aspect = (enum hdmitx_active_aspect)src->active_aspect; avi_info->scan_mode = (enum hdmitx_scan_mode)src->scan_mode; avi_info->colorspace = (enum hdmitx_colorspace)src->pix_format; avi_info->colorimetry = colorimetry; avi_info->extended_colorimetry = extended_colorimetry; avi_info->quantization_range = color.rgb_quantization; avi_info->ycc_quantization_range = color.ycc_quantization; avi_info->top_bar = src->top_bar; avi_info->bottom_bar = src->bottom_bar; avi_info->left_bar = src->left_bar; avi_info->right_bar = src->right_bar; avi_info->nups = (enum hdmitx_nups)src->nups; if (avi_info->video_code > VIC_5120X2160P100_64_27) { avi_info->version = 3; /* Avi infoframe version need be 3, when vic > 127. */ } else if (avi_info->colorimetry == HDMITX_COLORIMETRY_EXTENDED && avi_info->extended_colorimetry == HDMITX_EXTENDED_COLORIMETRY_RESERVED) { avi_info->version = 4; /* Avi infoframe version need be 4, when extended colorimetry is reserved. */ } } static td_void disp_hdr_info2hdmitx_drm_info(const ext_drv_hdr_metadata *src, struct drm_infoframe *dst, ext_dip_disp_out_type type) { struct hdmitx_meta_descriptor_1st *type1 = &dst->drm.meta_descriptor.type1; const ext_drv_hdr_mastering_display_info *mastering_info = TD_NULL; const ext_drv_hdr_content_light_level *content_info = TD_NULL; if (drv_hdmitx_drm_infoframe_init(&dst->drm) < 0) { hdmi_log_err("frame init fail.\n"); return; } if (type == EXT_DIP_DISP_TYPE_HDR10) { dst->drm_type = HDR10; dst->drm.eotf_type = HDMITX_EOTF_SMPTE_ST_2084; mastering_info = &src->hdr10_info.mastering_info; content_info = &src->hdr10_info.content_info; } else if (type == EXT_DIP_DISP_TYPE_HLG) { dst->drm_type = HLG; dst->drm.eotf_type = HDMITX_EOTF_HLG; mastering_info = &src->hlg_info.mastering_info; content_info = &src->hlg_info.content_info; } else { dst->drm_type = SDR; dst->drm.eotf_type = HDMITX_EOTF_SDR_LUMIN; } dst->drm.metadata_type = HDMITX_HDR_METADATA_ID_0; if (type == EXT_DIP_DISP_TYPE_HLG || type == EXT_DIP_DISP_TYPE_HDR10) { type1->primaries0_x = mastering_info->display_primaries_x[INDEX_0]; type1->primaries0_y = mastering_info->display_primaries_y[INDEX_0]; type1->primaries1_x = mastering_info->display_primaries_x[INDEX_1]; type1->primaries1_y = mastering_info->display_primaries_y[INDEX_1]; type1->primaries2_x = mastering_info->display_primaries_x[INDEX_2]; type1->primaries2_y = mastering_info->display_primaries_y[INDEX_2]; type1->white_point_x = mastering_info->white_point_x; type1->white_point_y = mastering_info->white_point_y; type1->max_luminance = mastering_info->max_display_mastering_luminance; type1->min_luminance = mastering_info->min_display_mastering_luminance; type1->max_light_level = content_info->max_content_light_level; type1->average_light_level = content_info->max_pic_average_light_level; } else { if (memset_s(type1, sizeof(*type1), 0x0, sizeof(*type1)) != EOK) { hdmi_log_err("memset_s fail.\n"); return; } } } static td_void disp_vender_info2hdmitx_vendor_info(const format_frame_info *src, struct hf14_vsif_infoframe *dst, ext_dip_disp_out_type type) { dst->enable = TD_TRUE; if (drv_hdmitx_vendor_infoframe_init(&dst->hf14) < 0) { hdmi_log_err("frame init fail.\n"); return; } stereo_mode_conv(src->frame_3d_mode, &dst->hf14.s3d_struct); if (dst->hf14.s3d_struct == HDMITX_3D_STRUCTURE_INVALID) { if (src->vic == VIC_3840X2160P30_16_9) { dst->hf14.vic = 1; /* hdmi vic is 1 */ } else if (src->vic == VIC_3840X2160P25_16_9) { dst->hf14.vic = 2; /* hdmi vic is 2 */ } else if (src->vic == VIC_3840X2160P24_16_9) { dst->hf14.vic = 3; /* hdmi vic is 3 */ } else if (src->vic == VIC_4096X2160P24_256_135) { dst->hf14.vic = 4; /* hdmi vic is 4 */ } } if (dst->hf14.vic) { dst->hf14.length = 5; /* hdmi vic is not zero, the length must be 5. */ } else if (dst->hf14.s3d_struct != HDMITX_3D_STRUCTURE_INVALID) { dst->hf14.length = 7; /* 3d struct is not none, the length must be 7. */ } if (type == EXT_DIP_DISP_TYPE_DOLBY) { dst->hf14.length = 0x18; /* dolby vision, the length must be 0x18. */ } } static td_void disp_dolby_info2hdmitx_dolby_info(const dolby_frame_info *src, struct dolby_vsif_infoframe *dst, ext_dip_disp_out_type type) { dst->enable = (type == EXT_DIP_DISP_TYPE_DOLBY_LL) ? TD_TRUE : TD_FALSE; if (drv_hdmitx_dolby_vendor_infoframe_init(&dst->dolby) < 0) { hdmi_log_err("frame init fail.\n"); return; } } static td_void disp2hdmitx_cuva_monitor_info(const cuva_frame_info *src, struct cuva_monitor_vsif_infoframe *dst, ext_dip_disp_out_type type) { dst->enable = (type == EXT_DIP_DISP_TYPE_CUVA_MONITOR) ? TD_TRUE : TD_FALSE; if (drv_hdmitx_cuva_monitor_vsif_init(&dst->cuva_monitor) < 0) { hdmi_log_err("frame init fail.\n"); return; } dst->cuva_monitor.system_start_code = src->system_start_code; dst->cuva_monitor.version_code = src->hdmi_version_code; dst->cuva_monitor.monitor_mode_enable = 1; } static td_void disp_forum_vendor_info2hdmitx_forum_vendor_info(const vrr_frame_info *src, struct hf_vsif_infoframe *dst, const format_frame_info *src_frame) { dst->enable = (src->allm_en == TD_TRUE) ? TD_TRUE : TD_FALSE; if (drv_hdmitx_forum_vendor_infoframe_init(&dst->hf_vsif) < 0) { hdmi_log_err("frame init fail.\n"); return; } stereo_mode_conv(src_frame->frame_3d_mode, &dst->hf_vsif.s3d_infoframe.s3d_struct); if (dst->hf_vsif.s3d_infoframe.s3d_struct != HDMITX_3D_STRUCTURE_INVALID) { dst->hf_vsif.length = 0x1B; /* the length set max value 27. */ dst->hf_vsif.s3d_valid = TD_TRUE; } else { dst->hf_vsif.length = 0x6; /* the length set min value 6. */ dst->hf_vsif.s3d_valid = TD_FALSE; } dst->hf_vsif.allm = src->allm_en; } static td_void disp_cuva_hdr_info2hdmitx_cuva_hdr_info(const cuva_frame_info *src, struct cuva_hdr_infoframe *dst, ext_dip_disp_out_type type) { td_u32 i; dst->enable = (type == EXT_DIP_DISP_TYPE_CUVA_RX) ? TD_TRUE : TD_FALSE; if (drv_hdmitx_cuva_hdr_ext_metadata_init(&dst->cuva) < 0) { hdmi_log_err("frame init fail.\n"); return; } dst->cuva.start_code = src->system_start_code; dst->cuva.version_code = src->hdmi_version_code; dst->cuva.min_maxrgb = src->minimum_maxrgb; dst->cuva.avg_maxrgb = src->average_maxrgb; dst->cuva.var_maxrgb = src->variance_maxrgb; dst->cuva.max_maxrgb = src->maximum_maxrgb; dst->cuva.max_lum_pb = src->targeted_system_display_maximum_luminance; dst->cuva.base_enable = src->base_flag; dst->cuva.m_p = src->base_param_mp; dst->cuva.m_m = src->base_param_mm; dst->cuva.m_a = src->base_param_ma; dst->cuva.m_b = src->base_param_mb; dst->cuva.m_n = src->base_param_mn; dst->cuva.k1 = src->base_param_k1; dst->cuva.k2 = src->base_param_k2; dst->cuva.k3 = src->base_param_k3; dst->cuva.delta_mode = src->base_param_delta_mode; dst->cuva.delta = src->base_param_delta; dst->cuva.enable_num = src->p3spline_num; dst->cuva.enable_flag = src->p3spline_flag; for (i = 0; i < HDMITX_CUVA_HDR_TH_NUM; i++) { dst->cuva.th[i].mode = src->p3spline_th_mode[i]; dst->cuva.th[i].mb = src->p3spline_th_mb[i]; dst->cuva.th[i].enable = src->p3spline_th_enable[i]; dst->cuva.th[i].delta1 = src->p3spline_th_data1[i]; dst->cuva.th[i].delta2 = src->p3spline_th_data2[i]; dst->cuva.th[i].strength = src->p3spline_th_strength[i]; } dst->cuva.saturation_num = src->color_saturation_num; for (i = 0; i < HDMITX_CUVA_HDR_GAIN_NUM; i++) { dst->cuva.gain[i] = src->color_saturation_gain[i]; } dst->cuva.transfer_character = src->transfer_character; dst->cuva.graphic_value_pq = src->graphic_display_value; dst->cuva.max_display_brightness = src->max_display_mastering_luminance; } static td_void disp_vrr_info2hdmitx_vrr_info(const vrr_frame_info *src, struct vrr_infoframe *dst, td_u8 video_code) { dst->enable = ((src->vrr_en == TD_TRUE) || (src->fva_factor > 1)) ? TD_TRUE : TD_FALSE; if (drv_hdmitx_vt_ext_metadata_init(&dst->vrr) < 0) { hdmi_log_err("frame init fail.\n"); return; } dst->vrr.fva_factor_m1 = (src->fva_factor > 1) ? (src->fva_factor - 1) : 0; dst->vrr.m_const = (src->vrr_en == TD_TRUE) ? src->m_const : 0; dst->vrr.vrr_en = src->vrr_en; if ((src->vrr_en == TD_FALSE) || (video_code != 0)) { dst->vrr.base_vfront = 0; dst->vrr.rb = 0; dst->vrr.base_refresh_rate = 0; } else { dst->vrr.base_vfront = src->base_vfront; dst->vrr.rb = src->rb; dst->vrr.base_refresh_rate = src->base_refresh_rate; } } static td_void vsif_switch(struct hf14_vsif_infoframe *hf14b_vsif, const struct hf_vsif_infoframe *hf_vsif) { if (hf_vsif->enable == TD_TRUE) { // default is hf14b_vsif, switch to hf_vsif when allm set hf14b_vsif->enable = TD_FALSE; } } static td_void disp_infoframe2hdmitx_infoframe(const ext_dip_disp_frame_pack_attr *pack_attr, struct hdmitx_infoframe *infoframe) { /* avi info */ disp_avi_info2hdmitx_avi_info(&pack_attr->format_info, &infoframe->avi_info); /* drm info */ disp_hdr_info2hdmitx_drm_info(&pack_attr->hdr_info, &infoframe->drm_info, pack_attr->hdr_type); /* vendor info */ disp_vender_info2hdmitx_vendor_info(&pack_attr->format_info, &infoframe->hf14_vsif, pack_attr->hdr_type); /* dolby info */ disp_dolby_info2hdmitx_dolby_info(&pack_attr->dolby_info, &infoframe->dolby_vsif, pack_attr->hdr_type); /* hf vsif */ disp_forum_vendor_info2hdmitx_forum_vendor_info(&pack_attr->vrr_info, &infoframe->hf_vsif, &pack_attr->format_info); /* cuva hdr info */ disp_cuva_hdr_info2hdmitx_cuva_hdr_info(&pack_attr->cuva_info, &infoframe->cuva_hdr_info, pack_attr->hdr_type); /* vrr info */ disp_vrr_info2hdmitx_vrr_info(&pack_attr->vrr_info, &infoframe->vrr_info, infoframe->avi_info.avi.video_code); /* cuva monitor info */ disp2hdmitx_cuva_monitor_info(&pack_attr->cuva_info, &infoframe->cuva_monitor_info, pack_attr->hdr_type); vsif_switch(&infoframe->hf14_vsif, &infoframe->hf_vsif); } static td_void write_packet_data_to_buffer(const td_u8 *data, td_u8 data_size, td_u8 *buffer, td_u32 buffer_len) { td_u8 *ptr = buffer; /* calc checksum */ *ptr++ = get_checksum(data, data_size - 1); if (memcpy_s(ptr, buffer_len - 1, data, data_size - 1) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return; } } static td_u32 hf14_vsif_infoframe_pack(struct ext_hdmitx *hdmi, td_u8 *buffer, td_u32 buffer_len) { td_s32 err; const td_u32 length = INFOFRAME_LENGTH; td_u8 temp_buf[INFOFRAME_LENGTH] = {0}; struct hf14_vsif_infoframe *cur_hf14_vsif = &hdmi->cur_infoframe.hf14_vsif; if (cur_hf14_vsif->enable != TD_TRUE) { soc_log_dbg("hf14 vsif not enable\n"); return 0; } err = drv_hdmitx_vendor_infoframe_pack(&cur_hf14_vsif->hf14, temp_buf, INFOFRAME_LENGTH); if (err < 0) { soc_log_dbg("Failed to pack vendor infoframe: %zd\n", length); return length; } write_packet_data_to_buffer(temp_buf, INFOFRAME_LENGTH, buffer, buffer_len); return length; } static td_bool sdm_infoframe_check(const struct drm_infoframe *cur, struct drm_infoframe *per) { if (osal_memncmp(cur, per, sizeof(*per)) == 0) { return TD_FALSE; } if (memcpy_s(per, sizeof(*per), cur, sizeof(*cur)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return TD_FALSE; } return TD_TRUE; } static td_u32 sdm_infoframe_pack(struct ext_hdmitx *hdmi, td_u8 *buffer, td_u32 buffer_len) { td_s32 err; const td_u32 length = INFOFRAME_LENGTH; td_u8 temp_buf[INFOFRAME_LENGTH] = {0}; struct drm_infoframe *cur_drm_info = &hdmi->cur_infoframe.drm_info; struct drm_infoframe *per_drm_info = &hdmi->per_infoframe.drm_info; if (sdm_infoframe_check(cur_drm_info, per_drm_info) != TD_TRUE) { soc_log_dbg("sdm infoframe don't change.\n"); return length; } err = drv_hdmitx_drm_infoframe_pack(&cur_drm_info->drm, temp_buf, INFOFRAME_LENGTH); if (err < 0) { soc_log_dbg("Failed to pack drm infoframe: %zd\n", length); return length; } write_packet_data_to_buffer(temp_buf, INFOFRAME_LENGTH, buffer, buffer_len); return length; } static td_u32 dolby_infoframe_pack(struct ext_hdmitx *hdmi, td_u8 *buffer, td_u32 buffer_len) { td_s32 err; const td_u32 length = INFOFRAME_LENGTH; td_u8 temp_buf[INFOFRAME_LENGTH] = {0}; struct dolby_vsif_infoframe *cur_dolby_vsif = &hdmi->cur_infoframe.dolby_vsif; if (cur_dolby_vsif->enable != TD_TRUE) { return 0; } /* dolby vsif pack */ err = drv_hdmitx_dolby_vendor_infoframe_pack(&cur_dolby_vsif->dolby, temp_buf, INFOFRAME_LENGTH); if (err < 0) { soc_log_dbg("Failed to pack dolby vsif: %zd\n", length); return length; } write_packet_data_to_buffer(temp_buf, INFOFRAME_LENGTH, buffer, buffer_len); return length; } static td_u32 cuva_monitor_infoframe_pack(struct ext_hdmitx *hdmi, td_u8 *buffer, td_u32 buffer_len) { td_s32 err; const td_u32 length = INFOFRAME_LENGTH; td_u8 temp_buf[INFOFRAME_LENGTH] = {0}; struct cuva_monitor_vsif_infoframe *cur_cuva_monitor = &hdmi->cur_infoframe.cuva_monitor_info; if (cur_cuva_monitor->enable != TD_TRUE) { return 0; } /* cuva monitor vsif pack */ err = drv_hdmitx_cuva_monitor_vsif_pack(&cur_cuva_monitor->cuva_monitor, temp_buf, INFOFRAME_LENGTH); if (err < 0) { soc_log_dbg("Failed to pack cuva monitor vsif: %zd\n", length); return length; } write_packet_data_to_buffer(temp_buf, INFOFRAME_LENGTH, buffer, buffer_len); return length; } static td_u32 cuva_hdr_infoframe_pack(struct ext_hdmitx *hdmi, td_u8 *buffer, td_u32 buffer_len) { td_s32 err; const td_u32 length = CUVA_INFOFRAM_PACKET_SIZE; td_u8 temp_buf[CUVA_INFOFRAM_PACKET_SIZE] = {0}; td_u8 offset = 0; td_u8 tmp_offset = 0; td_u32 i; struct cuva_hdr_infoframe *cur_cta_hdr_info = &hdmi->cur_infoframe.cuva_hdr_info; if (cur_cta_hdr_info->enable != TD_TRUE) { return 0; } /* cuva hdr infoframe pack */ err = drv_hdmitx_cuva_hdr_ext_metadata_pack(&cur_cta_hdr_info->cuva, temp_buf, CUVA_INFOFRAM_PACKET_SIZE); if (err < 0) { soc_log_dbg("Failed to pack cuva hdr infoframe: %zd\n", length); return length; } for (i = 0; i < 3; i++) { /* cuva infoframe has 3 packets. */ write_packet_data_to_buffer(temp_buf + tmp_offset, INFOFRAME_LENGTH, buffer + offset, buffer_len - offset); offset += 32; /* one packet has 32 bytes. */ tmp_offset += 31; /* one packet has 31 bytes. */ } return length; } static td_u32 vrr_infoframe_pack(struct ext_hdmitx *hdmi, td_u8 *buffer, td_u32 buffer_len) { td_s32 err; const td_u32 length = INFOFRAME_LENGTH; td_u8 temp_buf[INFOFRAME_LENGTH] = {0}; struct vrr_infoframe *cur_vrr_info = &hdmi->cur_infoframe.vrr_info; if (cur_vrr_info->enable != TD_TRUE) { return 0; } /* vrr infoframe pack */ err = drv_hdmitx_vt_ext_metadata_pack(&cur_vrr_info->vrr, temp_buf, INFOFRAME_LENGTH); if (err < 0) { soc_log_dbg("Failed to pack vrr emp infoframe: %zd\n", length); return length; } write_packet_data_to_buffer(temp_buf, INFOFRAME_LENGTH, buffer, buffer_len); return length; } static td_u32 hf_vsif_infoframe_pack(struct ext_hdmitx *hdmi, td_u8 *buffer, td_u32 buffer_len) { td_s32 err; const td_u32 length = INFOFRAME_LENGTH; td_u8 temp_buf[INFOFRAME_LENGTH] = {0}; struct hf_vsif_infoframe *cur_hf_vsif = &hdmi->cur_infoframe.hf_vsif; if (cur_hf_vsif->enable != TD_TRUE) { soc_log_dbg("hf vsif not enable\n"); return 0; } /* hf vsif pack */ err = drv_hdmitx_forum_vendor_infoframe_pack(&cur_hf_vsif->hf_vsif, temp_buf, INFOFRAME_LENGTH); if (err < 0) { soc_log_dbg("Failed to pack forum infoframe: %zd\n", length); return length; } write_packet_data_to_buffer(temp_buf, INFOFRAME_LENGTH, buffer, buffer_len); return length; } static td_bool avi_infoframe_check(const struct avi_infoframe *cur, struct avi_infoframe *per) { if (osal_memncmp(cur, per, sizeof(*per)) == 0) { return TD_FALSE; } if (memcpy_s(per, sizeof(*per), cur, sizeof(*cur)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return TD_FALSE; } return TD_TRUE; } static td_u32 avi_infoframe_fixed_pack(struct ext_hdmitx *hdmi, td_u8 *buffer, td_u32 buffer_len) { td_s32 err; const td_u32 length = INFOFRAME_LENGTH; td_u8 temp_buf[INFOFRAME_LENGTH] = {0}; static td_u8 *old_buffer = TD_NULL; struct avi_infoframe *cur_avi_info = &hdmi->cur_infoframe.avi_info; struct avi_infoframe *per_avi_info = &hdmi->per_infoframe.avi_info; cur_avi_info->enable = hal_ctrl_get_vdp_avi_path(&hdmi->controller->reg_info); if ((avi_infoframe_check(cur_avi_info, per_avi_info) != TD_TRUE) && (buffer == old_buffer)) { soc_log_dbg("no pack.\n"); return length; } old_buffer = buffer; /* avi info pack */ err = drv_hdmitx_avi_infoframe_pack(&cur_avi_info->avi, temp_buf, INFOFRAME_LENGTH); if (err < 0) { soc_log_dbg("Failed to pack AVI infoframe: %zd\n", length); return length; } write_packet_data_to_buffer(temp_buf, INFOFRAME_LENGTH, buffer, buffer_len); return length; } static td_u32 any_hdr_infoframe_pack(struct ext_hdmitx *hdmi, td_u8 *buffer_addr, td_u32 buffer_len) { td_u32 length; /* dolby infoframe pack. */ length = dolby_infoframe_pack(hdmi, buffer_addr, buffer_len); if (length > 0) { return length; } /* cuva hdr infoframe pack. */ length = cuva_hdr_infoframe_pack(hdmi, buffer_addr, buffer_len); if (length > 0) { return length; } /* cuva monitor infoframe pack. */ length = cuva_monitor_infoframe_pack(hdmi, buffer_addr, buffer_len); if (length > 0) { return length; } return length; } static td_u32 any_vrr_infoframe_pack(struct ext_hdmitx *hdmi, td_u8 *buffer_addr, td_u32 buffer_len) { td_u32 length; /* vrr infoframe pack */ length = vrr_infoframe_pack(hdmi, buffer_addr, buffer_len); return length; } static td_u32 extra_infoframe_pack(struct ext_hdmitx *hdmi, td_u8 *buffer_addr, td_u32 buffer_len) { td_u32 length; td_u32 total_length = 0; td_u8 *ptr = buffer_addr; length = any_hdr_infoframe_pack(hdmi, ptr, buffer_len); if (length == 0) { soc_log_dbg("no hdr.\n"); } ptr += length; total_length += length; length = any_vrr_infoframe_pack(hdmi, ptr, buffer_len - total_length); if (length == 0) { soc_log_dbg("no vrr.\n"); } ptr += length; total_length += length; length = hf_vsif_infoframe_pack(hdmi, ptr, buffer_len - total_length); if (length == 0) { soc_log_dbg("no allm.\n"); } ptr += length; total_length += length; return total_length; } static td_s32 get_hdmitx_pack_infoframe(td_u32 id, const ext_dip_disp_frame_pack_attr *pack_attr, ext_dip_disp_pack_buffer_info *pack_buffer_info) { td_u32 length; td_u8 *ptr = pack_buffer_info->pack_buffer_addr; td_u32 packed_len; struct ext_hdmitx *hdmi = TD_NULL; hdmi = get_hdmitx_by_id(id); if (hdmi == TD_NULL || hdmi->controller == TD_NULL) { hdmi_log_err("hdmitx%u uninit, please check soc_hdmitx.ko loaded\n", id); return HDMI_ERR_MODULE_UNINIT; } if (pack_buffer_info->pack_buffer_size < INFOFRAME_BUFFER_MAX) { hdmi_log_err("hdmitx%u buffer size less than send buffer size\n", id); return HDMI_ERR_INPUT_PARAM_INVALID; } /* Parse disp infoframe pack into hdmitx infoframe pack */ disp_infoframe2hdmitx_infoframe(pack_attr, &hdmi->cur_infoframe); /* debug for color space */ if (hdmi->controller->soft_status.dbg_info.dbg_colorspace != HDMITX_DEBUG_INFO_INVALID) { hdmi->cur_infoframe.avi_info.avi.colorspace = (enum hdmitx_colorspace)hdmi->controller->soft_status.dbg_info.dbg_colorspace; } /* drm infoframe pack */ length = sdm_infoframe_pack(hdmi, ptr, pack_buffer_info->pack_buffer_size); packed_len = length; pack_buffer_info->pack_buffer_payload += length; /* avi infoframe pack */ length = avi_infoframe_fixed_pack(hdmi, ptr + packed_len, pack_buffer_info->pack_buffer_size - packed_len); packed_len += length; pack_buffer_info->pack_buffer_payload += length; /* extra infoframe pack */ length = extra_infoframe_pack(hdmi, ptr + packed_len, pack_buffer_info->pack_buffer_size - packed_len); packed_len += length; pack_buffer_info->pack_buffer_payload += length; /* vsif pack */ length = hf14_vsif_infoframe_pack(hdmi, ptr + packed_len, pack_buffer_info->pack_buffer_size - packed_len); pack_buffer_info->pack_buffer_payload += length; /* when hdr type is not hdr10&hlg&switch, need stop sending drm packet. */ if (pack_attr->send_switch_sdr) { hal_ctrl_set_vdp_smd_path(&hdmi->controller->reg_info, TD_TRUE); } else { hal_ctrl_set_vdp_smd_path(&hdmi->controller->reg_info, TD_FALSE); } return TD_SUCCESS; } static td_s32 check_color_depth_validate(ext_drv_pixel_bitwidth bit_width) { td_s32 ret; switch (bit_width) { case EXT_DRV_PIXEL_BITWIDTH_DEFAULT: case EXT_DRV_PIXEL_BITWIDTH_8BIT: case EXT_DRV_PIXEL_BITWIDTH_10BIT: case EXT_DRV_PIXEL_BITWIDTH_12BIT: ret = TD_SUCCESS; break; default: ret = TD_FAILURE; break; } return ret; } static td_s32 hdmitx_reset_csc(td_u32 id, ext_dip_disp_reset_csc_info *csc_info) { struct ext_hdmitx *hdmi = TD_NULL; struct ext_display_mode *mode = TD_NULL; hdmi = get_hdmitx_by_id(id); if (hdmi == TD_NULL || hdmi->controller == TD_NULL) { soc_log_info("hdmi uninit, please check soc_hdmi.ko loaded\n"); return HDMI_ERR_MODULE_UNINIT; } soc_log_info("hdmitx%u reset csc, hact=%u vact=%u,in:fmt=%d depth=%d,out:fmt=%d depth=%d\n", id, csc_info->timing.hact, csc_info->timing.vact, csc_info->in_info.pixel_format, csc_info->in_info.data_width, csc_info->out_info.pixel_format, csc_info->out_info.data_width); mode = &hdmi->controller->soft_status.mode; mode->timing_data.in.color.color_format = csc_info->in_info.pixel_format; mode->timing_data.out.color.color_format = csc_info->out_info.pixel_format; pixel_bitwidth_conv(csc_info->in_info.data_width, &mode->timing_data.in.color_depth); pixel_bitwidth_conv(csc_info->out_info.data_width, &mode->timing_data.out.color_depth); mode->timing_data.in.detail.hactive = csc_info->timing.hact; mode->timing_data.in.detail.vactive = csc_info->timing.vact; if (hdmi->controller->soft_status.dbg_info.dbg_colorspace != HDMITX_DEBUG_INFO_INVALID) { mode->timing_data.out.color.color_format = (td_u32)hdmi->controller->soft_status.dbg_info.dbg_colorspace; } if (hdmi->controller->soft_status.dbg_info.dbg_colordepth != HDMITX_DEBUG_INFO_INVALID) { mode->timing_data.out.color_depth = (td_u32)hdmi->controller->soft_status.dbg_info.dbg_colordepth; } drv_hdmitx_controller_reset_csc(hdmi->controller); return TD_SUCCESS; } static td_s32 hdmi_module_disp_intf_suspend(td_void *data) { ext_dip_disp_intf *intf = (ext_dip_disp_intf*)data; if (intf->intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { return TD_FAILURE; } return hdmi_module_vo_suspend_func(intf->un_intf.hdmi, TD_FALSE); } static td_s32 hdmi_module_disp_intf_resume(td_void *data) { ext_dip_disp_intf *intf = (ext_dip_disp_intf*)data; if (intf->intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { return TD_FAILURE; } return hdmi_module_vo_resume_func(intf->un_intf.hdmi, TD_FALSE); } static td_s32 hdmi_module_disp_lowpower_enter(td_void *data) { return TD_SUCCESS; } static td_s32 hdmi_module_disp_lowpower_exit(td_void *data) { return TD_SUCCESS; } static td_s32 hdmi_module_disp_poweroff(td_void *data) { return TD_SUCCESS; } static td_s32 hdmi_module_disp_freeze(td_void *data) { return TD_SUCCESS; } static td_s32 hdmi_module_disp_intf_check_validate(ext_dip_disp_intf intf, td_u32 config_mode, ext_dip_disp_intf_info *intf_info) { struct ext_display_mode display_mode; if (intf_info == TD_NULL) { hdmi_log_err("hdmitx%d, param intf_info is NULL, please check param input\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } if (memset_s(&display_mode, sizeof(display_mode), 0, sizeof(display_mode)) != EOK) { hdmi_log_err("hdmitx%d, memset_s fail.\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } if (intf.intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { hdmi_log_err("hdmitx%d, intf_type not hdmi, please check param input\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } if (check_color_depth_validate(intf_info->out_info.data_width) == TD_FAILURE || check_color_depth_validate(intf_info->in_info.data_width) == TD_FAILURE) { hdmi_log_err("hdmitx%d, in data_width:%d, out data_width:%d error!\n", intf.un_intf.hdmi, intf_info->in_info.data_width, intf_info->out_info.data_width); return HDMI_ERR_MODE_NOT_AVAIL; } disp_intf_info2ext_display_mode(intf_info, &display_mode); return hdmi_module_vo_mode_validate_func(intf.un_intf.hdmi, config_mode, &display_mode); } static td_s32 hdmi_module_disp_intf_enable(ext_dip_disp_intf intf) { if (intf.intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { hdmi_log_err("hdmitx%d, intf_type not hdmi, please check param input\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } return hdmi_module_vo_display_on_func(intf.un_intf.hdmi); } static td_s32 hdmi_module_disp_intf_disable(ext_dip_disp_intf intf) { if (intf.intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { hdmi_log_err("hdmitx%d, intf_type not hdmi, please check param input\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } return hdmi_module_vo_display_off_func(intf.un_intf.hdmi); } static td_s32 hdmi_module_disp_intf_detach(ext_dip_disp_intf intf, td_void *data) { if (intf.intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { hdmi_log_err("hdmitx%d, intf_type not hdmi, please check param input\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } return hdmi_module_vo_detach_func(intf.un_intf.hdmi, data); } static td_s32 hdmi_module_disp_intf_attach(ext_dip_disp_intf intf, td_void *data) { if (intf.intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { hdmi_log_err("hdmitx%d, intf_type not hdmi, please check param input\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } return hdmi_module_vo_attach_func(intf.un_intf.hdmi, data); } static td_s32 hdmi_module_disp_intf_prepare(ext_dip_disp_intf intf, td_u32 config_mode, ext_dip_disp_intf_info *intf_info) { struct ext_display_mode display_mode; if (intf_info == TD_NULL) { hdmi_log_err("hdmitx%d, param intf_info is NULL, please check param input\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } if (memset_s(&display_mode, sizeof(display_mode), 0, sizeof(display_mode)) != EOK) { hdmi_log_err("hdmitx%d, memset_s fail.\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } if (intf.intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { hdmi_log_err("hdmitx%d, intf_type not hdmi, please check param input\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } disp_intf_info2ext_display_mode(intf_info, &display_mode); return hdmi_module_vo_prepare_func(intf.un_intf.hdmi, config_mode, &display_mode); } static td_s32 hdmi_module_disp_intf_config(ext_dip_disp_intf intf, td_u32 mode) { if (intf.intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { hdmi_log_err("hdmitx%d, intf_type not hdmi, please check param input\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } return hdmi_module_vo_mode_set_func(intf.un_intf.hdmi, mode); } static td_s32 hdmi_module_disp_intf_get_infoframe_pack(ext_dip_disp_intf intf, const ext_dip_disp_frame_pack_attr *frame, ext_dip_disp_pack_buffer_info *pack_buffer_info) { td_u32 hdmitx_id; if (frame == TD_NULL || pack_buffer_info == TD_NULL || pack_buffer_info->pack_buffer_addr == TD_NULL) { hdmi_log_err("Input params is NULL pointer.\n"); return TD_FAILURE; } if (intf.intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { hdmi_log_err("hdmitx%d, intf_type not hdmi, please check param input\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } hdmitx_id = intf_id_to_hdmi_id(intf.un_intf.hdmi); if (hdmitx_id >= MAX_HDMITX_ID) { hdmi_log_err("hdmitx%d, id is invalid, please check param input\n", hdmitx_id); return HDMI_ERR_INPUT_PARAM_INVALID; } return get_hdmitx_pack_infoframe(hdmitx_id, frame, pack_buffer_info); } static td_s32 hdmi_module_disp_intf_get_edid(ext_dip_disp_intf intf, td_void *data, td_u32 len, td_bool force) { td_s32 ret; struct ext_hdmitx *hdmi = TD_NULL; struct hdmitx_sub_module *sub_module = TD_NULL; struct hdmitx_connector *connector = TD_NULL; td_void *edid_raw = TD_NULL; td_s32 edid_size; if (data == TD_NULL || len == 0) { hdmi_log_err("hdmitx%d, Input params is invalid.\n", intf.un_intf.hdmi); return TD_FAILURE; } if (intf.intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { hdmi_log_err("hdmitx%d, intf_type not hdmi, please check param input\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } hdmi = get_hdmitx_by_id(intf.un_intf.hdmi); if (hdmi == TD_NULL || hdmi->controller == TD_NULL) { hdmi_log_err("hdmitx%d null ptr\n", intf.un_intf.hdmi); return TD_FAILURE; } soc_log_info("hdmitx%u, len %u force %d\n", hdmi->id, len, force); sub_module = &hdmi->controller->sub_module; connector = sub_module->connector; if (connector->hotplug == HPD_PLUGOUT) { hdmi_log_err("hdmitx%u, hpd is 0, please check the connected\n", hdmi->id); return TD_FAILURE; } if (force) { drv_hdmitx_connector_edid_reset(connector); connector->edid_raw = drv_hdmitx_connector_get_edid(connector, sub_module->ddc); } edid_raw = connector->edid_raw ? (td_void *)connector->edid_raw : (td_void *)connector->edid_raw_last; edid_size = connector->edid_raw ? connector->edid_size : connector->edid_size_last; if (edid_raw == TD_NULL || edid_size == 0) { soc_log_info("hdmitx%u, edid_raw data is invalid!, force is %d\n", hdmi->id, force); return TD_FAILURE; } if (len < connector->edid_size) { soc_log_err("hdmitx%u, input buffer is not enough for edid, buffer len: %u, required: %d\n", hdmi->id, len, connector->edid_size); return TD_FAILURE; } ret = memcpy_s(data, len, edid_raw, edid_size); if (ret != EOK) { soc_log_err("memcpy_s err, ret=%#x\n", ret); return ret; } return TD_SUCCESS; } static td_s32 hdmi_module_disp_intf_reset_csc(ext_dip_disp_intf intf, ext_dip_disp_reset_csc_info *csc_info) { td_u32 hdmitx_id; if (csc_info == TD_NULL) { soc_log_info("hdmi id = %d, param csc_info is NULL, please check param input\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } if (intf.intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { soc_log_info("hdmi id = %d, intf_type not hdmi, please check param input\n", intf.un_intf.hdmi); return HDMI_ERR_INPUT_PARAM_INVALID; } hdmitx_id = intf_id_to_hdmi_id(intf.un_intf.hdmi); if (hdmitx_id >= MAX_HDMITX_ID) { soc_log_info("hdmi_id = %d, id is invalid, please check param input\n", hdmitx_id); return HDMI_ERR_INPUT_PARAM_INVALID; } return hdmitx_reset_csc(hdmitx_id, csc_info); } static td_s32 hdmi_module_restore(struct ext_hdmitx *hdmi, struct ext_display_mode *display_mode) { td_s32 ret; struct hdmitx_controller *controller = hdmi->controller; struct hdmitx_sub_module *sub_module = &controller->sub_module; struct hdmitx_connector *connector = sub_module->connector; struct hdmitx_soft_status *soft_status = &controller->soft_status; struct hdmitx_timing_data *timing_data = &soft_status->mode.timing_data; struct hdmitx_timing_mode *timing_mode = TD_NULL; /* step 1: Need wait more than 100ms to Read HPD status,and read edid */ drv_hdmitx_connector_enable_hpd(controller, connector); /* step 2: restore ext_display_mode from register */ drv_hdmitx_controller_hw_get_display_mode(controller); timing_mode = drv_hdmitx_modes_get_timing_mode_by_vic(timing_data->in.vic); if (timing_mode == TD_NULL) { hdmi_log_err("err vic=%d\n", timing_data->in.vic); if (memcpy_s(&soft_status->mode.timing_data, sizeof(soft_status->mode.timing_data), &display_mode->timing_data, sizeof(display_mode->timing_data)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return TD_FAILURE; } } /* hw_config init */ if (hdmi_cur_hw_config_decision(hdmi) != TD_SUCCESS) { soc_log_info("hw config init fail!\n"); } drv_hdmitx_controller_cur_config_check(controller); if (memcpy_s(&soft_status->mode.hdr_data, sizeof(soft_status->mode.hdr_data), &display_mode->hdr_data, sizeof(display_mode->hdr_data)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return TD_FAILURE; } if (memcpy_s(&soft_status->mode.vrr_data, sizeof(soft_status->mode.vrr_data), &display_mode->vrr_data, sizeof(display_mode->vrr_data)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); return TD_FAILURE; } /* step 3: Check video mode valid */ soc_log_info("hdmitx%u vic=%u,c_fmt=%d,c_depth=%d\n", hdmi->id, timing_data->in.vic, timing_data->out.color.color_format, timing_data->out.color_depth); if (is_mode_exceed_port20_cap(hdmi, timing_data) == TD_TRUE) { soc_log_warn("hdmitx%u not support 8k, pls try another one", hdmi->id); } ret = drv_hdmitx_connector_mode_validate(connector, timing_data); if (ret == TD_FALSE) { hdmi_log_info("hdmitx%u, mode(vic=%u,c_fmt=%d,c_depth=%d) isn't avail\n", hdmi->id, timing_data->in.vic, timing_data->out.color.color_format, timing_data->out.color_depth); } soc_log_info("hdmi%d restore ok.\n", hdmi->id); return TD_SUCCESS; } static td_s32 hdmi_module_disp_intf_restore(td_void *data, td_void *dev) { struct ext_hdmitx *hdmi = TD_NULL; struct ext_display_mode display_mode; td_u32 hdmi_id; if (data == TD_NULL || dev == TD_NULL) { hdmi_log_err("data or dev is NULL, please check param input\n"); return TD_FAILURE; } ext_dip_disp_intf_info *intf_info = (ext_dip_disp_intf_info*)dev; ext_dip_disp_intf *intf = (ext_dip_disp_intf*)data; if (intf->intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { return TD_FAILURE; } hdmi_id = intf_id_to_hdmi_id(intf->un_intf.hdmi); if (hdmi_id == MAX_HDMITX_ID) { return TD_FAILURE; } soc_log_info("hdmitx%u\n", hdmi_id); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->vo_ops == TD_NULL) { return TD_FAILURE; } if (!hdmi->attached) { return TD_FAILURE; } disp_intf_info2ext_display_mode(intf_info, &display_mode); return hdmi_module_restore(hdmi, &display_mode); } static ext_dip_disp_intf_callback g_disp_callback[MAX_HDMITX_ID]; td_s32 hdmi_module_disp_register_callback(ext_dip_disp_intf intf, ext_dip_disp_intf_callback *callback) { td_u32 id; struct ext_hdmitx *hdmi = TD_NULL; if (intf.intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { hdmi_log_err("intf should hdmi type\n"); return TD_FAILURE; } id = (td_u32)intf.un_intf.hdmi; hdmi = get_hdmitx_by_id(id); if (hdmi == TD_NULL || callback == TD_NULL) { hdmi_log_err("hdmi%d null ptr\n", intf.un_intf.hdmi); return TD_FAILURE; } if (g_disp_callback[id].func != TD_NULL) { soc_log_warn("[hdmitx%d]you have register callback!\n", hdmi->id); return TD_FAILURE; } osal_mutex_lock(&hdmi->disp_mutex); g_disp_callback[id] = *callback; osal_mutex_unlock(&hdmi->disp_mutex); return TD_SUCCESS; } td_s32 hdmi_module_disp_unregister_callback(ext_dip_disp_intf intf, ext_dip_disp_intf_callback *callback) { td_u32 id; struct ext_hdmitx *hdmi = TD_NULL; if (intf.intf_type != EXT_DIP_DISP_INTF_TYPE_HDMI) { hdmi_log_err("hdmitx%d intf should hdmi type\n", intf.un_intf.hdmi); return TD_FAILURE; } id = (td_u32)intf.un_intf.hdmi; hdmi = get_hdmitx_by_id(id); if (hdmi == TD_NULL || callback == TD_NULL) { hdmi_log_err("hdmitx%d null ptr\n", intf.un_intf.hdmi); return TD_FAILURE; } if (g_disp_callback[id].func == TD_NULL) { hdmi_log_err("[hdmitx%d]you un-register callback!\n", hdmi->id); return TD_FAILURE; } osal_mutex_lock(&hdmi->disp_mutex); g_disp_callback[id].func = TD_NULL; g_disp_callback[id].private = TD_NULL; osal_mutex_unlock(&hdmi->disp_mutex); return TD_SUCCESS; } td_void drv_hdmitx_disp_event(struct ext_hdmitx *hdmi, ext_drv_rpt_tx_event event) { if (hdmi == TD_NULL) { hdmi_log_err("null ptr!\n"); return; } switch (event) { case EXT_DRV_RPT_TX_EVENT_PLUG_IN: event = EXT_DRV_RPT_TX_EVENT_PLUG_IN; break; case EXT_DRV_RPT_TX_EVENT_PLUG_OUT: event = EXT_DRV_RPT_TX_EVENT_PLUG_OUT; break; default: hdmi_log_err("[hdmitx%d]disp erro event %d\n", hdmi->id, event); return; } osal_mutex_lock(&hdmi->disp_mutex); if (g_disp_callback[hdmi->id].func != TD_NULL) { soc_log_info("[hdmitx%d]disp event %d\n", hdmi->id, event); g_disp_callback[hdmi->id].func((ext_drv_display)hdmi->id, event, g_disp_callback[hdmi->id].private); } osal_mutex_unlock(&hdmi->disp_mutex); return ; } dip_disp_pmops g_module_vo_pmops = { .disp_suspend = hdmi_module_disp_intf_suspend, .disp_resume = hdmi_module_disp_intf_resume, .disp_lowpower_enter = hdmi_module_disp_lowpower_enter, .disp_lowpower_exit = hdmi_module_disp_lowpower_exit, .disp_poweroff = hdmi_module_disp_poweroff, .disp_freeze = hdmi_module_disp_freeze, .disp_restore = hdmi_module_disp_intf_restore, }; ext_disp_intf_func g_module_vo_ops = { .disp_intf_pmops = &g_module_vo_pmops, .disp_intf_check_validate = hdmi_module_disp_intf_check_validate, .disp_intf_enable = hdmi_module_disp_intf_enable, .disp_intf_disable = hdmi_module_disp_intf_disable, .disp_intf_attach = hdmi_module_disp_intf_attach, .disp_intf_detach = hdmi_module_disp_intf_detach, .disp_intf_prepare = hdmi_module_disp_intf_prepare, .disp_intf_config = hdmi_module_disp_intf_config, .disp_intf_get_frame_pack = hdmi_module_disp_intf_get_infoframe_pack, .disp_intf_get_edid = hdmi_module_disp_intf_get_edid, .disp_intf_reset_csc = hdmi_module_disp_intf_reset_csc, .disp_intf_register_callback = hdmi_module_disp_register_callback, .disp_intf_unregister_callback = hdmi_module_disp_unregister_callback, .disp_intf_phy_cross_en = hdmitx_set_phy_cross_en, .disp_intf_get_phy_cross = hdmitx_get_phy_cross, }; static td_s32 hdmi_module_ao_get_eld_func(td_s32 ao_intf_id, td_void *data, td_u8 *buf, size_t len) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; if (buf == TD_NULL) { hdmi_log_err("param invalid, null ptr or len is 0.\n"); return HDMI_ERR_INPUT_PARAM_INVALID; } hdmi_id = intf_id_to_hdmi_id(ao_intf_id); if (hdmi_id == MAX_HDMITX_ID) { return HDMI_ERR_INPUT_PARAM_INVALID; } soc_log_info("hdmitx%u\n", hdmi_id); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->ao_ops == TD_NULL) { return TD_FAILURE; } return hdmi->ao_ops->get_eld(hdmi, data, buf, len); } static td_s32 hdmi_module_ao_hw_params_validate_func(td_s32 ao_intf_id, const struct ao_attr *attr) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; if (attr == TD_NULL) { return TD_FAILURE; } hdmi_id = intf_id_to_hdmi_id(ao_intf_id); if (hdmi_id == MAX_HDMITX_ID) { return HDMI_ERR_INPUT_PARAM_INVALID; } soc_log_info("hdmitx%u\n", hdmi_id); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->ao_ops == TD_NULL) { return TD_FAILURE; } return hdmi->ao_ops->hw_params_validate(hdmi, attr); } static td_s32 hdmi_module_ao_hw_params_func(td_s32 ao_intf_id, const struct ao_attr *attr) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; hdmi_id = intf_id_to_hdmi_id(ao_intf_id); if (hdmi_id == MAX_HDMITX_ID) { return HDMI_ERR_INPUT_PARAM_INVALID; } if (attr == TD_NULL) { hdmi_log_err("param invalid, null ptr.\n"); return HDMI_ERR_INPUT_PARAM_INVALID; } hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->ao_ops == TD_NULL) { return TD_FAILURE; } return hdmi->ao_ops->hw_params(hdmi, attr); } static td_s32 hdmi_module_ao_digital_mute_func(td_s32 ao_intf_id, const td_void *data, td_bool enable) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; hdmi_id = intf_id_to_hdmi_id(ao_intf_id); if (hdmi_id == MAX_HDMITX_ID) { return HDMI_ERR_INPUT_PARAM_INVALID; } hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->ao_ops == TD_NULL) { return TD_FAILURE; } return hdmi->ao_ops->digital_mute(hdmi, data, enable); } static td_s32 hdmi_module_ao_hpd_detect_func(td_s32 ao_intf_id, td_u32 *status) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; if (status == TD_NULL) { hdmi_log_err("input params is NULL pointer.\n"); return HDMI_ERR_INPUT_PARAM_INVALID; } hdmi_id = intf_id_to_hdmi_id(ao_intf_id); if (hdmi_id == MAX_HDMITX_ID) { hdmi_log_err("hdmi id exceed max id.\n"); return HDMI_ERR_INPUT_PARAM_INVALID; } soc_log_info("hdmitx%u\n", hdmi_id); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->ao_ops == TD_NULL) { hdmi_log_err("hdmi not init.\n"); return TD_FAILURE; } return hdmi->ao_ops->hpd_detect(hdmi, status); } static td_s32 hdmi_module_ao_register_notifier_func(td_s32 ao_intf_id, struct hdmitx_notifier *notifier) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; if (notifier == TD_NULL) { hdmi_log_err("param invalid\n"); return HDMI_ERR_INPUT_PARAM_INVALID; } hdmi_id = intf_id_to_hdmi_id(ao_intf_id); if (hdmi_id == MAX_HDMITX_ID) { return HDMI_ERR_INPUT_PARAM_INVALID; } soc_log_info("hdmitx%u priority=%d\n", hdmi_id, notifier->priority); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->ao_ops == TD_NULL) { return TD_FAILURE; } return hdmi->ao_ops->register_notifier(hdmi, notifier); } static td_s32 hdmi_module_ao_unregister_notifier_func(td_s32 ao_intf_id, struct hdmitx_notifier *notifier) { struct ext_hdmitx *hdmi = TD_NULL; td_u32 hdmi_id; if (notifier == TD_NULL) { hdmi_log_err("param invalid\n"); return HDMI_ERR_INPUT_PARAM_INVALID; } hdmi_id = intf_id_to_hdmi_id(ao_intf_id); if (hdmi_id == MAX_HDMITX_ID) { return HDMI_ERR_INPUT_PARAM_INVALID; } soc_log_info("hdmitx%u priority=%d\n", hdmi_id, notifier->priority); hdmi = get_hdmitx_by_id(hdmi_id); if (hdmi == TD_NULL || hdmi->ao_ops == TD_NULL) { return TD_FAILURE; } return hdmi->ao_ops->unregister_notifier(hdmi, notifier); } struct ext_ao_intf_module_ops g_module_ao_ops = { .get_eld = hdmi_module_ao_get_eld_func, .hw_params_validate = hdmi_module_ao_hw_params_validate_func, .hw_params = hdmi_module_ao_hw_params_func, .digital_mute = hdmi_module_ao_digital_mute_func, .hpd_detect = hdmi_module_ao_hpd_detect_func, .register_notifier = hdmi_module_ao_register_notifier_func, .unregister_notifier = hdmi_module_ao_unregister_notifier_func, }; td_s32 hdmi_module_rpt_register_callback(ext_drv_rpt_tx_id id, const ext_drv_rpt_tx_callback *callback) { td_s32 ret; struct ext_hdmitx *hdmi = TD_NULL; soc_log_info("hdmitx%d\n", id); hdmi = get_hdmitx_by_id(id); if (hdmi == TD_NULL || hdmi->controller == TD_NULL || callback == TD_NULL) { hdmi_log_err("hdmi%d null ptr\n", id); return TD_FAILURE; } ret = drv_hdmitx_hdcp_reg_rpt_callback(hdmi->controller->sub_module.hdcp, callback); return ret; } td_s32 hdmi_module_rpt_unregister_callback(ext_drv_rpt_tx_id id, const ext_drv_rpt_tx_callback *callback) { td_s32 ret; struct ext_hdmitx *hdmi = TD_NULL; soc_log_info("hdmitx%d\n", id); hdmi = get_hdmitx_by_id(id); if (hdmi == TD_NULL || hdmi->controller == TD_NULL || callback == TD_NULL) { hdmi_log_err("hdmi%d null ptr\n", id); return TD_FAILURE; } ret = drv_hdmitx_hdcp_unreg_rpt_callback(hdmi->controller->sub_module.hdcp, callback); return ret; } td_s32 hdmi_module_rpt_restart(ext_drv_rpt_tx_id id) { struct ext_hdmitx *hdmi = TD_NULL; soc_log_info("hdmitx%d\n", id); hdmi = get_hdmitx_by_id(id); if (hdmi == TD_NULL || hdmi->controller == TD_NULL) { hdmi_log_err("hdmi%d null ptr\n", id); return TD_FAILURE; } drv_hdmitx_hdcp_off(hdmi->controller->sub_module.hdcp); drv_hdmitx_hdcp_on(hdmi->controller->sub_module.hdcp); return TD_SUCCESS; } td_s32 hdmi_module_rpt_set_stream_id(ext_drv_rpt_tx_id id, ext_drv_rpt_tx_hdcp_stream_id stream_id) { td_u8 tmp_id; td_s32 ret; struct ext_hdmitx *hdmi = TD_NULL; soc_log_info("hdmitx%d stream_id=%d\n", id, stream_id); hdmi = get_hdmitx_by_id(id); if (hdmi == TD_NULL || hdmi->controller == TD_NULL) { hdmi_log_err("hdmi%d null ptr\n", id); return TD_FAILURE; } tmp_id = (td_u8)stream_id; ret = drv_hdmitx_hdcp_set_stream_id(hdmi->controller->sub_module.hdcp, tmp_id); return ret; } td_s32 hdmi_module_rpt_get_status(ext_drv_rpt_tx_id id, ext_drv_rpt_tx_status *status) { td_s32 ret; hdmitx_ioctl_getstatus data; struct ext_hdmitx *hdmi = TD_NULL; hdmi = get_hdmitx_by_id(id); if (hdmi == TD_NULL || hdmi->controller == TD_NULL || status == TD_NULL) { hdmi_log_err("hdmi%d null ptr\n", id); return TD_FAILURE; } ret = hdmitx_ioc_get_status(hdmi, (td_void *)&data); if (ret != TD_SUCCESS) { soc_log_warn("hdmi%d get status fail!\n", hdmi->id); return TD_FAILURE; } status->hotplug = (ext_drv_rpt_tx_hotplug_status)data.hotplug; status->rxsen = (ext_drv_rpt_tx_rsen_status)data.rsen; status->output_en = data.output_enable; soc_log_dbg("hdmitx%d hotplug=%d rxsen=%d output_en=%d\n", id, status->hotplug, status->rxsen, status->output_en); return TD_SUCCESS; } td_s32 hdmi_module_rpt_get_hdcp_status(ext_drv_rpt_tx_id id, ext_drv_rpt_tx_hdcp_status *status) { td_s32 ret; struct hdcp_usr_status usr_status; struct ext_hdmitx *hdmi = TD_NULL; hdmi = get_hdmitx_by_id(id); if (hdmi == TD_NULL || hdmi->controller == TD_NULL || status == TD_NULL) { hdmi_log_err("hdmi%d null ptr\n", id); return TD_FAILURE; } ret = drv_hdmitx_hdcp_get_user_status(hdmi->controller->sub_module.hdcp, &usr_status); if (ret != TD_SUCCESS) { soc_log_warn("hdmi%d get hdcp status fail!\n", hdmi->id); return TD_FAILURE; } status->auth_start = usr_status.auth_start; status->auth_success = usr_status.auth_success; status->cur_reauth_times = usr_status.cur_reauth_times; status->work_version = (ext_drv_rpt_tx_hdcp_ver)usr_status.work_version; status->err_code = (ext_drv_rpt_tx_hdcp_err_code)usr_status.err_code; soc_log_info("hdmitx%d auth_start=%d auth_success=%d cur_reauth_times=%u work_version=%d err_code=%d\n", id, status->auth_start, status->auth_success, status->cur_reauth_times, status->work_version, status->err_code); return TD_SUCCESS; } td_s32 hdmi_module_rpt_hdcp14_downstream_info(ext_drv_rpt_tx_id id, ext_drv_rpt_tx_hdcp14_downstream_info *info) { td_s32 ret; struct hdcp1x_sink_status *sink_st = TD_NULL; struct ext_hdmitx *hdmi = TD_NULL; soc_log_info("hdmitx%d\n", id); hdmi = get_hdmitx_by_id(id); if (hdmi == TD_NULL || hdmi->controller == TD_NULL || info == TD_NULL) { hdmi_log_err("hdmi%d null ptr\n", id); return TD_FAILURE; } sink_st = osal_kmalloc(sizeof(struct hdcp1x_sink_status), OSAL_GFP_KERNEL); if (sink_st == TD_NULL) { hdmi_log_err("hdmi%d null ptr\n", id); return TD_FAILURE; } ret = drv_hdmitx_hdcp_get_1x_sink_status(hdmi->controller->sub_module.hdcp, sink_st); if (ret != TD_SUCCESS) { soc_log_warn("hdmi%d get hdcp sink status fail!\n", hdmi->id); goto err_out_free; } if (memcpy_s(info->bksv, sizeof(info->bksv), sink_st->b_ksv, sizeof(sink_st->b_ksv)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); goto err_out_free; } if (memcpy_s(info->bksv_list, sizeof(info->bksv_list), sink_st->bksv_list_data, sizeof(sink_st->bksv_list_data)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); goto err_out_free; } info->downstream_is_rpt = !!sink_st->bcaps_p1.u8.repeater; info->depth = sink_st->bstatus.u16.depth; info->dev_cnt = sink_st->bstatus.u16.device_cnt; info->max_devs_exceeded = !!sink_st->bstatus.u16.max_devs_exceeded; info->max_cascade_exceeded = !!sink_st->bstatus.u16.max_cascade_exceeded; osal_kfree(sink_st); sink_st = TD_NULL; return TD_SUCCESS; err_out_free: osal_kfree(sink_st); sink_st = TD_NULL; return TD_FAILURE; } td_s32 hdmi_module_rpt_hdcp2x_downstream_info(ext_drv_rpt_tx_id id, ext_drv_rpt_tx_hdcp2x_downstream_info *info) { td_s32 ret; struct hdcp2x_sink_status *sink_st = TD_NULL; struct ext_hdmitx *hdmi = TD_NULL; soc_log_info("hdmitx%d\n", id); hdmi = get_hdmitx_by_id(id); if (hdmi == TD_NULL || hdmi->controller == TD_NULL || info == TD_NULL) { hdmi_log_err("hdmi%d null ptr\n", id); return TD_FAILURE; } sink_st = osal_kmalloc(sizeof(struct hdcp2x_sink_status), OSAL_GFP_KERNEL); if (sink_st == TD_NULL) { hdmi_log_err("hdmi%d null ptr\n", id); return TD_FAILURE; } ret = drv_hdmitx_hdcp_get_2x_sink_status(hdmi->controller->sub_module.hdcp, sink_st); if (ret != TD_SUCCESS) { soc_log_warn("hdmi%d get hdcp sink status fail!\n", hdmi->id); goto err_out_free; } if (memcpy_s(info->recvid, sizeof(info->recvid), sink_st->recv_id, sizeof(sink_st->recv_id)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); goto err_out_free; } if (memcpy_s(info->recvid_list, sizeof(info->recvid_list), sink_st->recv_id_list, sizeof(sink_st->recv_id_list)) != EOK) { hdmi_log_err("memcpy_s fail.\n"); goto err_out_free; } info->downstream_is_rpt = !!sink_st->rx_info.dev_cnt; info->depth = sink_st->rx_info.depth; info->dev_cnt = sink_st->rx_info.dev_cnt; info->max_devs_exceeded = sink_st->rx_info.max_devs_exceeded; info->max_cascade_exceeded = sink_st->rx_info.max_cascade_exceeded; info->hdcp20_repeater_downstream = !!sink_st->rx_info.max_hdcp20_down_stream; info->hdcp1x_device_downstream = sink_st->rx_info.max_hdcp1x_down_stream; osal_kfree(sink_st); sink_st = TD_NULL; return TD_SUCCESS; err_out_free: osal_kfree(sink_st); sink_st = TD_NULL; return TD_FAILURE; } struct ext_drv_rpt_tx_ops g_module_rpt_ops = { .register_callback = hdmi_module_rpt_register_callback, .unregister_callback = hdmi_module_rpt_unregister_callback, .restart = hdmi_module_rpt_restart, .set_stream_id = hdmi_module_rpt_set_stream_id, .get_status = hdmi_module_rpt_get_status, .get_hdcp_status = hdmi_module_rpt_get_hdcp_status, .get_hdcp14_downstream_info = hdmi_module_rpt_hdcp14_downstream_info, .get_hdcp2x_downstream_info = hdmi_module_rpt_hdcp2x_downstream_info, }; td_s32 __init hdmi_module_init(td_void) { td_s32 i; td_s32 ret; td_u8 hdmitx_id = 0; ext_chip_name_id chip_name_id; td_u8 hdmitx_port = 0; ext_drv_sys_get_chip_name_id(&chip_name_id); if (chip_name_id == CHIP_NAME_RESERVED5) { soc_log_err("not support hdmitx.\n"); return TD_FAILURE; } ret = ext_drv_callback_register(SOC_ID_HDMITX, SOC_ID_AO, "SOC_HDMITX", (td_void *)&g_module_ao_ops, TD_NULL); if (ret) { hdmi_log_err("register callback for ao failed, ret :%d\n", ret); return ret; } ret = ext_drv_callback_register(SOC_ID_HDMITX, SOC_ID_DISP, "SOC_HDMITX", (td_void *)&g_module_vo_ops, TD_NULL); if (ret) { hdmi_log_err("register callback for disp failed, ret :%d\n", ret); return ret; } ret = ext_drv_callback_register(SOC_ID_HDMITX, SOC_ID_HDMIRX, "SOC_HDMITX", (td_void *)&g_module_rpt_ops, TD_NULL); if (ret) { hdmi_log_err("register callback for hdmirx failed, ret :%d\n", ret); return ret; } /* Init the mapping */ g_hdmi_cnt = 0; for (i = 0; i < MAX_HDMITX_ID; i++) { g_mapping[i].hdmi_id = -1; g_mapping[i].hdmi = TD_NULL; } if (hal_cross_init() != TD_SUCCESS) { soc_log_alert("hal_cross_init fail\n"); return TD_FAILURE; } while (hdmitx_id < HDMITX_PORT_NUM) { if (hdmitx_check_valid_by_pdm_params(hdmitx_id, &hdmitx_port) != TD_TRUE) { hdmitx_id++; continue; } if (hdmitx_device_init(hdmitx_port) != TD_SUCCESS) { hdmi_log_err("hdmitx_device_init fail, hdmitx_id = %u\n", hdmitx_port); return TD_FAILURE; } hdmitx_id++; } soc_print("Load soc_hdmitx.ko success.\t(%s)\n", VERSION_STRING); return TD_SUCCESS; } static td_void __exit hdmi_module_exit(td_void) { hal_cross_deinit(); ext_drv_callback_unregister(SOC_ID_HDMITX); hdmitx_device_deinit(); soc_print("Unload soc_hdmitx.ko success.\t(%s)\n", VERSION_STRING); } #ifdef MODULE osal_module_init(hdmi_module_init); osal_module_exit(hdmi_module_exit); #else int drv_hdmi_mod_init(td_void) { return hdmi_module_init(); } EXPORT_SYMBOL(drv_hdmi_mod_init); td_void drv_hdmi_mod_exit(td_void) { hdmi_module_exit(); } EXPORT_SYMBOL(drv_hdmi_mod_exit); soc_initcall(drv_hdmi_mod_init); #endif OSAL_MODULE_DESCRIPTION("Huanglong HDMITX Driver"); OSAL_MODULE_LICENSE("GPL v2");