blob: c33751f15ee9d9226a0d56eb483aa858d09577dd [file]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/mm.h>
#include <linux/major.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/devinfo.h>
#include <linux/pinctrl/consumer.h>
#include <linux/amlogic/clk_measure.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/ctype.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/reboot.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
//#include <linux/amlogic/cpu_version.h>
#include <linux/amlogic/media/vout/vinfo.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#if IS_ENABLED(CONFIG_AMLOGIC_SND_SOC)
#include <linux/amlogic/media/sound/aout_notify.h>
#endif
#include <linux/amlogic/media/vout/hdmi_tx21/hdmi_info_global.h>
#include <linux/amlogic/media/vout/hdmi_tx21/hdmi_tx_ddc.h>
#include <linux/amlogic/media/vout/hdmi_tx21/hdmi_tx_module.h>
#include <linux/amlogic/media/vout/hdmi_tx21/hdmi_config.h>
#include <linux/amlogic/media/vout/hdmi_tx_ext.h>
#include <linux/amlogic/media/vrr/vrr.h>
#include <linux/extcon.h>
#include <linux/extcon-provider.h>
#include "hdmi_tx_ext.h"
#include "hdmi_tx.h"
#include <linux/amlogic/gki_module.h>
#include <linux/component.h>
#include <uapi/drm/drm_mode.h>
#include <drm/amlogic/meson_drm_bind.h>
#include <../../vin/tvin/tvin_global.h>
#include <../../vin/tvin/hdmirx/hdmi_rx_repeater.h>
#include <hdmitx_boot_parameters.h>
#include <hdmitx_drm_hook.h>
#include <hdmitx_sysfs_common.h>
#include <linux/amlogic/media/vout/hdmitx_common/hdmitx_common.h>
#define HDMI_TX_COUNT 32
#define HDMI_TX_POOL_NUM 6
#define HDMI_TX_RESOURCE_NUM 4
#define HDMI_TX_PWR_CTRL_NUM 6
static u8 hdmi_allm_passthough_en;
unsigned int rx_hdcp2_ver;
//static unsigned int hdcp_ctl_lvl;
#define TEE_HDCP_IOC_START _IOW('P', 0, int)
static struct class *hdmitx_class;
static int set_disp_mode_auto(void);
static void hdmitx_get_edid(struct hdmitx_dev *hdev);
static void hdmitx_set_drm_pkt(struct master_display_info_s *data);
static void hdmitx_set_vsif_pkt(enum eotf_type type, enum mode_type
tunnel_mode, struct dv_vsif_para *data, bool signal_sdr);
static void hdmitx_set_hdr10plus_pkt(u32 flag,
struct hdr10plus_para *data);
static void hdmitx_set_cuva_hdr_vsif(struct cuva_hdr_vsif_para *data);
static void hdmitx_set_cuva_hdr_vs_emds(struct cuva_hdr_vs_emds_para *data);
static void hdmitx_set_emp_pkt(u8 *data, u32 type, u32 size);
static int check_fbc_special(u8 *edid_dat);
static void clear_rx_vinfo(struct hdmitx_dev *hdev);
static void edidinfo_attach_to_vinfo(struct hdmitx_dev *hdev);
static void edidinfo_detach_to_vinfo(struct hdmitx_dev *hdev);
static void update_current_para(struct hdmitx_dev *hdev);
static void hdmi_tx_enable_ll_mode(bool enable);
static int hdmitx_hook_drm(struct device *device);
static int hdmitx_unhook_drm(struct device *device);
static void tee_comm_dev_reg(struct hdmitx_dev *hdev);
static void tee_comm_dev_unreg(struct hdmitx_dev *hdev);
static bool is_cur_tmds_div40(struct hdmitx_dev *hdev);
static void hdmitx_resend_div40(struct hdmitx_dev *hdev);
static int hdmitx_check_vic(int vic);
/*
* Normally, after the HPD in or late resume, there will reading EDID, and
* notify application to select a hdmi mode output. But during the mode
* setting moment, there may be HPD out. It will clear the edid data, ..., etc.
* To avoid such case, here adds the hdmimode_mutex to let the HPD in, HPD out
* handler and mode setting sequentially.
*/
/* static DEFINE_MUTEX(hdmimode_mutex); */
#ifdef CONFIG_AMLOGIC_VOUT_SERVE
static struct vinfo_s *hdmitx_get_current_vinfo(void *data);
#else
static struct vinfo_s *hdmitx_get_current_vinfo(void *data)
{
return NULL;
}
#endif
#ifdef CONFIG_OF
static struct amhdmitx_data_s amhdmitx_data_t7 = {
.chip_type = MESON_CPU_ID_T7,
.chip_name = "t7",
};
static struct amhdmitx_data_s amhdmitx_data_s5 = {
.chip_type = MESON_CPU_ID_S5,
.chip_name = "s5",
};
static const struct of_device_id meson_amhdmitx_of_match[] = {
{
.compatible = "amlogic, amhdmitx-t7",
.data = &amhdmitx_data_t7,
},
{
.compatible = "amlogic, amhdmitx-s5",
.data = &amhdmitx_data_s5,
},
{},
};
#else
#define meson_amhdmitx_dt_match NULL
#endif
static DEFINE_MUTEX(setclk_mutex);
static DEFINE_MUTEX(getedid_mutex);
static struct hdmitx_dev hdmitx21_device;
struct hdmitx_dev *get_hdmitx21_device(void)
{
return &hdmitx21_device;
}
EXPORT_SYMBOL(get_hdmitx21_device);
static int get_hdmitx_hdcp_ctl_lvl_to_drm(void)
{
pr_info("%s hdmitx21_%d\n", __func__, hdmitx21_device.hdcp_ctl_lvl);
return hdmitx21_device.hdcp_ctl_lvl;
}
int get_hdmitx21_init(void)
{
return hdmitx21_device.hdmi_init;
}
struct vsdb_phyaddr *get_hdmitx21_phy_addr(void)
{
return &hdmitx21_device.hdmi_info.vsdb_phy_addr;
}
static const struct dv_info dv_dummy;
static struct dv_info ext_dvinfo;
static int log21_level;
static bool hdmitx_edid_done;
static struct vout_device_s hdmitx_vdev = {
.dv_info = &hdmitx21_device.tx_comm.rxcap.dv_info,
.fresh_tx_hdr_pkt = hdmitx_set_drm_pkt,
.fresh_tx_vsif_pkt = hdmitx_set_vsif_pkt,
.fresh_tx_hdr10plus_pkt = hdmitx_set_hdr10plus_pkt,
.fresh_tx_cuva_hdr_vsif = hdmitx_set_cuva_hdr_vsif,
.fresh_tx_cuva_hdr_vs_emds = hdmitx_set_cuva_hdr_vs_emds,
.fresh_tx_emp_pkt = hdmitx_set_emp_pkt,
.get_attr = get21_attr,
.setup_attr = setup21_attr,
/* .video_mute = hdmitx21_video_mute_op, */
};
static struct extcon_dev *hdmitx_extcon_hdmi;
static const unsigned int hdmi_extcon_cable[] = {
EXTCON_DISP_HDMI,
EXTCON_NONE,
};
static struct hdmitx_uevent hdmi_events[] = {
{
.type = HDMITX_HPD_EVENT,
.env = "hdmitx_hpd=",
},
{
.type = HDMITX_HDCP_EVENT,
.env = "hdmitx_hdcp=",
},
{
.type = HDMITX_AUDIO_EVENT,
.env = "hdmitx_audio=",
},
{
.type = HDMITX_HDCPPWR_EVENT,
.env = "hdmitx_hdcppwr=",
},
{
.type = HDMITX_HDR_EVENT,
.env = "hdmitx_hdr=",
},
{
.type = HDMITX_RXSENSE_EVENT,
.env = "hdmitx_rxsense=",
},
{
.type = HDMITX_CEDST_EVENT,
.env = "hdmitx_cedst=",
},
{ /* end of hdmi_events[] */
.type = HDMITX_NONE_EVENT,
},
};
/* indicate plugout before systemcontrol boot */
static bool plugout_mute_flg;
static char hdmichecksum[11] = {
'i', 'n', 'v', 'a', 'l', 'i', 'd', 'c', 'r', 'c', '\0'
};
static char invalidchecksum[11] = {
'i', 'n', 'v', 'a', 'l', 'i', 'd', 'c', 'r', 'c', '\0'
};
static char emptychecksum[11] = {0};
int hdmitx21_set_uevent_state(enum hdmitx_event type, int state)
{
int ret = -1;
struct hdmitx_uevent *event;
for (event = hdmi_events; event->type != HDMITX_NONE_EVENT; event++) {
if (type == event->type)
break;
}
if (event->type == HDMITX_NONE_EVENT)
return ret;
event->state = state;
if (log21_level == 0xfe)
pr_info("[%s] event_type: %s%d\n", __func__, event->env, state);
return 0;
}
static u32 is_passthrough_switch;
int hdmitx21_set_uevent(enum hdmitx_event type, int val)
{
char env[MAX_UEVENT_LEN];
struct hdmitx_uevent *event = hdmi_events;
struct hdmitx_dev *hdev = get_hdmitx21_device();
char *envp[2];
int ret = -1;
for (event = hdmi_events; event->type != HDMITX_NONE_EVENT; event++) {
if (type == event->type)
break;
}
if (event->type == HDMITX_NONE_EVENT)
return ret;
if (event->state == val)
return ret;
event->state = val;
memset(env, 0, sizeof(env));
envp[0] = env;
envp[1] = NULL;
snprintf(env, MAX_UEVENT_LEN, "%s%d", event->env, val);
ret = kobject_uevent_env(&hdev->hdtx_dev->kobj, KOBJ_CHANGE, envp);
if (log21_level == 0xfe)
pr_info("%s[%d] %s %d\n", __func__, __LINE__, env, ret);
return ret;
}
/* There are 3 callback functions for front HDR/DV/HDR10+ modules to notify
* hdmi drivers to send out related HDMI infoframe
* hdmitx_set_drm_pkt() is for HDR 2084 SMPTE, HLG, etc.
* hdmitx_set_vsif_pkt() is for DV
* hdmitx_set_hdr10plus_pkt is for HDR10+
* Front modules may call the 2nd, and next call the 1st, and the realted flags
* are remained the same. So, add hdr_status_pos and place it in the above 3
* functions to record the position.
*/
static int hdr_status_pos;
static inline void hdmitx_notify_hpd(int hpd, void *p)
{
if (hpd)
hdmitx21_event_notify(HDMITX_PLUG, p);
else
hdmitx21_event_notify(HDMITX_UNPLUG, NULL);
}
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
#include <linux/amlogic/pm.h>
static void hdmitx_early_suspend(struct early_suspend *h)
{
struct hdmitx_dev *hdev = (struct hdmitx_dev *)h->param;
if (hdev->aon_output) {
pr_info("%s return, HDMI signal enabled\n", __func__);
return;
}
/* Here remove the hpd_lock. Suppose at beginning, the hdmi cable is
* unconnected, and system goes to suspend, during this time, cable
* plugin. In this time, there will be no CEC physical address update
* and must wait the resume.
*/
mutex_lock(&hdev->hdmimode_mutex);
/* under suspend, driver should not respond to mode setting,
* as it may cause logic abnormal, most importantly,
* it will enable hdcp and occupy DDC channel with high
* priority, though there's protection in system control,
* driver still need protection in case of old android version
*/
hdev->suspend_flag = true;
rx_hdcp2_ver = 0;
hdev->ready = 0;
hdmitx_vrr_disable();
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_SUSFLAG, 1);
usleep_range(10000, 10010);
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_AVMUTE_OP, SET_AVMUTE);
usleep_range(10000, 10010);
pr_info("HDMITX: Early Suspend\n");
frl_tx_stop(hdev);
hdmitx21_disable_hdcp(hdev);
hdmitx21_rst_stream_type(hdev->am_hdcp);
hdev->hwop.cntl(hdev, HDMITX_EARLY_SUSPEND_RESUME_CNTL,
HDMITX_EARLY_SUSPEND);
hdev->tx_comm.cur_VIC = HDMI_0_UNKNOWN;
hdev->output_blank_flag = 0;
hdmitx_set_vsif_pkt(0, 0, NULL, true);
hdmitx_set_hdr10plus_pkt(0, NULL);
clear_rx_vinfo(hdev);
hdmitx21_edid_clear(hdev);
hdmitx21_edid_ram_buffer_clear(hdev);
edidinfo_detach_to_vinfo(hdev);
/* clear audio/video mute flag of stream type */
hdmitx21_video_mute_op(1, VIDEO_MUTE_PATH_2);
hdmitx21_audio_mute_op(1, AUDIO_MUTE_PATH_3);
hdmitx21_set_uevent(HDMITX_HDCPPWR_EVENT, HDMI_SUSPEND);
hdmitx21_set_uevent(HDMITX_AUDIO_EVENT, 0);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CLR_AVI_PACKET, 0);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CLR_VSDB_PACKET, 0);
mutex_unlock(&hdev->hdmimode_mutex);
}
static int hdmitx_is_hdmi_vmode(char *mode_name)
{
enum hdmi_vic vic = hdmitx21_edid_vic_tab_map_vic(mode_name);
if (vic == HDMI_0_UNKNOWN)
return 0;
return 1;
}
static void hdmitx_late_resume(struct early_suspend *h)
{
const struct vinfo_s *info = hdmitx_get_current_vinfo(NULL);
struct hdmitx_dev *hdev = (struct hdmitx_dev *)h->param;
if (hdev->aon_output) {
pr_info("%s return, HDMI signal already enabled\n", __func__);
return;
}
mutex_lock(&hdev->hdmimode_mutex);
if (info && info->name && (hdmitx_is_hdmi_vmode(info->name) == 1))
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_HPLL_FAKE, 0);
hdev->hpd_lock = 0;
/* update status for hpd and switch/state */
hdev->tx_comm.hpd_state = !!(hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_HPD_GPI_ST, 0));
if (hdev->tx_comm.hpd_state)
hdev->already_used = 1;
pr_info("hdmitx hpd state: %d\n", hdev->tx_comm.hpd_state);
/* force to get EDID after resume */
if (hdev->tx_comm.hpd_state) {
/* add i2c soft reset before read EDID */
hdev->hwop.cntlddc(hdev, DDC_GLITCH_FILTER_RESET, 0);
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_I2C_RESET, 0);
hdmitx_get_edid(hdev);
hdmitx_edid_done = true;
}
if (hdev->tv_usage == 0)
hdmitx_notify_hpd(hdev->tx_comm.hpd_state,
hdev->tx_comm.edid_parsing ?
hdev->tx_comm.edid_ptr : NULL);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_AUDIO_MUTE_OP, AUDIO_MUTE);
/* set_disp_mode_auto(); */
/* force revert state to trigger uevent send */
if (hdev->tx_comm.hpd_state) {
hdmitx21_set_uevent_state(HDMITX_HPD_EVENT, 0);
hdmitx21_set_uevent_state(HDMITX_AUDIO_EVENT, 0);
} else {
hdmitx21_set_uevent_state(HDMITX_HPD_EVENT, 1);
hdmitx21_set_uevent_state(HDMITX_AUDIO_EVENT, 1);
}
hdev->suspend_flag = false;
extcon_set_state_sync(hdmitx_extcon_hdmi, EXTCON_DISP_HDMI,
hdev->tx_comm.hpd_state);
hdmitx21_set_uevent(HDMITX_HPD_EVENT, hdev->tx_comm.hpd_state);
hdmitx21_set_uevent(HDMITX_HDCPPWR_EVENT, HDMI_WAKEUP);
hdmitx21_set_uevent(HDMITX_AUDIO_EVENT, hdev->tx_comm.hpd_state);
pr_info("%s: late resume module %d\n", DEVICE_NAME, __LINE__);
hdev->hwop.cntl(hdev, HDMITX_EARLY_SUSPEND_RESUME_CNTL,
HDMITX_LATE_RESUME);
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_SUSFLAG, 0);
pr_info("HDMITX: Late Resume\n");
mutex_unlock(&hdev->hdmimode_mutex);
}
/* Set avmute_set signal to HDMIRX */
static int hdmitx_reboot_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
struct hdmitx_dev *hdev = container_of(nb, struct hdmitx_dev, nb);
hdev->ready = 0;
hdmitx_vrr_disable();
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_AVMUTE_OP, SET_AVMUTE);
usleep_range(10000, 10010);
frl_tx_stop(hdev);
if (hdev->rxsense_policy)
cancel_delayed_work(&hdev->work_rxsense);
if (hdev->cedst_policy)
cancel_delayed_work(&hdev->work_cedst);
hdmitx21_disable_hdcp(hdev);
hdmitx21_rst_stream_type(hdev->am_hdcp);
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_TMDS_PHY_OP, TMDS_PHY_DISABLE);
hdev->hwop.cntl(hdev, HDMITX_EARLY_SUSPEND_RESUME_CNTL,
HDMITX_EARLY_SUSPEND);
return NOTIFY_OK;
}
static struct early_suspend hdmitx_early_suspend_handler = {
.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN - 10,
.suspend = hdmitx_early_suspend,
.resume = hdmitx_late_resume,
.param = &hdmitx21_device,
};
#endif
#define INIT_FLAG_VDACOFF 0x1
/* unplug powerdown */
#define INIT_FLAG_POWERDOWN 0x2
#define INIT_FLAG_NOT_LOAD 0x80
static u8 init_flag;
#undef DISABLE_AUDIO
static void restore_mute(void)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
atomic_t kref_video_mute = hdev->kref_video_mute;
atomic_t kref_audio_mute = hdev->kref_audio_mute;
if (!(atomic_sub_and_test(0, &kref_video_mute))) {
pr_info("%s: hdmitx21_video_mute_op(0, 0) call\n", __func__);
hdmitx21_video_mute_op(0, 0);
}
if (!(atomic_sub_and_test(0, &kref_audio_mute))) {
pr_info("%s: hdmitx21_audio_mute_op(0,0) call\n", __func__);
hdmitx21_audio_mute_op(0, 0);
}
}
int get21_cur_vout_index(void)
/*
* return value: 1, vout; 2, vout2;
*/
{
int vout_index = 1;
return vout_index;
}
static int set_disp_mode(const char *mode)
{
int ret = -1;
enum hdmi_vic vic;
struct hdmitx_dev *hdev = get_hdmitx21_device();
vic = hdmitx21_edid_get_VIC(hdev, mode, 1);
if (strncmp(mode, "2160p30hz", strlen("2160p30hz")) == 0)
vic = HDMI_95_3840x2160p30_16x9;
else if (strncmp(mode, "2160p25hz", strlen("2160p25hz")) == 0)
vic = HDMI_94_3840x2160p25_16x9;
else if (strncmp(mode, "2160p24hz", strlen("2160p24hz")) == 0)
vic = HDMI_93_3840x2160p24_16x9;
else if (strncmp(mode, "smpte24hz", strlen("smpte24hz")) == 0)
vic = HDMI_98_4096x2160p24_256x135;
else
;/* nothing */
if (strncmp(mode, "1080p60hz", strlen("1080p60hz")) == 0)
vic = HDMI_16_1920x1080p60_16x9;
if (strncmp(mode, "1080p50hz", strlen("1080p50hz")) == 0)
vic = HDMI_31_1920x1080p50_16x9;
if (vic != HDMI_0_UNKNOWN) {
hdev->mux_hpd_if_pin_high_flag = 1;
if (hdev->vic_count == 0) {
if (hdev->unplug_powerdown)
return 0;
}
}
hdev->tx_comm.cur_VIC = HDMI_0_UNKNOWN;
ret = hdmitx21_set_display(hdev, vic);
if (ret >= 0) {
hdev->hwop.cntl(hdev, HDMITX_AVMUTE_CNTL, AVMUTE_CLEAR);
hdev->tx_comm.cur_VIC = vic;
hdev->audio_param_update_flag = 1;
restore_mute();
}
if (hdev->tx_comm.cur_VIC == HDMI_0_UNKNOWN) {
if (hdev->hpdmode == 2) {
/* edid will be read again when hpd is muxed and
* it is high
*/
hdmitx21_edid_clear(hdev);
hdev->mux_hpd_if_pin_high_flag = 0;
}
if (hdev->hwop.cntl) {
hdev->hwop.cntl(hdev,
HDMITX_HWCMD_TURNOFF_HDMIHW,
(hdev->hpdmode == 2) ? 1 : 0);
}
}
return ret;
}
static void hdmi_physical_size_update(struct hdmitx_dev *hdev)
{
u32 width, height;
struct vinfo_s *info = NULL;
info = hdmitx_get_current_vinfo(NULL);
if (!info || !info->name) {
pr_info("cann't get valid mode\n");
return;
}
if (info->mode == VMODE_HDMI) {
width = hdev->tx_comm.rxcap.physical_width;
height = hdev->tx_comm.rxcap.physical_height;
if (width == 0 || height == 0) {
info->screen_real_width = info->aspect_ratio_num;
info->screen_real_height = info->aspect_ratio_den;
} else {
info->screen_real_width = width;
info->screen_real_height = height;
}
pr_info("update physical size: %d %d\n",
info->screen_real_width, info->screen_real_height);
}
}
static void hdrinfo_to_vinfo(struct hdr_info *hdrinfo, struct hdmitx_dev *hdev)
{
memcpy(hdrinfo, &hdev->tx_comm.rxcap.hdr_info, sizeof(struct hdr_info));
hdrinfo->colorimetry_support = hdev->tx_comm.rxcap.colorimetry_data;
}
static void rxlatency_to_vinfo(struct vinfo_s *info, struct rx_cap *rx)
{
if (!info || !rx)
return;
info->rx_latency.vLatency = rx->vLatency;
info->rx_latency.aLatency = rx->aLatency;
info->rx_latency.i_vLatency = rx->i_vLatency;
info->rx_latency.i_aLatency = rx->i_aLatency;
}
static void edidinfo_attach_to_vinfo(struct hdmitx_dev *hdev)
{
struct vinfo_s *info = NULL;
/* get current vinfo */
info = hdmitx_get_current_vinfo(NULL);
if (!info || !info->name)
return;
if (strncmp(info->name, "480cvbs", 7) == 0 ||
strncmp(info->name, "576cvbs", 7) == 0 ||
strncmp(info->name, "null", 4) == 0)
return;
mutex_lock(&getedid_mutex);
hdrinfo_to_vinfo(&info->hdr_info, hdev);
memcpy(&ext_dvinfo, &hdev->tx_comm.rxcap.dv_info, sizeof(ext_dvinfo));
if (hdev->para && hdev->para->cd == COLORDEPTH_24B)
memset(&info->hdr_info, 0, sizeof(struct hdr_info));
rxlatency_to_vinfo(info, &hdev->tx_comm.rxcap);
hdmitx_vdev.dv_info = &hdev->tx_comm.rxcap.dv_info;
mutex_unlock(&getedid_mutex);
}
static void edidinfo_detach_to_vinfo(struct hdmitx_dev *hdev)
{
struct vinfo_s *info = NULL;
/* get current vinfo */
info = hdmitx_get_current_vinfo(NULL);
if (!info || !info->name)
return;
edidinfo_attach_to_vinfo(hdev);
memset(&info->hdr_info, 0, sizeof(struct hdr_info));
hdmitx_vdev.dv_info = &dv_dummy;
}
static void hdmitx_up_hdcp_timeout_handler(struct work_struct *work)
{
struct hdmitx_dev *hdev = container_of((struct delayed_work *)work,
struct hdmitx_dev, work_up_hdcp_timeout);
if (hdcp_need_control_by_upstream(hdev)) {
pr_info("hdmitx: enable hdcp as wait upstream hdcp timeout\n");
/* note: hdcp should only be started when hdmi signal ready */
mutex_lock(&hdev->hdmimode_mutex);
if (!hdev->ready || !hdev->tx_comm.hpd_state) {
pr_info("hdmitx: signal ready: %d, hpd_state: %d, eixt hdcp\n",
hdev->ready, hdev->tx_comm.hpd_state);
mutex_unlock(&hdev->hdmimode_mutex);
return;
}
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_AVMUTE_OP, CLR_AVMUTE);
hdmitx21_enable_hdcp(hdev);
mutex_unlock(&hdev->hdmimode_mutex);
} else {
pr_info("wait upstream hdcp timeout, but now not in hdmirx channel\n");
}
}
static void hdmitx_start_hdcp_handler(struct work_struct *work)
{
struct hdmitx_dev *hdev = container_of((struct delayed_work *)work,
struct hdmitx_dev, work_start_hdcp);
unsigned long timeout_sec;
struct hdmitx_common *tx_comm = &hdev->tx_comm;
if (hdcp_need_control_by_upstream(hdev)) {
if (is_passthrough_switch) {
pr_info("hdmitx: enable hdcp by passthrough switch mode\n");
/* note: hdcp should only be started when hdmi signal ready */
mutex_lock(&hdev->hdmimode_mutex);
if (!hdev->ready || !tx_comm->hpd_state) {
pr_info("hdmitx: signal ready: %d, hpd_state: %d, eixt hdcp\n",
hdev->ready, tx_comm->hpd_state);
is_passthrough_switch = 0;
mutex_unlock(&hdev->hdmimode_mutex);
return;
}
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_AVMUTE_OP, CLR_AVMUTE);
hdmitx21_enable_hdcp(hdev);
mutex_unlock(&hdev->hdmimode_mutex);
} else {
/* for source->hdmirx->hdmitx->tv, plug on tv side */
/* 1.for repeater CTS, only start hdcp by upstream side
* 2.however if upstream source side no signal output
* or never start hdcp auth with hdmirx(such as PC),
* then we add 5S timeout period, after 5S timeout,
* it means that no input source start hdcp auth with
* hdmirx, or "no signal" on hdmirx side, then hdmitx
* side will start hdcp auth itself.
* thus both 1/2 will be satisfied.
*/
pr_info("hdmitx: hdcp should started by upstream, wait...\n");
/* timeout period: hdcp1.4 5S, hdcp2.2 2S */
if (rx_hdcp2_ver)
timeout_sec = 2;
else
timeout_sec = hdev->up_hdcp_timeout_sec;
queue_delayed_work(hdev->hdmi_wq, &hdev->work_up_hdcp_timeout,
timeout_sec * HZ);
}
} else {
mutex_lock(&hdev->hdmimode_mutex);
if (!hdev->ready || !tx_comm->hpd_state) {
pr_info("hdmitx: signal ready: %d, hpd_state: %d, eixt hdcp2\n",
hdev->ready, tx_comm->hpd_state);
is_passthrough_switch = 0;
mutex_unlock(&hdev->hdmimode_mutex);
return;
}
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_AVMUTE_OP, CLR_AVMUTE);
hdmitx21_enable_hdcp(hdev);
mutex_unlock(&hdev->hdmimode_mutex);
}
/* clear after start hdcp */
is_passthrough_switch = 0;
}
static int check_vic_4x3_and_16x9(struct hdmitx_dev *hdev, enum hdmi_vic vic)
{
if (vic == HDMI_3_720x480p60_16x9 ||
vic == HDMI_7_720x480i60_16x9 ||
vic == HDMI_18_720x576p50_16x9 ||
vic == HDMI_22_720x576i50_16x9) {
if (hdmitx_check_vic(vic))
return vic;
return vic - 1;
} else if (vic == HDMI_2_720x480p60_4x3 ||
vic == HDMI_6_720x480i60_4x3 ||
vic == HDMI_17_720x576p50_4x3 ||
vic == HDMI_21_720x576i50_4x3) {
if (hdmitx_check_vic(vic))
return vic;
return vic + 1;
}
return vic;
}
static int set_disp_mode_auto(void)
{
int ret = -1;
struct vinfo_s *info = NULL;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
struct hdmi_format_para *para = NULL;
u8 mode[32];
enum hdmi_vic vic = HDMI_0_UNKNOWN;
mutex_lock(&hdev->hdmimode_mutex);
if (tx_comm->hpd_state == 0) {
pr_info("current hpd_state0, exit %s\n", __func__);
mutex_unlock(&hdev->hdmimode_mutex);
return -1;
}
if (hdev->suspend_flag) {
pr_info("currently under suspend, exit %s\n", __func__);
mutex_unlock(&hdev->hdmimode_mutex);
return -1;
}
memset(mode, 0, sizeof(mode));
hdev->ready = 0;
/* get current vinfo */
info = hdmitx_get_current_vinfo(NULL);
if (!info || !info->name) {
mutex_unlock(&hdev->hdmimode_mutex);
return -1;
}
pr_info("hdmitx: get current mode: %s\n", info->name);
hdmitx_vrr_disable();
if (strncmp(info->name, "invalid", strlen("invalid")) == 0) {
hdmitx21_disable_hdcp(hdev);
mutex_unlock(&hdev->hdmimode_mutex);
return -1;
}
/*update hdmi checksum to vout*/
memcpy(info->hdmichecksum, hdev->tx_comm.rxcap.chksum, 10);
hdmi_physical_size_update(hdev);
strncpy(mode, info->name, sizeof(mode));
mode[31] = '\0';
if (strstr(mode, "fp")) {
int i = 0;
for (; mode[i]; i++) {
if ((mode[i] == 'f') && (mode[i + 1] == 'p')) {
/* skip "f", 1080fp60hz -> 1080p60hz */
do {
mode[i] = mode[i + 1];
i++;
} while (mode[i]);
break;
}
}
}
para = hdmitx21_get_fmtpara(mode, tx_comm->fmt_attr);
if (!para) {
pr_info("%s[%d] %s %s\n", __func__, __LINE__, mode,
tx_comm->fmt_attr);
mutex_unlock(&hdev->hdmimode_mutex);
return -1;
}
/* disable hdcp before set mode if hdcp enabled.
* normally hdcp is disabled before setting mode
* when disable phy, but for special case of bootup,
* if mode changed as it's different with uboot mode,
* hdcp is not stopped firstly, and may hdcp fail
*/
if (!hdcp_need_control_by_upstream(hdev))
hdmitx21_disable_hdcp(hdev);
pr_info("setting hdmi mode %s %s\n", mode, tx_comm->fmt_attr);
pr_info("cd/cs/cr: %d/%d/%d\n", para->cd, para->cs, para->cr);
hdev->para = para;
vic = hdmitx21_edid_get_VIC(hdev, mode, 1);
if (strncmp(info->name, "2160p30hz", strlen("2160p30hz")) == 0) {
vic = HDMI_95_3840x2160p30_16x9;
} else if (strncmp(info->name, "2160p25hz",
strlen("2160p25hz")) == 0) {
vic = HDMI_94_3840x2160p25_16x9;
} else if (strncmp(info->name, "2160p24hz",
strlen("2160p24hz")) == 0) {
vic = HDMI_93_3840x2160p24_16x9;
} else if (strncmp(info->name, "smpte24hz",
strlen("smpte24hz")) == 0) {
vic = HDMI_98_4096x2160p24_256x135;
} else {
/* nothing */
}
if (tx_comm->rxcap.max_frl_rate) {
hdev->frl_rate = hdmitx21_select_frl_rate(hdev->dsc_en, vic,
hdev->para->cs, hdev->para->cd);
if (hdev->frl_rate > hdev->tx_max_frl_rate)
pr_info("Current frl_rate %d is larger than tx_max_frl_rate %d\n",
hdev->frl_rate, hdev->tx_max_frl_rate);
}
/* if manual_frl_rate is true, set to force frl_rate */
if (hdev->manual_frl_rate)
hdev->frl_rate = hdev->manual_frl_rate;
hdev->tx_comm.cur_VIC = HDMI_0_UNKNOWN;
/* if vic is HDMI_0_UNKNOWN, hdmitx21_set_display will disable HDMI */
edidinfo_detach_to_vinfo(hdev);
vic = check_vic_4x3_and_16x9(hdev, vic);
ret = hdmitx21_set_display(hdev, vic);
if (ret >= 0) {
hdev->hwop.cntl(hdev, HDMITX_AVMUTE_CNTL, AVMUTE_CLEAR);
hdev->tx_comm.cur_VIC = vic;
hdev->audio_param_update_flag = 1;
restore_mute();
}
if (hdev->tx_comm.cur_VIC == HDMI_0_UNKNOWN) {
if (hdev->hpdmode == 2) {
/* edid will be read again when hpd is muxed
* and it is high
*/
hdmitx21_edid_clear(hdev);
hdev->mux_hpd_if_pin_high_flag = 0;
}
/* If current display is NOT panel, needn't TURNOFF_HDMIHW */
if (strncmp(mode, "panel", 5) == 0) {
hdev->hwop.cntl(hdev, HDMITX_HWCMD_TURNOFF_HDMIHW,
(hdev->hpdmode == 2) ? 1 : 0);
}
}
hdmitx21_set_audio(hdev, &hdev->cur_audio_param);
if (hdev->cedst_policy) {
cancel_delayed_work(&hdev->work_cedst);
queue_delayed_work(hdev->cedst_wq, &hdev->work_cedst, 0);
}
hdev->output_blank_flag = 1;
edidinfo_attach_to_vinfo(hdev);
/* wait for TV detect signal stable,
* otherwise hdcp may easily auth fail
*/
hdev->ready = 1;
if (hdev->not_restart_hdcp) {
/* self clear */
hdev->not_restart_hdcp = 0;
pr_info("special mode switch, not start hdcp\n");
} else {
queue_delayed_work(hdev->hdmi_wq, &hdev->work_start_hdcp, HZ / 4);
}
mutex_unlock(&hdev->hdmimode_mutex);
return ret;
}
/*disp_mode attr*/
static ssize_t disp_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
int i = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmi_format_para *para = hdev->para;
struct hdmi_timing *timing = &para->timing;
struct vinfo_s *vinfo = &para->hdmitx_vinfo;
if (!para)
return pos;
pos += snprintf(buf + pos, PAGE_SIZE, "cd/cs/cr: %d/%d/%d\n", para->cd,
para->cs, para->cr);
pos += snprintf(buf + pos, PAGE_SIZE, "scramble/tmds_clk_div40: %d/%d\n",
para->scrambler_en, para->tmds_clk_div40);
pos += snprintf(buf + pos, PAGE_SIZE, "tmds_clk: %d\n", para->tmds_clk);
pos += snprintf(buf + pos, PAGE_SIZE, "vic: %d\n", timing->vic);
pos += snprintf(buf + pos, PAGE_SIZE, "name: %s\n", timing->name);
pos += snprintf(buf + pos, PAGE_SIZE, "enc_idx: %d\n", hdev->enc_idx);
if (timing->sname)
pos += snprintf(buf + pos, PAGE_SIZE, "sname: %s\n",
timing->sname);
pos += snprintf(buf + pos, PAGE_SIZE, "pi_mode: %c\n",
timing->pi_mode ? 'P' : 'I');
pos += snprintf(buf + pos, PAGE_SIZE, "h/v_freq: %d/%d\n",
timing->h_freq, timing->v_freq);
pos += snprintf(buf + pos, PAGE_SIZE, "pixel_freq: %d\n",
timing->pixel_freq);
pos += snprintf(buf + pos, PAGE_SIZE, "h_total: %d\n", timing->h_total);
pos += snprintf(buf + pos, PAGE_SIZE, "h_blank: %d\n", timing->h_blank);
pos += snprintf(buf + pos, PAGE_SIZE, "h_front: %d\n", timing->h_front);
pos += snprintf(buf + pos, PAGE_SIZE, "h_sync: %d\n", timing->h_sync);
pos += snprintf(buf + pos, PAGE_SIZE, "h_back: %d\n", timing->h_back);
pos += snprintf(buf + pos, PAGE_SIZE, "h_active: %d\n", timing->h_active);
pos += snprintf(buf + pos, PAGE_SIZE, "v_total: %d\n", timing->v_total);
pos += snprintf(buf + pos, PAGE_SIZE, "v_blank: %d\n", timing->v_blank);
pos += snprintf(buf + pos, PAGE_SIZE, "v_front: %d\n", timing->v_front);
pos += snprintf(buf + pos, PAGE_SIZE, "v_sync: %d\n", timing->v_sync);
pos += snprintf(buf + pos, PAGE_SIZE, "v_back: %d\n", timing->v_back);
pos += snprintf(buf + pos, PAGE_SIZE, "v_active: %d\n", timing->v_active);
pos += snprintf(buf + pos, PAGE_SIZE, "v_sync_ln: %d\n", timing->v_sync_ln);
pos += snprintf(buf + pos, PAGE_SIZE, "h/v_pol: %d/%d\n", timing->h_pol, timing->v_pol);
pos += snprintf(buf + pos, PAGE_SIZE, "h/v_pict: %d/%d\n", timing->h_pict, timing->v_pict);
pos += snprintf(buf + pos, PAGE_SIZE, "h/v_pixel: %d/%d\n",
timing->h_pixel, timing->v_pixel);
pos += snprintf(buf + pos, PAGE_SIZE, "name: %s\n", vinfo->name);
pos += snprintf(buf + pos, PAGE_SIZE, "mode: %d\n", vinfo->mode);
pos += snprintf(buf + pos, PAGE_SIZE, "ext_name: %s\n", vinfo->ext_name);
pos += snprintf(buf + pos, PAGE_SIZE, "frac: %d\n", vinfo->frac);
pos += snprintf(buf + pos, PAGE_SIZE, "width/height: %d/%d\n", vinfo->width, vinfo->height);
pos += snprintf(buf + pos, PAGE_SIZE, "field_height: %d\n", vinfo->field_height);
pos += snprintf(buf + pos, PAGE_SIZE, "aspect_ratio_num/den: %d/%d\n",
vinfo->aspect_ratio_num, vinfo->aspect_ratio_den);
pos += snprintf(buf + pos, PAGE_SIZE, "screen_real_width/height: %d/%d\n",
vinfo->screen_real_width, vinfo->screen_real_height);
pos += snprintf(buf + pos, PAGE_SIZE, "sync_duration_num/den: %d/%d\n",
vinfo->sync_duration_num, vinfo->sync_duration_den);
pos += snprintf(buf + pos, PAGE_SIZE, "video_clk: %d\n", vinfo->video_clk);
pos += snprintf(buf + pos, PAGE_SIZE, "h/vtotal: %d/%d\n", vinfo->htotal, vinfo->vtotal);
pos += snprintf(buf + pos, PAGE_SIZE, "hdmichecksum:\n");
for (i = 0; i < sizeof(vinfo->hdmichecksum); i++)
pos += snprintf(buf + pos, PAGE_SIZE, "%02x", vinfo->hdmichecksum[i]);
pos += snprintf(buf + pos, PAGE_SIZE, "\n");
pos += snprintf(buf + pos, PAGE_SIZE, "info_3d: %d\n", vinfo->info_3d);
pos += snprintf(buf + pos, PAGE_SIZE, "fr_adj_type: %d\n", vinfo->fr_adj_type);
pos += snprintf(buf + pos, PAGE_SIZE, "viu_color_fmt: %d\n", vinfo->viu_color_fmt);
pos += snprintf(buf + pos, PAGE_SIZE, "viu_mux: %d\n", vinfo->viu_mux);
/* master_display_info / hdr_info / rx_latency */
return pos;
}
static ssize_t disp_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
set_disp_mode(buf);
return count;
}
/*aud_mode attr*/
void setup21_attr(const char *buf)
{
char attr[16] = {0};
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
memcpy(attr, buf, sizeof(attr));
memcpy(tx_comm->fmt_attr, attr, sizeof(tx_comm->fmt_attr));
}
void get21_attr(char attr[16])
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
memcpy(attr, tx_comm->fmt_attr, sizeof(tx_comm->fmt_attr));
}
/* todo
*static int dump_edid_data(u32 type, char *path)
*{
* struct hdmitx_dev *hdev = get_hdmitx21_device();
* struct file *filp = NULL;
* loff_t pos = 0;
* char line[128] = {0};
* mm_segment_t old_fs = get_fs();
* u32 i = 0, j = 0, k = 0, size = 0, block_cnt = 0;
* u32 index = 0, tmp = 0;
*
* set_fs(KERNEL_DS);
* filp = filp_open(path, O_RDWR | O_CREAT, 0666);
* if (IS_ERR(filp)) {
* pr_info("[%s] failed to open/create file: |%s|\n",
* __func__, path);
* goto PROCESS_END;
* }
*
* block_cnt = hdev->EDID_buf[0x7e] + 1;
* if (hdev->EDID_buf[0x7e] != 0 &&
* hdev->EDID_buf[128 + 4] == 0xe2 &&
* hdev->EDID_buf[128 + 5] == 0x78)
* block_cnt = hdev->EDID_buf[128 + 6] + 1;
* if (type == 1) {
* // dump as bin file
* size = vfs_write(filp, hdev->EDID_buf,
* block_cnt * 128, &pos);
* } else if (type == 2) {
* //dump as txt file
*
* for (i = 0; i < block_cnt; i++) {
* for (j = 0; j < 8; j++) {
* for (k = 0; k < 16; k++) {
* index = i * 128 + j * 16 + k;
* tmp = hdev->EDID_buf[index];
* snprintf((char *)&line[k * 6], 7,
* "0x%02x, ",
* tmp);
* }
* line[16 * 6 - 1] = '\n';
* line[16 * 6] = 0x0;
* pos = (i * 8 + j) * 16 * 6;
* size += vfs_write(filp, line, 16 * 6, &pos);
* }
* }
* }
*
* pr_info("[%s] write %d bytes to file %s\n", __func__, size, path);
*
* vfs_fsync(filp, 0);
* filp_close(filp, NULL);
*
*PROCESS_END:
* set_fs(old_fs);
* return 0;
*}
*
*static int load_edid_data(u32 type, char *path)
*{
* struct file *filp = NULL;
* loff_t pos = 0;
* mm_segment_t old_fs = get_fs();
* struct hdmitx_dev *hdev = get_hdmitx21_device();
*
* struct kstat stat;
* u32 length = 0, max_len = EDID_MAX_BLOCK * 128;
* char *buf = NULL;
*
* set_fs(KERNEL_DS);
*
* filp = filp_open(path, O_RDONLY, 0444);
* if (IS_ERR(filp)) {
* pr_info("[%s] failed to open file: |%s|\n", __func__, path);
* goto PROCESS_END;
* }
*
* WARN_ON(vfs_stat(path, &stat));
*
* length = (stat.size > max_len) ? max_len : stat.size;
*
* buf = kmalloc(length, GFP_KERNEL);
* if (!buf)
* goto PROCESS_END;
*
* vfs_read(filp, buf, length, &pos);
*
* memcpy(hdev->EDID_buf, buf, length);
*
* kfree(buf);
* filp_close(filp, NULL);
*
* pr_info("[%s] %d bytes loaded from file %s\n", __func__, length, path);
*
* hdmitx21_edid_clear(hdev);
* hdmitx21_edid_parse(hdev);
* pr_info("[%s] new edid loaded!\n", __func__);
*
*PROCESS_END:
* set_fs(old_fs);
* return 0;
*}
*/
/*
* sink_type attr
* sink, or repeater
*/
static ssize_t sink_type_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (!hdev->tx_comm.hpd_state) {
pos += snprintf(buf + pos, PAGE_SIZE, "none\n");
return pos;
}
if (hdev->hdmi_info.vsdb_phy_addr.b)
pos += snprintf(buf + pos, PAGE_SIZE, "repeater\n");
else
pos += snprintf(buf + pos, PAGE_SIZE, "sink\n");
return pos;
}
static DEFINE_MUTEX(aud_mute_mutex);
void hdmitx21_audio_mute_op(u32 flag, unsigned int path)
{
static unsigned int aud_mute_path;
struct hdmitx_dev *hdev = get_hdmitx21_device();
mutex_lock(&aud_mute_mutex);
if (flag == 0)
aud_mute_path |= path;
else
aud_mute_path &= ~path;
hdev->tx_aud_cfg = !aud_mute_path;
if (flag == 0) {
pr_info("%s: AUD_MUTE path=0x%x\n", __func__, path);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_AUDIO_MUTE_OP, AUDIO_MUTE);
} else {
/* unmute only if none of the paths are muted */
if (aud_mute_path == 0) {
pr_info("%s: AUD_UNMUTE path=0x%x\n", __func__, path);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_AUDIO_MUTE_OP, AUDIO_UNMUTE);
}
}
mutex_unlock(&aud_mute_mutex);
}
static DEFINE_MUTEX(vid_mute_mutex);
void hdmitx21_video_mute_op(u32 flag, unsigned int path)
{
static unsigned int vid_mute_path;
struct hdmitx_dev *hdev = get_hdmitx21_device();
mutex_lock(&vid_mute_mutex);
if (flag == 0)
vid_mute_path |= path;
else
vid_mute_path &= ~path;
if (flag == 0) {
pr_info("%s: VID_MUTE path=0x%x\n", __func__, path);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_VIDEO_MUTE_OP, VIDEO_MUTE);
} else {
/* unmute only if none of the paths are muted */
if (vid_mute_path == 0) {
pr_info("%s: VID_UNMUTE path=0x%x\n", __func__, path);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_VIDEO_MUTE_OP, VIDEO_UNMUTE);
}
}
mutex_unlock(&vid_mute_mutex);
}
/*
* SDR/HDR uevent
* 1: SDR to HDR
* 0: HDR to SDR
*/
static void hdmitx_sdr_hdr_uevent(struct hdmitx_dev *hdev)
{
if (hdev->hdmi_last_hdr_mode == 0 &&
hdev->hdmi_current_hdr_mode != 0) {
/* SDR -> HDR*/
hdev->hdmi_last_hdr_mode = hdev->hdmi_current_hdr_mode;
hdmitx21_set_uevent(HDMITX_HDR_EVENT, 1);
} else if ((hdev->hdmi_last_hdr_mode != 0) &&
(hdev->hdmi_current_hdr_mode == 0)) {
/* HDR -> SDR*/
hdev->hdmi_last_hdr_mode = hdev->hdmi_current_hdr_mode;
hdmitx21_set_uevent(HDMITX_HDR_EVENT, 0);
}
}
static int hdmitx_check_vic(int vic)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
int i;
for (i = 0; i < hdev->tx_comm.rxcap.VIC_count && i < VIC_MAX_NUM; i++) {
if (vic == hdev->tx_comm.rxcap.VIC[i])
return 1;
}
return 0;
}
static int hdmitx_check_valid_aspect_ratio(enum hdmi_vic vic, int aspect_ratio)
{
switch (vic) {
case HDMI_2_720x480p60_4x3:
if (hdmitx_check_vic(HDMI_3_720x480p60_16x9)) {
if (aspect_ratio == AR_16X9)
return 1;
pr_info("same aspect_ratio = %d\n", aspect_ratio);
} else {
pr_info("TV not support dual aspect_ratio\n");
}
break;
case HDMI_3_720x480p60_16x9:
if (hdmitx_check_vic(HDMI_2_720x480p60_4x3)) {
if (aspect_ratio == AR_4X3)
return 1;
pr_info("same aspect_ratio = %d\n", aspect_ratio);
} else {
pr_info("TV not support dual aspect_ratio\n");
}
break;
case HDMI_17_720x576p50_4x3:
if (hdmitx_check_vic(HDMI_18_720x576p50_16x9)) {
if (aspect_ratio == AR_16X9)
return 1;
pr_info("same aspect_ratio = %d\n", aspect_ratio);
} else {
pr_info("TV not support dual aspect_ratio\n");
}
break;
case HDMI_18_720x576p50_16x9:
if (hdmitx_check_vic(HDMI_17_720x576p50_4x3)) {
if (aspect_ratio == AR_4X3)
return 1;
pr_info("same aspect_ratio = %d\n", aspect_ratio);
} else {
pr_info("TV not support dual aspect_ratio\n");
}
break;
default:
break;
}
pr_info("not support vic = %d\n", vic);
return 0;
}
int hdmitx21_get_aspect_ratio(void)
{
enum hdmi_vic vic = HDMI_0_UNKNOWN;
struct hdmitx_dev *hdev = get_hdmitx21_device();
vic = hdev->tx_hw.getstate(&hdev->tx_hw, STAT_VIDEO_VIC, 0);
if (vic == HDMI_2_720x480p60_4x3 || vic == HDMI_17_720x576p50_4x3)
return AR_4X3;
if (vic == HDMI_3_720x480p60_16x9 || vic == HDMI_18_720x576p50_16x9)
return AR_16X9;
return 0;
}
struct aspect_ratio_list *hdmitx21_get_support_ar_list(void)
{
static struct aspect_ratio_list ar_list[4];
int i = 0;
memset(ar_list, 0, sizeof(ar_list));
if (hdmitx_check_vic(HDMI_2_720x480p60_4x3)) {
ar_list[i].vic = HDMI_2_720x480p60_4x3;
ar_list[i].flag = TRUE;
ar_list[i].aspect_ratio_num = 4;
ar_list[i].aspect_ratio_den = 3;
i++;
}
if (hdmitx_check_vic(HDMI_3_720x480p60_16x9)) {
ar_list[i].vic = HDMI_3_720x480p60_16x9;
ar_list[i].flag = TRUE;
ar_list[i].aspect_ratio_num = 16;
ar_list[i].aspect_ratio_den = 9;
i++;
}
if (hdmitx_check_vic(HDMI_17_720x576p50_4x3)) {
ar_list[i].vic = HDMI_17_720x576p50_4x3;
ar_list[i].flag = TRUE;
ar_list[i].aspect_ratio_num = 4;
ar_list[i].aspect_ratio_den = 3;
i++;
}
if (hdmitx_check_vic(HDMI_18_720x576p50_16x9)) {
ar_list[i].vic = HDMI_18_720x576p50_16x9;
ar_list[i].flag = TRUE;
ar_list[i].aspect_ratio_num = 16;
ar_list[i].aspect_ratio_den = 9;
i++;
}
return &ar_list[0];
}
int hdmitx21_get_aspect_ratio_value(void)
{
int i;
int value = 0;
static struct aspect_ratio_list *ar_list;
struct hdmitx_dev *hdev = get_hdmitx21_device();
enum hdmi_vic vic = HDMI_0_UNKNOWN;
ar_list = hdmitx21_get_support_ar_list();
vic = hdev->tx_hw.getstate(&hdev->tx_hw, STAT_VIDEO_VIC, 0);
if (vic == HDMI_2_720x480p60_4x3 || vic == HDMI_3_720x480p60_16x9) {
for (i = 0; i < 4; i++) {
if (ar_list[i].vic == HDMI_2_720x480p60_4x3)
value++;
if (ar_list[i].vic == HDMI_3_720x480p60_16x9)
value++;
}
}
if (vic == HDMI_17_720x576p50_4x3 || vic == HDMI_18_720x576p50_16x9) {
for (i = 0; i < 4; i++) {
if (ar_list[i].vic == HDMI_17_720x576p50_4x3)
value++;
if (ar_list[i].vic == HDMI_18_720x576p50_16x9)
value++;
}
}
if (value > 1) {
value = hdmitx21_get_aspect_ratio();
return value;
}
return 0;
}
void hdmitx21_set_aspect_ratio(int aspect_ratio)
{
enum hdmi_vic vic = HDMI_0_UNKNOWN;
struct hdmitx_dev *hdev = get_hdmitx21_device();
int ret;
int aspect_ratio_vic = 0;
if (aspect_ratio != AR_4X3 && aspect_ratio != AR_16X9) {
pr_info("aspect ratio should be 1 or 2");
return;
}
vic = hdev->tx_hw.getstate(&hdev->tx_hw, STAT_VIDEO_VIC, 0);
ret = hdmitx_check_valid_aspect_ratio(vic, aspect_ratio);
pr_info("%s vic = %d, ret = %d\n", __func__, vic, ret);
if (!ret)
return;
switch (vic) {
case HDMI_2_720x480p60_4x3:
aspect_ratio_vic = (HDMI_3_720x480p60_16x9 << 2) + aspect_ratio;
break;
case HDMI_3_720x480p60_16x9:
aspect_ratio_vic = (HDMI_2_720x480p60_4x3 << 2) + aspect_ratio;
break;
case HDMI_17_720x576p50_4x3:
aspect_ratio_vic = (HDMI_18_720x576p50_16x9 << 2) + aspect_ratio;
break;
case HDMI_18_720x576p50_16x9:
aspect_ratio_vic = (HDMI_17_720x576p50_4x3 << 2) + aspect_ratio;
break;
default:
break;
}
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_ASPECT_RATIO, aspect_ratio_vic);
hdev->aspect_ratio = aspect_ratio;
pr_info("set new aspect ratio = %d\n", aspect_ratio);
}
static void hdr_work_func(struct work_struct *work)
{
struct hdmitx_dev *hdev =
container_of(work, struct hdmitx_dev, work_hdr);
struct hdmi_drm_infoframe *info = &hdev->infoframes.drm.drm;
if (hdev->hdr_transfer_feature == T_BT709 &&
hdev->hdr_color_feature == C_BT709) {
pr_info("%s: send zero DRM\n", __func__);
hdmi_drm_infoframe_init(info);
hdmi_drm_infoframe_set(info);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, hdev->colormetry);
msleep(1500);/*delay 1.5s*/
/* disable DRM packets completely ONLY if hdr transfer
* feature and color feature still demand SDR.
*/
if (hdr_status_pos == 4) {
/* zero hdr10+ VSIF being sent - disable it */
pr_info("%s: disable hdr10+ vsif\n", __func__);
/* hdmi_vend_infoframe_set(NULL); */
hdmi_vend_infoframe_rawset(NULL, NULL);
hdr_status_pos = 0;
}
if (hdev->hdr_transfer_feature == T_BT709 &&
hdev->hdr_color_feature == C_BT709) {
pr_info("%s: disable DRM\n", __func__);
hdmi_drm_infoframe_set(NULL);
hdev->hdmi_current_hdr_mode = 0;
hdmitx_sdr_hdr_uevent(hdev);
}
} else {
hdmitx_sdr_hdr_uevent(hdev);
}
}
#define hdmi_debug() \
do { \
if (log21_level == 0xff) \
pr_info("%s[%d]\n", __func__, __LINE__); \
} while (0)
static bool _check_hdmi_mode(void)
{
struct vinfo_s *vinfo = NULL;
vinfo = get_current_vinfo();
if (vinfo && vinfo->mode == VMODE_HDMI)
return 1;
vinfo = get_current_vinfo2();
if (vinfo && vinfo->mode == VMODE_HDMI)
return 1;
return 0;
}
bool is_cur_mode_hdmi(void)
{
return _check_hdmi_mode();
}
#define GET_LOW8BIT(a) ((a) & 0xff)
#define GET_HIGH8BIT(a) (((a) >> 8) & 0xff)
static void hdmitx_set_drm_pkt(struct master_display_info_s *data)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdr_info *hdr_info = &hdev->tx_comm.rxcap.hdr_info;
u8 drm_hb[3] = {0x87, 0x1, 26};
static u8 db[28] = {0x0};
u8 *drm_db = &db[1]; /* db[0] is the checksum */
unsigned long flags = 0;
if (!_check_hdmi_mode())
return;
hdmi_debug();
spin_lock_irqsave(&hdev->edid_spinlock, flags);
if (hdr_status_pos == 4) {
/* zero hdr10+ VSIF being sent - disable it */
pr_info("%s: disable hdr10+ zero vsif\n", __func__);
/* hdmi_vend_infoframe_set(NULL); */
hdmi_vend_infoframe_rawset(NULL, NULL);
hdr_status_pos = 0;
}
/*
*hdr_color_feature: bit 23-16: color_primaries
* 1:bt709 0x9:bt2020
*hdr_transfer_feature: bit 15-8: transfer_characteristic
* 1:bt709 0xe:bt2020-10 0x10:smpte-st-2084 0x12:hlg(todo)
*/
if (data) {
hdev->hdr_transfer_feature = (data->features >> 8) & 0xff;
hdev->hdr_color_feature = (data->features >> 16) & 0xff;
hdev->colormetry = (data->features >> 30) & 0x1;
}
if (hdr_status_pos != 1 && hdr_status_pos != 3)
pr_info("%s: tf=%d, cf=%d, colormetry=%d\n",
__func__,
hdev->hdr_transfer_feature,
hdev->hdr_color_feature,
hdev->colormetry);
hdr_status_pos = 1;
/* if VSIF/DV or VSIF/HDR10P packet is enabled, disable it */
if (hdmitx21_dv_en()) {
update_current_para(hdev);
hdmi_avi_infoframe_config(CONF_AVI_CS, hdev->para->cs);
/* if using VSIF/DOVI, then only clear DV_VS10_SIG, else disable VSIF */
if (hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CLR_DV_VS10_SIG, 0) == 0)
/* hdmi_vend_infoframe_set(NULL); */
hdmi_vend_infoframe_rawset(NULL, NULL);
}
/* hdr10+ content on a hdr10 sink case */
if (hdev->hdr_transfer_feature == 0x30) {
if (hdr_info->hdr10plus_info.ieeeoui != 0x90848B ||
hdr_info->hdr10plus_info.application_version != 1) {
hdev->hdr_transfer_feature = T_SMPTE_ST_2084;
pr_info("%s: HDR10+ not supported, treat as hdr10\n",
__func__);
}
}
if (!data || !hdev->tx_comm.rxcap.hdr_info2.hdr_support) {
drm_hb[1] = 0;
drm_hb[2] = 0;
drm_db[0] = 0;
hdmi_drm_infoframe_set(NULL);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, hdev->colormetry);
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
return;
}
/*SDR*/
if (hdev->hdr_transfer_feature == T_BT709 &&
hdev->hdr_color_feature == C_BT709) {
/* send zero drm only for HDR->SDR transition */
if (drm_db[0] == 0x02 || drm_db[0] == 0x03) {
pr_info("%s: HDR->SDR, drm_db[0]=%d\n",
__func__, drm_db[0]);
hdev->colormetry = 0;
hdmi_avi_infoframe_config(CONF_AVI_BT2020, 0);
schedule_work(&hdev->work_hdr);
drm_db[0] = 0;
}
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
return;
}
drm_db[1] = 0x0;
drm_db[2] = GET_LOW8BIT(data->primaries[0][0]);
drm_db[3] = GET_HIGH8BIT(data->primaries[0][0]);
drm_db[4] = GET_LOW8BIT(data->primaries[0][1]);
drm_db[5] = GET_HIGH8BIT(data->primaries[0][1]);
drm_db[6] = GET_LOW8BIT(data->primaries[1][0]);
drm_db[7] = GET_HIGH8BIT(data->primaries[1][0]);
drm_db[8] = GET_LOW8BIT(data->primaries[1][1]);
drm_db[9] = GET_HIGH8BIT(data->primaries[1][1]);
drm_db[10] = GET_LOW8BIT(data->primaries[2][0]);
drm_db[11] = GET_HIGH8BIT(data->primaries[2][0]);
drm_db[12] = GET_LOW8BIT(data->primaries[2][1]);
drm_db[13] = GET_HIGH8BIT(data->primaries[2][1]);
drm_db[14] = GET_LOW8BIT(data->white_point[0]);
drm_db[15] = GET_HIGH8BIT(data->white_point[0]);
drm_db[16] = GET_LOW8BIT(data->white_point[1]);
drm_db[17] = GET_HIGH8BIT(data->white_point[1]);
drm_db[18] = GET_LOW8BIT(data->luminance[0]);
drm_db[19] = GET_HIGH8BIT(data->luminance[0]);
drm_db[20] = GET_LOW8BIT(data->luminance[1]);
drm_db[21] = GET_HIGH8BIT(data->luminance[1]);
drm_db[22] = GET_LOW8BIT(data->max_content);
drm_db[23] = GET_HIGH8BIT(data->max_content);
drm_db[24] = GET_LOW8BIT(data->max_frame_average);
drm_db[25] = GET_HIGH8BIT(data->max_frame_average);
/* bt2020 + gamma transfer */
if (hdev->hdr_transfer_feature == T_BT709 &&
hdev->hdr_color_feature == C_BT2020) {
if (hdev->sdr_hdr_feature == 0) {
hdmi_drm_infoframe_set(NULL);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, SET_AVI_BT2020);
} else if (hdev->sdr_hdr_feature == 1) {
memset(db, 0, sizeof(db));
hdmi_drm_infoframe_rawset(drm_hb, db);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, SET_AVI_BT2020);
} else {
drm_db[0] = 0x02; /* SMPTE ST 2084 */
hdmi_drm_infoframe_rawset(drm_hb, db);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, SET_AVI_BT2020);
}
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
return;
}
/*must clear hdr mode*/
hdev->hdmi_current_hdr_mode = 0;
/* SMPTE ST 2084 and (BT2020 or NON_STANDARD) */
if (hdev->tx_comm.rxcap.hdr_info2.hdr_support & 0x4) {
if (hdev->hdr_transfer_feature == T_SMPTE_ST_2084 &&
hdev->hdr_color_feature == C_BT2020)
hdev->hdmi_current_hdr_mode = 1;
else if (hdev->hdr_transfer_feature == T_SMPTE_ST_2084 &&
hdev->hdr_color_feature != C_BT2020)
hdev->hdmi_current_hdr_mode = 2;
}
/*HLG and BT2020*/
if (hdev->tx_comm.rxcap.hdr_info2.hdr_support & 0x8) {
if (hdev->hdr_color_feature == C_BT2020 &&
(hdev->hdr_transfer_feature == T_BT2020_10 ||
hdev->hdr_transfer_feature == T_HLG))
hdev->hdmi_current_hdr_mode = 3;
}
switch (hdev->hdmi_current_hdr_mode) {
case 1:
/*standard HDR*/
drm_db[0] = 0x02; /* SMPTE ST 2084 */
hdmi_drm_infoframe_rawset(drm_hb, db);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, SET_AVI_BT2020);
break;
case 2:
/*non standard*/
drm_db[0] = 0x02; /* no standard SMPTE ST 2084 */
hdmi_drm_infoframe_rawset(drm_hb, db);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, CLR_AVI_BT2020);
break;
case 3:
/*HLG*/
drm_db[0] = 0x03;/* HLG is 0x03 */
hdmi_drm_infoframe_rawset(drm_hb, db);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, SET_AVI_BT2020);
break;
case 0:
default:
/*other case*/
hdmi_drm_infoframe_set(NULL);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, CLR_AVI_BT2020);
break;
}
/* if sdr/hdr mode change ,notify uevent to userspace*/
if (hdev->hdmi_current_hdr_mode != hdev->hdmi_last_hdr_mode)
schedule_work(&hdev->work_hdr);
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
}
static void update_current_para(struct hdmitx_dev *hdev)
{
struct vinfo_s *info = NULL;
u8 mode[32];
struct hdmitx_common *tx_comm = &hdev->tx_comm;
info = hdmitx_get_current_vinfo(NULL);
if (!info)
return;
memset(mode, 0, sizeof(mode));
strncpy(mode, info->name, sizeof(mode) - 1);
hdev->para = hdmitx21_get_fmtpara(mode, tx_comm->fmt_attr);
}
static struct vsif_debug_save vsif_debug_info;
static void hdmitx_set_vsif_pkt(enum eotf_type type,
enum mode_type tunnel_mode,
struct dv_vsif_para *data,
bool signal_sdr)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct dv_vsif_para para = {0};
u8 ven_hb[3] = {0x81, 0x01};
u8 db1[28] = {0x00};
u8 *ven_db1 = &db1[1];
u8 db2[28] = {0x00};
u8 *ven_db2 = &db2[1];
u8 len = 0;
u32 vic = hdev->tx_comm.cur_VIC;
u32 hdmi_vic_4k_flag = 0;
static enum eotf_type ltype = EOTF_T_NULL;
static u8 ltmode = -1;
enum hdmi_tf_type hdr_type = HDMI_NONE;
unsigned long flags = 0;
if (!_check_hdmi_mode())
return;
hdmi_debug();
spin_lock_irqsave(&hdev->edid_spinlock, flags);
if (hdev->bist_lock) {
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
return;
}
if (!data)
memcpy(&vsif_debug_info.data, &para,
sizeof(struct dv_vsif_para));
else
memcpy(&vsif_debug_info.data, data,
sizeof(struct dv_vsif_para));
vsif_debug_info.type = type;
vsif_debug_info.tunnel_mode = tunnel_mode;
vsif_debug_info.signal_sdr = signal_sdr;
if (hdev->ready == 0) {
ltype = EOTF_T_NULL;
ltmode = -1;
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
return;
}
if (hdev->tx_comm.rxcap.dv_info.ieeeoui != DV_IEEE_OUI) {
if (type == 0 && !data && signal_sdr)
pr_info("TV not support DV, clr dv_vsif\n");
}
if (hdr_status_pos != 2)
pr_info("%s: type = %d\n", __func__, type);
hdr_status_pos = 2;
/* if DRM/HDR packet is enabled, disable it */
hdr_type = hdmitx21_get_cur_hdr_st();
if (hdr_type != HDMI_NONE && hdr_type != HDMI_HDR_SDR) {
hdev->hdr_transfer_feature = T_BT709;
hdev->hdr_color_feature = C_BT709;
hdev->colormetry = 0;
hdmi_avi_infoframe_config(CONF_AVI_BT2020, hdev->colormetry);
schedule_work(&hdev->work_hdr);
}
hdev->hdmi_current_eotf_type = type;
hdev->hdmi_current_tunnel_mode = tunnel_mode;
/*ver0 and ver1_15 and ver1_12bit with ll= 0 use hdmi 1.4b VSIF*/
if (hdev->tx_comm.rxcap.dv_info.ver == 0 ||
(hdev->tx_comm.rxcap.dv_info.ver == 1 &&
hdev->tx_comm.rxcap.dv_info.length == 0xE) ||
(hdev->tx_comm.rxcap.dv_info.ver == 1 &&
hdev->tx_comm.rxcap.dv_info.length == 0xB &&
hdev->tx_comm.rxcap.dv_info.low_latency == 0)) {
if (vic == HDMI_95_3840x2160p30_16x9 ||
vic == HDMI_94_3840x2160p25_16x9 ||
vic == HDMI_93_3840x2160p24_16x9 ||
vic == HDMI_98_4096x2160p24_256x135)
hdmi_vic_4k_flag = 1;
switch (type) {
case EOTF_T_DOLBYVISION:
len = 0x18;
hdev->dv_src_feature = 1;
break;
case EOTF_T_HDR10:
case EOTF_T_SDR:
case EOTF_T_NULL:
default:
len = 0x05;
hdev->dv_src_feature = 0;
break;
}
ven_hb[2] = len;
ven_db1[0] = 0x03;
ven_db1[1] = 0x0c;
ven_db1[2] = 0x00;
ven_db1[3] = 0x00;
if (hdmi_vic_4k_flag) {
ven_db1[3] = 0x20;
if (vic == HDMI_95_3840x2160p30_16x9)
ven_db1[4] = 0x1;
else if (vic == HDMI_94_3840x2160p25_16x9)
ven_db1[4] = 0x2;
else if (vic == HDMI_93_3840x2160p24_16x9)
ven_db1[4] = 0x3;
else/*vic == HDMI_98_4096x2160p24_256x135*/
ven_db1[4] = 0x4;
}
if (type == EOTF_T_DV_AHEAD) {
hdmi_vend_infoframe_rawset(ven_hb, db1);
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
return;
}
if (type == EOTF_T_DOLBYVISION) {
/* disable forced gaming in this mode because if we are
* here with forced gaming on, it means TV is not DV LL
* capable
*/
if (hdev->ll_user_set_mode == HDMI_LL_MODE_ENABLE &&
(hdev->tx_comm.allm_mode == 1 || hdev->tx_comm.ct_mode == 1)) {
pr_info("hdmitx: Dolby H14b VSIF, disable forced game mode\n");
hdmi_tx_enable_ll_mode(false);
}
/*first disable drm package*/
hdmi_drm_infoframe_set(NULL);
hdmi_vend_infoframe_rawset(ven_hb, db1);
/* Dolby Vision Source System-on-Chip Platform Kit Version 2.6:
* 4.4.1 Expected AVI-IF for Dolby Vision output, need BT2020 for DV
*/
hdmi_avi_infoframe_config(CONF_AVI_BT2020, SET_AVI_BT2020);/*BT2020*/
if (tunnel_mode == RGB_8BIT) {
hdmi_avi_infoframe_config(CONF_AVI_CS, HDMI_COLORSPACE_RGB);
hdmi_avi_infoframe_config(CONF_AVI_Q01, RGB_RANGE_FUL);
} else {
hdmi_avi_infoframe_config(CONF_AVI_CS, HDMI_COLORSPACE_YUV422);
hdmi_avi_infoframe_config(CONF_AVI_YQ01, YCC_RANGE_FUL);
}
} else {
if (hdmi_vic_4k_flag)
hdmi_vend_infoframe_rawset(ven_hb, db1);
else
/* hdmi_vend_infoframe_set(NULL); */
hdmi_vend_infoframe_rawset(NULL, NULL);
if (signal_sdr) {
pr_info("hdmitx: Dolby H14b VSIF, switching signal to SDR\n");
update_current_para(hdev);
hdmi_avi_infoframe_config(CONF_AVI_CS, hdev->para->cs);
hdmi_avi_infoframe_config(CONF_AVI_Q01, RGB_RANGE_LIM);
hdmi_avi_infoframe_config(CONF_AVI_YQ01, YCC_RANGE_LIM);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, CLR_AVI_BT2020);/*BT709*/
/* re-enable forced game mode if selected by the user */
if (hdev->ll_user_set_mode == HDMI_LL_MODE_ENABLE) {
pr_info("hdmitx: Dolby H14b VSIF disabled, re-enable forced game mode\n");
hdmi_tx_enable_ll_mode(true);
}
}
}
}
/*ver1_12 with low_latency = 1 and ver2 use Dolby VSIF*/
if (hdev->tx_comm.rxcap.dv_info.ver == 2 ||
(hdev->tx_comm.rxcap.dv_info.ver == 1 &&
hdev->tx_comm.rxcap.dv_info.length == 0xB &&
hdev->tx_comm.rxcap.dv_info.low_latency == 1) ||
type == EOTF_T_LL_MODE) {
if (!data)
data = &para;
len = 0x1b;
switch (type) {
case EOTF_T_DOLBYVISION:
case EOTF_T_LL_MODE:
hdev->dv_src_feature = 1;
break;
case EOTF_T_HDR10:
case EOTF_T_SDR:
case EOTF_T_NULL:
default:
hdev->dv_src_feature = 0;
break;
}
ven_hb[2] = len;
ven_db2[0] = 0x46;
ven_db2[1] = 0xd0;
ven_db2[2] = 0x00;
if (data->ver2_l11_flag == 1) {
ven_db2[3] = data->vers.ver2_l11.low_latency |
data->vers.ver2_l11.dobly_vision_signal << 1 |
data->vers.ver2_l11.src_dm_version << 5;
ven_db2[4] = data->vers.ver2_l11.eff_tmax_PQ_hi
| data->vers.ver2_l11.auxiliary_MD_present << 6
| data->vers.ver2_l11.backlt_ctrl_MD_present << 7
| 0x20; /*L11_MD_Present*/
ven_db2[5] = data->vers.ver2_l11.eff_tmax_PQ_low;
ven_db2[6] = data->vers.ver2_l11.auxiliary_runmode;
ven_db2[7] = data->vers.ver2_l11.auxiliary_runversion;
ven_db2[8] = data->vers.ver2_l11.auxiliary_debug0;
ven_db2[9] = (data->vers.ver2_l11.content_type)
| (data->vers.ver2_l11.content_sub_type << 4);
ven_db2[10] = (data->vers.ver2_l11.intended_white_point)
| (data->vers.ver2_l11.crf << 4);
ven_db2[11] = data->vers.ver2_l11.l11_byte2;
ven_db2[12] = data->vers.ver2_l11.l11_byte3;
} else {
ven_db2[3] = (data->vers.ver2.low_latency) |
(data->vers.ver2.dobly_vision_signal << 1) |
(data->vers.ver2.src_dm_version << 5);
ven_db2[4] = (data->vers.ver2.eff_tmax_PQ_hi)
| (data->vers.ver2.auxiliary_MD_present << 6)
| (data->vers.ver2.backlt_ctrl_MD_present << 7);
ven_db2[5] = data->vers.ver2.eff_tmax_PQ_low;
ven_db2[6] = data->vers.ver2.auxiliary_runmode;
ven_db2[7] = data->vers.ver2.auxiliary_runversion;
ven_db2[8] = data->vers.ver2.auxiliary_debug0;
}
if (type == EOTF_T_DV_AHEAD) {
hdmi_vend_infoframe_rawset(ven_hb, db2);
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
return;
}
/*Dolby Vision standard case*/
if (type == EOTF_T_DOLBYVISION) {
/* disable forced gaming in this mode because if we are
* here with forced gaming on, it means TV is not DV LL
* capable
*/
if (hdev->ll_user_set_mode == HDMI_LL_MODE_ENABLE &&
(hdev->tx_comm.allm_mode == 1 || hdev->tx_comm.ct_mode == 1)) {
pr_info("hdmitx: Dolby VSIF, disable forced game mode\n");
hdmi_tx_enable_ll_mode(false);
}
/*first disable drm package*/
hdmi_drm_infoframe_set(NULL);
hdmi_vend_infoframe_rawset(ven_hb, db2);
/* Dolby Vision Source System-on-Chip Platform Kit Version 2.6:
* 4.4.1 Expected AVI-IF for Dolby Vision output, need BT2020 for DV
*/
hdmi_avi_infoframe_config(CONF_AVI_BT2020, SET_AVI_BT2020);/*BT.2020*/
if (tunnel_mode == RGB_8BIT) {/*RGB444*/
hdmi_avi_infoframe_config(CONF_AVI_CS, HDMI_COLORSPACE_RGB);
hdmi_avi_infoframe_config(CONF_AVI_Q01, RGB_RANGE_FUL);
} else {/*YUV422*/
hdmi_avi_infoframe_config(CONF_AVI_CS, HDMI_COLORSPACE_YUV422);
hdmi_avi_infoframe_config(CONF_AVI_YQ01, YCC_RANGE_FUL);
}
}
/*Dolby Vision low-latency case*/
else if (type == EOTF_T_LL_MODE) {
/* make sure forced game mode is enabled as there could be DV std
* to DV LL transition during uboot to kernel transition because
* of game mode forced enabled by user.
*/
if (hdev->ll_user_set_mode == HDMI_LL_MODE_ENABLE &&
hdev->tx_comm.allm_mode == 0 && hdev->tx_comm.ct_mode == 0) {
pr_debug("hdmitx: Dolby LL VSIF, enable forced game mode\n");
hdmi_tx_enable_ll_mode(true);
}
/*first disable drm package*/
hdmi_drm_infoframe_set(NULL);
hdmi_vend_infoframe_rawset(ven_hb, db2);
/* Dolby vision HDMI Signaling Case25,
* UCD323 not declare bt2020 colorimetry,
* need to forcely send BT.2020
*/
hdmi_avi_infoframe_config(CONF_AVI_BT2020, SET_AVI_BT2020);
if (tunnel_mode == RGB_10_12BIT) {/*10/12bit RGB444*/
hdmi_avi_infoframe_config(CONF_AVI_CS, HDMI_COLORSPACE_RGB);
hdmi_avi_infoframe_config(CONF_AVI_Q01, RGB_RANGE_LIM);
} else if (tunnel_mode == YUV444_10_12BIT) {
/*10/12bit YUV444*/
hdmi_avi_infoframe_config(CONF_AVI_CS, HDMI_COLORSPACE_YUV444);
hdmi_avi_infoframe_config(CONF_AVI_YQ01, YCC_RANGE_LIM);
} else {/*YUV422*/
hdmi_avi_infoframe_config(CONF_AVI_CS, HDMI_COLORSPACE_YUV422);
hdmi_avi_infoframe_config(CONF_AVI_YQ01, YCC_RANGE_LIM);
}
} else { /*SDR case*/
pr_info("hdmitx: Dolby VSIF, ven_db2[3]) = %d\n", ven_db2[3]);
hdmi_vend_infoframe_rawset(ven_hb, db2);
if (signal_sdr) {
pr_info("hdmitx: Dolby VSIF, switching signal to SDR\n");
update_current_para(hdev);
pr_info("vic:%d, cd:%d, cs:%d, cr:%d\n",
hdev->para->timing.vic, hdev->para->cd,
hdev->para->cs, hdev->para->cr);
hdmi_avi_infoframe_config(CONF_AVI_CS, hdev->para->cs);
hdmi_avi_infoframe_config(CONF_AVI_Q01, RGB_RANGE_DEFAULT);
hdmi_avi_infoframe_config(CONF_AVI_YQ01, YCC_RANGE_LIM);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, CLR_AVI_BT2020);/*BT709*/
/* re-enable forced game mode if selected by the user */
if (hdev->ll_user_set_mode == HDMI_LL_MODE_ENABLE) {
pr_info("hdmitx: Dolby VSIF disabled, re-enable forced game mode\n");
hdmi_tx_enable_ll_mode(true);
}
}
}
}
hdmitx21_dither_config(hdev);
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
}
static void hdmitx_set_hdr10plus_pkt(u32 flag,
struct hdr10plus_para *data)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
u8 ven_hb[3] = {0x81, 0x01, 0x1b};
u8 db[28] = {0x00};
u8 *ven_db = &db[1];
if (!_check_hdmi_mode())
return;
hdmi_debug();
if (hdev->bist_lock)
return;
if (flag == HDR10_PLUS_ZERO_VSIF) {
/* needed during hdr10+ to sdr transition */
pr_info("%s: zero vsif\n", __func__);
hdmi_vend_infoframe_rawset(ven_hb, db);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, CLR_AVI_BT2020);
hdev->hdr10plus_feature = 0;
hdr_status_pos = 4;
return;
}
if (!data || !flag) {
pr_info("%s: null vsif\n", __func__);
/* hdmi_vend_infoframe_set(NULL); */
hdmi_vend_infoframe_rawset(NULL, NULL);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, CLR_AVI_BT2020);
hdev->hdr10plus_feature = 0;
return;
}
if (hdev->hdr10plus_feature != 1)
pr_info("%s: flag = %d\n", __func__, flag);
hdev->hdr10plus_feature = 1;
hdr_status_pos = 3;
ven_db[0] = 0x8b;
ven_db[1] = 0x84;
ven_db[2] = 0x90;
ven_db[3] = ((data->application_version & 0x3) << 6) |
((data->targeted_max_lum & 0x1f) << 1);
ven_db[4] = data->average_maxrgb;
ven_db[5] = data->distribution_values[0];
ven_db[6] = data->distribution_values[1];
ven_db[7] = data->distribution_values[2];
ven_db[8] = data->distribution_values[3];
ven_db[9] = data->distribution_values[4];
ven_db[10] = data->distribution_values[5];
ven_db[11] = data->distribution_values[6];
ven_db[12] = data->distribution_values[7];
ven_db[13] = data->distribution_values[8];
ven_db[14] = ((data->num_bezier_curve_anchors & 0xf) << 4) |
((data->knee_point_x >> 6) & 0xf);
ven_db[15] = ((data->knee_point_x & 0x3f) << 2) |
((data->knee_point_y >> 8) & 0x3);
ven_db[16] = data->knee_point_y & 0xff;
ven_db[17] = data->bezier_curve_anchors[0];
ven_db[18] = data->bezier_curve_anchors[1];
ven_db[19] = data->bezier_curve_anchors[2];
ven_db[20] = data->bezier_curve_anchors[3];
ven_db[21] = data->bezier_curve_anchors[4];
ven_db[22] = data->bezier_curve_anchors[5];
ven_db[23] = data->bezier_curve_anchors[6];
ven_db[24] = data->bezier_curve_anchors[7];
ven_db[25] = data->bezier_curve_anchors[8];
ven_db[26] = ((data->graphics_overlay_flag & 0x1) << 7) |
((data->no_delay_flag & 0x1) << 6);
hdmi_vend_infoframe_rawset(ven_hb, db);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, SET_AVI_BT2020);
}
static void hdmitx_set_cuva_hdr_vsif(struct cuva_hdr_vsif_para *data)
{
unsigned long flags = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
unsigned char ven_hb[3] = {0x81, 0x01, 0x1b};
unsigned char db[28] = {0x00};
unsigned char *ven_db = &db[1];
if (!_check_hdmi_mode())
return;
spin_lock_irqsave(&hdev->edid_spinlock, flags);
if (!data) {
hdmi_vend_infoframe_rawset(NULL, NULL);
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
return;
}
ven_db[0] = GET_OUI_BYTE0(CUVA_IEEEOUI);
ven_db[1] = GET_OUI_BYTE1(CUVA_IEEEOUI);
ven_db[2] = GET_OUI_BYTE2(CUVA_IEEEOUI);
ven_db[3] = data->system_start_code;
ven_db[4] = (data->version_code & 0xf) << 4;
hdmi_vend_infoframe_rawset(ven_hb, db);
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
}
static void hdmitx_set_cuva_hdr_vs_emds(struct cuva_hdr_vs_emds_para *data)
{
struct hdmi_packet_t vs_emds[3];
unsigned long flags;
struct hdmitx_dev *hdev = get_hdmitx21_device();
int max_size;
if (!_check_hdmi_mode())
return;
memset(vs_emds, 0, sizeof(vs_emds));
spin_lock_irqsave(&hdev->edid_spinlock, flags);
if (!data) {
hdmitx_dhdr_send(NULL, 0);
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
return;
}
hdr_status_pos = 4;
max_size = sizeof(struct hdmi_packet_t) * 3;
vs_emds[0].hb[0] = 0x7f;
vs_emds[0].hb[1] = 1 << 7;
vs_emds[0].hb[2] = 0; /* Sequence_Index */
vs_emds[0].pb[0] = (1 << 7) | (1 << 4) | (1 << 2) | (1 << 1);
vs_emds[0].pb[1] = 0; /* rsvd */
vs_emds[0].pb[2] = 0; /* Organization_ID */
vs_emds[0].pb[3] = 0; /* Data_Set_Tag_MSB */
vs_emds[0].pb[4] = 2; /* Data_Set_Tag_LSB */
vs_emds[0].pb[5] = 0; /* Data_Set_Length_MSB */
vs_emds[0].pb[6] = 0x38; /* Data_Set_Length_LSB */
vs_emds[0].pb[7] = GET_OUI_BYTE0(CUVA_IEEEOUI);
vs_emds[0].pb[8] = GET_OUI_BYTE1(CUVA_IEEEOUI);
vs_emds[0].pb[9] = GET_OUI_BYTE2(CUVA_IEEEOUI);
vs_emds[0].pb[10] = data->system_start_code;
vs_emds[0].pb[11] = ((data->version_code & 0xf) << 4) |
((data->min_maxrgb_pq >> 8) & 0xf);
vs_emds[0].pb[12] = data->min_maxrgb_pq & 0xff;
vs_emds[0].pb[13] = (data->avg_maxrgb_pq >> 8) & 0xf;
vs_emds[0].pb[14] = data->avg_maxrgb_pq & 0xff;
vs_emds[0].pb[15] = (data->var_maxrgb_pq >> 8) & 0xf;
vs_emds[0].pb[16] = data->var_maxrgb_pq & 0xff;
vs_emds[0].pb[17] = (data->max_maxrgb_pq >> 8) & 0xf;
vs_emds[0].pb[18] = data->max_maxrgb_pq & 0xff;
vs_emds[0].pb[19] = (data->targeted_max_lum_pq >> 8) & 0xf;
vs_emds[0].pb[20] = data->targeted_max_lum_pq & 0xff;
vs_emds[0].pb[21] = ((data->transfer_character & 1) << 7) |
((data->base_enable_flag & 0x1) << 6) |
((data->base_param_m_p >> 8) & 0x3f);
vs_emds[0].pb[22] = data->base_param_m_p & 0xff;
vs_emds[0].pb[23] = data->base_param_m_m & 0x3f;
vs_emds[0].pb[24] = (data->base_param_m_a >> 8) & 0x3;
vs_emds[0].pb[25] = data->base_param_m_a & 0xff;
vs_emds[0].pb[26] = (data->base_param_m_b >> 8) & 0x3;
vs_emds[0].pb[27] = data->base_param_m_b & 0xff;
vs_emds[1].hb[0] = 0x7f;
vs_emds[1].hb[1] = 0;
vs_emds[1].hb[2] = 1; /* Sequence_Index */
vs_emds[1].pb[0] = data->base_param_m_n & 0x3f;
vs_emds[1].pb[1] = (((data->base_param_k[0] & 3) << 4) |
((data->base_param_k[1] & 3) << 2) |
((data->base_param_k[2] & 3) << 0));
vs_emds[1].pb[2] = data->base_param_delta_enable_mode & 0x7;
vs_emds[1].pb[3] = data->base_param_enable_delta & 0x7f;
vs_emds[1].pb[4] = (((data->_3spline_enable_num & 0x3) << 3) |
((data->_3spline_enable_flag & 1) << 2) |
(data->_3spline_data[0].th_enable_mode & 0x3));
vs_emds[1].pb[5] = data->_3spline_data[0].th_enable_mb;
vs_emds[1].pb[6] = (data->_3spline_data[0].th_enable >> 8) & 0xf;
vs_emds[1].pb[7] = data->_3spline_data[0].th_enable & 0xff;
vs_emds[1].pb[8] =
(data->_3spline_data[0].th_enable_delta[0] >> 8) & 0x3;
vs_emds[1].pb[9] = data->_3spline_data[0].th_enable_delta[0] & 0xff;
vs_emds[1].pb[10] =
(data->_3spline_data[0].th_enable_delta[1] >> 8) & 0x3;
vs_emds[1].pb[11] = data->_3spline_data[0].th_enable_delta[1] & 0xff;
vs_emds[1].pb[12] = data->_3spline_data[0].enable_strength;
vs_emds[1].pb[13] = data->_3spline_data[1].th_enable_mode & 0x3;
vs_emds[1].pb[14] = data->_3spline_data[1].th_enable_mb;
vs_emds[1].pb[15] = (data->_3spline_data[1].th_enable >> 8) & 0xf;
vs_emds[1].pb[16] = data->_3spline_data[1].th_enable & 0xff;
vs_emds[1].pb[17] =
(data->_3spline_data[1].th_enable_delta[0] >> 8) & 0x3;
vs_emds[1].pb[18] = data->_3spline_data[1].th_enable_delta[0] & 0xff;
vs_emds[1].pb[19] =
(data->_3spline_data[1].th_enable_delta[1] >> 8) & 0x3;
vs_emds[1].pb[20] = data->_3spline_data[1].th_enable_delta[1] & 0xff;
vs_emds[1].pb[21] = data->_3spline_data[1].enable_strength;
vs_emds[1].pb[22] = data->color_saturation_num;
vs_emds[1].pb[23] = data->color_saturation_gain[0];
vs_emds[1].pb[24] = data->color_saturation_gain[1];
vs_emds[1].pb[25] = data->color_saturation_gain[2];
vs_emds[1].pb[26] = data->color_saturation_gain[3];
vs_emds[1].pb[27] = data->color_saturation_gain[4];
vs_emds[2].hb[0] = 0x7f;
vs_emds[2].hb[1] = (1 << 6);
vs_emds[2].hb[2] = 2; /* Sequence_Index */
vs_emds[2].pb[0] = data->color_saturation_gain[5];
vs_emds[2].pb[1] = data->color_saturation_gain[6];
vs_emds[2].pb[2] = data->color_saturation_gain[7];
vs_emds[2].pb[3] = data->graphic_src_display_value;
vs_emds[2].pb[4] = 0; /* Reserved */
vs_emds[2].pb[5] = data->max_display_mastering_lum >> 8;
vs_emds[2].pb[6] = data->max_display_mastering_lum & 0xff;
hdmitx_dhdr_send((u8 *)&vs_emds, max_size);
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
}
/* reserved, left blank here, move to hdmi_tx_vrr.c file */
static void hdmitx_set_emp_pkt(u8 *data, u32 type, u32 size)
{
}
/*config attr*/
static ssize_t config_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
u8 *conf;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "cur_VIC: %d\n", hdev->tx_comm.cur_VIC);
if (hdev->para) {
switch (hdev->para->cd) {
case COLORDEPTH_24B:
conf = "8bit";
break;
case COLORDEPTH_30B:
conf = "10bit";
break;
case COLORDEPTH_36B:
conf = "12bit";
break;
case COLORDEPTH_48B:
conf = "16bit";
break;
default:
conf = "reserved";
}
pos += snprintf(buf + pos, PAGE_SIZE, "colordepth: %s\n",
conf);
switch (hdev->para->cs) {
case HDMI_COLORSPACE_RGB:
conf = "RGB";
break;
case HDMI_COLORSPACE_YUV422:
conf = "422";
break;
case HDMI_COLORSPACE_YUV444:
conf = "444";
break;
case HDMI_COLORSPACE_YUV420:
conf = "420";
break;
default:
conf = "reserved";
}
pos += snprintf(buf + pos, PAGE_SIZE, "colorspace: %s\n",
conf);
}
switch (hdev->tx_aud_cfg) {
case 0:
conf = "off";
break;
case 1:
conf = "on";
break;
case 2:
conf = "auto";
break;
default:
conf = "none";
}
pos += snprintf(buf + pos, PAGE_SIZE, "audio config: %s\n", conf);
switch (hdev->hdmi_audio_off_flag) {
case 0:
conf = "on";
break;
case 1:
conf = "off";
break;
default:
conf = "none";
}
pos += snprintf(buf + pos, PAGE_SIZE, "audio on/off: %s\n", conf);
switch (hdev->tx_aud_src) {
case 0:
conf = "SPDIF";
break;
case 1:
conf = "I2S";
break;
default:
conf = "none";
}
pos += snprintf(buf + pos, PAGE_SIZE, "audio source: %s\n", conf);
switch (hdev->cur_audio_param.type) {
case CT_REFER_TO_STREAM:
conf = "refer to stream header";
break;
case CT_PCM:
conf = "L-PCM";
break;
case CT_AC_3:
conf = "AC-3";
break;
case CT_MPEG1:
conf = "MPEG1";
break;
case CT_MP3:
conf = "MP3";
break;
case CT_MPEG2:
conf = "MPEG2";
break;
case CT_AAC:
conf = "AAC";
break;
case CT_DTS:
conf = "DTS";
break;
case CT_ATRAC:
conf = "ATRAC";
break;
case CT_ONE_BIT_AUDIO:
conf = "One Bit Audio";
break;
case CT_DD_P:
conf = "Dobly Digital+";
break;
case CT_DTS_HD:
conf = "DTS_HD";
break;
case CT_MAT:
conf = "MAT";
break;
case CT_DST:
conf = "DST";
break;
case CT_WMA:
conf = "WMA";
break;
default:
conf = "MAX";
}
pos += snprintf(buf + pos, PAGE_SIZE, "audio type: %s\n", conf);
switch (hdev->cur_audio_param.channel_num) {
case CC_REFER_TO_STREAM:
conf = "refer to stream header";
break;
case CC_2CH:
conf = "2 channels";
break;
case CC_3CH:
conf = "3 channels";
break;
case CC_4CH:
conf = "4 channels";
break;
case CC_5CH:
conf = "5 channels";
break;
case CC_6CH:
conf = "6 channels";
break;
case CC_7CH:
conf = "7 channels";
break;
case CC_8CH:
conf = "8 channels";
break;
default:
conf = "MAX";
}
pos += snprintf(buf + pos, PAGE_SIZE, "audio channel num: %s\n", conf);
switch (hdev->cur_audio_param.sample_rate) {
case FS_REFER_TO_STREAM:
conf = "refer to stream header";
break;
case FS_32K:
conf = "32kHz";
break;
case FS_44K1:
conf = "44.1kHz";
break;
case FS_48K:
conf = "48kHz";
break;
case FS_88K2:
conf = "88.2kHz";
break;
case FS_96K:
conf = "96kHz";
break;
case FS_176K4:
conf = "176.4kHz";
break;
case FS_192K:
conf = "192kHz";
break;
case FS_768K:
conf = "768kHz";
break;
default:
conf = "MAX";
}
pos += snprintf(buf + pos, PAGE_SIZE, "audio sample rate: %s\n", conf);
switch (hdev->cur_audio_param.sample_size) {
case SS_REFER_TO_STREAM:
conf = "refer to stream header";
break;
case SS_16BITS:
conf = "16bit";
break;
case SS_20BITS:
conf = "20bit";
break;
case SS_24BITS:
conf = "24bit";
break;
default:
conf = "MAX";
}
pos += snprintf(buf + pos, PAGE_SIZE, "audio sample size: %s\n", conf);
if (hdev->flag_3dfp)
conf = "FramePacking";
else if (hdev->flag_3dss)
conf = "SidebySide";
else if (hdev->flag_3dtb)
conf = "TopButtom";
else
conf = "off";
pos += snprintf(buf + pos, PAGE_SIZE, "3D config: %s\n", conf);
return pos;
}
static ssize_t config_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret = 0;
struct master_display_info_s data = {0};
struct hdr10plus_para hdr_data = {0x1, 0x2, 0x3};
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct cuva_hdr_vs_emds_para cuva_data = {0x1, 0x2, 0x3};
pr_info("hdmitx: config: %s\n", buf);
if (strncmp(buf, "unplug_powerdown", 16) == 0) {
if (buf[16] == '0')
hdev->unplug_powerdown = 0;
else
hdev->unplug_powerdown = 1;
} else if (strncmp(buf, "info", 4) == 0) {
pr_info("%x %x %x %x %x %x\n",
hdmitx21_get_cur_hdr_st(),
hdmitx21_get_cur_dv_st(),
hdmitx21_get_cur_hdr10p_st(),
hdmitx21_hdr_en(),
hdmitx21_dv_en(),
hdmitx21_hdr10p_en()
);
} else if (strncmp(buf, "3d", 2) == 0) {
/* Second, set 3D parameters */
if (strncmp(buf + 2, "tb", 2) == 0) {
hdev->flag_3dtb = 1;
hdev->flag_3dss = 0;
hdev->flag_3dfp = 0;
hdmi21_set_3d(hdev, T3D_TAB, 0);
} else if ((strncmp(buf + 2, "lr", 2) == 0) ||
(strncmp(buf + 2, "ss", 2) == 0)) {
unsigned long sub_sample_mode = 0;
hdev->flag_3dtb = 0;
hdev->flag_3dss = 1;
hdev->flag_3dfp = 0;
if (buf[2])
ret = kstrtoul(buf + 2, 10,
&sub_sample_mode);
/* side by side */
hdmi21_set_3d(hdev, T3D_SBS_HALF,
sub_sample_mode);
} else if (strncmp(buf + 2, "fp", 2) == 0) {
hdev->flag_3dtb = 0;
hdev->flag_3dss = 0;
hdev->flag_3dfp = 1;
hdmi21_set_3d(hdev, T3D_FRAME_PACKING, 0);
} else if (strncmp(buf + 2, "off", 3) == 0) {
hdev->flag_3dfp = 0;
hdev->flag_3dtb = 0;
hdev->flag_3dss = 0;
hdmi21_set_3d(hdev, T3D_DISABLE, 0);
}
} else if (strncmp(buf, "sdr", 3) == 0) {
data.features = 0x00010100;
hdmitx_set_drm_pkt(&data);
} else if (strncmp(buf, "hdr", 3) == 0) {
data.features = 0x00091000;
hdmitx_set_drm_pkt(&data);
} else if (strncmp(buf, "hlg", 3) == 0) {
data.features = 0x00091200;
hdmitx_set_drm_pkt(&data);
} else if (strncmp(buf, "vsif", 4) == 0) {
hdmitx_set_vsif_pkt(buf[4] - '0', buf[5] == '1', NULL, true);
} else if (strncmp(buf, "emp", 3) == 0) {
hdmitx_set_emp_pkt(NULL, 1, 1);
} else if (strncmp(buf, "hdr10+", 6) == 0) {
hdmitx_set_hdr10plus_pkt(1, &hdr_data);
} else if (strncmp(buf, "cuva", 4) == 0) {
hdmitx_set_cuva_hdr_vs_emds(&cuva_data);
}
return count;
}
void hdmitx21_ext_set_audio_output(int enable)
{
hdmitx21_audio_mute_op(enable, AUDIO_MUTE_PATH_1);
pr_info("%s enable:%d\n", __func__, enable);
}
int hdmitx21_ext_get_audio_status(void)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
return !!hdev->tx_aud_cfg;
}
void hdmitx21_ext_set_i2s_mask(char ch_num, char ch_msk)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
static u32 update_flag = -1;
if (!(ch_num == 2 || ch_num == 4 ||
ch_num == 6 || ch_num == 8)) {
pr_info("err chn setting, must be 2, 4, 6 or 8, Rst as def\n");
hdev->aud_output_ch = 0;
if (update_flag != hdev->aud_output_ch) {
update_flag = hdev->aud_output_ch;
hdev->hdmi_ch = 0;
hdmitx21_set_audio(hdev, &hdev->cur_audio_param);
}
}
if (ch_msk == 0) {
pr_info("err chn msk, must larger than 0\n");
return;
}
hdev->aud_output_ch = (ch_num << 4) + ch_msk;
if (update_flag != hdev->aud_output_ch) {
update_flag = hdev->aud_output_ch;
hdev->hdmi_ch = 0;
hdmitx21_set_audio(hdev, &hdev->cur_audio_param);
}
}
char hdmitx21_ext_get_i2s_mask(void)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
return hdev->aud_output_ch & 0xf;
}
static ssize_t aud_mute_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
atomic_read(&hdev->kref_audio_mute));
return pos;
}
static ssize_t aud_mute_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
atomic_t kref_audio_mute = hdev->kref_audio_mute;
if (buf[0] == '1') {
atomic_inc(&kref_audio_mute);
hdmitx21_audio_mute_op(0, AUDIO_MUTE_PATH_2);
}
if (buf[0] == '0') {
if (!(atomic_sub_and_test(0, &kref_audio_mute)))
atomic_dec(&kref_audio_mute);
hdmitx21_audio_mute_op(1, AUDIO_MUTE_PATH_2);
}
hdev->kref_audio_mute = kref_audio_mute;
return count;
}
static ssize_t vid_mute_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
atomic_read(&hdev->kref_video_mute));
return pos;
}
static ssize_t vid_mute_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
atomic_t kref_video_mute = hdev->kref_video_mute;
if (buf[0] == '1') {
atomic_inc(&kref_video_mute);
hdmitx21_video_mute_op(0, VIDEO_MUTE_PATH_1);
}
if (buf[0] == '0') {
if (!(atomic_sub_and_test(0, &kref_video_mute))) {
atomic_dec(&kref_video_mute);
}
hdmitx21_video_mute_op(1, VIDEO_MUTE_PATH_1);
}
hdev->kref_video_mute = kref_video_mute;
return count;
}
static ssize_t debug_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
hdev->hwop.debugfun(hdev, buf);
return count;
}
static bool is_vic_support_y420(enum hdmi_vic vic)
{
unsigned int i = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct rx_cap *prxcap = &hdev->tx_comm.rxcap;
bool ret = false;
for (i = 0; i < Y420_VIC_MAX_NUM; i++) {
if (prxcap->y420_vic[i]) {
if (prxcap->y420_vic[i] == vic) {
ret = true;
break;
}
} else {
ret = false;
break;
}
}
return ret;
}
bool is_current_4k_format(void)
{
int i;
struct vinfo_s *info = hdmitx_get_current_vinfo(NULL);
static const char * const hdmi4k[] = {
"2160p",
"smpte",
NULL
};
if (!info || !info->name)
return false;
for (i = 0; hdmi4k[i]; i++) {
if (strstr(info->name, hdmi4k[i]))
return true;
}
return false;
}
/**/
static ssize_t disp_cap_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int i, pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct rx_cap *prxcap = &hdev->tx_comm.rxcap;
const struct hdmi_timing *timing = NULL;
enum hdmi_vic vic;
for (i = 0; i < prxcap->VIC_count; i++) {
vic = prxcap->VIC[i];
if (vic == HDMI_2_720x480p60_4x3 ||
vic == HDMI_6_720x480i60_4x3 ||
vic == HDMI_17_720x576p50_4x3 ||
vic == HDMI_21_720x576i50_4x3) {
if (hdmitx_check_vic(vic + 1))
continue;
timing = hdmitx21_gettiming_from_vic(vic + 1);
} else {
timing = hdmitx21_gettiming_from_vic(vic);
}
if (timing) {
pos += snprintf(buf + pos, PAGE_SIZE, "%s",
timing->sname ? timing->sname : timing->name);
if (vic == prxcap->native_vic ||
vic == prxcap->native_vic2)
pos += snprintf(buf + pos, PAGE_SIZE, "*");
pos += snprintf(buf + pos, PAGE_SIZE, "\n");
}
if (is_vic_support_y420(vic)) {
/* backup only for old android */
/* pos += snprintf(buf + pos, PAGE_SIZE, "%s420\n", */
/* timing->sname ? timing->sname : timing->name); */
}
}
return pos;
}
static ssize_t preferred_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct rx_cap *prxcap = &hdev->tx_comm.rxcap;
pos += snprintf(buf + pos, PAGE_SIZE, "%s\n",
hdmitx21_edid_vic_to_string(prxcap->preferred_mode));
return pos;
}
/* cea_cap, a clone of disp_cap */
static ssize_t cea_cap_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return disp_cap_show(dev, attr, buf);
}
static ssize_t vesa_cap_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
/* TODO */
return 0;
}
static void _show_pcm_ch(struct rx_cap *prxcap, int i,
int *ppos, char *buf)
{
const char * const aud_sample_size[] = {"ReferToStreamHeader",
"16", "20", "24", NULL};
int j = 0;
for (j = 0; j < 3; j++) {
if (prxcap->RxAudioCap[i].cc3 & (1 << j))
*ppos += snprintf(buf + *ppos, PAGE_SIZE, "%s/",
aud_sample_size[j + 1]);
}
*ppos += snprintf(buf + *ppos - 1, PAGE_SIZE, " bit\n") - 1;
}
/**/
static ssize_t aud_cap_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
int i, pos = 0, j;
static const char * const aud_ct[] = {
"ReferToStreamHeader", "PCM", "AC-3", "MPEG1", "MP3",
"MPEG2", "AAC", "DTS", "ATRAC", "OneBitAudio",
"Dolby_Digital+", "DTS-HD", "MAT", "DST", "WMA_Pro",
"Reserved", NULL};
static const char * const aud_sampling_frequency[] = {
"ReferToStreamHeader", "32", "44.1", "48", "88.2", "96",
"176.4", "192", NULL};
struct rx_cap *prxcap = &hdev->tx_comm.rxcap;
struct dolby_vsadb_cap *cap = &prxcap->dolby_vsadb_cap;
pos += snprintf(buf + pos, PAGE_SIZE,
"CodingType MaxChannels SamplingFreq SampleSize\n");
for (i = 0; i < prxcap->AUD_count; i++) {
if (prxcap->RxAudioCap[i].audio_format_code == CT_CXT) {
if ((prxcap->RxAudioCap[i].cc3 >> 3) == 0xb) {
pos += snprintf(buf + pos, PAGE_SIZE, "MPEG-H, 8ch, ");
for (j = 0; j < 7; j++) {
if (prxcap->RxAudioCap[i].freq_cc & (1 << j))
pos += snprintf(buf + pos, PAGE_SIZE, "%s/",
aud_sampling_frequency[j + 1]);
}
pos += snprintf(buf + pos - 1, PAGE_SIZE, " kHz\n");
}
continue;
}
pos += snprintf(buf + pos, PAGE_SIZE, "%s",
aud_ct[prxcap->RxAudioCap[i].audio_format_code]);
/* todo: cc3 & 3 */
if (prxcap->RxAudioCap[i].audio_format_code == CT_DD_P &&
(prxcap->RxAudioCap[i].cc3 & 1))
pos += snprintf(buf + pos, PAGE_SIZE, "/ATMOS");
if (prxcap->RxAudioCap[i].audio_format_code != CT_CXT)
pos += snprintf(buf + pos, PAGE_SIZE, ", %d ch, ",
prxcap->RxAudioCap[i].channel_num_max + 1);
for (j = 0; j < 7; j++) {
if (prxcap->RxAudioCap[i].freq_cc & (1 << j))
pos += snprintf(buf + pos, PAGE_SIZE, "%s/",
aud_sampling_frequency[j + 1]);
}
pos += snprintf(buf + pos - 1, PAGE_SIZE, " kHz, ") - 1;
switch (prxcap->RxAudioCap[i].audio_format_code) {
case CT_PCM:
_show_pcm_ch(prxcap, i, &pos, buf);
break;
case CT_AC_3:
case CT_MPEG1:
case CT_MP3:
case CT_MPEG2:
case CT_AAC:
case CT_DTS:
case CT_ATRAC:
case CT_ONE_BIT_AUDIO:
pos += snprintf(buf + pos, PAGE_SIZE,
"MaxBitRate %dkHz\n",
prxcap->RxAudioCap[i].cc3 * 8);
break;
case CT_DD_P:
case CT_DTS_HD:
case CT_MAT:
case CT_DST:
pos += snprintf(buf + pos, PAGE_SIZE, "DepValue 0x%x\n",
prxcap->RxAudioCap[i].cc3);
break;
case CT_WMA:
default:
break;
}
}
if (cap->ieeeoui == DOVI_IEEEOUI) {
/*
*Dolby Vendor Specific:
* headphone_playback_only:0,
* center_speaker:1,
* surround_speaker:1,
* height_speaker:1,
* Ver:1.0,
* MAT_PCM_48kHz_only:1,
* e61146d0007001,
*/
pos += snprintf(buf + pos, PAGE_SIZE,
"Dolby Vendor Specific:\n");
if (cap->dolby_vsadb_ver == 0)
pos += snprintf(buf + pos, PAGE_SIZE, " Ver:1.0,\n");
else
pos += snprintf(buf + pos, PAGE_SIZE,
" Ver:Reversed,\n");
pos += snprintf(buf + pos, PAGE_SIZE,
" center_speaker:%d,\n", cap->spk_center);
pos += snprintf(buf + pos, PAGE_SIZE,
" surround_speaker:%d,\n", cap->spk_surround);
pos += snprintf(buf + pos, PAGE_SIZE,
" height_speaker:%d,\n", cap->spk_height);
pos += snprintf(buf + pos, PAGE_SIZE,
" headphone_playback_only:%d,\n", cap->headphone_only);
pos += snprintf(buf + pos, PAGE_SIZE,
" MAT_PCM_48kHz_only:%d,\n", cap->mat_48k_pcm_only);
pos += snprintf(buf + pos, PAGE_SIZE, " ");
for (i = 0; i < 7; i++)
pos += snprintf(buf + pos, PAGE_SIZE, "%02x",
cap->rawdata[i]);
pos += snprintf(buf + pos, PAGE_SIZE, ",\n");
}
return pos;
}
/**/
static ssize_t lipsync_cap_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct rx_cap *prxcap = &hdev->tx_comm.rxcap;
pos += snprintf(buf + pos, PAGE_SIZE, "Lipsync(in ms)\n");
pos += snprintf(buf + pos, PAGE_SIZE, "%d, %d\n",
prxcap->vLatency, prxcap->aLatency);
return pos;
}
/**/
static ssize_t hdmi_hdr_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
/* pos = 4 */
if (hdr_status_pos == 4) {
pos += snprintf(buf + pos, PAGE_SIZE, "HDR10-GAMMA_CUVA");
return pos;
}
/* pos = 3 */
if (hdr_status_pos == 3 || hdev->hdr10plus_feature) {
pos += snprintf(buf + pos, PAGE_SIZE, "HDR10Plus-VSIF");
return pos;
}
/* pos = 2 */
if (hdr_status_pos == 2) {
if (hdev->hdmi_current_eotf_type == EOTF_T_DOLBYVISION) {
pos += snprintf(buf + pos, PAGE_SIZE,
"DolbyVision-Std");
return pos;
}
if (hdev->hdmi_current_eotf_type == EOTF_T_LL_MODE) {
pos += snprintf(buf + pos, PAGE_SIZE,
"DolbyVision-Lowlatency");
return pos;
}
}
/* pos = 1 */
if (hdr_status_pos == 1) {
if (hdev->hdr_transfer_feature == T_SMPTE_ST_2084) {
if (hdev->hdr_color_feature == C_BT2020) {
pos += snprintf(buf + pos, PAGE_SIZE,
"HDR10-GAMMA_ST2084");
return pos;
}
pos += snprintf(buf + pos, PAGE_SIZE, "HDR10-others");
return pos;
}
if (hdev->hdr_color_feature == C_BT2020 &&
(hdev->hdr_transfer_feature == T_BT2020_10 ||
hdev->hdr_transfer_feature == T_HLG)) {
pos += snprintf(buf + pos, PAGE_SIZE,
"HDR10-GAMMA_HLG");
return pos;
}
}
/* default is SDR */
pos += snprintf(buf + pos, PAGE_SIZE, "SDR");
return pos;
}
static int hdmi_hdr_status_to_drm(void)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
/* pos = 3 */
if (hdr_status_pos == 3 || hdev->hdr10plus_feature)
return HDR10PLUS_VSIF;
/* pos = 2 */
if (hdr_status_pos == 2) {
if (hdev->hdmi_current_eotf_type == EOTF_T_DOLBYVISION)
return dolbyvision_std;
if (hdev->hdmi_current_eotf_type == EOTF_T_LL_MODE)
return dolbyvision_lowlatency;
}
/* pos = 1 */
if (hdr_status_pos == 1) {
if (hdev->hdr_transfer_feature == T_SMPTE_ST_2084) {
if (hdev->hdr_color_feature == C_BT2020)
return HDR10_GAMMA_ST2084;
else
return HDR10_others;
}
if (hdev->hdr_color_feature == C_BT2020 &&
(hdev->hdr_transfer_feature == T_BT2020_10 ||
hdev->hdr_transfer_feature == T_HLG))
return HDR10_GAMMA_HLG;
}
/* default is SDR */
return SDR;
}
/**/
static ssize_t dc_cap_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i;
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct rx_cap *prxcap = &hdev->tx_comm.rxcap;
const struct dv_info *dv = &hdev->tx_comm.rxcap.dv_info;
const struct dv_info *dv2 = &hdev->tx_comm.rxcap.dv_info2;
if (prxcap->dc_36bit_420)
pos += snprintf(buf + pos, PAGE_SIZE, "420,12bit\n");
if (prxcap->dc_30bit_420)
pos += snprintf(buf + pos, PAGE_SIZE, "420,10bit\n");
for (i = 0; i < Y420_VIC_MAX_NUM; i++) {
if (prxcap->y420_vic[i]) {
pos += snprintf(buf + pos, PAGE_SIZE,
"420,8bit\n");
break;
}
}
if (prxcap->native_Mode & (1 << 5)) {
if (prxcap->dc_y444) {
if (prxcap->dc_36bit || dv->sup_10b_12b_444 == 0x2 ||
dv2->sup_10b_12b_444 == 0x2)
if (!hdev->vend_id_hit)
pos += snprintf(buf + pos, PAGE_SIZE, "444,12bit\n");
if (prxcap->dc_30bit || dv->sup_10b_12b_444 == 0x1 ||
dv2->sup_10b_12b_444 == 0x1) {
if (!hdev->vend_id_hit)
pos += snprintf(buf + pos, PAGE_SIZE, "444,10bit\n");
}
}
pos += snprintf(buf + pos, PAGE_SIZE, "444,8bit\n");
}
/* y422, not check dc */
if (prxcap->native_Mode & (1 << 4)) {
pos += snprintf(buf + pos, PAGE_SIZE, "422,12bit\n");
pos += snprintf(buf + pos, PAGE_SIZE, "422,10bit\n");
pos += snprintf(buf + pos, PAGE_SIZE, "422,8bit\n");
}
//nextrgb:
if (prxcap->dc_36bit || dv->sup_10b_12b_444 == 0x2 ||
dv2->sup_10b_12b_444 == 0x2)
if (!hdev->vend_id_hit)
pos += snprintf(buf + pos, PAGE_SIZE, "rgb,12bit\n");
if (prxcap->dc_30bit || dv->sup_10b_12b_444 == 0x1 ||
dv2->sup_10b_12b_444 == 0x1)
if (!hdev->vend_id_hit)
pos += snprintf(buf + pos, PAGE_SIZE, "rgb,10bit\n");
pos += snprintf(buf + pos, PAGE_SIZE, "rgb,8bit\n");
return pos;
}
static bool valid_mode;
static char cvalid_mode[32];
static bool pre_process_str(char *name)
{
int i;
u32 flag = 0;
char *color_format[4] = {"444", "422", "420", "rgb"};
for (i = 0; i < 4 ; i++) {
if (strstr(name, color_format[i]))
flag++;
}
if (flag >= 2)
return 0;
else
return 1;
}
static ssize_t valid_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmi_format_para *para = NULL;
if (cvalid_mode[0]) {
valid_mode = pre_process_str(cvalid_mode);
if (valid_mode == 0) {
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n\r",
valid_mode);
return pos;
}
para = hdmitx21_tst_fmt_name(cvalid_mode, cvalid_mode);
if (!para) {
pos += snprintf(buf + pos, PAGE_SIZE, "0\n\r");
return pos;
}
}
valid_mode = hdmitx21_edid_check_valid_mode(hdev, para);
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n\r", valid_mode);
return pos;
}
static ssize_t valid_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
memset(cvalid_mode, 0, sizeof(cvalid_mode));
strncpy(cvalid_mode, buf, sizeof(cvalid_mode));
cvalid_mode[31] = '\0';
return count;
}
static ssize_t allm_cap_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct rx_cap *prxcap = &hdev->tx_comm.rxcap;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n\r", prxcap->allm);
return pos;
}
static ssize_t allm_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n\r", hdev->tx_comm.allm_mode);
return pos;
}
static inline int com_str(const char *buf, const char *str)
{
return strncmp(buf, str, strlen(str)) == 0;
}
static ssize_t allm_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
pr_info("hdmitx: store allm_mode as %s\n", buf);
if (com_str(buf, "0")) {
// disable ALLM
tx_comm->allm_mode = 0;
hdmitx21_construct_vsif(hdev, VT_ALLM, 0, NULL);
if (_is_hdmi14_4k(tx_comm->cur_VIC) &&
!hdmitx21_dv_en() &&
!hdmitx21_hdr10p_en())
hdmitx21_construct_vsif(hdev, VT_HDMI14_4K, 1, NULL);
}
if (com_str(buf, "1")) {
tx_comm->allm_mode = 1;
hdmitx21_construct_vsif(hdev, VT_ALLM, 1, NULL);
tx_comm->ct_mode = 0;
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CT_MODE, SET_CT_OFF);
}
if (com_str(buf, "-1")) {
if (tx_comm->allm_mode == 1) {
tx_comm->allm_mode = 0;
hdmi_vend_infoframe2_rawset(NULL, NULL);
}
}
return count;
}
static void hdmi_tx_enable_ll_mode(bool enable)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
if (enable) {
if (tx_comm->rxcap.allm) {
/* dolby vision CTS case89 requirement: if IFDB with no
* additional VSIF support in EDID, then only
* send DV-VSIF, not send HF-VSIF
*/
if (hdmitx21_dv_en() &&
(tx_comm->rxcap.ifdb_present &&
tx_comm->rxcap.additional_vsif_num < 1)) {
pr_info("%s: can't send HF-VSIF, ifdb_present: %d, additional_vsif_num: %d\n",
__func__, tx_comm->rxcap.ifdb_present,
tx_comm->rxcap.additional_vsif_num);
return;
}
tx_comm->allm_mode = 1;
pr_info("%s: enabling ALLM, enable:%d, allm:%d, cnc3:%d\n",
__func__, enable, tx_comm->rxcap.allm, tx_comm->rxcap.cnc3);
hdmitx21_construct_vsif(hdev, VT_ALLM, 1, NULL);
tx_comm->ct_mode = 0;
hdev->it_content = 0;
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CT_MODE, SET_CT_OFF);
} else if (tx_comm->rxcap.cnc3) {
/* disable ALLM first if enabled*/
if (tx_comm->allm_mode == 1) {
tx_comm->allm_mode = 0;
pr_info("%s: disabling ALLM before enabling game mode, enable:%d, allm:%d, cnc3:%d\n",
__func__, enable, tx_comm->rxcap.allm, tx_comm->rxcap.cnc3);
hdmitx21_construct_vsif(hdev, VT_ALLM, 0, NULL);
if (_is_hdmi14_4k(tx_comm->cur_VIC))
hdmitx21_construct_vsif(hdev, VT_HDMI14_4K, 1, NULL);
/* if not hdmi1.4 4k, need to sent > 4 frames and shorter than 1S
* HF-VSIF with allm_mode = 0, and then disable HF-VSIF according
* 10.2.1 HF-VSIF Transitions in hdmi2.1a. TODO:
*/
}
tx_comm->ct_mode = 1;
hdev->it_content = 1;
pr_info("%s: enabling GAME Mode, enable:%d, allm:%d, cnc3:%d\n",
__func__, enable, tx_comm->rxcap.allm, tx_comm->rxcap.cnc3);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CT_MODE,
SET_CT_GAME | IT_CONTENT << 4);
} else {
/* for safety, clear ALLM/HDMI1.X GAME if enabled */
/* disable ALLM */
if (tx_comm->allm_mode == 1) {
tx_comm->allm_mode = 0;
pr_info("%s: disabling ALLM, enable:%d, allm:%d, cnc3:%d\n",
__func__, enable, tx_comm->rxcap.allm, tx_comm->rxcap.cnc3);
hdmitx21_construct_vsif(hdev, VT_ALLM, 0, NULL);
if (_is_hdmi14_4k(tx_comm->cur_VIC) &&
!hdmitx21_dv_en() &&
!hdmitx21_hdr10p_en())
hdmitx21_construct_vsif(hdev, VT_HDMI14_4K, 1, NULL);
}
/* clear content type */
if (tx_comm->ct_mode == 1) {
tx_comm->ct_mode = 0;
hdev->it_content = 0;
pr_info("%s: disabling GAME Mode disabled, enable:%d, allm:%d, cnc3:%d\n",
__func__, enable, tx_comm->rxcap.allm, tx_comm->rxcap.cnc3);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CT_MODE, SET_CT_OFF);
}
}
} else {
/* disable ALLM */
if (tx_comm->allm_mode == 1) {
tx_comm->allm_mode = 0;
pr_info("%s: disabling ALLM, enable:%d, allm:%d, cnc3:%d\n",
__func__, enable, tx_comm->rxcap.allm, tx_comm->rxcap.cnc3);
hdmitx21_construct_vsif(hdev, VT_ALLM, 0, NULL);
if (_is_hdmi14_4k(tx_comm->cur_VIC) &&
!hdmitx21_dv_en() &&
!hdmitx21_hdr10p_en())
hdmitx21_construct_vsif(hdev, VT_HDMI14_4K, 1, NULL);
}
/* clear content type */
if (tx_comm->ct_mode == 1) {
tx_comm->ct_mode = 0;
hdev->it_content = 0;
pr_info("%s: disabling GAME Mode disabled, enable:%d, allm:%d, cnc3:%d\n",
__func__, enable, tx_comm->rxcap.allm, tx_comm->rxcap.cnc3);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CT_MODE, SET_CT_OFF);
}
}
}
/* for decoder/hwc or sysctl to control the low latency mode,
* as they don't care if sink support ALLM OR HDMI1.X game mode
* so need hdmitx driver to device to send ALLM OR HDMI1.X game
* mode according to capability of EDID
*/
static ssize_t ll_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (hdev->tx_comm.rxcap.allm) {
if (hdev->tx_comm.allm_mode == 1)
pos += snprintf(buf + pos, PAGE_SIZE, "HDMI2.1_ALLM_ENABLED\n\r");
else
pos += snprintf(buf + pos, PAGE_SIZE, "HDMI2.1_ALLM_DISABLED\n\r");
}
if (hdev->tx_comm.rxcap.cnc3) {
if (hdev->tx_comm.ct_mode == 1)
pos += snprintf(buf + pos, PAGE_SIZE, "HDMI1.x_GAME_MODE_ENABLED\n\r");
else
pos += snprintf(buf + pos, PAGE_SIZE, "HDMI1.x_GAME_MODE_DISABLED\n\r");
}
if (!hdev->tx_comm.rxcap.allm && !hdev->tx_comm.rxcap.cnc3)
pos += snprintf(buf + pos, PAGE_SIZE, "HDMI_LATENCY_MODE_UNKNOWN\n\r");
return pos;
}
/* 1.echo 1 to enable ALLM OR HDMI1.X game mode
* if sink support ALLM, then output ALLM mode;
* else if support HDMI1.X game mode, then output
* HDMI1.X game mode; else, do nothing
* 2.echo 0 to disable ALLM and HDMI1.X game mode
*/
static ssize_t ll_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
hdev->ll_enabled_in_auto_mode = com_str(buf, "1");
pr_info("hdmitx: store ll_enabled_in_auto_mode: %d, ll_user_set_mode:%d\n",
hdev->ll_enabled_in_auto_mode, hdev->ll_user_set_mode);
if (hdev->ll_user_set_mode == HDMI_LL_MODE_AUTO) {
pr_info("hdmitx: store ll_mode as %s, calling hdmi_tx_enable_ll_mode()\n", buf);
hdmi_tx_enable_ll_mode(hdev->ll_enabled_in_auto_mode);
} else {
pr_info("hdmitx: ll mode is forced on/off: %d\n", hdev->ll_user_set_mode);
}
return count;
}
/* for user to force enable/disable low-latency modes
*/
static ssize_t ll_user_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
switch (hdev->ll_user_set_mode) {
case HDMI_LL_MODE_ENABLE:
pos += snprintf(buf + pos, PAGE_SIZE, "HDMI_LL_MODE_ENABLE\n\r");
break;
case HDMI_LL_MODE_DISABLE:
pos += snprintf(buf + pos, PAGE_SIZE, "HDMI_LL_MODE_DISABLE\n\r");
break;
case HDMI_LL_MODE_AUTO:
default:
pos += snprintf(buf + pos, PAGE_SIZE, "HDMI_LL_MODE_AUTO\n\r");
break;
}
return pos;
}
/* 1.echo enable to enable ALLM OR HDMI1.X game mode
* if sink support ALLM, then output ALLM mode;
* else if support HDMI1.X game mode, then output
* HDMI1.X game mode; else, do nothing
* 2.echo disable to disable ALLM and HDMI1.X game mode
* 3.echo auto to enable/disable low-latency mode per
* content type
*/
static ssize_t ll_user_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
pr_info("hdmitx: store ll_user_set_mode as %s\n", buf);
if (com_str(buf, "enable")) {
hdev->ll_user_set_mode = HDMI_LL_MODE_ENABLE;
hdmi_tx_enable_ll_mode(true);
} else if (com_str(buf, "disable")) {
hdev->ll_user_set_mode = HDMI_LL_MODE_DISABLE;
hdmi_tx_enable_ll_mode(false);
} else {
hdev->ll_user_set_mode = HDMI_LL_MODE_AUTO;
hdmi_tx_enable_ll_mode(hdev->ll_enabled_in_auto_mode);
}
return count;
}
/* for game console-> hdmirx -> hdmitx -> TV
* interface for hdmirx module
* ret: false if not update, true if updated
*/
bool hdmitx_update_latency_info(struct tvin_latency_s *latency_info)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
bool it_content = false;
/* when switch between hdmirx source(ALLM) and hdmitx home(non-ALLM),
* the ALLM/1.4 Game will change, need to mute before change
*/
bool video_mute = false;
if (!hdmi_allm_passthough_en)
return false;
if (!latency_info)
return false;
pr_info("%s: ll_enabled_in_auto_mode: %d, ll_user_set_mode:%d\n",
__func__, hdev->ll_enabled_in_auto_mode, hdev->ll_user_set_mode);
if (hdev->ll_user_set_mode != HDMI_LL_MODE_AUTO) {
pr_info("%s: non-auto mode, return, allm_mode: %d, it_content: %d, cn_type: %d\n",
__func__,
latency_info->allm_mode,
latency_info->it_content,
latency_info->cn_type);
return false;
}
pr_info("%s: allm_mode: %d, it_content: %d, cn_type: %d\n",
__func__, latency_info->allm_mode, latency_info->it_content, latency_info->cn_type);
if (tx_comm->allm_mode == latency_info->allm_mode &&
hdev->it_content == latency_info->it_content &&
tx_comm->ct_mode == latency_info->cn_type) {
pr_info("latency_info not changed, exit\n");
return false;
}
/* refer to allm_mode_store() */
if (latency_info->allm_mode) {
if (tx_comm->rxcap.allm) {
//if (hdmitx21_dv_en() &&
//(hdev->rxcap.ifdb_present &&
//hdev->rxcap.additional_vsif_num < 1)) {
//pr_info("%s: DV enabled, but ifdb_present: %d,
//additional_vsif_num: %d\n",
//__func__, hdev->rxcap.ifdb_present,
//hdev->rxcap.additional_vsif_num);
//return false;
//}
if (!get_rx_active_sts()) {
video_mute = true;
//hdmitx21_video_mute_op(0, VIDEO_MUTE_PATH_4);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_VIDEO_MUTE_OP,
VIDEO_MUTE);
}
tx_comm->allm_mode = 1;
pr_info("%s: enabling ALLM\n", __func__);
hdmitx21_construct_vsif(hdev, VT_ALLM, 1, NULL);
tx_comm->ct_mode = 0;
hdev->it_content = 0;
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CT_MODE, SET_CT_OFF);
}
} else {
if (!get_rx_active_sts()) {
video_mute = true;
//hdmitx21_video_mute_op(0, VIDEO_MUTE_PATH_4);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_VIDEO_MUTE_OP, VIDEO_MUTE);
}
/* disable ALLM firstly */
if (tx_comm->allm_mode == 1) {
tx_comm->allm_mode = 0;
pr_info("%s: disabling ALLM before enable/disable game mode\n", __func__);
hdmitx21_construct_vsif(hdev, VT_ALLM, 0, NULL);
if (_is_hdmi14_4k(tx_comm->cur_VIC) &&
!hdmitx21_dv_en() &&
!hdmitx21_hdr10p_en())
hdmitx21_construct_vsif(hdev, VT_HDMI14_4K, 1, NULL);
}
hdev->it_content = latency_info->it_content;
it_content = hdev->it_content;
if (tx_comm->rxcap.cnc3 && latency_info->cn_type == GAME) {
tx_comm->ct_mode = 1;
pr_info("%s: enabling GAME mode\n", __func__);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CT_MODE,
SET_CT_GAME | it_content << 4);
} else if (tx_comm->rxcap.cnc0 && latency_info->cn_type == GRAPHICS &&
latency_info->it_content == 1) {
tx_comm->ct_mode = 2;
pr_info("%s: enabling GRAPHICS mode\n", __func__);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CT_MODE,
SET_CT_GRAPHICS | it_content << 4);
} else if (tx_comm->rxcap.cnc1 && latency_info->cn_type == PHOTO) {
tx_comm->ct_mode = 3;
pr_info("%s: enabling PHOTO mode\n", __func__);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CT_MODE,
SET_CT_PHOTO | it_content << 4);
} else if (tx_comm->rxcap.cnc2 && latency_info->cn_type == CINEMA) {
tx_comm->ct_mode = 4;
pr_info("%s: enabling CINEMA mode\n", __func__);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CT_MODE,
SET_CT_CINEMA | it_content << 4);
} else {
tx_comm->ct_mode = 0;
pr_info("%s: No GAME or CT mode\n", __func__);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CT_MODE,
SET_CT_OFF | it_content << 4);
}
}
return true;
}
EXPORT_SYMBOL(hdmitx_update_latency_info);
static ssize_t vrr_cap_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return _vrr_cap_show(dev, attr, buf);
}
static DEFINE_MUTEX(avmute_mutex);
void hdmitx21_av_mute_op(u32 flag, unsigned int path)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
static unsigned int avmute_path;
mutex_lock(&avmute_mutex);
if (flag == SET_AVMUTE) {
avmute_path |= path;
pr_info("%s: AVMUTE path=0x%x\n", __func__, path);
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_AVMUTE_OP, SET_AVMUTE);
} else if (flag == CLR_AVMUTE) {
avmute_path &= ~path;
/* unmute only if none of the paths are muted */
if (avmute_path == 0) {
pr_info("%s: AV UNMUTE path=0x%x\n", __func__, path);
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_AVMUTE_OP, CLR_AVMUTE);
}
} else if (flag == OFF_AVMUTE) {
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_AVMUTE_OP, OFF_AVMUTE);
}
mutex_unlock(&avmute_mutex);
}
/*
* 1: set avmute
* -1: clear avmute
* 0: off avmute
*/
static ssize_t avmute_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int cmd = OFF_AVMUTE;
static int mask0;
static int mask1;
pr_info("%s %s\n", __func__, buf);
if (strncmp(buf, "-1", 2) == 0) {
cmd = CLR_AVMUTE;
mask0 = -1;
} else if (strncmp(buf, "0", 1) == 0) {
cmd = OFF_AVMUTE;
mask0 = 0;
} else if (strncmp(buf, "1", 1) == 0) {
cmd = SET_AVMUTE;
mask0 = 1;
}
if (strncmp(buf, "r-1", 3) == 0) {
cmd = CLR_AVMUTE;
mask1 = -1;
} else if (strncmp(buf, "r0", 2) == 0) {
cmd = OFF_AVMUTE;
mask1 = 0;
} else if (strncmp(buf, "r1", 2) == 0) {
cmd = SET_AVMUTE;
mask1 = 1;
}
if (mask0 == 1 || mask1 == 1)
cmd = SET_AVMUTE;
else if ((mask0 == -1) && (mask1 == -1))
cmd = CLR_AVMUTE;
hdmitx21_av_mute_op(cmd, AVMUTE_PATH_1);
return count;
}
static ssize_t avmute_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
int ret = 0;
int pos = 0;
ret = hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_READ_AVMUTE_OP, 0);
pos += snprintf(buf + pos, PAGE_SIZE, "%d", ret);
return pos;
}
static ssize_t rxsense_policy_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int val = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (isdigit(buf[0])) {
val = buf[0] - '0';
pr_info("hdmitx: set rxsense_policy as %d\n", val);
if (val == 0 || val == 1)
hdev->rxsense_policy = val;
else
pr_info("only accept as 0 or 1\n");
}
if (hdev->rxsense_policy)
queue_delayed_work(hdev->rxsense_wq,
&hdev->work_rxsense, 0);
else
cancel_delayed_work(&hdev->work_rxsense);
return count;
}
static ssize_t rxsense_policy_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdev->rxsense_policy);
return pos;
}
/* cedst_policy: 0, no CED feature
* 1, auto mode, depends on RX scdc_present
* 2, forced CED feature
*/
static ssize_t cedst_policy_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
int val = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (isdigit(buf[0])) {
val = buf[0] - '0';
pr_info("hdmitx: set cedst_policy as %d\n", val);
if (val == 0 || val == 1 || val == 2) {
hdev->cedst_policy = val;
if (val == 1) { /* Auto mode, depends on Rx */
/* check RX scdc_present */
if (hdev->tx_comm.rxcap.scdc_present)
hdev->cedst_policy = 1;
else
hdev->cedst_policy = 0;
}
if (val == 2) /* Force mode */
hdev->cedst_policy = 1;
/* assgin cedst_en from dts or here */
hdev->cedst_en = hdev->cedst_policy;
} else {
pr_info("only accept as 0, 1(auto), or 2(force)\n");
}
}
if (hdev->cedst_policy)
queue_delayed_work(hdev->cedst_wq, &hdev->work_cedst, 0);
else
cancel_delayed_work(&hdev->work_cedst);
return count;
}
static ssize_t cedst_policy_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdev->cedst_policy);
return pos;
}
static ssize_t cedst_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct ced_cnt *ced = &hdev->ced_cnt;
struct scdc_locked_st *ch_st = &hdev->chlocked_st;
if (!ch_st->clock_detected)
pos += snprintf(buf + pos, PAGE_SIZE, "clock undetected\n");
if (!ch_st->ch0_locked)
pos += snprintf(buf + pos, PAGE_SIZE, "CH0 unlocked\n");
if (!ch_st->ch1_locked)
pos += snprintf(buf + pos, PAGE_SIZE, "CH1 unlocked\n");
if (!ch_st->ch2_locked)
pos += snprintf(buf + pos, PAGE_SIZE, "CH2 unlocked\n");
if (ced->ch0_valid && ced->ch0_cnt)
pos += snprintf(buf + pos, PAGE_SIZE, "CH0 ErrCnt 0x%x\n",
ced->ch0_cnt);
if (ced->ch1_valid && ced->ch1_cnt)
pos += snprintf(buf + pos, PAGE_SIZE, "CH1 ErrCnt 0x%x\n",
ced->ch1_cnt);
if (ced->ch2_valid && ced->ch2_cnt)
pos += snprintf(buf + pos, PAGE_SIZE, "CH2 ErrCnt 0x%x\n",
ced->ch2_cnt);
memset(ced, 0, sizeof(*ced));
return pos;
}
static ssize_t sspll_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
int val = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (isdigit(buf[0])) {
val = buf[0] - '0';
pr_info("set sspll : %d\n", val);
if (val == 0 || val == 1)
hdev->sspll = val;
else
pr_info("sspll only accept as 0 or 1\n");
}
return count;
}
static ssize_t sspll_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdev->sspll);
return pos;
}
static void set_frac_rate_policy(int val)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
hdev->tx_comm.frac_rate_policy = val;
}
static int get_frac_rate_policy(void)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
return hdev->tx_comm.frac_rate_policy;
}
static ssize_t hdcp_type_policy_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
return count;
}
static ssize_t hdcp_type_policy_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
return pos;
}
static ssize_t hdcp_lstore_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
int lstore = hdmitx21_device.lstore;
if (lstore < 0x10) {
lstore = 0;
if (get_hdcp2_lstore())
lstore |= BIT(1);
if (get_hdcp1_lstore())
lstore |= BIT(0);
}
if ((lstore & 0x3) == 0x3) {
pos += snprintf(buf + pos, PAGE_SIZE, "22+14\n");
} else {
if (lstore & 0x1)
pos += snprintf(buf + pos, PAGE_SIZE, "14\n");
if (lstore & 0x2)
pos += snprintf(buf + pos, PAGE_SIZE, "22\n");
}
return pos;
}
static ssize_t hdcp_lstore_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
/* debug usage for key store check
* echo value > hdcp_lstore. value can be
* -1: automatically check stored key when enable hdcp
* 0: same as no hdcp key stored
* 11: only hdcp1.x key stored
* 12: only hdcp2.x key stored
* 13: both hdcp1.x and hdcp2.x key stored
*/
pr_info("hdcp: set lstore as %s\n", buf);
if (strncmp(buf, "-1", 2) == 0)
hdmitx21_device.lstore = 0x0;
if (strncmp(buf, "0", 1) == 0 ||
strncmp(buf, "10", 2) == 0)
hdmitx21_device.lstore = 0x10;
if (strncmp(buf, "11", 2) == 0)
hdmitx21_device.lstore = 0x11;
if (strncmp(buf, "12", 2) == 0)
hdmitx21_device.lstore = 0x12;
if (strncmp(buf, "13", 2) == 0)
hdmitx21_device.lstore = 0x13;
return count;
}
static ssize_t div40_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n", hdev->div40);
return pos;
}
/* echo 1 > div40, force send 1:40 tmds bit clk ratio
* echo 0 > div40, send 1:10 tmds bit clk ratio if scdc_present
* echo 2 > div40, force send 1:10 tmds bit clk ratio
*/
static ssize_t div40_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
hdev->hwop.cntlddc(hdev, DDC_SCDC_DIV40_SCRAMB, buf[0] - '0');
return count;
}
static ssize_t hdcp_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
unsigned int hdcp_ret = 0;
u32 hdcp_mode = hdmitx21_get_hdcp_mode();
switch (hdcp_mode) {
case 1:
pos += snprintf(buf + pos, PAGE_SIZE, "14");
break;
case 2:
pos += snprintf(buf + pos, PAGE_SIZE, "22");
break;
default:
pos += snprintf(buf + pos, PAGE_SIZE, "off");
break;
}
if (hdmitx21_device.hdcp_ctl_lvl > 0 && hdcp_mode > 0) {
if (hdcp_mode == 1)
hdcp_ret = get_hdcp1_result();
else if (hdcp_mode == 2)
hdcp_ret = get_hdcp2_result();
else
hdcp_ret = 0;
if (hdcp_ret == 1)
pos += snprintf(buf + pos, PAGE_SIZE, ": succeed\n");
else
pos += snprintf(buf + pos, PAGE_SIZE, ": fail\n");
}
return pos;
}
/* note: below store is just for debug, no mutex in it */
static ssize_t hdcp_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (strncmp(buf, "f1", 2) == 0) {
hdev->hdcp_mode = 0x1;
hdcp_mode_set(1);
}
if (strncmp(buf, "f2", 2) == 0) {
hdev->hdcp_mode = 0x2;
hdcp_mode_set(2);
}
if (buf[0] == '0') {
hdev->hdcp_mode = 0x00;
hdcp_mode_set(0);
}
return count;
}
/* Indicate whether a rptx under repeater */
static ssize_t hdmi_repeater_tx_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
!!hdev->repeater_tx);
return pos;
}
static ssize_t def_stream_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdev->def_stream_type);
return pos;
}
static ssize_t def_stream_type_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
u8 val = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (isdigit(buf[0])) {
val = buf[0] - '0';
pr_info("set def_stream_type as %d\n", val);
if (val == 0 || val == 1)
hdev->def_stream_type = val;
else
pr_info("only accept as 0 or 1\n");
}
return count;
}
static ssize_t propagate_stream_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdcp_t *p_hdcp = (struct hdcp_t *)hdev->am_hdcp;
if (p_hdcp->ds_repeater && p_hdcp->hdcp_type == HDCP_VER_HDCP2X) {
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
p_hdcp->csm_message.streamid_type & 0xFF);
} else {
pr_info("no stream type, as ds_repeater: %d, hdcp_type: %d\n",
p_hdcp->ds_repeater, p_hdcp->hdcp_type);
}
return pos;
}
static ssize_t cont_smng_method_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdcp_t *p_hdcp = (struct hdcp_t *)hdev->am_hdcp;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
p_hdcp->cont_smng_method);
return pos;
}
/* content stream management update method:
* when upstream side received new content stream
* management message, there're two method to
* update content stream type that propagated
* to downstream hdcp2.3 repeater:
* 0(default): only send content stream management
* message with new stream type to downstream
* 1: init new re-auth with downstream
*/
static ssize_t cont_smng_method_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
u8 val = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdcp_t *p_hdcp = (struct hdcp_t *)hdev->am_hdcp;
if (isdigit(buf[0])) {
val = buf[0] - '0';
pr_info("set cont_smng_method as %d\n", val);
if (val == 0 || val == 1)
p_hdcp->cont_smng_method = val;
else
pr_info("only accept as 0 or 1\n");
}
return count;
}
static ssize_t is_passthrough_switch_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n", is_passthrough_switch);
return pos;
}
static ssize_t is_passthrough_switch_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
u8 val = 0;
if (isdigit(buf[0])) {
val = buf[0] - '0';
pr_info("set is_passthrough_switch as %d\n", val);
if (val == 0 || val == 1)
is_passthrough_switch = val;
else
pr_info("only accept as 0 or 1\n");
}
return count;
}
/* is hdcp cts test equipment */
static ssize_t is_hdcp_cts_te_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n", hdmitx21_edid_only_support_sd(hdev));
return pos;
}
static ssize_t frl_rate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n", hdev->frl_rate);
switch (hdev->frl_rate) {
case FRL_3G3L:
pos += snprintf(buf + pos, PAGE_SIZE, "FRL_3G3L\n");
break;
case FRL_6G3L:
pos += snprintf(buf + pos, PAGE_SIZE, "FRL_6G3L\n");
break;
case FRL_6G4L:
pos += snprintf(buf + pos, PAGE_SIZE, "FRL_6G4L\n");
break;
case FRL_8G4L:
pos += snprintf(buf + pos, PAGE_SIZE, "FRL_8G4L\n");
break;
case FRL_10G4L:
pos += snprintf(buf + pos, PAGE_SIZE, "FRL_10G4L\n");
break;
case FRL_12G4L:
pos += snprintf(buf + pos, PAGE_SIZE, "FRL_12G4L\n");
break;
default:
break;
}
return pos;
}
static ssize_t frl_rate_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
u8 val = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
/* if rx don't support FRL, return */
if (!hdev->tx_comm.rxcap.max_frl_rate)
pr_info("rx not support FRL\n");
/* forced FRL rate setting */
if (buf[0] == 'f' && isdigit(buf[1])) {
val = buf[1] - '0';
if (val > FRL_12G4L) {
pr_info("set frl_rate in 0 ~ 6\n");
return count;
}
hdev->manual_frl_rate = val;
pr_info("set tx frl_rate as %d\n", val);
}
if (hdev->manual_frl_rate > hdev->tx_comm.rxcap.max_frl_rate)
pr_info("larger than rx max_frl_rate %d\n",
hdev->tx_comm.rxcap.max_frl_rate);
return count;
}
static ssize_t dsc_en_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n", hdev->dsc_en);
return pos;
}
static ssize_t dsc_en_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
u8 val = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (isdigit(buf[0])) {
val = buf[0] - '0';
if (val != 0 && val != 1) {
pr_info("set dsc_en in 0 ~ 1\n");
return count;
}
hdev->dsc_en = val;
pr_info("set dsc_en as %d\n", val);
}
return count;
}
#include <linux/amlogic/media/vout/hdmi_tx/hdmi_rptx.h>
void direct21_hdcptx14_opr(enum rptx_hdcp14_cmd cmd, void *args)
{
}
EXPORT_SYMBOL(direct21_hdcptx14_opr);
/* Special FBC check */
static int check_fbc_special(u8 *edid_dat)
{
if (edid_dat[250] == 0xfb && edid_dat[251] == 0x0c)
return 1;
else
return 0;
}
static ssize_t hdcp_ver_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
if (!tx_comm->hpd_state) {
pr_info("%s: hpd low, just return 14\n", __func__);
pos += snprintf(buf + pos, PAGE_SIZE, "14\n\r");
return pos;
}
if (rx_hdcp2_ver) {
pos += snprintf(buf + pos, PAGE_SIZE, "22\n\r");
} else {
//on hotplug case
/* note that, when do hdcp repeater1.4 CTS,
* hdcp port access will affect item 3a-02 Irregular
* procedure: (First part of authentication) HDCP port
* access. Refer to hdcp1.4 cts spec: "If DUT does
* not read an HDCP register past 4 seconds after
* the previous attempt, then FAIL". after read hdcp
* version soon after plugin(access failed as TE
* not ack), our hdmitx side should keep retrying
* in 4S. but source TE start hdcp auth with
* hdmirx side too late(more than 4S), as hdcp auth
* of hdmitx side is started by hdmirx side, it will
* time out the access of hdcp port of 4 second.
* so for repeater CTS, should not read hdcp version
* whenever you want. Here add protect to only read
* hdcp version when currently not in hdmirx channel.
* special customer want to read downstream hdcp version
* after hotplug, and only sen 4K output when EDID
* support 4K && support hdcp2.2. so add is_4k_sink()
* decision, it won't affect hdcp repeater CTS.
*/
if (hdcp_need_control_by_upstream(hdev) && !is_4k_sink(hdev)) {
pr_info("%s: currently should not read hdcp version\n", __func__);
} else if (hdmitx21_get_hdcp_mode() == 0) {
if (get_hdcp2_lstore() && is_rx_hdcp2ver()) {
pos += snprintf(buf + pos, PAGE_SIZE, "22\n\r");
rx_hdcp2_ver = 1;
}
pr_info("%s: hdev->hdcp_mode: 0, rx_hdcp2_ver = %d\n",
__func__, rx_hdcp2_ver);
}
}
/* Here, must assume RX support HDCP14, otherwise affect 1A-03 */
pos += snprintf(buf + pos, PAGE_SIZE, "14\n\r");
return pos;
}
static ssize_t rxsense_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
int sense;
struct hdmitx_dev *hdev = get_hdmitx21_device();
sense = hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_TMDS_RXSENSE, 0);
pos += snprintf(buf + pos, PAGE_SIZE, "%d", sense);
return pos;
}
static ssize_t hdmi_used_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d",
hdev->already_used);
return pos;
}
static ssize_t fake_plug_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
return snprintf(buf, PAGE_SIZE, "%d", hdev->tx_comm.hpd_state);
}
static ssize_t fake_plug_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
pr_info("hdmitx: fake plug %s\n", buf);
if (strncmp(buf, "1", 1) == 0)
tx_comm->hpd_state = 1;
if (strncmp(buf, "0", 1) == 0) {
tx_comm->hpd_state = 0;
/* below is special for customer */
//remove hdr caps from vinfo so that AMDV module does
//not start sending HDR10 when changing HDR caps
//until vinfo is updated while setting mode
edidinfo_detach_to_vinfo(hdev);
}
/*notify to drm hdmi*/
hdmitx_hpd_notify_unlocked(&hdev->tx_comm);
extcon_set_state_sync(hdmitx_extcon_hdmi, EXTCON_DISP_HDMI,
hdev->tx_comm.hpd_state);
hdmitx21_set_uevent(HDMITX_HPD_EVENT, tx_comm->hpd_state);
return count;
}
static ssize_t ready_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\r\n",
hdev->ready);
return pos;
}
static ssize_t ready_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (strncmp(buf, "0", 1) == 0)
hdev->ready = 0;
if (strncmp(buf, "1", 1) == 0)
hdev->ready = 1;
return count;
}
static ssize_t support_3d_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdev->tx_comm.rxcap.threeD_present);
return pos;
}
static ssize_t aon_output_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\r\n",
hdev->aon_output);
return pos;
}
static ssize_t aon_output_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (strncmp(buf, "0", 1) == 0)
hdev->aon_output = 0;
if (strncmp(buf, "1", 1) == 0)
hdev->aon_output = 1;
return count;
}
static ssize_t hdr_priority_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\r\n",
hdev->tx_comm.hdr_priority);
return pos;
}
/* hide or enable HDR capabilities.
* 0 : No HDR capabilities are hidden
* 1 : DV Capabilities are hidden
* 2 : All HDR capabilities are hidden
*/
static ssize_t hdr_priority_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
unsigned int val = 0;
struct vinfo_s *info = NULL;
if ((strncmp("0", buf, 1) == 0) || (strncmp("1", buf, 1) == 0) ||
(strncmp("2", buf, 1) == 0)) {
val = buf[0] - '0';
}
if (val == tx_comm->hdr_priority)
return count;
info = hdmitx_get_current_vinfo(NULL);
if (!info)
return count;
mutex_lock(&hdev->hdmimode_mutex);
tx_comm->hdr_priority = val;
if (tx_comm->hdr_priority == 1) {
//clear dv support
memset(&tx_comm->rxcap.dv_info, 0x00, sizeof(struct dv_info));
hdmitx_vdev.dv_info = &dv_dummy;
//restore hdr support
memcpy(&tx_comm->rxcap.hdr_info, &tx_comm->rxcap.hdr_info2,
sizeof(struct hdr_info));
//restore BT2020 support
tx_comm->rxcap.colorimetry_data = tx_comm->rxcap.colorimetry_data2;
hdrinfo_to_vinfo(&info->hdr_info, hdev);
} else if (tx_comm->hdr_priority == 2) {
//clear dv support
memset(&tx_comm->rxcap.dv_info, 0x00, sizeof(struct dv_info));
hdmitx_vdev.dv_info = &dv_dummy;
//clear hdr support
memset(&tx_comm->rxcap.hdr_info, 0x00, sizeof(struct hdr_info));
//clear BT2020 support
tx_comm->rxcap.colorimetry_data = tx_comm->rxcap.colorimetry_data2 & 0x1F;
memset(&info->hdr_info, 0, sizeof(struct hdr_info));
} else {
//restore dv support
memcpy(&tx_comm->rxcap.dv_info, &tx_comm->rxcap.dv_info2, sizeof(struct dv_info));
//restore hdr support
memcpy(&tx_comm->rxcap.hdr_info, &tx_comm->rxcap.hdr_info2,
sizeof(struct hdr_info));
//restore BT2020 support
tx_comm->rxcap.colorimetry_data = tx_comm->rxcap.colorimetry_data2;
edidinfo_attach_to_vinfo(hdev);
}
/* hdmitx21_event_notify(HDMITX_HDR_PRIORITY, &hdev->hdr_priority); */
/* force trigger plugin event
* hdmitx21_set_uevent_state(HDMITX_HPD_EVENT, 0);
* hdmitx21_set_uevent(HDMITX_HPD_EVENT, 1);
*/
mutex_unlock(&hdev->hdmimode_mutex);
return count;
}
/* hdcp fail event method 1: add hdcp fail uevent filter
* below need_filter_hdcp_off and filter_hdcp_off_period
* sysfs node are for this filter
*/
static ssize_t need_filter_hdcp_off_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\r\n",
hdev->need_filter_hdcp_off);
return pos;
}
/* if need to filter hdcp fail uevent, systemcontrol
* write 1 to this node. for example:
* when start player game movie, it switch from DV STD to DV LL,
* before switch mode, write 1 to this node. it means that it
* won't send hdcp fail uevent if hdcp fail but retry auth
* pass during filter_hdcp_off_period seconds.
* note: need_filter_hdcp_off is self cleared after filter
* period expired
*/
static ssize_t need_filter_hdcp_off_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
unsigned int val = 0;
if ((strncmp("0", buf, 1) == 0) || (strncmp("1", buf, 1) == 0))
val = buf[0] - '0';
hdev->need_filter_hdcp_off = val;
pr_info("hdmitx: set need_filter_hdcp_off: %d\n", val);
return count;
}
/* if hdcp fail but retry auth pass during this period(unit: second),
* then won't sent hdcp fail uevent. the default is 6 second
*/
static ssize_t filter_hdcp_off_period_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\r\n",
hdev->filter_hdcp_off_period);
return pos;
}
/* example: write 5 to filter 5 second */
static ssize_t filter_hdcp_off_period_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
unsigned long filter_second = 0;
pr_info("set hdcp fail filter_second: %s\n", buf);
if (kstrtoul(buf, 10, &filter_second) == 0)
hdev->filter_hdcp_off_period = filter_second;
return count;
}
/* hdcp fail event method 2: don't stop-restart hdcp auth */
/* when start play game movie, it will switch from
* DV STD to DV LL, hdcp will stop and restart after
* mode setting done.
* now provide an option: when DV STD <-> DV LL
* switch caused by game movie, systemcontrol write 1
* to not_restart_hdcp node before do mode switch, it
* means that this colorspace(mode) switch will only
* switch mode, but won't stop->restart hdcp action to
* prevent hdcp fail uevent sent to app.
* note: 1.not sure if it will always work on different TV.
* 2.after mode switch done, not_restart_hdcp will be self cleared
*/
static ssize_t not_restart_hdcp_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\r\n",
hdev->not_restart_hdcp);
return pos;
}
static ssize_t not_restart_hdcp_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
unsigned int val = 0;
if ((strncmp("0", buf, 1) == 0) || (strncmp("1", buf, 1) == 0))
val = buf[0] - '0';
hdev->not_restart_hdcp = val;
pr_info("hdmitx: set not_restart_hdcp: %d\n", val);
return count;
}
static ssize_t sysctrl_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pos += snprintf(buf + pos, PAGE_SIZE, "%d\r\n",
hdev->systemcontrol_on);
return pos;
}
static ssize_t sysctrl_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (strncmp(buf, "0", 1) == 0)
hdev->systemcontrol_on = false;
if (strncmp(buf, "1", 1) == 0)
hdev->systemcontrol_on = true;
return count;
}
static ssize_t hdcp_ctl_lvl_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\r\n",
hdmitx21_device.hdcp_ctl_lvl);
return pos;
}
static ssize_t hdcp_ctl_lvl_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long ctl_lvl = 0xf;
pr_info("set hdcp_ctl_lvl: %s\n", buf);
if (kstrtoul(buf, 10, &ctl_lvl) == 0) {
if (ctl_lvl <= 2)
hdmitx21_device.hdcp_ctl_lvl = ctl_lvl;
}
return count;
}
static ssize_t hdmitx21_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "1\n");
}
static DEVICE_ATTR_RW(disp_mode);
static DEVICE_ATTR_RW(vid_mute);
static DEVICE_ATTR_RO(sink_type);
static DEVICE_ATTR_RW(config);
static DEVICE_ATTR_WO(debug);
static DEVICE_ATTR_RO(disp_cap);
static DEVICE_ATTR_RO(vrr_cap);
static DEVICE_ATTR_RO(preferred_mode);
static DEVICE_ATTR_RO(cea_cap);
static DEVICE_ATTR_RO(vesa_cap);
static DEVICE_ATTR_RO(aud_cap);
static DEVICE_ATTR_RW(aud_mute);
static DEVICE_ATTR_RO(lipsync_cap);
static DEVICE_ATTR_RO(hdmi_hdr_status);
static DEVICE_ATTR_RO(dc_cap);
static DEVICE_ATTR_RW(valid_mode);
static DEVICE_ATTR_RO(allm_cap);
static DEVICE_ATTR_RW(allm_mode);
static DEVICE_ATTR_RW(ll_mode);
static DEVICE_ATTR_RW(ll_user_mode);
static DEVICE_ATTR_RW(avmute);
static DEVICE_ATTR_RW(sspll);
static DEVICE_ATTR_RW(rxsense_policy);
static DEVICE_ATTR_RW(cedst_policy);
static DEVICE_ATTR_RO(cedst_count);
static DEVICE_ATTR_RW(hdcp_mode);
static DEVICE_ATTR_RO(hdcp_ver);
static DEVICE_ATTR_RW(hdcp_type_policy);
static DEVICE_ATTR_RW(hdcp_lstore);
static DEVICE_ATTR_RO(hdmi_repeater_tx);
static DEVICE_ATTR_RW(div40);
static DEVICE_ATTR_RO(hdmi_used);
static DEVICE_ATTR_RO(rxsense_state);
static DEVICE_ATTR_RW(fake_plug);
static DEVICE_ATTR_RW(ready);
static DEVICE_ATTR_RO(support_3d);
static DEVICE_ATTR_RO(hdmitx21);
static DEVICE_ATTR_RW(def_stream_type);
static DEVICE_ATTR_RW(hdcp_ctl_lvl);
static DEVICE_ATTR_RW(sysctrl_enable);
static DEVICE_ATTR_RW(aon_output);
static DEVICE_ATTR_RO(propagate_stream_type);
static DEVICE_ATTR_RW(cont_smng_method);
static DEVICE_ATTR_RW(hdr_priority_mode);
static DEVICE_ATTR_RW(is_passthrough_switch);
static DEVICE_ATTR_RO(is_hdcp_cts_te);
static DEVICE_ATTR_RW(need_filter_hdcp_off);
static DEVICE_ATTR_RW(filter_hdcp_off_period);
static DEVICE_ATTR_RW(not_restart_hdcp);
static DEVICE_ATTR_RW(frl_rate);
static DEVICE_ATTR_RW(dsc_en);
#ifdef CONFIG_AMLOGIC_VOUT_SERVE
static struct vinfo_s *hdmitx_get_current_vinfo(void *data)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (!hdev->para)
return NULL;
return &hdev->para->hdmitx_vinfo;
}
static int hdmitx_set_current_vmode(enum vmode_e mode, void *data)
{
struct vinfo_s *vinfo = NULL;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
hdmitx_register_vrr(hdev);
if (!(mode & VMODE_INIT_BIT_MASK)) {
set_disp_mode_auto();
} else {
pr_info("already display in uboot\n");
hdev->ready = 1;
/* if hdmitx already output under uboot,
* here get the current parameters of
* output mode, which may be used later
*/
update_current_para(hdev);
if (tx_comm->rxcap.max_frl_rate) {
hdev->frl_rate = hdmitx21_select_frl_rate(hdev->dsc_en,
tx_comm->cur_VIC, hdev->para->cs, hdev->para->cd);
if (hdev->frl_rate > hdev->tx_max_frl_rate)
pr_info("Current frl_rate %d is larger than tx_max_frl_rate %d\n",
hdev->frl_rate, hdev->tx_max_frl_rate);
}
edidinfo_attach_to_vinfo(hdev);
vinfo = get_current_vinfo();
if (vinfo) {
vinfo->cur_enc_ppc = 1;
if (hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_IS_FRL_MODE, 0))
vinfo->cur_enc_ppc = 4;
pr_info("vinfo: set cur_enc_ppc as %d\n", vinfo->cur_enc_ppc);
}
}
return 0;
}
static enum vmode_e hdmitx_validate_vmode(char *_mode, u32 frac, void *data)
{
struct hdmi_format_para *para = NULL;
struct hdmitx_dev *hdev = get_hdmitx21_device();
char mode[32] = {0};
char *y420;
strncpy(mode, _mode, sizeof(mode));
mode[31] = 0;
/* if current mode contains string 420, subtract 420 */
y420 = strstr(mode, "420");
if (y420)
*y420 = '\0';
para = hdmitx21_get_fmtpara(mode, hdev->tx_comm.fmt_attr);
if (para) {
/* //remove frac support for vout api
*if (frac)
* hdev->frac_rate_policy = 1;
*else
* hdev->frac_rate_policy = 0;
*/
hdev->para->hdmitx_vinfo.info_3d = NON_3D;
hdev->para->hdmitx_vinfo.vout_device = &hdmitx_vdev;
return VMODE_HDMI;
}
return VMODE_MAX;
}
static int hdmitx_vmode_is_supported(enum vmode_e mode, void *data)
{
if ((mode & VMODE_MODE_BIT_MASK) == VMODE_HDMI)
return true;
else
return false;
}
static int hdmitx_module_disable(enum vmode_e cur_vmod, void *data)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CLR_AVI_PACKET, 0);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CLR_VSDB_PACKET, 0);
frl_tx_stop(hdev);
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_TMDS_PHY_OP, TMDS_PHY_DISABLE);
/* hdmitx21_disable_clk(hdev); */
hdev->para = hdmitx21_get_fmtpara("invalid", tx_comm->fmt_attr);
hdmitx_validate_vmode("null", 0, NULL);
if (hdev->cedst_policy)
cancel_delayed_work(&hdev->work_cedst);
if (hdev->rxsense_policy)
queue_delayed_work(hdev->rxsense_wq, &hdev->work_rxsense, 0);
hdmitx_unregister_vrr(hdev);
return 0;
}
static int hdmitx_vout_state;
static int hdmitx_vout_set_state(int index, void *data)
{
hdmitx_vout_state |= (1 << index);
return 0;
}
static int hdmitx_vout_clr_state(int index, void *data)
{
hdmitx_vout_state &= ~(1 << index);
return 0;
}
static int hdmitx_vout_get_state(void *data)
{
return hdmitx_vout_state;
}
/* if cs/cd/frac_rate is changed, then return 0 */
static int hdmitx_check_same_vmodeattr(char *name, void *data)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
if (memcmp(tx_comm->backup_fmt_attr, tx_comm->fmt_attr, 16) == 0 &&
tx_comm->backup_frac_rate_policy == tx_comm->frac_rate_policy)
return 1;
memcpy(tx_comm->backup_fmt_attr, tx_comm->fmt_attr, 16);
tx_comm->backup_frac_rate_policy = tx_comm->frac_rate_policy;
return 0;
}
static int hdmitx_vout_get_disp_cap(char *buf, void *data)
{
return disp_cap_show(NULL, NULL, buf);
}
static bool drm_hdmitx_get_vrr_cap(void)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (hdev->tx_comm.rxcap.neg_mvrr ||
hdev->tx_comm.rxcap.fva ||
hdev->tx_comm.rxcap.vrr_max ||
hdev->tx_comm.rxcap.vrr_min) {
pr_info("%s support vrr\n", __func__);
return true;
}
pr_info("%s not support vrr\n", __func__);
return false;
}
static bool is_vic_supported(enum hdmi_vic brr_vic)
{
int i;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct rx_cap *prxcap = &hdev->tx_comm.rxcap;
for (i = 0; i < prxcap->VIC_count; i++) {
if (brr_vic == prxcap->VIC[i])
return 1;
}
return 0;
}
static void add_vic_to_group(enum hdmi_vic vic, struct drm_vrr_mode_group *group)
{
const struct hdmi_timing *timing;
timing = hdmitx21_gettiming_from_vic(vic);
if (timing && is_vic_supported(vic)) {
group->brr_vic = vic;
group->width = timing->h_active;
group->height = timing->v_active;
group->vrr_min = 24; /* fixed value */
group->vrr_max = timing->v_freq / 1000;
}
}
static int drm_hdmitx_get_vrr_mode_group(struct drm_vrr_mode_group *group, int max_group)
{
int i;
enum hdmi_vic brr_vic[] = {
HDMI_16_1920x1080p60_16x9,
HDMI_63_1920x1080p120_16x9,
HDMI_4_1280x720p60_16x9,
HDMI_47_1280x720p120_16x9,
HDMI_97_3840x2160p60_16x9,
HDMI_102_4096x2160p60_256x135,
};
if (!drm_hdmitx_get_vrr_cap())
return 0;
if (!group || max_group == 0)
return 0;
for (i = 0; i < ARRAY_SIZE(brr_vic); i++)
add_vic_to_group(brr_vic[i], group + i);
return i;
}
static void hdmitx_set_bist(u32 num, void *data)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (hdev->hwop.debug_bist)
hdev->hwop.debug_bist(hdev, num);
}
static int hdmitx_vout_set_vframe_rate_hint(int duration, void *data)
{
return hdmitx_set_fr_hint(duration, data);
}
static int hdmitx_vout_get_vframe_rate_hint(void *data)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (!hdev)
return 0;
return hdev->fr_duration;
}
static struct vout_server_s hdmitx_vout_server = {
.name = "hdmitx_vout_server",
.op = {
.get_vinfo = hdmitx_get_current_vinfo,
.set_vmode = hdmitx_set_current_vmode,
.validate_vmode = hdmitx_validate_vmode,
.check_same_vmodeattr = hdmitx_check_same_vmodeattr,
.vmode_is_supported = hdmitx_vmode_is_supported,
.disable = hdmitx_module_disable,
.set_state = hdmitx_vout_set_state,
.clr_state = hdmitx_vout_clr_state,
.get_state = hdmitx_vout_get_state,
.get_disp_cap = hdmitx_vout_get_disp_cap,
.set_vframe_rate_hint = hdmitx_vout_set_vframe_rate_hint,
.get_vframe_rate_hint = hdmitx_vout_get_vframe_rate_hint,
.set_bist = hdmitx_set_bist,
#ifdef CONFIG_PM
.vout_suspend = NULL,
.vout_resume = NULL,
#endif
},
.data = NULL,
};
#endif
#ifdef CONFIG_AMLOGIC_VOUT2_SERVE
static struct vout_server_s hdmitx_vout2_server = {
.name = "hdmitx_vout2_server",
.op = {
.get_vinfo = hdmitx_get_current_vinfo,
.set_vmode = hdmitx_set_current_vmode,
.validate_vmode = hdmitx_validate_vmode,
.check_same_vmodeattr = hdmitx_check_same_vmodeattr,
.vmode_is_supported = hdmitx_vmode_is_supported,
.disable = hdmitx_module_disable,
.set_state = hdmitx_vout_set_state,
.clr_state = hdmitx_vout_clr_state,
.get_state = hdmitx_vout_get_state,
.get_disp_cap = hdmitx_vout_get_disp_cap,
.set_vframe_rate_hint = NULL,
.get_vframe_rate_hint = NULL,
.set_bist = hdmitx_set_bist,
#ifdef CONFIG_PM
.vout_suspend = NULL,
.vout_resume = NULL,
#endif
},
.data = NULL,
};
#endif
#ifdef CONFIG_AMLOGIC_VOUT3_SERVE
static struct vout_server_s hdmitx_vout3_server = {
.name = "hdmitx_vout3_server",
.op = {
.get_vinfo = hdmitx_get_current_vinfo,
.set_vmode = hdmitx_set_current_vmode,
.validate_vmode = hdmitx_validate_vmode,
.check_same_vmodeattr = hdmitx_check_same_vmodeattr,
.vmode_is_supported = hdmitx_vmode_is_supported,
.disable = hdmitx_module_disable,
.set_state = hdmitx_vout_set_state,
.clr_state = hdmitx_vout_clr_state,
.get_state = hdmitx_vout_get_state,
.get_disp_cap = hdmitx_vout_get_disp_cap,
.set_vframe_rate_hint = NULL,
.get_vframe_rate_hint = NULL,
.set_bist = hdmitx_set_bist,
#ifdef CONFIG_PM
.vout_suspend = NULL,
.vout_resume = NULL,
#endif
},
.data = NULL,
};
#endif
#if IS_ENABLED(CONFIG_AMLOGIC_SND_SOC)
#include <linux/soundcard.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/control.h>
static struct rate_map_fs map_fs[] = {
{0, FS_REFER_TO_STREAM},
{32000, FS_32K},
{44100, FS_44K1},
{48000, FS_48K},
{88200, FS_88K2},
{96000, FS_96K},
{176400, FS_176K4},
{192000, FS_192K},
};
static enum hdmi_audio_fs aud_samp_rate_map(u32 rate)
{
int i = 0;
for (i = 0; i < ARRAY_SIZE(map_fs); i++) {
if (map_fs[i].rate == rate)
return map_fs[i].fs;
}
pr_info("get FS_MAX\n");
return FS_MAX;
}
u32 aud_sr_idx_to_val(enum hdmi_audio_fs e_sr_idx)
{
int i = 0;
for (i = 0; i < ARRAY_SIZE(map_fs); i++) {
if (map_fs[i].fs == e_sr_idx)
return map_fs[i].rate / 1000;
}
pr_info("wrong idx: %d\n", e_sr_idx);
return 1;
}
static u8 *aud_type_string[] = {
"CT_REFER_TO_STREAM",
"CT_PCM",
"CT_AC_3",
"CT_MPEG1",
"CT_MP3",
"CT_MPEG2",
"CT_AAC",
"CT_DTS",
"CT_ATRAC",
"CT_ONE_BIT_AUDIO",
"CT_DOLBY_D",
"CT_DTS_HD",
"CT_MAT",
"CT_DST",
"CT_WMA",
"CT_MAX",
};
static struct size_map aud_size_map_ss[] = {
{0, SS_REFER_TO_STREAM},
{16, SS_16BITS},
{20, SS_20BITS},
{24, SS_24BITS},
{32, SS_MAX},
};
static enum hdmi_audio_sampsize aud_size_map(u32 bits)
{
int i;
for (i = 0; i < ARRAY_SIZE(aud_size_map_ss); i++) {
if (bits == aud_size_map_ss[i].sample_bits)
return aud_size_map_ss[i].ss;
}
pr_info("get SS_MAX\n");
return SS_MAX;
}
static bool hdmitx_set_i2s_mask(char ch_num, char ch_msk)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
static u32 update_flag = -1;
if (!(ch_num == 2 || ch_num == 4 ||
ch_num == 6 || ch_num == 8)) {
pr_info("err chn setting, must be 2, 4, 6 or 8, Rst as def\n");
hdev->aud_output_ch = 0;
if (update_flag != hdev->aud_output_ch) {
update_flag = hdev->aud_output_ch;
hdev->hdmi_ch = 0;
}
return 0;
}
if (ch_msk == 0) {
pr_info("err chn msk, must larger than 0\n");
return 0;
}
hdev->aud_output_ch = (ch_num << 4) + ch_msk;
if (update_flag != hdev->aud_output_ch) {
update_flag = hdev->aud_output_ch;
hdev->hdmi_ch = 0;
}
return 1;
}
static int hdmitx_notify_callback_a(struct notifier_block *block,
unsigned long cmd, void *para);
static struct notifier_block hdmitx_notifier_nb_a = {
.notifier_call = hdmitx_notify_callback_a,
};
static int hdmitx_notify_callback_a(struct notifier_block *block,
unsigned long cmd, void *para)
{
int i, audio_check = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct rx_cap *prxcap = &hdev->tx_comm.rxcap;
struct aud_para *aud_param = (struct aud_para *)para;
struct hdmitx_audpara *audio_param = &hdev->cur_audio_param;
enum hdmi_audio_fs n_rate = aud_samp_rate_map(aud_param->rate);
enum hdmi_audio_sampsize n_size = aud_size_map(aud_param->size);
hdev->audio_param_update_flag = 0;
hdev->audio_notify_flag = 0;
if (hdmitx_set_i2s_mask(aud_param->chs, aud_param->i2s_ch_mask))
hdev->audio_param_update_flag = 1;
if (audio_param->sample_rate != n_rate) {
audio_param->sample_rate = n_rate;
hdev->audio_param_update_flag = 1;
pr_info("aout notify sample rate: %d\n", n_rate);
}
if (audio_param->type != cmd) {
audio_param->type = cmd;
pr_info("aout notify format %s\n",
aud_type_string[audio_param->type & 0xff]);
hdev->audio_param_update_flag = 1;
}
if (audio_param->sample_size != n_size) {
audio_param->sample_size = n_size;
hdev->audio_param_update_flag = 1;
pr_info("aout notify sample size: %d\n", n_size);
}
if (audio_param->channel_num != (aud_param->chs - 1)) {
audio_param->channel_num = aud_param->chs - 1;
hdev->audio_param_update_flag = 1;
pr_info("aout notify channel num: %d\n", aud_param->chs);
}
if (audio_param->aud_src_if != aud_param->aud_src_if) {
pr_info("cur aud_src_if %d, new aud_src_if: %d\n",
audio_param->aud_src_if, aud_param->aud_src_if);
audio_param->aud_src_if = aud_param->aud_src_if;
hdev->audio_param_update_flag = 1;
}
memcpy(audio_param->status, aud_param->status, sizeof(aud_param->status));
if (log21_level == 1) {
for (i = 0; i < sizeof(audio_param->status); i++)
pr_info("%02x", audio_param->status[i]);
pr_info("\n");
}
if (hdev->tx_aud_cfg == 2) {
pr_info("auto mode\n");
/* Detect whether Rx is support current audio format */
for (i = 0; i < prxcap->AUD_count; i++) {
if (prxcap->RxAudioCap[i].audio_format_code == cmd)
audio_check = 1;
}
/* sink don't support current audio mode */
if (!audio_check && cmd != CT_PCM) {
pr_info("Sink not support this audio format %lu\n",
cmd);
hdev->tx_hw.cntlconfig(&hdev->tx_hw,
CONF_AUDIO_MUTE_OP, AUDIO_MUTE);
hdev->audio_param_update_flag = 0;
}
}
if (hdev->audio_param_update_flag == 0)
;
else
hdev->audio_notify_flag = 1;
if ((!(hdev->hdmi_audio_off_flag)) && hdev->audio_param_update_flag) {
/* plug-in & update audio param */
if (hdev->tx_comm.hpd_state == 1) {
hdmitx21_set_audio(hdev,
&hdev->cur_audio_param);
if (hdev->audio_notify_flag == 1 || hdev->audio_step == 1) {
hdev->audio_notify_flag = 0;
hdev->audio_step = 0;
}
hdev->audio_param_update_flag = 0;
pr_info("set audio param\n");
}
}
if (aud_param->fifo_rst)
; /* hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_AUDIO_RESET, 1); */
return 0;
}
#endif
u32 hdmitx21_check_edid_all_zeros(u8 *buf)
{
u32 i = 0, j = 0;
u32 chksum = 0;
for (j = 0; j < EDID_MAX_BLOCK; j++) {
chksum = 0;
for (i = 0; i < 128; i++)
chksum += buf[i + j * 128];
if (chksum != 0)
return 0;
}
return 1;
}
static void hdmitx_get_edid(struct hdmitx_dev *hdev)
{
struct hdmitx_common *tx_comm = &hdev->tx_comm;
mutex_lock(&getedid_mutex);
/* TODO hdmitx21_edid_ram_buffer_clear(hdev); */
hdev->hwop.cntlddc(hdev, DDC_RESET_EDID, 0);
hdev->hwop.cntlddc(hdev, DDC_PIN_MUX_OP, PIN_MUX);
/* start reading edid first time */
hdev->hwop.cntlddc(hdev, DDC_EDID_READ_DATA, 0);
hdev->hwop.cntlddc(hdev, DDC_EDID_GET_DATA, 0);
if (hdmitx21_check_edid_all_zeros(tx_comm->EDID_buf)) {
hdev->hwop.cntlddc(hdev, DDC_GLITCH_FILTER_RESET, 0);
hdev->hwop.cntlddc(hdev, DDC_EDID_READ_DATA, 0);
hdev->hwop.cntlddc(hdev, DDC_EDID_GET_DATA, 0);
}
/* If EDID is not correct at first time, then retry */
if (!check21_dvi_hdmi_edid_valid(hdev->tx_comm.EDID_buf)) {
msleep(100);
/* start reading edid second time */
hdev->hwop.cntlddc(hdev, DDC_EDID_READ_DATA, 0);
hdev->hwop.cntlddc(hdev, DDC_EDID_GET_DATA, 1);
if (hdmitx21_check_edid_all_zeros(hdev->tx_comm.EDID_buf1)) {
hdev->hwop.cntlddc(hdev, DDC_GLITCH_FILTER_RESET, 0);
hdev->hwop.cntlddc(hdev, DDC_EDID_READ_DATA, 0);
hdev->hwop.cntlddc(hdev, DDC_EDID_GET_DATA, 1);
}
}
hdmitx21_edid_clear(hdev);
hdmitx21_edid_parse(hdev);
hdmitx21_edid_buf_compare_print(hdev);
if (tx_comm->hdr_priority == 1) { /* clear dv_info */
struct dv_info *dv = &hdev->tx_comm.rxcap.dv_info;
memset(dv, 0, sizeof(struct dv_info));
pr_info("clear dv_info\n");
}
if (tx_comm->hdr_priority == 2) { /* clear dv_info/hdr_info */
struct dv_info *dv = &hdev->tx_comm.rxcap.dv_info;
struct hdr_info *hdr = &hdev->tx_comm.rxcap.hdr_info;
memset(dv, 0, sizeof(struct dv_info));
memset(hdr, 0, sizeof(struct hdr_info));
pr_info("clear dv_info/hdr_info\n");
}
mutex_unlock(&getedid_mutex);
}
static void hdmitx_rxsense_process(struct work_struct *work)
{
int sense;
struct hdmitx_dev *hdev = container_of((struct delayed_work *)work,
struct hdmitx_dev, work_rxsense);
sense = hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_TMDS_RXSENSE, 0);
hdmitx21_set_uevent(HDMITX_RXSENSE_EVENT, sense);
queue_delayed_work(hdev->rxsense_wq, &hdev->work_rxsense, HZ);
}
static void hdmitx_cedst_process(struct work_struct *work)
{
int ced;
struct hdmitx_dev *hdev = container_of((struct delayed_work *)work,
struct hdmitx_dev, work_cedst);
ced = hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_TMDS_CEDST, 0);
/* firstly send as 0, then real ced, A trigger signal */
hdmitx21_set_uevent(HDMITX_CEDST_EVENT, 0);
hdmitx21_set_uevent(HDMITX_CEDST_EVENT, ced);
queue_delayed_work(hdev->cedst_wq, &hdev->work_cedst, HZ);
queue_delayed_work(hdev->cedst_wq, &hdev->work_cedst, HZ);
}
static bool is_tv_changed(void)
{
bool ret = false;
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (memcmp(hdmichecksum, hdev->tx_comm.rxcap.chksum, 10) &&
memcmp(emptychecksum, hdev->tx_comm.rxcap.chksum, 10) &&
memcmp(invalidchecksum, hdmichecksum, 10)) {
ret = true;
pr_info("hdmi crc is diff between uboot and kernel\n");
}
return ret;
}
static void hdmitx_hpd_plugin_handler(struct work_struct *work)
{
struct vinfo_s *info = NULL;
struct hdmitx_dev *hdev = container_of((struct delayed_work *)work,
struct hdmitx_dev, work_hpd_plugin);
mutex_lock(&hdev->tx_comm.setclk_mutex);
mutex_lock(&hdev->hdmimode_mutex);
hdev->already_used = 1;
if (!(hdev->hdmitx_event & (HDMI_TX_HPD_PLUGIN))) {
mutex_unlock(&hdev->hdmimode_mutex);
mutex_unlock(&hdev->tx_comm.setclk_mutex);
return;
}
if (hdev->rxsense_policy) {
cancel_delayed_work(&hdev->work_rxsense);
queue_delayed_work(hdev->rxsense_wq, &hdev->work_rxsense, 0);
}
pr_info("plugin\n");
/* there's such case: plugin irq->hdmitx resume + read EDID +
* resume uevent->mode setting + hdcp auth->plugin handler read
* EDID, now EDID already read done and hdcp already started,
* not read EDID again.
*/
if (!hdmitx_edid_done) {
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_I2C_RESET, 0);
hdmitx_get_edid(hdev);
hdmitx_edid_done = true;
}
hdev->hdmitx_event &= ~HDMI_TX_HPD_PLUGIN;
/* start reading E-EDID */
if (hdev->repeater_tx)
rx_repeat_hpd_state(1);
hdev->cedst_policy = hdev->cedst_en & hdev->tx_comm.rxcap.scdc_present;
hdmi_physical_size_update(hdev);
if (hdev->tx_comm.rxcap.ieeeoui != HDMI_IEEE_OUI)
hdev->tx_hw.cntlconfig(&hdev->tx_hw,
CONF_HDMI_DVI_MODE, DVI_MODE);
else
hdev->tx_hw.cntlconfig(&hdev->tx_hw,
CONF_HDMI_DVI_MODE, HDMI_MODE);
if (hdev->repeater_tx) {
if (check_fbc_special(&hdev->tx_comm.EDID_buf[0]) ||
check_fbc_special(&hdev->tx_comm.EDID_buf1[0]))
rx_set_repeater_support(0);
else
rx_set_repeater_support(1);
}
info = hdmitx_get_current_vinfo(NULL);
if (info && info->mode == VMODE_HDMI)
hdmitx21_set_audio(hdev, &hdev->cur_audio_param);
if (plugout_mute_flg) {
/* 1.TV not changed: just clear avmute and continue output
* 2.if TV changed:
* keep avmute (will be cleared by systemcontrol);
* clear pkt, packets need to be cleared, otherwise,
* if plugout from DV/HDR TV, and plugin to non-DV/HDR
* TV, packets may not be cleared. pkt sending will
* be callbacked later after vinfo attached.
*/
if (is_cur_tmds_div40(hdev))
hdmitx_resend_div40(hdev);
if (!is_tv_changed()) {
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_AVMUTE_OP,
CLR_AVMUTE);
} else {
/* keep avmute & clear pkt */
hdmitx_set_vsif_pkt(0, 0, NULL, true);
hdmitx_set_hdr10plus_pkt(0, NULL);
hdmitx_set_drm_pkt(NULL);
}
edidinfo_attach_to_vinfo(hdev);
plugout_mute_flg = false;
}
hdev->tx_comm.hpd_state = 1;
if (hdev->tv_usage == 0)
hdmitx_notify_hpd(hdev->tx_comm.hpd_state,
hdev->tx_comm.edid_parsing ?
hdev->tx_comm.edid_ptr : NULL);
/* under early suspend, only update uevent state, not
* post to system, in case old android system will
* set hdmi mode
*/
if (hdev->suspend_flag) {
extcon_set_state(hdmitx_extcon_hdmi, EXTCON_DISP_HDMI, 1);
hdmitx21_set_uevent_state(HDMITX_HPD_EVENT, 1);
hdmitx21_set_uevent_state(HDMITX_AUDIO_EVENT, 1);
} else {
extcon_set_state_sync(hdmitx_extcon_hdmi, EXTCON_DISP_HDMI, 1);
hdmitx21_set_uevent(HDMITX_HPD_EVENT, 1);
hdmitx21_set_uevent(HDMITX_AUDIO_EVENT, 1);
}
/* Should be started at end of output */
cancel_delayed_work(&hdev->work_cedst);
if (hdev->cedst_policy)
queue_delayed_work(hdev->cedst_wq, &hdev->work_cedst, 0);
mutex_unlock(&hdev->hdmimode_mutex);
mutex_unlock(&hdev->tx_comm.setclk_mutex);
/*notify to drm hdmi*/
hdmitx_hpd_notify_unlocked(&hdev->tx_comm);
}
static void clear_rx_vinfo(struct hdmitx_dev *hdev)
{
struct vinfo_s *info = hdmitx_get_current_vinfo(NULL);
if (info) {
memset(&info->hdr_info, 0, sizeof(info->hdr_info));
memset(&info->rx_latency, 0, sizeof(info->rx_latency));
}
}
static void hdmitx_aud_hpd_plug_handler(struct work_struct *work)
{
int st;
struct hdmitx_dev *hdev = container_of((struct delayed_work *)work,
struct hdmitx_dev, work_aud_hpd_plug);
st = hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_HPD_GPI_ST, 0);
pr_info("%s state:%d\n", __func__, st);
hdmitx21_set_uevent(HDMITX_AUDIO_EVENT, st);
}
static void hdmitx_hpd_plugout_handler(struct work_struct *work)
{
struct hdmitx_dev *hdev = container_of((struct delayed_work *)work,
struct hdmitx_dev, work_hpd_plugout);
struct hdcp_t *p_hdcp = (struct hdcp_t *)hdev->am_hdcp;
mutex_lock(&hdev->tx_comm.setclk_mutex);
mutex_lock(&hdev->hdmimode_mutex);
if (!(hdev->hdmitx_event & (HDMI_TX_HPD_PLUGOUT))) {
mutex_unlock(&hdev->hdmimode_mutex);
mutex_unlock(&hdev->tx_comm.setclk_mutex);
return;
}
hdmitx_vrr_disable();
if (hdev->cedst_policy)
cancel_delayed_work(&hdev->work_cedst);
edidinfo_detach_to_vinfo(hdev);
frl_tx_stop(hdev);
rx_hdcp2_ver = 0;
is_passthrough_switch = 0;
pr_info("plugout\n");
/* when plugout before systemcontrol boot, setavmute
* but keep output not changed, and wait for plugin
* NOTE: TV maybe changed(such as DV <-> non-DV)
*/
if (!hdev->systemcontrol_on &&
hdmitx21_uboot_already_display(hdev)) {
plugout_mute_flg = true;
/* edidinfo_detach_to_vinfo(hdev); */
clear_rx_vinfo(hdev);
hdev->hdmitx_event &= ~HDMI_TX_HPD_PLUGOUT;
hdmitx21_edid_clear(hdev);
hdmi_physical_size_update(hdev);
hdmitx21_edid_ram_buffer_clear(hdev);
hdmitx_edid_done = false;
hdev->tx_comm.hpd_state = 0;
if (hdev->tv_usage == 0) {
rx_edid_physical_addr(0, 0, 0, 0);
hdmitx_notify_hpd(hdev->tx_comm.hpd_state, NULL);
}
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_AVMUTE_OP, SET_AVMUTE);
hdmitx21_set_uevent(HDMITX_HPD_EVENT, 0);
hdmitx21_set_uevent(HDMITX_AUDIO_EVENT, 0);
mutex_unlock(&hdev->hdmimode_mutex);
mutex_unlock(&hdev->tx_comm.setclk_mutex);
/*notify to drm hdmi*/
hdmitx_hpd_notify_unlocked(&hdev->tx_comm);
return;
}
/*after plugout, DV mode can't be supported*/
hdmitx_set_vsif_pkt(0, 0, NULL, true);
hdmitx_set_hdr10plus_pkt(0, NULL);
/* clear any VSIF packet left over because of vendor<->vendor2 switch */
hdmi_vend_infoframe_rawset(NULL, NULL);
/* stop ALLM packet by hdmitx itself? or stop by upstream
* hdmirx side(in hdmirx channel) / hwc when stop playing
* ALLM video(online stream)?
*/
/* dolby vision CTS case91: clear HF-VSIF for safety */
hdmi_vend_infoframe2_rawset(NULL, NULL);
hdev->ready = 0;
if (hdev->repeater_tx)
rx_repeat_hpd_state(0);
hdev->tx_hw.cntlconfig(&hdev->tx_hw, CONF_CLR_AVI_PACKET, 0);
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_TMDS_PHY_OP, TMDS_PHY_DISABLE);
hdev->hwop.cntlddc(hdev, DDC_HDCP_SET_TOPO_INFO, 0);
hdev->hdmitx_event &= ~HDMI_TX_HPD_PLUGOUT;
hdmitx21_disable_hdcp(hdev);
hdmitx21_rst_stream_type(hdev->am_hdcp);
p_hdcp->saved_upstream_type = 0;
p_hdcp->rx_update_flag = 0;
clear_rx_vinfo(hdev);
hdmitx21_edid_clear(hdev);
hdmi_physical_size_update(hdev);
hdmitx21_edid_ram_buffer_clear(hdev);
hdmitx_edid_done = false;
hdev->tx_comm.hpd_state = 0;
hdev->ll_enabled_in_auto_mode = false;
hdev->pre_tmds_clk_div40 = false;
if (hdev->tv_usage == 0) {
rx_edid_physical_addr(0, 0, 0, 0);
hdmitx_notify_hpd(hdev->tx_comm.hpd_state, NULL);
}
/* clear audio/video mute flag of stream type */
hdmitx21_video_mute_op(1, VIDEO_MUTE_PATH_2);
hdmitx21_audio_mute_op(1, AUDIO_MUTE_PATH_3);
/* under early suspend, only update uevent state, not
* post to system, in case old android system will
* set hdmi mode
*/
if (hdev->suspend_flag) {
extcon_set_state(hdmitx_extcon_hdmi, EXTCON_DISP_HDMI, 0);
hdmitx21_set_uevent_state(HDMITX_HPD_EVENT, 0);
hdmitx21_set_uevent_state(HDMITX_AUDIO_EVENT, 0);
} else {
extcon_set_state_sync(hdmitx_extcon_hdmi, EXTCON_DISP_HDMI, 0);
hdmitx21_set_uevent(HDMITX_HPD_EVENT, 0);
hdmitx21_set_uevent(HDMITX_AUDIO_EVENT, 0);
}
mutex_unlock(&hdev->hdmimode_mutex);
mutex_unlock(&hdev->tx_comm.setclk_mutex);
/*notify to drm hdmi*/
hdmitx_hpd_notify_unlocked(&hdev->tx_comm);
}
int get21_hpd_state(void)
{
int ret;
struct hdmitx_dev *hdev = get_hdmitx21_device();
mutex_lock(&hdev->tx_comm.setclk_mutex);
ret = hdev->tx_comm.hpd_state;
mutex_unlock(&hdev->tx_comm.setclk_mutex);
return ret;
}
/******************************
* hdmitx kernel task
*******************************/
static bool is_cur_tmds_div40(struct hdmitx_dev *hdev)
{
const struct hdmi_timing *tp;
const char *name;
unsigned int act_clk = 0;
struct hdmitx_common *tx_comm = &hdev->tx_comm;
if (!hdev)
return false;
tp = hdmitx21_gettiming_from_vic(hdev->tx_comm.cur_VIC);
if (tp) {
name = tp->sname ? tp->sname : tp->name;
hdev->para = hdmitx21_get_fmtpara(name,
tx_comm->fmt_attr);
}
if (!hdev->para)
return false;
act_clk = hdev->para->tmds_clk / 1000;
pr_info("hdmitx: get vic %d cscd %s act_clk %d\n",
hdev->tx_comm.cur_VIC, tx_comm->fmt_attr, act_clk);
if (act_clk > 340)
return true;
return false;
}
static void hdmitx_resend_div40(struct hdmitx_dev *hdev)
{
hdev->hwop.cntlddc(hdev, DDC_SCDC_DIV40_SCRAMB, 1);
}
/*****************************
* hdmitx driver file_operations
*
******************************/
static int amhdmitx_open(struct inode *node, struct file *file)
{
struct hdmitx_dev *hdmitx_in_devp;
/* Get the per-device structure that contains this cdev */
hdmitx_in_devp = container_of(node->i_cdev, struct hdmitx_dev, cdev);
file->private_data = hdmitx_in_devp;
return 0;
}
static int amhdmitx_release(struct inode *node, struct file *file)
{
return 0;
}
static const struct file_operations amhdmitx_fops = {
.owner = THIS_MODULE,
.open = amhdmitx_open,
.release = amhdmitx_release,
};
static int get_dt_vend_init_data(struct device_node *np,
struct vendor_info_data *vend)
{
int ret;
ret = of_property_read_string(np, "vendor_name",
(const char **)&vend->vendor_name);
if (ret)
pr_info("not find vendor name\n");
ret = of_property_read_u32(np, "vendor_id", &vend->vendor_id);
if (ret)
pr_info("not find vendor id\n");
ret = of_property_read_string(np, "product_desc",
(const char **)&vend->product_desc);
if (ret)
pr_info("not find product desc\n");
return 0;
}
void hdmitx21_fmt_attr(struct hdmitx_dev *hdev)
{
struct hdmitx_common *tx_comm = &hdev->tx_comm;
if (strlen(tx_comm->fmt_attr) >= 8) {
pr_info("fmt_attr %s\n", tx_comm->fmt_attr);
return;
}
memset(tx_comm->fmt_attr, 0, sizeof(tx_comm->fmt_attr));
switch (hdev->para->cs) {
case HDMI_COLORSPACE_RGB:
memcpy(tx_comm->fmt_attr, "rgb,", 5);
break;
case HDMI_COLORSPACE_YUV422:
memcpy(tx_comm->fmt_attr, "422,", 5);
break;
case HDMI_COLORSPACE_YUV444:
memcpy(tx_comm->fmt_attr, "444,", 5);
break;
case HDMI_COLORSPACE_YUV420:
memcpy(tx_comm->fmt_attr, "420,", 5);
break;
default:
break;
}
switch (hdev->para->cd) {
case COLORDEPTH_24B:
strcat(tx_comm->fmt_attr, "8bit");
break;
case COLORDEPTH_30B:
strcat(tx_comm->fmt_attr, "10bit");
break;
case COLORDEPTH_36B:
strcat(tx_comm->fmt_attr, "12bit");
break;
case COLORDEPTH_48B:
strcat(tx_comm->fmt_attr, "16bit");
break;
default:
break;
}
pr_info("fmt_attr %s\n", tx_comm->fmt_attr);
}
static void hdmitx_init_fmt_attr(struct hdmitx_dev *hdev)
{
struct hdmitx_common *tx_comm = &hdev->tx_comm;
if (!hdev->para)
return;
if (strlen(tx_comm->fmt_attr) >= 8) {
pr_info("fmt_attr %s\n", tx_comm->fmt_attr);
return;
}
memset(tx_comm->fmt_attr, 0, sizeof(tx_comm->fmt_attr));
switch (hdev->para->cs) {
case HDMI_COLORSPACE_RGB:
memcpy(tx_comm->fmt_attr, "rgb,", 5);
break;
case HDMI_COLORSPACE_YUV422:
memcpy(tx_comm->fmt_attr, "422,", 5);
break;
case HDMI_COLORSPACE_YUV444:
memcpy(tx_comm->fmt_attr, "444,", 5);
break;
case HDMI_COLORSPACE_YUV420:
memcpy(tx_comm->fmt_attr, "420,", 5);
break;
default:
break;
}
switch (hdev->para->cd) {
case COLORDEPTH_24B:
strcat(tx_comm->fmt_attr, "8bit");
break;
case COLORDEPTH_30B:
strcat(tx_comm->fmt_attr, "10bit");
break;
case COLORDEPTH_36B:
strcat(tx_comm->fmt_attr, "12bit");
break;
case COLORDEPTH_48B:
strcat(tx_comm->fmt_attr, "16bit");
break;
default:
break;
}
pr_debug("fmt_attr %s\n", tx_comm->fmt_attr);
}
/* for notify to cec */
static BLOCKING_NOTIFIER_HEAD(hdmitx21_event_notify_list);
int hdmitx21_event_notifier_regist(struct notifier_block *nb)
{
int ret = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (!nb)
return ret;
ret = blocking_notifier_chain_register(&hdmitx21_event_notify_list, nb);
/* update status when register */
if (!ret && nb->notifier_call) {
if (hdev->tv_usage == 0)
hdmitx_notify_hpd(hdev->tx_comm.hpd_state,
hdev->tx_comm.edid_parsing ?
hdev->tx_comm.edid_ptr : NULL);
if (hdev->physical_addr != 0xffff) {
if (hdev->tv_usage == 0)
hdmitx21_event_notify(HDMITX_PHY_ADDR_VALID,
&hdev->physical_addr);
}
}
return ret;
}
int hdmitx21_event_notifier_unregist(struct notifier_block *nb)
{
int ret;
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (hdev->hdmi_init != 1)
return 1;
ret = blocking_notifier_chain_unregister(&hdmitx21_event_notify_list,
nb);
return ret;
}
void hdmitx21_event_notify(unsigned long state, void *arg)
{
blocking_notifier_call_chain(&hdmitx21_event_notify_list, state, arg);
}
void hdmitx21_hdcp_status(int hdmi_authenticated)
{
hdmitx21_set_uevent(HDMITX_HDCP_EVENT, hdmi_authenticated);
}
/* for compliance with p/q/r/s/t, need both extcon and hdmi_event
* there're mixed combination, P/Q only listen to extcon;
* while R/S only listen to uevent
* t need listen to extcon(only hdmi hpd) and uevent
*/
static int hdmitx_extcon_register(struct platform_device *pdev, struct device *dev)
{
int ret;
/*hdmitx extcon hdmi*/
hdmitx_extcon_hdmi = devm_extcon_dev_allocate(&pdev->dev, hdmi_extcon_cable);
if (IS_ERR(hdmitx_extcon_hdmi)) {
pr_info("%s[%d] hdmitx_extcon_hdmi allocated failed\n", __func__, __LINE__);
if (PTR_ERR(hdmitx_extcon_hdmi) != -ENODEV)
return PTR_ERR(hdmitx_extcon_hdmi);
hdmitx_extcon_hdmi = NULL;
}
ret = devm_extcon_dev_register(&pdev->dev, hdmitx_extcon_hdmi);
if (ret < 0)
pr_info("%s[%d] hdmitx_extcon_hdmi register failed\n", __func__, __LINE__);
return 0;
}
static void hdmitx_init_parameters(struct hdmitx_info *info)
{
memset(info, 0, sizeof(struct hdmitx_info));
info->video_out_changing_flag = 1;
info->audio_flag = 1;
info->audio_info.type = CT_REFER_TO_STREAM;
info->audio_info.format = AF_I2S;
info->audio_info.fs = FS_44K1;
info->audio_info.ss = SS_16BITS;
info->audio_info.channels = CC_2CH;
info->audio_out_changing_flag = 1;
}
static int amhdmitx21_device_init(struct hdmitx_dev *hdmi_dev)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
static struct hdmi_format_para para;
if (!hdmi_dev)
return 1;
memset(&para, 0, sizeof(para));
pr_info("Ver: %s\n", HDMITX_VER);
hdmi_dev->hdtx_dev = NULL;
hdev->para = &para;
hdev->physical_addr = 0xffff;
hdev->hdmi_last_hdr_mode = 0;
hdev->hdmi_current_hdr_mode = 0;
hdev->unplug_powerdown = 0;
hdev->vic_count = 0;
hdev->force_audio_flag = 0;
hdev->ready = 0;
hdev->systemcontrol_on = 0;
hdev->rxsense_policy = 0; /* no RxSense by default */
/* enable or disable HDMITX SSPLL, enable by default */
hdev->sspll = 1;
/*
* 0, do not unmux hpd when off or unplug ;
* 1, unmux hpd when unplug;
* 2, unmux hpd when unplug or off;
*/
hdev->hpdmode = 1;
hdev->flag_3dfp = 0;
hdev->flag_3dss = 0;
hdev->flag_3dtb = 0;
hdev->def_stream_type = DEFAULT_STREAM_TYPE;
if ((init_flag & INIT_FLAG_POWERDOWN) &&
hdev->hpdmode == 2)
hdev->mux_hpd_if_pin_high_flag = 0;
else
hdev->mux_hpd_if_pin_high_flag = 1;
hdev->audio_param_update_flag = 0;
/* 1: 2ch */
hdev->hdmi_ch = 1;
/* default audio configure is on */
hdev->tx_aud_cfg = 1;
hdmitx_init_parameters(&hdev->hdmi_info);
hdev->need_filter_hdcp_off = false;
/* default 6S */
hdev->filter_hdcp_off_period = 6;
hdev->not_restart_hdcp = false;
/* wait for upstream start hdcp auth 5S */
hdev->up_hdcp_timeout_sec = 5;
return 0;
}
static int amhdmitx_get_dt_info(struct platform_device *pdev)
{
int ret = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct pinctrl *pin;
//const char *pin_name;
//const struct of_device_id *of_id;
#ifdef CONFIG_OF
int val;
phandle phandler;
struct device_node *init_data;
const struct of_device_id *match;
#endif
match = of_match_device(meson_amhdmitx_of_match, &pdev->dev);
if (!match) {
pr_info("unable to get matched device\n");
return -1;
}
pr_info("get matched device\n");
//hdev->data = match->data;
/* pinmux set */
if (pdev->dev.of_node) {
pin = devm_pinctrl_get(&pdev->dev);
if (!pin)
pr_info("get pin control fail\n");
hdev->pinctrl_default = pinctrl_lookup_state(pin, "hdmitx_hpd");
pinctrl_select_state(pin, hdev->pinctrl_default);
hdev->pinctrl_i2c = pinctrl_lookup_state(pin, "hdmitx_ddc");
pinctrl_select_state(pin, hdev->pinctrl_i2c);
/* rx_pr("hdmirx: pinmux:%p, name:%s\n", */
/* pin, pin_name); */
pr_info("get pin control\n");
/* rx_pr("hdmirx: pinmux:%p, name:%s\n", */
/* pin, pin_name); */
} else {
pr_info("node null\n");
}
#ifdef CONFIG_OF
if (pdev->dev.of_node) {
int dongle_mode = 0;
int pxp_mode = 0;
memset(&hdev->config_data, 0,
sizeof(struct hdmi_config_platform_data));
/* Get chip type and name information */
match = of_match_device(meson_amhdmitx_of_match, &pdev->dev);
if (!match) {
pr_info("%s: no match table\n", __func__);
return -1;
}
hdev->data = (struct amhdmitx_data_s *)match->data;
pr_info("chip_type:%d chip_name:%s\n",
hdev->data->chip_type,
hdev->data->chip_name);
/* Get pxp_mode information */
ret = of_property_read_u32(pdev->dev.of_node, "pxp_mode",
&pxp_mode);
hdev->pxp_mode = 0;
if (!ret)
pr_info("hdev->pxp_mode: %d\n", hdev->pxp_mode);
/* Get dongle_mode information */
ret = of_property_read_u32(pdev->dev.of_node, "dongle_mode",
&dongle_mode);
hdev->dongle_mode = !!dongle_mode;
if (!ret)
pr_info("hdev->dongle_mode: %d\n",
hdev->dongle_mode);
/* Get repeater_tx information */
ret = of_property_read_u32(pdev->dev.of_node,
"repeater_tx", &val);
if (!ret)
hdev->repeater_tx = val;
else
hdev->repeater_tx = 0;
/* Get repeater_tx information */
ret = of_property_read_u32(pdev->dev.of_node,
"tv_usage", &val);
if (!ret)
hdev->tv_usage = val;
else
hdev->tv_usage = 0;
/* if for tv usage, then should not support hdcp repeater */
if (hdev->tv_usage == 1)
hdev->repeater_tx = 0;
ret = of_property_read_u32(pdev->dev.of_node,
"cedst_en", &val);
if (!ret)
hdev->cedst_en = !!val;
hdev->tx_max_frl_rate = FRL_10G4L; /* default */
ret = of_property_read_u32(pdev->dev.of_node, "tx_max_frl_rate", &val);
if (!ret) {
if (val > FRL_12G4L || val == FRL_NONE)
pr_info("wrong tx_max_frl_rate %d\n", val);
else
hdev->tx_max_frl_rate = val;
}
ret = of_property_read_u32(pdev->dev.of_node,
"hdcp_type_policy", &val);
if (!ret) {
if (val == 2)
;
if (val == 1)
;
}
ret = of_property_read_u32(pdev->dev.of_node,
"enc_idx", &val);
hdev->enc_idx = 0; /* default 0 */
if (!ret) {
if (val == 2)
hdev->enc_idx = 2;
}
ret = of_property_read_u32(pdev->dev.of_node, "vrr_type", &val);
hdev->vrr_type = 0; /* default 0 */
if (!ret) {
if (val == 1 || val == 2)
hdev->vrr_type = val;
}
/* hdcp ctrl 0:sysctrl, 1: drv, 2: linux app */
ret = of_property_read_u32(pdev->dev.of_node,
"hdcp_ctl_lvl", &hdmitx21_device.hdcp_ctl_lvl);
pr_info("hdcp_ctl_lvl[%d-%d]\n", hdmitx21_device.hdcp_ctl_lvl, ret);
if (ret)
hdmitx21_device.hdcp_ctl_lvl = 0;
/* Get vendor information */
ret = of_property_read_u32(pdev->dev.of_node,
"vend-data", &val);
if (ret)
pr_info("not find match init-data\n");
if (ret == 0) {
phandler = val;
init_data = of_find_node_by_phandle(phandler);
if (!init_data)
pr_info("not find device node\n");
hdev->config_data.vend_data =
kzalloc(sizeof(struct vendor_info_data), GFP_KERNEL);
if (!(hdev->config_data.vend_data))
pr_info("not allocate memory\n");
ret = get_dt_vend_init_data
(init_data, hdev->config_data.vend_data);
if (ret)
pr_info("not find vend_init_data\n");
}
/* Get power control */
ret = of_property_read_u32(pdev->dev.of_node,
"pwr-ctrl", &val);
if (ret)
pr_info("not find match pwr-ctl\n");
if (ret == 0) {
phandler = val;
init_data = of_find_node_by_phandle(phandler);
if (!init_data)
pr_info("not find device node\n");
hdev->config_data.pwr_ctl = kzalloc((sizeof(struct hdmi_pwr_ctl)) *
HDMI_TX_PWR_CTRL_NUM, GFP_KERNEL);
if (!hdev->config_data.pwr_ctl)
pr_info("can not get pwr_ctl mem\n");
else
memset(hdev->config_data.pwr_ctl, 0, sizeof(struct hdmi_pwr_ctl));
if (ret)
pr_info("not find pwr_ctl\n");
}
/* Get reg information */
ret = hdmitx21_init_reg_map(pdev);
if (ret < 0)
pr_err("ERROR: hdmitx io_remap fail!\n");
}
#else
hdmi_pdata = pdev->dev.platform_data;
if (!hdmi_pdata) {
pr_info("not get platform data\n");
r = -ENOENT;
} else {
pr_info("get hdmi platform data\n");
}
#endif
hdev->irq_hpd = platform_get_irq_byname(pdev, "hdmitx_hpd");
if (hdev->irq_hpd == -ENXIO) {
pr_err("%s: ERROR: hdmitx hpd irq No not found\n",
__func__);
return -ENXIO;
}
pr_info("hpd irq = %d\n", hdev->irq_hpd);
tx_vrr_params_init();
hdev->irq_vrr_vsync = platform_get_irq_byname(pdev, "vrr_vsync");
if (hdev->irq_vrr_vsync == -ENXIO) {
pr_err("%s: ERROR: hdmitx vrr_vsync irq No not found\n",
__func__);
return -ENXIO;
}
pr_info("vrr vsync irq = %d\n", hdev->irq_vrr_vsync);
ret = of_property_read_u32(pdev->dev.of_node, "arc_rx_en", &val);
if (!ret)
hdev->arc_rx_en = val;
else
hdev->arc_rx_en = 0;
return ret;
}
/*
* amhdmitx_clktree_probe
* get clktree info from dts
*/
static void amhdmitx_clktree_probe(struct device *hdmitx_dev)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct clk *hdmi_clk_vapb, *hdmi_clk_vpu;
struct clk *venci_top_gate, *venci_0_gate, *venci_1_gate;
hdmi_clk_vapb = devm_clk_get(hdmitx_dev, "hdmi_vapb_clk");
if (IS_ERR(hdmi_clk_vapb)) {
pr_warn("vapb_clk failed to probe\n");
} else {
hdev->hdmitx_clk_tree.hdmi_clk_vapb = hdmi_clk_vapb;
clk_prepare_enable(hdev->hdmitx_clk_tree.hdmi_clk_vapb);
}
hdmi_clk_vpu = devm_clk_get(hdmitx_dev, "hdmi_vpu_clk");
if (IS_ERR(hdmi_clk_vpu)) {
pr_warn("vpu_clk failed to probe\n");
} else {
hdev->hdmitx_clk_tree.hdmi_clk_vpu = hdmi_clk_vpu;
clk_prepare_enable(hdev->hdmitx_clk_tree.hdmi_clk_vpu);
}
venci_top_gate = devm_clk_get(hdmitx_dev, "venci_top_gate");
if (IS_ERR(venci_top_gate))
pr_warn("venci_top_gate failed to probe\n");
else
hdev->hdmitx_clk_tree.venci_top_gate = venci_top_gate;
venci_0_gate = devm_clk_get(hdmitx_dev, "venci_0_gate");
if (IS_ERR(venci_0_gate))
pr_warn("venci_0_gate failed to probe\n");
else
hdev->hdmitx_clk_tree.venci_0_gate = venci_0_gate;
venci_1_gate = devm_clk_get(hdmitx_dev, "venci_1_gate");
if (IS_ERR(venci_1_gate))
pr_warn("venci_1_gate failed to probe\n");
else
hdev->hdmitx_clk_tree.venci_1_gate = venci_1_gate;
}
void amhdmitx21_vpu_dev_register(struct hdmitx_dev *hdev)
{
hdev->hdmitx_vpu_clk_gate_dev =
vpu_dev_register(VPU_VENCI, DEVICE_NAME);
}
static void amhdmitx_infoframe_init(struct hdmitx_dev *hdev)
{
int ret = 0;
ret = hdmi_vendor_infoframe_init(&hdev->infoframes.vend.vendor.hdmi);
if (!ret)
pr_info("%s[%d] init vendor infoframe failed %d\n", __func__, __LINE__, ret);
hdmi_avi_infoframe_init(&hdev->infoframes.avi.avi);
// TODO, panic
// hdmi_spd_infoframe_init(&hdev->infoframes.spd.spd,
// hdev->config_data.vend_data->vendor_name,
// hdev->config_data.vend_data->product_desc);
hdmi_audio_infoframe_init(&hdev->infoframes.aud.audio);
hdmi_drm_infoframe_init(&hdev->infoframes.drm.drm);
}
static int hdmitx21_status_check(void *data)
{
int clk[3];
int idx[3];
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (hdev->data && hdev->data->chip_type != MESON_CPU_ID_S5)
return 0;
/* for S5, here need check the clk index 89 & 16 */
idx[0] = 89; /* htx_tmds20_clk */
idx[1] = 16; /* vid_pll0_clk */
idx[2] = 92; /* cts_htx_tmds_clk */
while (1) {
msleep_interruptible(1000);
if (!hdev->ready)
continue;
/* skip FRL mode */
if (hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_IS_FRL_MODE, 0))
continue;
clk[0] = meson_clk_measure(idx[0]);
clk[1] = meson_clk_measure(idx[1]);
if (clk[0] && clk[1])
continue;
if (!clk[0]) {
pr_debug("the clock[%d] is %d\n", idx[0], clk[0]);
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_CLK_DIV_RST, idx[0]);
pr_debug("reset the clock div for %d\n", idx[0]);
pr_info("the clock[%d] is %d\n", idx[0], meson_clk_measure(idx[0]));
}
if (!clk[1]) {
pr_debug("the clock[%d] is %d\n", idx[1], clk[1]);
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_CLK_DIV_RST, idx[1]);
pr_debug("reset the clock div for %d\n", idx[1]);
pr_info("the clock[%d] is %d\n", idx[1], meson_clk_measure(idx[1]));
}
/* resend the SCDC/DIV40 config */
if (!clk[0] || !clk[1]) {
clk[2] = meson_clk_measure(idx[2]);
if (clk[2] >= 340000000)
hdev->hwop.cntlddc(hdev, DDC_SCDC_DIV40_SCRAMB, 1);
else
hdev->hwop.cntlddc(hdev, DDC_SCDC_DIV40_SCRAMB, 0);
}
}
return 0;
}
static int amhdmitx_probe(struct platform_device *pdev)
{
int r, ret = 0;
struct device *dev;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
pr_debug("%s start\n", __func__);
hdmitx_common_init(&hdev->tx_comm);
amhdmitx21_device_init(hdev);
amhdmitx_infoframe_init(hdev);
ret = amhdmitx_get_dt_info(pdev);
/* if (ret) */
/* return ret; */
amhdmitx_clktree_probe(&pdev->dev);
if (0) /* TODO */
amhdmitx21_vpu_dev_register(hdev);
r = alloc_chrdev_region(&hdev->hdmitx_id, 0, HDMI_TX_COUNT,
DEVICE_NAME);
cdev_init(&hdev->cdev, &amhdmitx_fops);
hdev->cdev.owner = THIS_MODULE;
r = cdev_add(&hdev->cdev, hdev->hdmitx_id, HDMI_TX_COUNT);
hdmitx_class = class_create(THIS_MODULE, "amhdmitx");
if (IS_ERR(hdmitx_class)) {
unregister_chrdev_region(hdev->hdmitx_id, HDMI_TX_COUNT);
return -1;
}
dev = device_create(hdmitx_class, NULL, hdev->hdmitx_id, NULL,
"amhdmitx%d", 0); /* kernel>=2.6.27 */
if (!dev) {
pr_info("device_create create error\n");
class_destroy(hdmitx_class);
r = -EEXIST;
return r;
}
hdev->hdtx_dev = dev;
ret = device_create_file(dev, &dev_attr_disp_mode);
ret = device_create_file(dev, &dev_attr_vid_mute);
ret = device_create_file(dev, &dev_attr_sink_type);
ret = device_create_file(dev, &dev_attr_config);
ret = device_create_file(dev, &dev_attr_debug);
ret = device_create_file(dev, &dev_attr_disp_cap);
ret = device_create_file(dev, &dev_attr_vrr_cap);
ret = device_create_file(dev, &dev_attr_preferred_mode);
ret = device_create_file(dev, &dev_attr_cea_cap);
ret = device_create_file(dev, &dev_attr_vesa_cap);
ret = device_create_file(dev, &dev_attr_aud_cap);
ret = device_create_file(dev, &dev_attr_lipsync_cap);
ret = device_create_file(dev, &dev_attr_hdmi_hdr_status);
ret = device_create_file(dev, &dev_attr_aud_mute);
ret = device_create_file(dev, &dev_attr_avmute);
ret = device_create_file(dev, &dev_attr_sspll);
ret = device_create_file(dev, &dev_attr_rxsense_policy);
ret = device_create_file(dev, &dev_attr_rxsense_state);
ret = device_create_file(dev, &dev_attr_cedst_policy);
ret = device_create_file(dev, &dev_attr_cedst_count);
ret = device_create_file(dev, &dev_attr_hdcp_mode);
ret = device_create_file(dev, &dev_attr_hdcp_ver);
ret = device_create_file(dev, &dev_attr_hdcp_type_policy);
ret = device_create_file(dev, &dev_attr_hdmi_repeater_tx);
ret = device_create_file(dev, &dev_attr_hdcp_lstore);
ret = device_create_file(dev, &dev_attr_div40);
ret = device_create_file(dev, &dev_attr_hdmi_used);
ret = device_create_file(dev, &dev_attr_fake_plug);
ret = device_create_file(dev, &dev_attr_ready);
ret = device_create_file(dev, &dev_attr_support_3d);
ret = device_create_file(dev, &dev_attr_dc_cap);
ret = device_create_file(dev, &dev_attr_valid_mode);
ret = device_create_file(dev, &dev_attr_allm_cap);
ret = device_create_file(dev, &dev_attr_allm_mode);
ret = device_create_file(dev, &dev_attr_ll_mode);
ret = device_create_file(dev, &dev_attr_ll_user_mode);
ret = device_create_file(dev, &dev_attr_hdmitx21);
ret = device_create_file(dev, &dev_attr_aon_output);
ret = device_create_file(dev, &dev_attr_def_stream_type);
ret = device_create_file(dev, &dev_attr_hdcp_ctl_lvl);
ret = device_create_file(dev, &dev_attr_sysctrl_enable);
ret = device_create_file(dev, &dev_attr_propagate_stream_type);
ret = device_create_file(dev, &dev_attr_cont_smng_method);
ret = device_create_file(dev, &dev_attr_frl_rate);
ret = device_create_file(dev, &dev_attr_dsc_en);
ret = device_create_file(dev, &dev_attr_hdr_priority_mode);
ret = device_create_file(dev, &dev_attr_is_passthrough_switch);
ret = device_create_file(dev, &dev_attr_is_hdcp_cts_te);
ret = device_create_file(dev, &dev_attr_need_filter_hdcp_off);
ret = device_create_file(dev, &dev_attr_filter_hdcp_off_period);
ret = device_create_file(dev, &dev_attr_not_restart_hdcp);
hdev->task = kthread_run(hdmitx21_status_check, (void *)hdev,
"kthread_hdmist_check");
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
register_early_suspend(&hdmitx_early_suspend_handler);
#endif
hdev->nb.notifier_call = hdmitx_reboot_notifier;
register_reboot_notifier(&hdev->nb);
hdmitx21_meson_init(hdev);
mutex_init(&hdev->hdmimode_mutex);
tx_comm->hpd_state = !!(hdev->tx_hw.cntlmisc(&hdev->tx_hw,
MISC_HPD_GPI_ST, 0));
#ifdef CONFIG_AMLOGIC_VOUT_SERVE
vout_register_server(&hdmitx_vout_server);
#endif
#ifdef CONFIG_AMLOGIC_VOUT2_SERVE
vout2_register_server(&hdmitx_vout2_server);
#endif
#ifdef CONFIG_AMLOGIC_VOUT3_SERVE
vout3_register_server(&hdmitx_vout3_server);
#endif
#if IS_ENABLED(CONFIG_AMLOGIC_SND_SOC)
if (!hdev->pxp_mode && hdmitx21_uboot_audio_en()) {
struct hdmitx_audpara *audpara = &hdev->cur_audio_param;
audpara->sample_rate = FS_48K;
audpara->type = CT_PCM;
audpara->sample_size = SS_16BITS;
audpara->channel_num = 2 - 1;
}
if (!hdev->pxp_mode)
aout_register_client(&hdmitx_notifier_nb_a);
#endif
hdmitx_extcon_register(pdev, dev);
/* update fmt_attr */
hdmitx_init_fmt_attr(hdev);
tx_comm->hpd_state = !!hdev->tx_hw.cntlmisc(&hdev->tx_hw,
MISC_HPD_GPI_ST, 0);
hdmitx21_set_uevent(HDMITX_HDCPPWR_EVENT, HDMI_WAKEUP);
INIT_WORK(&hdev->work_hdr, hdr_work_func);
/* When init hdmi, clear the hdmitx module edid ram and edid buffer. */
hdmitx21_edid_clear(hdev);
hdmitx21_edid_ram_buffer_clear(hdev);
hdev->hdmi_wq = alloc_workqueue(DEVICE_NAME,
WQ_HIGHPRI | WQ_CPU_INTENSIVE, 0);
INIT_DELAYED_WORK(&hdev->work_hpd_plugin, hdmitx_hpd_plugin_handler);
INIT_DELAYED_WORK(&hdev->work_hpd_plugout, hdmitx_hpd_plugout_handler);
INIT_DELAYED_WORK(&hdev->work_start_hdcp, hdmitx_start_hdcp_handler);
INIT_DELAYED_WORK(&hdev->work_up_hdcp_timeout, hdmitx_up_hdcp_timeout_handler);
INIT_DELAYED_WORK(&hdev->work_aud_hpd_plug,
hdmitx_aud_hpd_plug_handler);
INIT_DELAYED_WORK(&hdev->work_internal_intr, hdmitx_top_intr_handler);
/* for rx sense feature */
hdev->rxsense_wq = alloc_workqueue("hdmitx_rxsense",
WQ_SYSFS | WQ_FREEZABLE, 0);
INIT_DELAYED_WORK(&hdev->work_rxsense, hdmitx_rxsense_process);
/* for cedst feature */
hdev->cedst_wq = alloc_workqueue("hdmitx_cedst",
WQ_SYSFS | WQ_FREEZABLE, 0);
INIT_DELAYED_WORK(&hdev->work_cedst, hdmitx_cedst_process);
hdev->tx_aud_cfg = 1; /* default audio configure is on */
hdmitx21_hdcp_init();
hdmitx_setupirqs(hdev);
if (tx_comm->hpd_state) {
/* need to get edid before vout probe */
hdev->already_used = 1;
hdmitx_get_edid(hdev);
}
/* Trigger HDMITX IRQ*/
if (hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_HPD_GPI_ST, 0)) {
/* When bootup mbox and TV simultaneously,
* TV may not handle SCDC/DIV40
*/
if (is_cur_tmds_div40(hdev))
hdmitx_resend_div40(hdev);
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_TRIGGER_HPD, 1);
hdev->already_used = 1;
} else {
/* may plugout during uboot finish--kernel start,
* treat it as normal hotplug out, for > 3.4G case
*/
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_TRIGGER_HPD, 0);
}
hdev->hdmi_init = 1;
/* ll mode init values */
hdev->ll_enabled_in_auto_mode = false;
hdev->ll_user_set_mode = HDMI_LL_MODE_AUTO;
/*bind to drm.*/
hdmitx_hook_drm(&pdev->dev);
tee_comm_dev_reg(hdev);
pr_info("%s end\n", __func__);
/*everything is ready, create sysfs here.*/
hdmitx_sysfs_common_create(dev, &hdev->tx_comm, &hdev->tx_hw);
return r;
}
static int amhdmitx_remove(struct platform_device *pdev)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct device *dev = hdev->hdtx_dev;
/*remove sysfs before uninit/*/
hdmitx_sysfs_common_destroy(dev);
tee_comm_dev_unreg(hdev);
/*unbind from drm.*/
hdmitx_unhook_drm(&pdev->dev);
cancel_work_sync(&hdev->work_hdr);
if (hdev->hwop.uninit)
hdev->hwop.uninit(hdev);
hdev->hpd_event = 0xff;
kthread_stop(hdev->task);
#ifdef CONFIG_AMLOGIC_VOUT_SERVE
vout_unregister_server(&hdmitx_vout_server);
#endif
#ifdef CONFIG_AMLOGIC_VOUT2_SERVE
vout2_unregister_server(&hdmitx_vout2_server);
#endif
#ifdef CONFIG_AMLOGIC_VOUT3_SERVE
vout3_unregister_server(&hdmitx_vout3_server);
#endif
#if IS_ENABLED(CONFIG_AMLOGIC_SND_SOC)
aout_unregister_client(&hdmitx_notifier_nb_a);
#endif
/* Remove the cdev */
device_remove_file(dev, &dev_attr_disp_mode);
device_remove_file(dev, &dev_attr_vid_mute);
device_remove_file(dev, &dev_attr_sink_type);
device_remove_file(dev, &dev_attr_config);
device_remove_file(dev, &dev_attr_debug);
device_remove_file(dev, &dev_attr_disp_cap);
device_remove_file(dev, &dev_attr_vrr_cap);
device_remove_file(dev, &dev_attr_preferred_mode);
device_remove_file(dev, &dev_attr_cea_cap);
device_remove_file(dev, &dev_attr_vesa_cap);
device_remove_file(dev, &dev_attr_dc_cap);
device_remove_file(dev, &dev_attr_valid_mode);
device_remove_file(dev, &dev_attr_allm_cap);
device_remove_file(dev, &dev_attr_allm_mode);
device_remove_file(dev, &dev_attr_ll_mode);
device_remove_file(dev, &dev_attr_ll_user_mode);
device_remove_file(dev, &dev_attr_hdmi_used);
device_remove_file(dev, &dev_attr_fake_plug);
device_remove_file(dev, &dev_attr_ready);
device_remove_file(dev, &dev_attr_support_3d);
device_remove_file(dev, &dev_attr_aud_mute);
device_remove_file(dev, &dev_attr_avmute);
device_remove_file(dev, &dev_attr_sspll);
device_remove_file(dev, &dev_attr_rxsense_policy);
device_remove_file(dev, &dev_attr_rxsense_state);
device_remove_file(dev, &dev_attr_cedst_policy);
device_remove_file(dev, &dev_attr_cedst_count);
device_remove_file(dev, &dev_attr_div40);
device_remove_file(dev, &dev_attr_hdcp_type_policy);
device_remove_file(dev, &dev_attr_hdmi_repeater_tx);
device_remove_file(dev, &dev_attr_hdmi_hdr_status);
device_remove_file(dev, &dev_attr_hdmitx21);
device_remove_file(dev, &dev_attr_hdcp_ver);
device_remove_file(dev, &dev_attr_def_stream_type);
device_remove_file(dev, &dev_attr_aon_output);
device_remove_file(dev, &dev_attr_hdcp_ctl_lvl);
device_remove_file(dev, &dev_attr_sysctrl_enable);
device_remove_file(dev, &dev_attr_propagate_stream_type);
device_remove_file(dev, &dev_attr_cont_smng_method);
device_remove_file(dev, &dev_attr_frl_rate);
device_remove_file(dev, &dev_attr_dsc_en);
device_remove_file(dev, &dev_attr_hdr_priority_mode);
device_remove_file(dev, &dev_attr_is_passthrough_switch);
device_remove_file(dev, &dev_attr_is_hdcp_cts_te);
device_remove_file(dev, &dev_attr_need_filter_hdcp_off);
device_remove_file(dev, &dev_attr_filter_hdcp_off_period);
device_remove_file(dev, &dev_attr_not_restart_hdcp);
cdev_del(&hdev->cdev);
device_destroy(hdmitx_class, hdev->hdmitx_id);
class_destroy(hdmitx_class);
unregister_chrdev_region(hdev->hdmitx_id, HDMI_TX_COUNT);
hdmitx_common_destroy(&hdev->tx_comm);
return 0;
}
static void amhdmitx_shutdown(struct platform_device *pdev)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (hdev->aon_output) {
hdmitx21_disable_hdcp(hdev);
return;
}
}
#ifdef CONFIG_PM
static int amhdmitx_suspend(struct platform_device *pdev,
pm_message_t state)
{
return 0;
}
static int amhdmitx_resume(struct platform_device *pdev)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
pr_info("%s: I2C_REACTIVE\n", DEVICE_NAME);
hdev->tx_hw.cntlmisc(&hdev->tx_hw, MISC_I2C_REACTIVE, 0);
return 0;
}
#endif
static struct platform_driver amhdmitx_driver = {
.probe = amhdmitx_probe,
.remove = amhdmitx_remove,
#ifdef CONFIG_PM
.suspend = amhdmitx_suspend,
.resume = amhdmitx_resume,
#endif
.shutdown = amhdmitx_shutdown,
.driver = {
.name = DEVICE_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(meson_amhdmitx_of_match),
}
};
int __init amhdmitx21_init(void)
{
struct hdmitx_boot_param *param = get_hdmitx_boot_params();
if (param->init_state & INIT_FLAG_NOT_LOAD)
return 0;
return platform_driver_register(&amhdmitx_driver);
}
void __exit amhdmitx21_exit(void)
{
pr_info("%s...\n", __func__);
// TODO stop hdcp
platform_driver_unregister(&amhdmitx_driver);
}
//MODULE_DESCRIPTION("AMLOGIC HDMI TX driver");
//MODULE_LICENSE("GPL");
//MODULE_VERSION("1.0.0");
/* besides characters defined in separator, '\"' are used as separator;
* and any characters in '\"' will not act as separator
*/
static char *next_token_ex(char *separator, char *buf, u32 size,
u32 offset, u32 *token_len,
u32 *token_offset)
{
char *ptoken = NULL;
char last_separator = 0;
char trans_char_flag = 0;
if (buf) {
for (; offset < size; offset++) {
int ii = 0;
char ch;
if (buf[offset] == '\\') {
trans_char_flag = 1;
continue;
}
while (((ch = separator[ii++]) != buf[offset]) && (ch))
;
if (ch) {
if (!ptoken) {
continue;
} else {
if (last_separator != '"') {
*token_len = (unsigned int)
(buf + offset - ptoken);
*token_offset = offset;
return ptoken;
}
}
} else if (!ptoken) {
if (trans_char_flag && (buf[offset] == '"'))
last_separator = buf[offset];
ptoken = &buf[offset];
} else if ((trans_char_flag && (buf[offset] == '"')) &&
(last_separator == '"')) {
*token_len = (unsigned int)(buf + offset - ptoken - 2);
*token_offset = offset + 1;
return ptoken + 1;
}
trans_char_flag = 0;
}
if (ptoken) {
*token_len = (unsigned int)(buf + offset - ptoken);
*token_offset = offset;
}
}
return ptoken;
}
/* check the colorattribute from uboot */
static void check_hdmiuboot_attr(char *token)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
char attr[16] = {0};
const char * const cs[] = {
"444", "422", "rgb", "420", NULL};
const char * const cd[] = {
"8bit", "10bit", "12bit", "16bit", NULL};
int i;
if (tx_comm->fmt_attr[0] != 0)
return;
if (!token)
return;
for (i = 0; cs[i]; i++) {
if (strstr(token, cs[i])) {
if (strlen(cs[i]) < sizeof(attr))
strcpy(attr, cs[i]);
strcat(attr, ",");
break;
}
}
for (i = 0; cd[i]; i++) {
if (strstr(token, cd[i])) {
if (strlen(cd[i]) < sizeof(attr))
if (strlen(cd[i]) <
(sizeof(attr) - strlen(attr)))
strcat(attr, cd[i]);
strncpy(tx_comm->fmt_attr, attr,
sizeof(tx_comm->fmt_attr));
tx_comm->fmt_attr[15] = '\0';
break;
}
}
memcpy(tx_comm->backup_fmt_attr, tx_comm->fmt_attr,
sizeof(tx_comm->fmt_attr));
}
static int hdmitx21_boot_para_setup(char *s)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
char separator[] = {' ', ',', ';', 0x0};
char *token;
u32 token_len = 0;
u32 token_offset = 0;
u32 offset = 0;
int size = strlen(s);
memset(tx_comm->fmt_attr, 0, sizeof(tx_comm->fmt_attr));
memset(tx_comm->backup_fmt_attr, 0,
sizeof(tx_comm->backup_fmt_attr));
do {
token = next_token_ex(separator, s, size, offset,
&token_len, &token_offset);
if (token) {
if (token_len == 3 &&
strncmp(token, "off", token_len) == 0) {
init_flag |= INIT_FLAG_NOT_LOAD;
}
check_hdmiuboot_attr(token);
}
offset = token_offset;
} while (token);
return 0;
}
__setup("hdmitx=", hdmitx21_boot_para_setup);
static int hdmitx21_boot_frac_rate(char *str)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
if (strncmp("0", str, 1) == 0)
tx_comm->frac_rate_policy = 0;
else
tx_comm->frac_rate_policy = 1;
pr_info("hdmitx boot frac_rate_policy: %d",
tx_comm->frac_rate_policy);
tx_comm->backup_frac_rate_policy = tx_comm->frac_rate_policy;
return 0;
}
__setup("frac_rate_policy=", hdmitx21_boot_frac_rate);
static int hdmitx21_boot_hdr_priority(char *str)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmitx_common *tx_comm = &hdev->tx_comm;
unsigned int val = 0;
if ((strncmp("1", str, 1) == 0) || (strncmp("2", str, 1) == 0)) {
val = str[0] - '0';
tx_comm->hdr_priority = val;
pr_info("hdmitx boot hdr_priority: %d\n", val);
}
return 0;
}
__setup("hdr_priority=", hdmitx21_boot_hdr_priority);
static int get_hdmi21_checksum(char *str)
{
snprintf(hdmichecksum, sizeof(hdmichecksum), "%s", str);
pr_info("get hdmi checksum: %s\n", hdmichecksum);
return 0;
}
__setup("hdmichecksum=", get_hdmi21_checksum);
MODULE_PARM_DESC(log21_level, "\n log21_level\n");
module_param(log21_level, int, 0644);
/*************DRM connector API**************/
static int drm_hdmitx_detect_hpd(void)
{
return hdmitx21_device.tx_comm.hpd_state;
}
static int drm_hdmitx_get_vic_list(int **vics)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct rx_cap *prxcap = &hdev->tx_comm.rxcap;
enum hdmi_vic vic;
const struct hdmi_timing *timing;
int len = prxcap->VIC_count;
int i;
int count = 0;
int *viclist = 0;
if (len == 0)
return 0;
viclist = kmalloc_array(len, sizeof(int), GFP_KERNEL);
for (i = 0; i < len; i++) {
vic = prxcap->VIC[i];
timing = hdmitx21_gettiming_from_vic(vic);
if (timing) {
viclist[count] = vic;
count++;
}
}
/* TODO if count is non-zero, viclist will free in drm caller */
if (count == 0)
kfree(viclist);
else
*vics = viclist;
return count;
}
static int drm_hdmitx_get_timing_para(int vic, struct drm_hdmitx_timing_para *para)
{
const struct hdmi_timing *timing;
if (vic == HDMI_2_720x480p60_4x3 ||
vic == HDMI_6_720x480i60_4x3 ||
vic == HDMI_17_720x576p50_4x3 ||
vic == HDMI_21_720x576i50_4x3) {
if (hdmitx_check_vic(vic + 1))
return -1;
vic++;
}
timing = hdmitx21_gettiming_from_vic(vic);
if (!timing)
return -1;
if (timing->sname)
memcpy(para->name, timing->sname, DRM_DISPLAY_MODE_LEN);
else if (timing->name)
memcpy(para->name, timing->name, DRM_DISPLAY_MODE_LEN);
else
return -1;
if (timing->v_freq % 1000 == 0) {
para->sync_dura_num = timing->v_freq / 1000;
para->sync_dura_den = 1;
} else {
para->sync_dura_num = timing->v_freq;
para->sync_dura_den = 1000;
}
para->pi_mode = timing->pi_mode;
para->pix_repeat_factor = timing->pixel_repetition_factor;
para->h_pol = timing->h_pol;
para->v_pol = timing->v_pol;
para->pixel_freq = timing->pixel_freq;
para->h_active = timing->h_active;
para->h_front = timing->h_front;
para->h_sync = timing->h_sync;
para->h_total = timing->h_total;
if (!timing->pi_mode)
para->v_active = timing->v_active / 2;
else
para->v_active = timing->v_active;
para->v_front = timing->v_front;
para->v_sync = timing->v_sync;
para->v_total = timing->v_total;
return 0;
}
static bool drm_hdmitx_chk_mode_attr_sup(char *mode, char *attr)
{
struct hdmi_format_para *para = NULL;
bool valid = false;
if (hdmitx21_device.hdmi_init != 1)
return valid;
if (!mode || !attr)
return valid;
valid = pre_process_str(attr);
if (!valid)
return valid;
para = hdmitx21_tst_fmt_name(mode, attr);
if (para) {
pr_info("sname = %s\n", para->hdmitx_vinfo.name);
pr_info("char_clk = %d\n", para->tmds_clk);
pr_info("cd = %d\n", para->cd);
pr_info("cs = %d\n", para->cs);
}
valid = hdmitx21_edid_check_valid_mode(&hdmitx21_device, para);
return valid;
}
/*hdcp functions*/
static void drm_hdmitx_hdcp_init(void)
{
}
static void drm_hdmitx_hdcp_exit(void)
{
}
static void drm_hdmitx_hdcp_enable(int hdcp_type)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
switch (hdcp_type) {
case HDCP_NULL:
pr_err("%s enabled HDCP_NULL\n", __func__);
break;
case HDCP_MODE14:
hdev->hdcp_mode = 0x1;
hdcp_mode_set(1);
break;
case HDCP_MODE22:
hdev->hdcp_mode = 0x2;
hdcp_mode_set(2);
break;
default:
pr_err("%s unknown hdcp %d\n", __func__, hdcp_type);
break;
};
}
static void drm_hdmitx_hdcp_disable(void)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
hdev->hdcp_mode = 0x00;
hdcp_mode_set(0);
}
static void drm_hdmitx_hdcp_disconnect(void)
{
drm_hdmitx_hdcp_disable();
}
static unsigned int drm_hdmitx_get_tx_hdcp_cap(void)
{
int lstore = 0;
if (get_hdcp2_lstore())
lstore |= HDCP_MODE22;
if (get_hdcp1_lstore())
lstore |= HDCP_MODE14;
pr_info("%s tx hdcp [%d]\n", __func__, lstore);
return lstore;
}
static unsigned int drm_hdmitx_get_rx_hdcp_cap(void)
{
unsigned int rxhdcp = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
/* if TX don't have HDCP22 key, skip RX hdcp22 ver */
/* note that during hdcp1.4 authentication, read hdcp version
* of connected TV set(capable of hdcp2.2) may cause TV
* switch its hdcp mode, and flash screen. should not
* read hdcp version of sink during hdcp1.4 authentication.
* if hdcp1.4 authentication currently, force return hdcp1.4
*/
if (hdev->hdcp_mode == 0x1) {
rxhdcp = HDCP_MODE14;
} else if (get_hdcp2_lstore() && is_rx_hdcp2ver()) {
rx_hdcp2_ver = 1;
rxhdcp = HDCP_MODE22 | HDCP_MODE14;
} else {
rx_hdcp2_ver = 0;
rxhdcp = HDCP_MODE14;
}
pr_info("%s rx hdcp [%d]\n", __func__, rxhdcp);
return rxhdcp;
}
static void drm_hdmitx_register_hdcp_notify(struct connector_hdcp_cb *cb)
{
if (hdmitx21_device.drm_hdcp_cb.hdcp_notify)
pr_err("Register hdcp notify again!?\n");
hdmitx21_device.drm_hdcp_cb.hdcp_notify = cb->hdcp_notify;
hdmitx21_device.drm_hdcp_cb.data = cb->data;
}
static struct meson_hdmitx_dev drm_hdmitx_instance = {
.base = {
.ver = MESON_DRM_CONNECTOR_V10,
},
.detect = drm_hdmitx_detect_hpd,
.get_vic_list = drm_hdmitx_get_vic_list,
.get_timing_para_by_vic = drm_hdmitx_get_timing_para,
.test_attr = drm_hdmitx_chk_mode_attr_sup,
.get_hdmi_hdr_status = hdmi_hdr_status_to_drm,
.set_aspect_ratio = hdmitx21_set_aspect_ratio,
.get_aspect_ratio = hdmitx21_get_aspect_ratio_value,
.set_frac = set_frac_rate_policy,
.get_frac = get_frac_rate_policy,
/*hdcp apis*/
.hdcp_init = drm_hdmitx_hdcp_init,
.hdcp_exit = drm_hdmitx_hdcp_exit,
.hdcp_enable = drm_hdmitx_hdcp_enable,
.hdcp_disable = drm_hdmitx_hdcp_disable,
.hdcp_disconnect = drm_hdmitx_hdcp_disconnect,
.get_tx_hdcp_cap = drm_hdmitx_get_tx_hdcp_cap,
.get_rx_hdcp_cap = drm_hdmitx_get_rx_hdcp_cap,
.register_hdcp_notify = drm_hdmitx_register_hdcp_notify,
.get_vrr_cap = drm_hdmitx_get_vrr_cap,
.get_vrr_mode_group = drm_hdmitx_get_vrr_mode_group,
.get_hdcp_ctl_lvl = get_hdmitx_hdcp_ctl_lvl_to_drm,
};
int hdmitx_hook_drm(struct device *device)
{
return hdmitx_bind_meson_drm(device,
&hdmitx21_device.tx_comm,
&hdmitx21_device.tx_hw,
&drm_hdmitx_instance);
}
int hdmitx_unhook_drm(struct device *device)
{
return hdmitx_unbind_meson_drm(device,
&hdmitx21_device.tx_comm,
&hdmitx21_device.tx_hw,
&drm_hdmitx_instance);
}
/*************DRM connector API end**************/
/****** tee_hdcp key related start ******/
static long hdcp_comm_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg)
{
int rtn_val;
struct hdmitx_dev *hdev = &hdmitx21_device;
switch (cmd) {
case TEE_HDCP_IOC_START:
/* notify by TEE, hdcp key ready */
rtn_val = 0;
pr_info("tee load hdcp key ready\n");
if (hdev->tx_comm.hpd_state == 1 &&
hdev->ready &&
hdmitx21_get_hdcp_mode() == 0) {
pr_info("hdmi ready but hdcp not enabled, enable now\n");
if (hdcp_need_control_by_upstream(hdev))
pr_info("hdmitx: currently hdcp should started by upstream\n");
else
hdmitx21_enable_hdcp(hdev);
}
break;
default:
rtn_val = -EPERM;
break;
}
return rtn_val;
}
static const struct file_operations hdcp_comm_file_operations = {
.owner = THIS_MODULE,
.unlocked_ioctl = hdcp_comm_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = hdcp_comm_ioctl,
#endif
};
static void tee_comm_dev_reg(struct hdmitx_dev *hdev)
{
int ret;
hdev->hdcp_comm_device.minor = MISC_DYNAMIC_MINOR;
hdev->hdcp_comm_device.name = "tee_comm_hdcp";
hdev->hdcp_comm_device.fops = &hdcp_comm_file_operations;
ret = misc_register(&hdev->hdcp_comm_device);
if (ret < 0)
pr_err("%s misc_register fail\n", __func__);
}
static void tee_comm_dev_unreg(struct hdmitx_dev *hdev)
{
misc_deregister(&hdev->hdcp_comm_device);
}
/****** tee_hdcp key related end ******/