You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4937 lines
160 KiB
4937 lines
160 KiB
/*
|
|
* 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 <linux/huanglong/securec.h>
|
|
#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");
|