blob: 9179bcaa08d3d6fe6b922c3c9ed895c719c106f4 [file] [log] [blame]
// 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/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 "hdmi_tx_ext.h"
#include "hdmi_tx.h"
#include <linux/amlogic/gki_module.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 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 hdmitx_register_vrr(struct hdmitx_dev *hdev);
static void hdmitx_unregister_vrr(struct hdmitx_dev *hdev);
#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 const struct of_device_id meson_amhdmitx_of_match[] = {
{
.compatible = "amlogic, amhdmitx-t7",
.data = &amhdmitx_data_t7,
},
{},
};
#else
#define meson_amhdmitx_dt_match NULL
#endif
static DEFINE_MUTEX(setclk_mutex);
static DEFINE_MUTEX(getedid_mutex);
static struct hdmitx_dev hdmitx21_device = {
.frac_rate_policy = 1,
};
struct hdmitx_dev *get_hdmitx21_device(void)
{
return &hdmitx21_device;
}
EXPORT_SYMBOL(get_hdmitx21_device);
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 int log21_level;
static struct vout_device_s hdmitx_vdev = {
.dv_info = &hdmitx21_device.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 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,
},
};
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);
/* 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)
{
if (hpd)
hdmitx21_event_notify(HDMITX_PLUG, NULL);
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;
hdev->ready = 0;
hdev->hpd_lock = 1;
hdev->hwop.cntlmisc(hdev, MISC_SUSFLAG, 1);
usleep_range(10000, 10010);
hdev->hwop.cntlmisc(hdev, MISC_AVMUTE_OP, SET_AVMUTE);
usleep_range(10000, 10010);
pr_info("HDMITX: Early Suspend\n");
hdev->hdcp_mode = 0;
hdcp_mode_set(0);
hdev->hwop.cntl(hdev, HDMITX_EARLY_SUSPEND_RESUME_CNTL,
HDMITX_EARLY_SUSPEND);
hdev->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);
hdmitx21_set_uevent(HDMITX_HDCPPWR_EVENT, HDMI_SUSPEND);
hdev->hwop.cntlconfig(hdev, CONF_CLR_AVI_PACKET, 0);
hdev->hwop.cntlconfig(hdev, CONF_CLR_VSDB_PACKET, 0);
}
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 (info && info->name && (hdmitx_is_hdmi_vmode(info->name) == 1))
hdev->hwop.cntlmisc(hdev, MISC_HPLL_FAKE, 0);
hdev->hpd_lock = 0;
/* update status for hpd and switch/state */
hdev->hpd_state = !!(hdev->hwop.cntlmisc(hdev, MISC_HPD_GPI_ST, 0));
if (hdev->hpd_state)
hdev->already_used = 1;
pr_info("hdmitx hpd state: %d\n", hdev->hpd_state);
hdmitx_notify_hpd(hdev->hpd_state);
/*force to get EDID after resume for Amplifier Power case*/
if (hdev->hpd_state)
hdmitx_get_edid(hdev);
hdev->hwop.cntlconfig(hdev, CONF_AUDIO_MUTE_OP, AUDIO_MUTE);
set_disp_mode_auto();
hdmitx21_set_uevent(HDMITX_HPD_EVENT, hdev->hpd_state);
hdmitx21_set_uevent(HDMITX_HDCPPWR_EVENT, HDMI_WAKEUP);
hdmitx21_set_uevent(HDMITX_AUDIO_EVENT, hdev->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->hwop.cntlmisc(hdev, MISC_SUSFLAG, 0);
pr_info("HDMITX: Late Resume\n");
}
/* 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;
hdev->hwop.cntlmisc(hdev, MISC_AVMUTE_OP, SET_AVMUTE);
usleep_range(10000, 10010);
hdcp_mode_set(0);
hdev->hwop.cntlmisc(hdev, 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
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->cur_VIC = HDMI_0_UNKNOWN;
ret = hdmitx21_set_display(hdev, vic);
if (ret >= 0) {
hdev->hwop.cntl(hdev, HDMITX_AVMUTE_CNTL, AVMUTE_CLEAR);
hdev->cur_VIC = vic;
hdev->audio_param_update_flag = 1;
}
if (hdev->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->rxcap.physical_width;
height = hdev->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 vinfo_s *info, struct hdmitx_dev *hdev)
{
memcpy(&info->hdr_info, &hdev->rxcap.hdr_info, sizeof(struct hdr_info));
info->hdr_info.colorimetry_support = hdev->rxcap.colorimetry_data;
pr_info("update rx hdr info %x\n", info->hdr_info.hdr_support);
}
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, hdev);
if (hdev->para && hdev->para->cd == COLORDEPTH_24B)
memset(&info->hdr_info, 0, sizeof(struct hdr_info));
rxlatency_to_vinfo(info, &hdev->rxcap);
hdmitx_vdev.dv_info = &hdev->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 int set_disp_mode_auto(void)
{
int ret = -1;
struct vinfo_s *info = NULL;
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct hdmi_format_para *para = NULL;
u8 mode[32];
enum hdmi_vic vic = HDMI_0_UNKNOWN;
memset(mode, 0, sizeof(mode));
hdev->ready = 0;
/* get current vinfo */
info = hdmitx_get_current_vinfo(NULL);
if (!info || !info->name)
return -1;
pr_info("hdmitx: get current mode: %s\n", info->name);
if (strncmp(info->name, "invalid", strlen("invalid")) == 0) {
hdev->hdcp_mode = 0;
hdcp_mode_set(0);
return -1;
}
/*update hdmi checksum to vout*/
memcpy(info->hdmichecksum, hdev->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, hdev->fmt_attr);
if (!para) {
pr_info("%s[%d] %s %s\n", __func__, __LINE__, mode,
hdev->fmt_attr);
return -1;
}
pr_info("setting hdmi mode %s %s\n", mode, hdev->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 */
}
hdev->cur_VIC = HDMI_0_UNKNOWN;
/* if vic is HDMI_0_UNKNOWN, hdmitx21_set_display will disable HDMI */
edidinfo_detach_to_vinfo(hdev);
ret = hdmitx21_set_display(hdev, vic);
if (ret >= 0) {
hdev->hwop.cntl(hdev, HDMITX_AVMUTE_CNTL, AVMUTE_CLEAR);
hdev->cur_VIC = vic;
hdev->audio_param_update_flag = 1;
}
if (hdev->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;
hdev->ready = 1;
edidinfo_attach_to_vinfo(hdev);
if (get_hdcp2_lstore() & is_rx_hdcp2ver()) {
hdev->hdcp_mode = 2;
hdcp_mode_set(2);
} else {
if (get_hdcp1_lstore()) {
hdev->hdcp_mode = 1;
hdcp_mode_set(1);
} else {
hdev->hdcp_mode = 0;
}
}
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;
}
static ssize_t attr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (!memcmp(hdev->fmt_attr, "default,", 7)) {
memset(hdev->fmt_attr, 0,
sizeof(hdev->fmt_attr));
hdmitx21_fmt_attr(hdev);
}
pos += snprintf(buf + pos, PAGE_SIZE, "%s\n\r", hdev->fmt_attr);
return pos;
}
static ssize_t attr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
strncpy(hdev->fmt_attr, buf, sizeof(hdev->fmt_attr));
hdev->fmt_attr[15] = '\0';
if (!memcmp(hdev->fmt_attr, "rgb", 3))
hdev->para->cs = HDMI_COLORSPACE_RGB;
else if (!memcmp(hdev->fmt_attr, "422", 3))
hdev->para->cs = HDMI_COLORSPACE_YUV422;
else if (!memcmp(hdev->fmt_attr, "420", 3))
hdev->para->cs = HDMI_COLORSPACE_YUV420;
else
hdev->para->cs = HDMI_COLORSPACE_YUV444;
return count;
}
/*aud_mode attr*/
void setup21_attr(const char *buf)
{
char attr[16] = {0};
struct hdmitx_dev *hdev = get_hdmitx21_device();
memcpy(attr, buf, sizeof(attr));
memcpy(hdev->fmt_attr, attr, sizeof(hdev->fmt_attr));
}
void get21_attr(char attr[16])
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
memcpy(attr, hdev->fmt_attr, sizeof(hdev->fmt_attr));
}
/*edid attr*/
static ssize_t edid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
return hdmitx21_edid_dump(hdev, buf, PAGE_SIZE);
}
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 (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;
}
static ssize_t edid_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
u32 argn = 0;
char *p = NULL, *para = NULL, *argv[8] = {NULL};
u32 path_length = 0;
u32 index = 0, tmp = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
p = kstrdup(buf, GFP_KERNEL);
if (!p)
return count;
do {
para = strsep(&p, " ");
if (para) {
argv[argn] = para;
argn++;
if (argn > 7)
break;
}
} while (para);
if (buf[0] == 'h') {
int i;
pr_info("EDID hash value:\n");
for (i = 0; i < 20; i++)
pr_info("%02x", hdev->EDID_hash[i]);
pr_info("\n");
}
if (buf[0] == 'd') {
int ii, jj;
unsigned long block_idx;
int ret;
ret = kstrtoul(buf + 1, 16, &block_idx);
if (block_idx < EDID_MAX_BLOCK) {
for (ii = 0; ii < 8; ii++) {
for (jj = 0; jj < 16; jj++) {
index = block_idx * 128 + ii * 16 + jj;
tmp = hdev->EDID_buf1[index];
pr_info("%02x ", tmp);
}
pr_info("\n");
}
pr_info("\n");
}
}
if (buf[0] == 'e') {
int ii, jj;
unsigned long block_idx;
int ret;
ret = kstrtoul(buf + 1, 16, &block_idx);
if (block_idx < EDID_MAX_BLOCK) {
for (ii = 0; ii < 8; ii++) {
for (jj = 0; jj < 16; jj++) {
index = block_idx * 128 + ii * 16 + jj;
tmp = hdev->EDID_buf1[index];
pr_info("%02x ", tmp);
}
pr_info("\n");
}
pr_info("\n");
}
}
if (!strncmp(argv[0], "save", strlen("save"))) {
u32 type = 0;
if (argn != 3) {
pr_info("[%s] cmd format: save bin/txt edid_file_path\n",
__func__);
goto PROCESS_END;
}
if (!strncmp(argv[1], "bin", strlen("bin")))
type = 1;
else if (!strncmp(argv[1], "txt", strlen("txt")))
type = 2;
if (type == 1 || type == 2) {
/* clean '\n' from file path*/
path_length = strlen(argv[2]);
if (argv[2][path_length - 1] == '\n')
argv[2][path_length - 1] = 0x0;
dump_edid_data(type, argv[2]);
}
} else if (!strncmp(argv[0], "load", strlen("load"))) {
if (argn != 2) {
pr_info("[%s] cmd format: load edid_file_path\n",
__func__);
goto PROCESS_END;
}
/* clean '\n' from file path*/
path_length = strlen(argv[1]);
if (argv[1][path_length - 1] == '\n')
argv[1][path_length - 1] = 0x0;
load_edid_data(0, argv[1]);
}
PROCESS_END:
kfree(p);
return 16;
}
/* rawedid attr */
static ssize_t rawedid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
int i;
struct hdmitx_dev *hdev = get_hdmitx21_device();
int num;
/* prevent null prt */
if (!hdev->edid_ptr)
hdev->edid_ptr = hdev->EDID_buf;
if (hdev->edid_ptr[0x7e] < 4)
num = (hdev->edid_ptr[0x7e] + 1) * 0x80;
else
num = 0x100;
for (i = 0; i < num; i++)
pos += snprintf(buf + pos, PAGE_SIZE, "%02x",
hdev->edid_ptr[i]);
pos += snprintf(buf + pos, PAGE_SIZE, "\n");
return pos;
}
/*
* edid_parsing attr
* If RX edid data are all correct, HEAD(00ffffffffffff00), checksum,
* version, etc), then return "ok". Otherwise, "ng"
* Actually, in some old televisions, EDID is stored in EEPROM.
* some bits in EEPROM may reverse with time.
* But it does not affect edid_parsing.
* Therefore, we consider the RX edid data are all correct, return "OK"
*/
static ssize_t edid_parsing_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "ok\n");
return pos;
}
/*
* 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->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;
}
void hdmitx21_audio_mute_op(u32 flag)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
hdev->tx_aud_cfg = flag;
if (flag == 0)
hdev->hwop.cntlconfig(hdev, CONF_AUDIO_MUTE_OP, AUDIO_MUTE);
else
hdev->hwop.cntlconfig(hdev, CONF_AUDIO_MUTE_OP, AUDIO_UNMUTE);
}
void hdmitx21_video_mute_op(u32 flag)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (flag == 0)
hdev->hwop.cntlconfig(hdev, CONF_VIDEO_MUTE_OP, VIDEO_MUTE);
else
hdev->hwop.cntlconfig(hdev, CONF_VIDEO_MUTE_OP, VIDEO_UNMUTE);
}
/*
* 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 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);
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)
/* Init drm_db[0] from Uboot status */
static void init_drm_db0(struct hdmitx_dev *hdev, u8 *dat)
{
static int once_flag = 1;
if (once_flag) {
once_flag = 0;
*dat = hdev->hwop.getstate(hdev, STAT_HDR_TYPE, 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;
}
#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->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);
init_drm_db0(hdev, &drm_db[0]);
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);
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->hwop.cntlconfig(hdev, CONF_CLR_DV_VS10_SIG, 0) == 0)
hdmi_vend_infoframe_set(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->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->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->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];
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, hdev->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->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 || hdev->rxcap.dv_info.ieeeoui
!= DV_IEEE_OUI) {
ltype = EOTF_T_NULL;
ltmode = -1;
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
return;
}
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->rxcap.dv_info.ver == 0 ||
(hdev->rxcap.dv_info.ver == 1 &&
hdev->rxcap.dv_info.length == 0xE) ||
(hdev->rxcap.dv_info.ver == 1 &&
hdev->rxcap.dv_info.length == 0xB &&
hdev->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) {
/*first disable drm package*/
hdmi_drm_infoframe_set(NULL);
hdmi_vend_infoframe_rawset(ven_hb, db1);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, CLR_AVI_BT2020);/*BT709*/
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);
if (signal_sdr) {
pr_info("hdmitx: 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*/
}
}
}
/*ver1_12 with low_latency = 1 and ver2 use Dolby VSIF*/
if (hdev->rxcap.dv_info.ver == 2 ||
(hdev->rxcap.dv_info.ver == 1 &&
hdev->rxcap.dv_info.length == 0xB &&
hdev->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;
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);
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) {
/*first disable drm package*/
hdmi_drm_infoframe_set(NULL);
hdmi_vend_infoframe_rawset(ven_hb, db2);
hdmi_avi_infoframe_config(CONF_AVI_BT2020, CLR_AVI_BT2020);/*BT709*/
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) {
/*first disable drm package*/
hdmi_drm_infoframe_set(NULL);
hdmi_vend_infoframe_rawset(ven_hb, db2);
if (hdev->rxcap.colorimetry_data & 0xe0)
/*if RX support BT2020, then output BT2020*/
hdmi_avi_infoframe_config(CONF_AVI_BT2020, SET_AVI_BT2020);
else
hdmi_avi_infoframe_config(CONF_AVI_BT2020, CLR_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*/
}
}
}
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_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);
}
struct hdmi_packet_t {
u8 hb[3];
u8 pb[28];
u8 no_used; /* padding to 32 bytes */
};
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();
static unsigned char *virt_ptr;
static unsigned char *virt_ptr_align32bit;
unsigned long phys_ptr;
if (!_check_hdmi_mode())
return;
memset(vs_emds, 0, sizeof(vs_emds));
spin_lock_irqsave(&hdev->edid_spinlock, flags);
if (!data) {
hdev->hwop.cntlconfig(hdev, CONF_EMP_NUMBER, 0);
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
return;
}
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;
if (!virt_ptr) { /* init virt_ptr and virt_ptr_align32bit */
virt_ptr = kzalloc((sizeof(vs_emds) + 0x1f), GFP_KERNEL);
virt_ptr_align32bit = (unsigned char *)
((((unsigned long)virt_ptr) + 0x1f) & (~0x1f));
}
memcpy(virt_ptr_align32bit, vs_emds, sizeof(vs_emds));
phys_ptr = virt_to_phys(virt_ptr_align32bit);
pr_info("emp_pkt phys_ptr: %lx\n", phys_ptr);
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
}
#define EMP_FIRST 0x80
#define EMP_LAST 0x40
static void hdmitx_set_emp_pkt(u8 *data, u32 type,
u32 size)
{
u32 number;
u32 remainder;
u8 *virt_ptr;
u8 *virt_ptr_align32bit;
unsigned long phys_ptr;
u32 i;
struct hdmitx_dev *hdev = get_hdmitx21_device();
u32 ds_type = 0;
u8 AFR = 0;
u8 VFR = 0;
u8 sync = 0;
u8 new = 0;
u8 end = 0;
u32 organzation_id = 0;
u32 data_set_tag = 0;
u32 data_set_length = 0;
if (!_check_hdmi_mode())
return;
hdmi_debug();
if (!data) {
pr_info("the data is null\n");
return;
}
if (size <= 21) {
number = 1;
remainder = size;
} else {
number = ((size - 21) / 28) + 2;
remainder = (size - 21) % 28;
}
virt_ptr = kzalloc(sizeof(unsigned char) * (number + 0x1f),
GFP_KERNEL);
if (!virt_ptr)
return;
pr_info("emp_pkt virt_ptr: %p\n", virt_ptr);
virt_ptr_align32bit = (u8 *)
((((unsigned long)virt_ptr) + 0x1f) & (~0x1f));
pr_info("emp_pkt virt_ptr_align32bit: %p\n", virt_ptr_align32bit);
memset(virt_ptr_align32bit, 0, sizeof(unsigned char) * (number + 0x1f));
switch (type) {
case VENDOR_SPECIFIC_EM_DATA:
break;
case COMPRESS_VIDEO_TRAMSPORT:
break;
case HDR_DYNAMIC_METADATA:
ds_type = 1;
sync = 1;
VFR = 1;
AFR = 0;
new = 0x1; /*todo*/
end = 0x1; /*todo*/
organzation_id = 2;
break;
case VIDEO_TIMING_EXTENDED:
break;
default:
break;
}
for (i = 0; i < number; i++) {
/*HB[0]-[2]*/
virt_ptr_align32bit[i * 32 + 0] = 0x7F;
if (i == 0)
virt_ptr_align32bit[i * 32 + 1] |= EMP_FIRST;
if (i == number)
virt_ptr_align32bit[i * 32 + 1] |= EMP_LAST;
virt_ptr_align32bit[i * 32 + 2] = number;
/*PB[0]-[6]*/
if (i == 0) {
virt_ptr_align32bit[3] = (new << 7) | (end << 6) |
(ds_type << 4) | (AFR << 3) |
(VFR << 2) | (sync << 1);
virt_ptr_align32bit[4] = 0;/*Rsvd*/
virt_ptr_align32bit[5] = organzation_id;
virt_ptr_align32bit[6] = (data_set_tag >> 8) & 0xFF;
virt_ptr_align32bit[7] = data_set_tag & 0xFF;
virt_ptr_align32bit[8] = (data_set_length >> 8)
& 0xFF;
virt_ptr_align32bit[9] = data_set_length & 0xFF;
}
if (number == 1) {
memcpy(&virt_ptr_align32bit[10], &data[0],
sizeof(unsigned char) * remainder);
} else {
if (i == 0) {
/*MD: first package need PB[7]-[27]*/
memcpy(&virt_ptr_align32bit[10], &data[0],
sizeof(unsigned char) * 21);
} else if (i != number) {
/*MD: following package need PB[0]-[27]*/
memcpy(&virt_ptr_align32bit[i * 32 + 10],
&data[(i - 1) * 28 + 21],
sizeof(unsigned char) * 28);
} else {
/*MD: the last package need PB[0] to end */
memcpy(&virt_ptr_align32bit[0],
&data[(i - 1) * 28 + 21],
sizeof(unsigned char) * remainder);
}
}
/*PB[28]*/
virt_ptr_align32bit[i * 32 + 31] = 0;
}
phys_ptr = virt_to_phys(virt_ptr_align32bit);
pr_info("emp_pkt phys_ptr: %lx\n", phys_ptr);
hdev->hwop.cntlconfig(hdev, CONF_EMP_NUMBER, number);
hdev->hwop.cntlconfig(hdev, CONF_EMP_PHY_ADDR, phys_ptr);
}
/*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->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_DOLBY_D:
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();
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);
}
return count;
}
void hdmitx21_ext_set_audio_output(int enable)
{
hdmitx21_audio_mute_op(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 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);
if (atomic_read(&kref_video_mute) == 1)
hdmitx21_video_mute_op(0);
}
if (buf[0] == '0') {
if (!(atomic_sub_and_test(0, &kref_video_mute))) {
atomic_dec(&kref_video_mute);
if (atomic_sub_and_test(0, &kref_video_mute))
hdmitx21_video_mute_op(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 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->rxcap;
const struct hdmi_timing *timing = NULL;
enum hdmi_vic vic;
for (i = 0; i < prxcap->VIC_count; i++) {
vic = prxcap->VIC[i];
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");
}
}
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->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");
}
/**/
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",
"Dobly_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->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++) {
pos += snprintf(buf + pos, PAGE_SIZE, "%s",
aud_ct[prxcap->RxAudioCap[i].audio_format_code]);
if (prxcap->RxAudioCap[i].audio_format_code == CT_DOLBY_D &&
(prxcap->RxAudioCap[i].cc3 & 1))
pos += snprintf(buf + pos, PAGE_SIZE, "/ATMOS");
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, ");
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_DOLBY_D:
case CT_DTS_HD:
case CT_MAT:
case CT_DST:
pos += snprintf(buf + pos, PAGE_SIZE, "DepVaule 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 hdmi_hdr_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
/* 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 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->rxcap;
const struct dv_info *dv = &hdev->rxcap.dv_info;
const struct dv_info *dv2 = &hdev->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");
pos += snprintf(buf + pos, PAGE_SIZE, "420,8bit\n");
} else {
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->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");
}
if (prxcap->dc_36bit || dv->sup_yuv422_12bit ||
dv2->sup_yuv422_12bit)
if (!hdev->vend_id_hit)
pos += snprintf(buf + pos, PAGE_SIZE, "422,12bit\n");
} else {
if (prxcap->native_Mode & (1 << 5))
pos += snprintf(buf + pos, PAGE_SIZE, "444,8bit\n");
if (prxcap->native_Mode & (1 << 4))
pos += snprintf(buf + pos, PAGE_SIZE, "422,12bit\n");
}
//nextrgb:
if (prxcap->dc_36bit || dv->sup_10b_12b_444 == 0x2 ||
dv2->sup_10b_12b_444 == 0x2)
if (!hdev->vend_id_hit)
;
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,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->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->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();
pr_info("hdmitx: store allm_mode as %s\n", buf);
if (com_str(buf, "0")) {
// disable ALLM
hdev->allm_mode = 0;
hdmitx21_construct_vsif(hdev, VT_ALLM, 0, NULL);
if (_is_hdmi14_4k(hdev->cur_VIC))
hdmitx21_construct_vsif(hdev, VT_HDMI14_4K, 1, NULL);
}
if (com_str(buf, "1")) {
hdev->allm_mode = 1;
hdmitx21_construct_vsif(hdev, VT_ALLM, 1, NULL);
hdev->hwop.cntlconfig(hdev, CONF_CT_MODE, SET_CT_OFF);
}
if (com_str(buf, "-1")) {
if (hdev->allm_mode == 1) {
hdev->allm_mode = 0;
hdmi_vend_infoframe_set(NULL);
}
}
return count;
}
static ssize_t contenttype_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->rxcap;
if (prxcap->cnc0)
pos += snprintf(buf + pos, PAGE_SIZE, "graphics\n\r");
if (prxcap->cnc1)
pos += snprintf(buf + pos, PAGE_SIZE, "photo\n\r");
if (prxcap->cnc2)
pos += snprintf(buf + pos, PAGE_SIZE, "cinema\n\r");
if (prxcap->cnc3)
pos += snprintf(buf + pos, PAGE_SIZE, "game\n\r");
return pos;
}
static ssize_t contenttype_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
if (hdev->ct_mode == 0)
pos += snprintf(buf + pos, PAGE_SIZE, "off\n\r");
if (hdev->ct_mode == 1)
pos += snprintf(buf + pos, PAGE_SIZE, "game\n\r");
if (hdev->ct_mode == 2)
pos += snprintf(buf + pos, PAGE_SIZE, "graphics\n\r");
if (hdev->ct_mode == 3)
pos += snprintf(buf + pos, PAGE_SIZE, "photo\n\r");
if (hdev->ct_mode == 4)
pos += snprintf(buf + pos, PAGE_SIZE, "cinema\n\r");
return pos;
}
static ssize_t contenttype_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 contenttype_mode as %s\n", buf);
if (hdev->allm_mode == 1) {
hdev->allm_mode = 0;
hdmitx21_construct_vsif(hdev, VT_ALLM, 0, NULL);
}
if (_is_hdmi14_4k(hdev->cur_VIC))
hdmitx21_construct_vsif(hdev, VT_HDMI14_4K, 1, NULL);
hdev->ct_mode = 0;
hdev->hwop.cntlconfig(hdev, CONF_CT_MODE, SET_CT_OFF);
if (com_str(buf, "1") || com_str(buf, "game")) {
hdev->ct_mode = 1;
hdev->hwop.cntlconfig(hdev, CONF_CT_MODE, SET_CT_GAME);
}
if (com_str(buf, "2") || com_str(buf, "graphics")) {
hdev->ct_mode = 2;
hdev->hwop.cntlconfig(hdev, CONF_CT_MODE, SET_CT_GRAPHICS);
}
if (com_str(buf, "3") || com_str(buf, "photo")) {
hdev->ct_mode = 3;
hdev->hwop.cntlconfig(hdev, CONF_CT_MODE, SET_CT_PHOTO);
}
if (com_str(buf, "4") || com_str(buf, "cinema")) {
hdev->ct_mode = 4;
hdev->hwop.cntlconfig(hdev, CONF_CT_MODE, SET_CT_CINEMA);
}
return count;
}
/**/
static ssize_t _hdr_cap_show(struct device *dev,
struct device_attribute *attr,
char *buf,
const struct hdr_info *hdr)
{
int pos = 0;
unsigned int i, j;
int hdr10plugsupported = 0;
const struct cuva_info *cuva = &hdr->cuva_info;
const struct hdr10_plus_info *hdr10p = &hdr->hdr10plus_info;
if (hdr10p->ieeeoui == HDR10_PLUS_IEEE_OUI &&
hdr10p->application_version != 0xFF)
hdr10plugsupported = 1;
pos += snprintf(buf + pos, PAGE_SIZE, "HDR10Plus Supported: %d\n",
hdr10plugsupported);
pos += snprintf(buf + pos, PAGE_SIZE, "HDR Static Metadata:\n");
pos += snprintf(buf + pos, PAGE_SIZE, " Supported EOTF:\n");
pos += snprintf(buf + pos, PAGE_SIZE, " Traditional SDR: %d\n",
!!(hdr->hdr_support & 0x1));
pos += snprintf(buf + pos, PAGE_SIZE, " Traditional HDR: %d\n",
!!(hdr->hdr_support & 0x2));
pos += snprintf(buf + pos, PAGE_SIZE, " SMPTE ST 2084: %d\n",
!!(hdr->hdr_support & 0x4));
pos += snprintf(buf + pos, PAGE_SIZE, " Hybrid Log-Gamma: %d\n",
!!(hdr->hdr_support & 0x8));
pos += snprintf(buf + pos, PAGE_SIZE, " Supported SMD type1: %d\n",
hdr->static_metadata_type1);
pos += snprintf(buf + pos, PAGE_SIZE, " Luminance Data\n");
pos += snprintf(buf + pos, PAGE_SIZE, " Max: %d\n",
hdr->lumi_max);
pos += snprintf(buf + pos, PAGE_SIZE, " Avg: %d\n",
hdr->lumi_avg);
pos += snprintf(buf + pos, PAGE_SIZE, " Min: %d\n\n",
hdr->lumi_min);
pos += snprintf(buf + pos, PAGE_SIZE, "HDR Dynamic Metadata:");
for (i = 0; i < 4; i++) {
if (hdr->dynamic_info[i].type == 0)
continue;
pos += snprintf(buf + pos, PAGE_SIZE,
"\n metadata_version: %x\n",
hdr->dynamic_info[i].type);
pos += snprintf(buf + pos, PAGE_SIZE,
" support_flags: %x\n",
hdr->dynamic_info[i].support_flags);
pos += snprintf(buf + pos, PAGE_SIZE,
" optional_fields:");
for (j = 0; j <
(hdr->dynamic_info[i].of_len - 3); j++)
pos += snprintf(buf + pos, PAGE_SIZE, " %x",
hdr->dynamic_info[i].optional_fields[j]);
}
pos += snprintf(buf + pos, PAGE_SIZE, "\n\ncolorimetry_data: %x\n",
hdr->colorimetry_support);
if (cuva->ieeeoui == CUVA_IEEEOUI) {
pos += snprintf(buf + pos, PAGE_SIZE, "CUVA supported: 1\n");
pos += snprintf(buf + pos, PAGE_SIZE,
" system_start_code: %u\n", cuva->system_start_code);
pos += snprintf(buf + pos, PAGE_SIZE,
" version_code: %u\n", cuva->version_code);
pos += snprintf(buf + pos, PAGE_SIZE,
" display_maximum_luminance: %u\n",
cuva->display_max_lum);
pos += snprintf(buf + pos, PAGE_SIZE,
" display_minimum_luminance: %u\n",
cuva->display_min_lum);
pos += snprintf(buf + pos, PAGE_SIZE,
" monitor_mode_support: %u\n", cuva->monitor_mode_sup);
pos += snprintf(buf + pos, PAGE_SIZE,
" rx_mode_support: %u\n", cuva->rx_mode_sup);
for (i = 0; i < (cuva->length + 1); i++)
pos += snprintf(buf + pos, PAGE_SIZE, "%02x",
cuva->rawdata[i]);
pos += snprintf(buf + pos, PAGE_SIZE, "\n");
}
return pos;
}
static ssize_t hdr_cap_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
const struct hdr_info *info = &hdev->rxcap.hdr_info;
if (hdev->hdr_priority == 2) {
pos += snprintf(buf + pos, PAGE_SIZE,
"mask rx hdr capability\n");
return pos;
}
return _hdr_cap_show(dev, attr, buf, info);
}
static ssize_t hdr_cap2_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
const struct hdr_info *info2 = &hdev->rxcap.hdr_info2;
return _hdr_cap_show(dev, attr, buf, info2);
}
static ssize_t vrr_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->rxcap;
struct vrr_device_s *vrr = &hdev->hdmitx_vrr_dev;
pos += snprintf(buf + pos, PAGE_SIZE,
"neg_mvrr: %d\n", prxcap->neg_mvrr);
pos += snprintf(buf + pos, PAGE_SIZE,
"fva: %d\n", prxcap->fva);
pos += snprintf(buf + pos, PAGE_SIZE,
"allm: %d\n", prxcap->allm);
pos += snprintf(buf + pos, PAGE_SIZE,
"fapa_start_location: %d\n", prxcap->fapa_start_loc);
pos += snprintf(buf + pos, PAGE_SIZE,
"qms: %d\n", prxcap->qms);
pos += snprintf(buf + pos, PAGE_SIZE,
"qms_tfr_max: %d\n", prxcap->qms_tfr_max);
pos += snprintf(buf + pos, PAGE_SIZE,
"qms_tfr_min: %d\n", prxcap->qms_tfr_min);
pos += snprintf(buf + pos, PAGE_SIZE,
"mdelta: %d\n", prxcap->mdelta);
pos += snprintf(buf + pos, PAGE_SIZE,
"RX_CAP vrr_max: %d\n", prxcap->vrr_max);
pos += snprintf(buf + pos, PAGE_SIZE,
"RX_CAP vrr_min: %d\n", prxcap->vrr_min);
pos += snprintf(buf + pos, PAGE_SIZE,
"vrr.vfreq_max: %d\n", vrr->vfreq_max);
pos += snprintf(buf + pos, PAGE_SIZE,
"vrr.vfreq_min: %d\n", vrr->vfreq_min);
pos += snprintf(buf + pos, PAGE_SIZE,
"vrr.vline_max: %d\n", vrr->vline_max);
pos += snprintf(buf + pos, PAGE_SIZE,
"vrr.vline_min: %d\n", vrr->vline_min);
return pos;
}
static ssize_t _show_dv_cap(struct device *dev,
struct device_attribute *attr,
char *buf,
const struct dv_info *dv)
{
int pos = 0;
int i;
if (dv->ieeeoui != DV_IEEE_OUI) {
pos += snprintf(buf + pos, PAGE_SIZE,
"The Rx don't support DolbyVision\n");
return pos;
}
pos += snprintf(buf + pos, PAGE_SIZE,
"DolbyVision RX support list:\n");
if (dv->ver == 0) {
pos += snprintf(buf + pos, PAGE_SIZE,
"VSVDB Version: V%d\n", dv->ver);
pos += snprintf(buf + pos, PAGE_SIZE,
"2160p%shz: 1\n",
dv->sup_2160p60hz ? "60" : "30");
pos += snprintf(buf + pos, PAGE_SIZE,
"Support mode:\n");
pos += snprintf(buf + pos, PAGE_SIZE,
" DV_RGB_444_8BIT\n");
if (dv->sup_yuv422_12bit)
pos += snprintf(buf + pos, PAGE_SIZE,
" DV_YCbCr_422_12BIT\n");
}
if (dv->ver == 1) {
pos += snprintf(buf + pos, PAGE_SIZE,
"VSVDB Version: V%d(%d-byte)\n",
dv->ver, dv->length + 1);
if (dv->length == 0xB) {
pos += snprintf(buf + pos, PAGE_SIZE,
"2160p%shz: 1\n",
dv->sup_2160p60hz ? "60" : "30");
pos += snprintf(buf + pos, PAGE_SIZE,
"Support mode:\n");
pos += snprintf(buf + pos, PAGE_SIZE,
" DV_RGB_444_8BIT\n");
if (dv->sup_yuv422_12bit)
pos += snprintf(buf + pos, PAGE_SIZE,
" DV_YCbCr_422_12BIT\n");
if (dv->low_latency == 0x01)
pos += snprintf(buf + pos, PAGE_SIZE,
" LL_YCbCr_422_12BIT\n");
}
if (dv->length == 0xE) {
pos += snprintf(buf + pos, PAGE_SIZE,
"2160p%shz: 1\n",
dv->sup_2160p60hz ? "60" : "30");
pos += snprintf(buf + pos, PAGE_SIZE,
"Support mode:\n");
pos += snprintf(buf + pos, PAGE_SIZE,
" DV_RGB_444_8BIT\n");
if (dv->sup_yuv422_12bit)
pos += snprintf(buf + pos, PAGE_SIZE,
" DV_YCbCr_422_12BIT\n");
}
}
if (dv->ver == 2) {
pos += snprintf(buf + pos, PAGE_SIZE,
"VSVDB Version: V%d\n", dv->ver);
pos += snprintf(buf + pos, PAGE_SIZE,
"2160p%shz: 1\n",
dv->sup_2160p60hz ? "60" : "30");
pos += snprintf(buf + pos, PAGE_SIZE,
"Support mode:\n");
if (dv->Interface != 0x00 && dv->Interface != 0x01) {
pos += snprintf(buf + pos, PAGE_SIZE,
" DV_RGB_444_8BIT\n");
if (dv->sup_yuv422_12bit)
pos += snprintf(buf + pos, PAGE_SIZE,
" DV_YCbCr_422_12BIT\n");
}
pos += snprintf(buf + pos, PAGE_SIZE,
" LL_YCbCr_422_12BIT\n");
if (dv->Interface == 0x01 || dv->Interface == 0x03) {
if (dv->sup_10b_12b_444 == 0x1) {
pos += snprintf(buf + pos, PAGE_SIZE,
" LL_RGB_444_10BIT\n");
}
if (dv->sup_10b_12b_444 == 0x2) {
pos += snprintf(buf + pos, PAGE_SIZE,
" LL_RGB_444_12BIT\n");
}
}
}
pos += snprintf(buf + pos, PAGE_SIZE,
"IEEEOUI: 0x%06x\n", dv->ieeeoui);
pos += snprintf(buf + pos, PAGE_SIZE,
"EMP: %d\n", dv->dv_emp_cap);
pos += snprintf(buf + pos, PAGE_SIZE, "VSVDB: ");
for (i = 0; i < (dv->length + 1); i++)
pos += snprintf(buf + pos, PAGE_SIZE, "%02x",
dv->rawdata[i]);
pos += snprintf(buf + pos, PAGE_SIZE, "\n");
return pos;
}
static ssize_t dv_cap_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
const struct dv_info *dv = &hdev->rxcap.dv_info;
if (dv->ieeeoui != DV_IEEE_OUI || hdev->hdr_priority) {
pos += snprintf(buf + pos, PAGE_SIZE,
"The Rx don't support DolbyVision\n");
return pos;
}
return _show_dv_cap(dev, attr, buf, dv);
}
static ssize_t dv_cap2_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
const struct dv_info *dv2 = &hdev->rxcap.dv_info2;
return _show_dv_cap(dev, attr, buf, dv2);
}
/*
* 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;
struct hdmitx_dev *hdev = get_hdmitx21_device();
static int mask0;
static int mask1;
static DEFINE_MUTEX(avmute_mutex);
pr_info("%s %s\n", __func__, buf);
mutex_lock(&avmute_mutex);
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;
hdev->hwop.cntlmisc(hdev, MISC_AVMUTE_OP, cmd);
mutex_unlock(&avmute_mutex);
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->hwop.cntlmisc(hdev, MISC_READ_AVMUTE_OP, 0);
pos += snprintf(buf + pos, PAGE_SIZE, "%d", ret);
return pos;
}
/*
* 1: enable hdmitx phy
* 0: disable hdmitx phy
*/
static ssize_t phy_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int cmd = TMDS_PHY_ENABLE;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pr_info("%s %s\n", __func__, buf);
if (strncmp(buf, "0", 1) == 0)
cmd = TMDS_PHY_DISABLE;
else if (strncmp(buf, "1", 1) == 0)
cmd = TMDS_PHY_ENABLE;
else
pr_info("set phy wrong: %s\n", buf);
hdev->hwop.cntlmisc(hdev, MISC_TMDS_PHY_OP, cmd);
return count;
}
static ssize_t phy_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return 0;
}
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->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 ssize_t frac_rate_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("set frac_rate_policy as %d\n", val);
if (val == 0 || val == 1)
hdev->frac_rate_policy = val;
else
pr_info("only accept as 0 or 1\n");
}
return count;
}
static ssize_t frac_rate_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->frac_rate_policy);
return pos;
}
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 = 0;
if (get_hdcp2_lstore())
lstore |= BIT(1);
if (get_hdcp1_lstore())
lstore |= BIT(0);
if ((lstore & BIT(1)) && (lstore & BIT(0))) {
pos += snprintf(buf + pos, PAGE_SIZE, "22+14\n");
return pos;
}
if (lstore & BIT(1))
pos += snprintf(buf + pos, PAGE_SIZE, "22\n");
if (lstore & BIT(0))
pos += snprintf(buf + pos, PAGE_SIZE, "14\n");
return pos;
}
static ssize_t hdcp_lstore_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
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;
}
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] == '1');
hdev->div40 = (buf[0] == '1');
return count;
}
static ssize_t hdcp_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", hdev->hdcp_mode);
return pos;
}
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 = 0x11;
hdcp_mode_set(1);
}
if (strncmp(buf, "f2", 2) == 0) {
hdev->hdcp_mode = 0x12;
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;
}
#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 hpd_state_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->hpd_state);
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->hwop.cntlmisc(hdev, 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->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();
pr_info("hdmitx: fake plug %s\n", buf);
if (strncmp(buf, "1", 1) == 0)
hdev->hpd_state = 1;
if (strncmp(buf, "0", 1) == 0)
hdev->hpd_state = 0;
hdmitx21_set_uevent(HDMITX_HPD_EVENT, hdev->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->rxcap.threeD_present);
return pos;
}
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(attr);
static DEVICE_ATTR_RW(vid_mute);
static DEVICE_ATTR_RW(edid);
static DEVICE_ATTR_RO(rawedid);
static DEVICE_ATTR_RO(sink_type);
static DEVICE_ATTR_RO(edid_parsing);
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_RO(hdmi_hdr_status);
static DEVICE_ATTR_RO(hdr_cap);
static DEVICE_ATTR_RO(hdr_cap2);
static DEVICE_ATTR_RO(dv_cap);
static DEVICE_ATTR_RO(dv_cap2);
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_RO(contenttype_cap);
static DEVICE_ATTR_RW(contenttype_mode);
static DEVICE_ATTR_RW(avmute);
static DEVICE_ATTR_RW(phy);
static DEVICE_ATTR_RW(sspll);
static DEVICE_ATTR_RW(frac_rate_policy);
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_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(hpd_state);
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);
#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;
}
/* fr_tab[]
* 1080p24hz, 24:1
* 1080p23.976hz, 2997:125
* 25/50/100/200hz, no change
*/
static struct frac_rate_table fr_tab[] = {
{"24hz", 24, 1, 2997, 125},
{"30hz", 30, 1, 2997, 100},
{"60hz", 60, 1, 2997, 50},
{"120hz", 120, 1, 2997, 25},
{"240hz", 120, 1, 5994, 25},
{NULL},
};
static void recalc_vinfo_sync_duration(struct vinfo_s *info, u32 frac)
{
struct frac_rate_table *fr = &fr_tab[0];
pr_info("hdmitx: recalc before %s %d %d, frac %d\n", info->name,
info->sync_duration_num, info->sync_duration_den, info->frac);
while (fr->hz) {
if (strstr(info->name, fr->hz)) {
if (frac) {
info->sync_duration_num = fr->sync_num_dec;
info->sync_duration_den = fr->sync_den_dec;
info->frac = 1;
} else {
info->sync_duration_num = fr->sync_num_int;
info->sync_duration_den = fr->sync_den_int;
info->frac = 0;
}
break;
}
fr++;
}
pr_info("recalc after %s %d %d, frac %d\n", info->name,
info->sync_duration_num, info->sync_duration_den, info->frac);
}
static int hdmitx_set_current_vmode(enum vmode_e mode, void *data)
{
struct vinfo_s *vinfo;
struct hdmitx_dev *hdev = get_hdmitx21_device();
/* get current vinfo and refesh */
vinfo = hdmitx_get_current_vinfo(NULL);
if (vinfo && vinfo->name)
recalc_vinfo_sync_duration(vinfo,
hdev->frac_rate_policy);
hdmitx_register_vrr(hdev);
if (!(mode & VMODE_INIT_BIT_MASK)) {
set_disp_mode_auto();
} else {
pr_info("alread display in uboot\n");
update_current_para(hdev);
edidinfo_attach_to_vinfo(hdev);
}
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};
strncpy(mode, _mode, sizeof(mode));
mode[31] = 0;
para = hdmitx21_get_fmtpara(mode, hdev->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();
hdev->hwop.cntlconfig(hdev, CONF_CLR_AVI_PACKET, 0);
hdev->hwop.cntlconfig(hdev, CONF_CLR_VSDB_PACKET, 0);
hdev->hwop.cntlmisc(hdev, MISC_TMDS_PHY_OP, TMDS_PHY_DISABLE);
hdmitx21_disable_clk(hdev);
hdev->para = hdmitx21_get_fmtpara("invalid", hdev->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();
if (memcmp(hdev->backup_fmt_attr, hdev->fmt_attr, 16) == 0 &&
hdev->backup_frac_rate_policy == hdev->frac_rate_policy)
return 1;
memcpy(hdev->backup_fmt_attr, hdev->fmt_attr, 16);
hdev->backup_frac_rate_policy = hdev->frac_rate_policy;
return 0;
}
static int hdmitx_vout_get_disp_cap(char *buf, void *data)
{
return disp_cap_show(NULL, NULL, buf);
}
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 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 = 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_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
#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;
}
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 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->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 (audio_param->sample_rate != n_rate) {
audio_param->sample_rate = n_rate;
hdev->audio_param_update_flag = 1;
}
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;
}
if (audio_param->channel_num != (aud_param->chs - 1)) {
audio_param->channel_num = aud_param->chs - 1;
hdev->audio_param_update_flag = 1;
}
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->hwop.cntlconfig(hdev,
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->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->hwop.cntlmisc(hdev, 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)
{
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 frist 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(hdev->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->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->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 (hdev->hdr_priority == 1) { /* clear dv_info */
struct dv_info *dv = &hdev->rxcap.dv_info;
memset(dv, 0, sizeof(struct dv_info));
pr_info("clear dv_info\n");
}
if (hdev->hdr_priority == 2) { /* clear dv_info/hdr_info */
struct dv_info *dv = &hdev->rxcap.dv_info;
struct hdr_info *hdr = &hdev->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->hwop.cntlmisc(hdev, 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->hwop.cntlmisc(hdev, 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 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(&setclk_mutex);
hdev->already_used = 1;
if (!(hdev->hdmitx_event & (HDMI_TX_HPD_PLUGIN))) {
mutex_unlock(&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");
hdev->hwop.cntlmisc(hdev, MISC_I2C_RESET, 0);
hdev->hdmitx_event &= ~HDMI_TX_HPD_PLUGIN;
/* start reading E-EDID */
if (hdev->repeater_tx)
rx_repeat_hpd_state(1);
hdmitx_get_edid(hdev);
hdev->cedst_policy = hdev->cedst_en & hdev->rxcap.scdc_present;
hdmi_physical_size_update(hdev);
if (hdev->rxcap.ieeeoui != HDMI_IEEE_OUI)
hdev->hwop.cntlconfig(hdev,
CONF_HDMI_DVI_MODE, DVI_MODE);
else
hdev->hwop.cntlconfig(hdev,
CONF_HDMI_DVI_MODE, HDMI_MODE);
mutex_lock(&getedid_mutex);
mutex_unlock(&getedid_mutex);
if (hdev->repeater_tx) {
if (check_fbc_special(&hdev->EDID_buf[0]) ||
check_fbc_special(&hdev->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);
hdev->hpd_state = 1;
hdmitx_notify_hpd(hdev->hpd_state);
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(&setclk_mutex);
}
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->hwop.cntlmisc(hdev, 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);
mutex_lock(&setclk_mutex);
if (!(hdev->hdmitx_event & (HDMI_TX_HPD_PLUGOUT))) {
mutex_unlock(&setclk_mutex);
return;
}
if (hdev->cedst_policy)
cancel_delayed_work(&hdev->work_cedst);
edidinfo_detach_to_vinfo(hdev);
pr_info("plugout\n");
if (!!(hdev->hwop.cntlmisc(hdev, MISC_HPD_GPI_ST, 0))) {
pr_info("hpd gpio high\n");
hdev->hdmitx_event &= ~HDMI_TX_HPD_PLUGOUT;
mutex_unlock(&setclk_mutex);
return;
}
/*after plugout, DV mode can't be supported*/
hdmitx_set_vsif_pkt(0, 0, NULL, true);
hdmitx_set_hdr10plus_pkt(0, NULL);
hdev->ready = 0;
if (hdev->repeater_tx)
rx_repeat_hpd_state(0);
hdev->hwop.cntlconfig(hdev, CONF_CLR_AVI_PACKET, 0);
hdev->hwop.cntlmisc(hdev, MISC_TMDS_PHY_OP, TMDS_PHY_DISABLE);
hdev->hdmitx_event &= ~HDMI_TX_HPD_PLUGOUT;
hdev->hdcp_mode = 0;
hdcp_mode_set(0);
clear_rx_vinfo(hdev);
rx_edid_physical_addr(0, 0, 0, 0);
hdmitx21_edid_clear(hdev);
hdmi_physical_size_update(hdev);
hdmitx21_edid_ram_buffer_clear(hdev);
hdev->hpd_state = 0;
hdmitx_notify_hpd(hdev->hpd_state);
hdmitx21_set_uevent(HDMITX_HPD_EVENT, 0);
hdmitx21_set_uevent(HDMITX_AUDIO_EVENT, 0);
mutex_unlock(&setclk_mutex);
}
int get21_hpd_state(void)
{
int ret;
struct hdmitx_dev *hdev = get_hdmitx21_device();
mutex_lock(&setclk_mutex);
ret = hdev->hpd_state;
mutex_unlock(&setclk_mutex);
return ret;
}
/******************************
* hdmitx kernel task
*******************************/
static bool is_cur_tmds_div40(struct hdmitx_dev *hdev)
{
return 0;
}
static void hdmitx_resend_div40(struct hdmitx_dev *hdev)
{
hdev->hwop.cntlddc(hdev, DDC_SCDC_DIV40_SCRAMB, 1);
hdev->div40 = 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)
{
if (strlen(hdev->fmt_attr) >= 8) {
pr_info("fmt_attr %s\n", hdev->fmt_attr);
return;
}
memset(hdev->fmt_attr, 0, sizeof(hdev->fmt_attr));
switch (hdev->para->cs) {
case HDMI_COLORSPACE_RGB:
memcpy(hdev->fmt_attr, "rgb,", 5);
break;
case HDMI_COLORSPACE_YUV422:
memcpy(hdev->fmt_attr, "422,", 5);
break;
case HDMI_COLORSPACE_YUV444:
memcpy(hdev->fmt_attr, "444,", 5);
break;
case HDMI_COLORSPACE_YUV420:
memcpy(hdev->fmt_attr, "420,", 5);
break;
default:
break;
}
switch (hdev->para->cd) {
case COLORDEPTH_24B:
strcat(hdev->fmt_attr, "8bit");
break;
case COLORDEPTH_30B:
strcat(hdev->fmt_attr, "10bit");
break;
case COLORDEPTH_36B:
strcat(hdev->fmt_attr, "12bit");
break;
case COLORDEPTH_48B:
strcat(hdev->fmt_attr, "16bit");
break;
default:
break;
}
pr_info("fmt_attr %s\n", hdev->fmt_attr);
}
static void hdmitx_init_fmt_attr(struct hdmitx_dev *hdev)
{
if (!hdev->para)
return;
if (strlen(hdev->fmt_attr) >= 8) {
pr_info("fmt_attr %s\n", hdev->fmt_attr);
return;
}
memset(hdev->fmt_attr, 0, sizeof(hdev->fmt_attr));
switch (hdev->para->cs) {
case HDMI_COLORSPACE_RGB:
memcpy(hdev->fmt_attr, "rgb,", 5);
break;
case HDMI_COLORSPACE_YUV422:
memcpy(hdev->fmt_attr, "422,", 5);
break;
case HDMI_COLORSPACE_YUV444:
memcpy(hdev->fmt_attr, "444,", 5);
break;
case HDMI_COLORSPACE_YUV420:
memcpy(hdev->fmt_attr, "420,", 5);
break;
default:
break;
}
switch (hdev->para->cd) {
case COLORDEPTH_24B:
strcat(hdev->fmt_attr, "8bit");
break;
case COLORDEPTH_30B:
strcat(hdev->fmt_attr, "10bit");
break;
case COLORDEPTH_36B:
strcat(hdev->fmt_attr, "12bit");
break;
case COLORDEPTH_48B:
strcat(hdev->fmt_attr, "16bit");
break;
default:
break;
}
pr_info("fmt_attr %s\n", hdev->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) {
hdmitx_notify_hpd(hdev->hpd_state);
if (hdev->physical_addr != 0xffff)
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);
}
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 amhdmitx_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->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;
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);
return 0;
}
static void amhdmitx_get_drm_info(void)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct device_node *drm_node;
u8 *drm_status;
int ret = 0;
drm_node = of_find_node_by_path("/drm-amhdmitx");
if (drm_node) {
ret =
of_property_read_string(drm_node, "status",
(const char **)&drm_status);
if (ret) {
pr_info("not find drm_feature\n");
} else {
if (memcmp(drm_status, "okay", 4) == 0)
hdev->drm_feature = 1;
else
hdev->drm_feature = 0;
pr_info("hdev->drm_feature : %d\n",
hdev->drm_feature);
}
} else {
pr_info("not find drm_amhdmitx\n");
}
}
static int amhdmitx_get_dt_info(struct platform_device *pdev)
{
int ret = 0;
struct hdmitx_dev *hdev = get_hdmitx21_device();
#ifdef CONFIG_OF
int val;
phandle phandler;
struct device_node *init_data;
const struct of_device_id *match;
#endif
/* HDMITX pinctrl config for hdp and ddc*/
if (pdev->dev.pins) {
hdev->pdev = &pdev->dev;
hdev->pinctrl_default =
pinctrl_lookup_state(pdev->dev.pins->p, "default");
if (IS_ERR(hdev->pinctrl_default))
pr_info("no default of pinctrl state\n");
hdev->pinctrl_i2c =
pinctrl_lookup_state(pdev->dev.pins->p, "hdmitx_i2c");
if (IS_ERR(hdev->pinctrl_i2c))
pr_info("no hdmitx_i2c of pinctrl state\n");
pinctrl_select_state(pdev->dev.pins->p,
hdev->pinctrl_default);
}
#ifdef CONFIG_OF
if (pdev->dev.of_node) {
int dongle_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 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;
ret = of_property_read_u32(pdev->dev.of_node,
"cedst_en", &val);
if (!ret)
hdev->cedst_en = !!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;
}
/* 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 drm feature information */
amhdmitx_get_drm_info();
/* Get reg information */
ret = hdmitx21_init_reg_map(pdev);
}
#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);
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_0_gate failed to probe\n");
else
hdev->hdmitx_clk_tree.venci_1_gate = venci_1_gate;
}
void amhdmitx21_vpu_dev_regiter(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);
ret = hdmi_avi_infoframe_init(&hdev->infoframes.avi.avi);
if (ret)
pr_info("init avi infoframe failed\n");
// 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 void hdmitx_unregister_vrr(struct hdmitx_dev *hdev)
{
int ret;
ret = aml_vrr_unregister_device(hdev->enc_idx);
pr_info("%s ret = %d\n", __func__, ret);
}
static void hdmitx_register_vrr(struct hdmitx_dev *hdev)
{
int ret;
char *name = "hdmitx_vrr";
struct rx_cap *prxcap = &hdev->rxcap;
struct vinfo_s *vinfo;
struct vrr_device_s *vrr = &hdev->hdmitx_vrr_dev;
vinfo = hdmitx_get_current_vinfo(NULL);
if (!vinfo || vinfo->mode != VMODE_HDMI)
return;
vrr->output_src = VRR_OUTPUT_ENCP;
vrr->vfreq_max = prxcap->vrr_min;
vrr->vfreq_min = prxcap->vrr_max;
vrr->vline_max =
vinfo->vtotal * (prxcap->vrr_max / prxcap->vrr_min);
if (prxcap->vrr_max == 0)
vrr->vline_min = 0;
else
vrr->vline_min = vinfo->vtotal;
if (prxcap->vrr_max < 100 || prxcap->vrr_min > 48)
vrr->enable = 0;
else
vrr->enable = 1;
strncpy(vrr->name, name, VRR_NAME_LEN_MAX);
ret = aml_vrr_register_device(vrr, hdev->enc_idx);
pr_info("%s ret = %d\n", __func__, ret);
}
static int amhdmitx_probe(struct platform_device *pdev)
{
int r, ret = 0;
struct device *dev;
struct hdmitx_dev *hdev = get_hdmitx21_device();
pr_debug("%s start\n", __func__);
amhdmitx_device_init(hdev);
amhdmitx_infoframe_init(hdev);
ret = amhdmitx_get_dt_info(pdev);
if (ret)
return ret;
amhdmitx_clktree_probe(&pdev->dev);
amhdmitx21_vpu_dev_regiter(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_attr);
ret = device_create_file(dev, &dev_attr_vid_mute);
ret = device_create_file(dev, &dev_attr_edid);
ret = device_create_file(dev, &dev_attr_rawedid);
ret = device_create_file(dev, &dev_attr_sink_type);
ret = device_create_file(dev, &dev_attr_edid_parsing);
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_hdmi_hdr_status);
ret = device_create_file(dev, &dev_attr_hdr_cap);
ret = device_create_file(dev, &dev_attr_hdr_cap2);
ret = device_create_file(dev, &dev_attr_dv_cap);
ret = device_create_file(dev, &dev_attr_dv_cap2);
ret = device_create_file(dev, &dev_attr_avmute);
ret = device_create_file(dev, &dev_attr_phy);
ret = device_create_file(dev, &dev_attr_frac_rate_policy);
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_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_hpd_state);
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_contenttype_cap);
ret = device_create_file(dev, &dev_attr_contenttype_mode);
ret = device_create_file(dev, &dev_attr_hdmitx21);
#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);
hdev->hpd_state = !!(hdev->hwop.cntlmisc(hdev, 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
#if IS_ENABLED(CONFIG_AMLOGIC_SND_SOC)
if (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;
}
aout_register_client(&hdmitx_notifier_nb_a);
#endif
pr_info("%s[%d]\n", __func__, __LINE__);
/* update fmt_attr */
hdmitx_init_fmt_attr(hdev);
pr_info("%s[%d]\n", __func__, __LINE__);
hdev->hpd_state = !!hdev->hwop.cntlmisc(hdev, MISC_HPD_GPI_ST, 0);
hdmitx_notify_hpd(hdev->hpd_state);
hdmitx21_set_uevent(HDMITX_HDCPPWR_EVENT, HDMI_WAKEUP);
INIT_WORK(&hdev->work_hdr, hdr_work_func);
pr_info("%s[%d]\n", __func__, __LINE__);
/* When init hdmi, clear the hdmitx module edid ram and edid buffer. */
hdmitx21_edid_clear(hdev);
hdmitx21_edid_ram_buffer_clear(hdev);
pr_info("%s[%d]\n", __func__, __LINE__);
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_aud_hpd_plug,
hdmitx_aud_hpd_plug_handler);
INIT_DELAYED_WORK(&hdev->work_internal_intr, hdmitx_top_intr_handler);
pr_info("%s[%d]\n", __func__, __LINE__);
/* 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);
pr_info("%s[%d]\n", __func__, __LINE__);
hdev->tx_aud_cfg = 1; /* default audio configure is on */
/* When bootup mbox and TV simutanously, TV may not handle SCDC/DIV40 */
if (hdev->hpd_state && is_cur_tmds_div40(hdev))
hdmitx_resend_div40(hdev);
hdmitx21_hdcp_init();
/*Direct Rander Management use another irq*/
if (hdev->drm_feature == 0)
hdmitx_setupirqs(hdev);
pr_info("%s[%d]\n", __func__, __LINE__);
if (hdev->hpd_state) {
/* need to get edid before vout probe */
hdev->already_used = 1;
hdmitx_get_edid(hdev);
}
pr_info("%s[%d]\n", __func__, __LINE__);
/* Trigger HDMITX IRQ*/
if (hdev->hwop.cntlmisc(hdev, MISC_HPD_GPI_ST, 0))
hdev->hwop.cntlmisc(hdev, MISC_TRIGGER_HPD, 0);
pr_info("%s[%d]\n", __func__, __LINE__);
hdev->hdmi_init = 1;
pr_info("%s end\n", __func__);
return r;
}
static int amhdmitx_remove(struct platform_device *pdev)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
struct device *dev = hdev->hdtx_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
#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_attr);
device_remove_file(dev, &dev_attr_vid_mute);
device_remove_file(dev, &dev_attr_edid);
device_remove_file(dev, &dev_attr_rawedid);
device_remove_file(dev, &dev_attr_sink_type);
device_remove_file(dev, &dev_attr_edid_parsing);
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_hdr_cap);
device_remove_file(dev, &dev_attr_hdr_cap2);
device_remove_file(dev, &dev_attr_dv_cap);
device_remove_file(dev, &dev_attr_dv_cap2);
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_contenttype_cap);
device_remove_file(dev, &dev_attr_contenttype_mode);
device_remove_file(dev, &dev_attr_hpd_state);
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_avmute);
device_remove_file(dev, &dev_attr_frac_rate_policy);
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);
cdev_del(&hdev->cdev);
device_destroy(hdmitx_class, hdev->hdmitx_id);
class_destroy(hdmitx_class);
unregister_chrdev_region(hdev->hdmitx_id, HDMI_TX_COUNT);
return 0;
}
#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->hwop.cntlmisc(hdev, 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
.driver = {
.name = DEVICE_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(meson_amhdmitx_of_match),
#ifdef CONFIG_HIBERNATION
.pm = &amhdmitx_pm,
#endif
}
};
int __init amhdmitx21_init(void)
{
if (init_flag & 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);
}
#ifndef MODULE
subsys_initcall(amhdmitx21_init);
module_exit(amhdmitx21_exit);
#endif
//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();
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 (hdev->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(hdev->fmt_attr, attr,
sizeof(hdev->fmt_attr));
hdev->fmt_attr[15] = '\0';
break;
}
}
memcpy(hdev->backup_fmt_attr, hdev->fmt_attr,
sizeof(hdev->fmt_attr));
}
static int hdmitx21_boot_para_setup(char *s)
{
struct hdmitx_dev *hdev = get_hdmitx21_device();
char separator[] = {' ', ',', ';', 0x0};
char *token;
u32 token_len = 0;
u32 token_offset = 0;
u32 offset = 0;
int size = strlen(s);
memset(hdev->fmt_attr, 0, sizeof(hdev->fmt_attr));
memset(hdev->backup_fmt_attr, 0,
sizeof(hdev->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();
if (strncmp("0", str, 1) == 0)
hdev->frac_rate_policy = 0;
else
hdev->frac_rate_policy = 1;
pr_info("hdmitx boot frac_rate_policy: %d",
hdev->frac_rate_policy);
hdev->backup_frac_rate_policy = hdev->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();
unsigned int val = 0;
if ((strncmp("1", str, 1) == 0) || (strncmp("2", str, 1) == 0)) {
val = str[0] - '0';
hdev->hdr_priority = val;
pr_info("hdmitx boot hdr_priority: %d\n", val);
}
return 0;
}
__setup("hdr_priority=", hdmitx21_boot_hdr_priority);
MODULE_PARM_DESC(log21_level, "\n log21_level\n");
module_param(log21_level, int, 0644);