blob: eed9f9666f05554ac8361d174cf6367836dd56e2 [file] [log] [blame]
/*
* drivers/amlogic/media/vout/hdmitx/hdmi_tx_20/hdmi_tx_main_drm.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#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/consumer.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/ctype.h>
#include <linux/extcon.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/extcon.h>
#include <linux/i2c.h>
#include <linux/string.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/amlogic/media/vout/vinfo.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#ifdef CONFIG_AMLOGIC_SND_SOC
#include <linux/amlogic/media/sound/aout_notify.h>
#endif
#include <linux/amlogic/media/vout/hdmi_tx/hdmi_info_global.h>
#include <linux/amlogic/media/vout/hdmi_tx/hdmi_tx_ddc.h>
#include <linux/amlogic/media/vout/hdmi_tx/hdmi_tx_module.h>
#include <linux/amlogic/media/vout/hdmi_tx/hdmi_config.h>
#include "hw/tvenc_conf.h"
#include "hw/common.h"
#include "hw/hw_clk.h"
#include "hdmi_tx_hdcp.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_hdr10plus_pkt(unsigned int flag,
struct hdr10plus_para *data);
static void hdmitx_set_emp_pkt(unsigned char *data,
unsigned int type, unsigned int size);
static int check_fbc_special(unsigned char *edid_dat);
static struct vinfo_s *hdmitx_get_current_vinfo(void);
static void hdmitx_fmt_attr(struct hdmitx_dev *hdev);
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 DEFINE_MUTEX(setclk_mutex);
static DEFINE_MUTEX(getedid_mutex);
static struct hdmitx_dev hdmitx_device = {
.frac_rate_policy = 1,
};
static const struct dv_info dv_dummy;
static int log_level;
int hdr_status_pos;
static char hdmichecksum[11] = {
'i', 'n', 'v', 'a', 'l', 'i', 'd', 'c', 'r', 'c', '\0'
};
static char invalidchecksum[11] = {
'i', 'n', 'v', 'a', 'l', 'i', 'd', 'c', 'r', 'c', '\0'
};
static char emptychecksum[11] = {0};
static struct vinfo_s *hdmitx_get_current_vinfo(void)
{
return hdmitx_device.vinfo;
}
#define hdmi_debug() \
do { \
if (log_level == 0xff) \
pr_info("%s[%d]\n", __func__, __LINE__); \
} while (0)
void update_current_para(struct hdmitx_dev *hdev)
{
struct vinfo_s *info = NULL;
unsigned char mode[32];
info = hdmitx_get_current_vinfo();
if (!info)
return;
memset(mode, 0, sizeof(mode));
strncpy(mode, info->name, sizeof(mode) - 1);
if (strstr(hdev->fmt_attr, "420")) {
if (!strstr(mode, "420"))
strncat(mode, "420", 3);
}
hdev->para = hdmi_get_fmt_name(mode, hdev->fmt_attr);
}
struct vsif_debug_save vsif_debug_info;
struct vsif_debug_save hsty_vsif_config_data[8];
unsigned int hsty_vsif_config_loc, hsty_vsif_config_num;
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 = &hdmitx_device;
struct dv_vsif_para para = {0};
unsigned char VEN_HB[3] = {0x81, 0x01};
unsigned char VEN_DB1[24] = {0x00};
unsigned char VEN_DB2[27] = {0x00};
unsigned char len = 0;
unsigned int vic = hdev->cur_VIC;
unsigned int hdmi_vic_4k_flag = 0;
static enum eotf_type ltype = EOTF_T_NULL;
static unsigned char ltmode = -1;
unsigned long flags = 0;
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 (hsty_vsif_config_loc > 7)
hsty_vsif_config_loc = 0;
memcpy(&hsty_vsif_config_data[hsty_vsif_config_loc++],
&vsif_debug_info, sizeof(struct vsif_debug_save));
if (hsty_vsif_config_num < 0xfffffff0)
hsty_vsif_config_num++;
else
hsty_vsif_config_num = 8;
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 ((hdev->chip_type) < MESON_CPU_ID_GXL) {
pr_info("hdmitx: not support DolbyVision\n");
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
return;
}
if (hdr_status_pos != 2)
pr_info("hdmitx_set_vsif_pkt: type = %d\n", type);
hdr_status_pos = 2;
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_3840x2160p30_16x9) ||
(vic == HDMI_3840x2160p25_16x9) ||
(vic == HDMI_3840x2160p24_16x9) ||
(vic == HDMI_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_3840x2160p30_16x9)
VEN_DB1[4] = 0x1;
else if (vic == HDMI_3840x2160p25_16x9)
VEN_DB1[4] = 0x2;
else if (vic == HDMI_3840x2160p24_16x9)
VEN_DB1[4] = 0x3;
else/*vic == HDMI_4096x2160p24_256x135*/
VEN_DB1[4] = 0x4;
}
if (type == EOTF_T_DOLBYVISION) {
/*first disable drm package*/
hdmitx_device.hwop.setpacket(HDMI_PACKET_DRM,
NULL, NULL);
hdev->hwop.setpacket(HDMI_PACKET_VEND, VEN_DB1, VEN_HB);
hdev->hwop.cntlconfig(hdev, CONF_AVI_BT2020,
CLR_AVI_BT2020);/*BT709*/
if (tunnel_mode == RGB_8BIT) {
hdev->hwop.cntlconfig(hdev,
CONF_AVI_RGBYCC_INDIC,
COLORSPACE_RGB444);
hdev->hwop.cntlconfig(hdev, CONF_AVI_Q01,
RGB_RANGE_FUL);
} else {
hdev->hwop.cntlconfig(hdev,
CONF_AVI_RGBYCC_INDIC,
COLORSPACE_YUV422);
hdev->hwop.cntlconfig(hdev, CONF_AVI_YQ01,
YCC_RANGE_FUL);
}
} else {
if (hdmi_vic_4k_flag)
hdev->hwop.setpacket(
HDMI_PACKET_VEND, VEN_DB1, VEN_HB);
else
hdev->hwop.setpacket(
HDMI_PACKET_VEND, NULL, NULL);
if (signal_sdr) {
pr_info("hdmitx: H14b VSIF, switching signal to SDR\n");
update_current_para(hdev);
hdev->hwop.cntlconfig(hdev,
CONF_AVI_RGBYCC_INDIC, hdev->para->cs);
hdev->hwop.cntlconfig(hdev,
CONF_AVI_Q01, RGB_RANGE_DEFAULT);
hdev->hwop.cntlconfig(hdev,
CONF_AVI_YQ01, YCC_RANGE_LIM);
hdev->hwop.cntlconfig(hdev, 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;
}
/*Dolby Vision standard case*/
if (type == EOTF_T_DOLBYVISION) {
/*first disable drm package*/
hdmitx_device.hwop.setpacket(HDMI_PACKET_DRM,
NULL, NULL);
hdev->hwop.setpacket(HDMI_PACKET_VEND, VEN_DB2, VEN_HB);
hdev->hwop.cntlconfig(hdev, CONF_AVI_BT2020,
CLR_AVI_BT2020);/*BT709*/
if (tunnel_mode == RGB_8BIT) {/*RGB444*/
hdev->hwop.cntlconfig(hdev,
CONF_AVI_RGBYCC_INDIC,
COLORSPACE_RGB444);
hdev->hwop.cntlconfig(hdev, CONF_AVI_Q01,
RGB_RANGE_FUL);
} else {/*YUV422*/
hdev->hwop.cntlconfig(hdev,
CONF_AVI_RGBYCC_INDIC,
COLORSPACE_YUV422);
hdev->hwop.cntlconfig(hdev, CONF_AVI_YQ01,
YCC_RANGE_FUL);
}
}
/*Dolby Vision low-latency case*/
else if (type == EOTF_T_LL_MODE) {
/*first disable drm package*/
hdmitx_device.hwop.setpacket(HDMI_PACKET_DRM,
NULL, NULL);
hdev->hwop.setpacket(HDMI_PACKET_VEND, VEN_DB2, VEN_HB);
if (hdev->rxcap.colorimetry_data & 0xe0)
/*if RX support BT2020, then output BT2020*/
hdev->hwop.cntlconfig(hdev, CONF_AVI_BT2020,
SET_AVI_BT2020);/*BT2020*/
else
hdev->hwop.cntlconfig(hdev, CONF_AVI_BT2020,
CLR_AVI_BT2020);/*BT709*/
if (tunnel_mode == RGB_10_12BIT) {/*10/12bit RGB444*/
hdev->hwop.cntlconfig(hdev,
CONF_AVI_RGBYCC_INDIC,
COLORSPACE_RGB444);
hdev->hwop.cntlconfig(hdev, CONF_AVI_Q01,
RGB_RANGE_LIM);
} else if (tunnel_mode == YUV444_10_12BIT) {
/*10/12bit YUV444*/
hdev->hwop.cntlconfig(hdev,
CONF_AVI_RGBYCC_INDIC,
COLORSPACE_YUV444);
hdev->hwop.cntlconfig(hdev, CONF_AVI_YQ01,
YCC_RANGE_LIM);
} else {/*YUV422*/
hdev->hwop.cntlconfig(hdev,
CONF_AVI_RGBYCC_INDIC,
COLORSPACE_YUV422);
hdev->hwop.cntlconfig(hdev, CONF_AVI_YQ01,
YCC_RANGE_LIM);
}
}
/*SDR case*/
else {
pr_info("hdmitx: Dolby VSIF, VEN_DB2[3]) = %d\n",
VEN_DB2[3]);
hdev->hwop.setpacket(
HDMI_PACKET_VEND, VEN_DB2, VEN_HB);
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->vic, hdev->para->cd,
hdev->para->cs, hdev->para->cr);
hdev->hwop.cntlconfig(hdev,
CONF_AVI_RGBYCC_INDIC, hdev->para->cs);
hdev->hwop.cntlconfig(hdev,
CONF_AVI_Q01, RGB_RANGE_DEFAULT);
hdev->hwop.cntlconfig(hdev,
CONF_AVI_YQ01, YCC_RANGE_LIM);
hdev->hwop.cntlconfig(hdev, CONF_AVI_BT2020,
CLR_AVI_BT2020);/*BT709*/
}
}
}
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
}
struct vout_device_s hdmitx_vdev = {
.dv_info = &hdmitx_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_emp_pkt = hdmitx_set_emp_pkt,
};
struct hdmi_config_platform_data *hdmi_pdata;
static const unsigned int hdmi_cable[] = {
EXTCON_DISP_HDMI,
EXTCON_NONE
};
struct extcon_dev *hdmitx_extcon_hdmi;
struct extcon_dev *hdmitx_extcon_audio;
struct extcon_dev *hdmitx_extcon_power;
struct extcon_dev *hdmitx_extcon_hdr;
struct extcon_dev *hdmitx_extcon_rxsense;
struct extcon_dev *hdmitx_extcon_hdcp;
struct extcon_dev *hdmitx_extcon_cedst;
/* 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.
*/
void hdmitx_notify_hpd(int hpd, void *p)
{
if (hpd)
hdmitx_event_notify(HDMITX_PLUG, p);
else
hdmitx_event_notify(HDMITX_UNPLUG, NULL);
}
EXPORT_SYMBOL(hdmitx_notify_hpd);
#if defined(CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND) && !defined(CONFIG_AMLOGIC_DRM)
#include <linux/amlogic/pm.h>
static void hdmitx_early_suspend(struct early_suspend *h)
{
struct hdmitx_dev *phdmi = (struct hdmitx_dev *)h->param;
struct hdmitx_dev *hdev = phdmi;
phdmi->ready = 0;
phdmi->hpd_lock = 1;
hdev->hwop.cntlmisc(hdev, MISC_SUSFLAG, 1);
usleep_range(10000, 10010);
phdmi->hwop.cntlmisc(phdmi, MISC_AVMUTE_OP, SET_AVMUTE);
usleep_range(100000, 100010);
pr_info(SYS "HDMITX: Early Suspend\n");
phdmi->hwop.cntl((struct hdmitx_dev *)h->param,
HDMITX_EARLY_SUSPEND_RESUME_CNTL, HDMITX_EARLY_SUSPEND);
phdmi->cur_VIC = 0;
phdmi->output_blank_flag = 0;
phdmi->hwop.cntlddc(phdmi, DDC_HDCP_MUX_INIT, 1);
phdmi->hwop.cntlddc(phdmi, DDC_HDCP_OP, HDCP14_OFF);
hdmitx_set_vsif_pkt(0, 0, NULL, true);
hdmitx_set_hdr10plus_pkt(0, NULL);
clear_rx_vinfo(hdev);
hdmitx_edid_clear(hdev);
hdmitx_edid_ram_buffer_clear(hdev);
edidinfo_detach_to_vinfo(hdev);
extcon_set_state_sync(hdmitx_extcon_power, EXTCON_DISP_HDMI, 0);
phdmi->hwop.cntlconfig(&hdmitx_device, CONF_CLR_AVI_PACKET, 0);
phdmi->hwop.cntlconfig(&hdmitx_device, CONF_CLR_VSDB_PACKET, 0);
/*close vpu clk*/
if (phdmi->hdmitx_clk_tree.hdmi_clk_vapb)
clk_disable_unprepare(phdmi->hdmitx_clk_tree.hdmi_clk_vapb);
if (phdmi->hdmitx_clk_tree.hdmi_clk_vpu)
clk_disable_unprepare(phdmi->hdmitx_clk_tree.hdmi_clk_vpu);
/* for huawei TV, it will display green screen pattern under
* 4k50/60hz y420 deep color when receive amvute. After disable
* phy of box, TV will continue mute and stay in still frame
* mode for a few frames, if it receives scdc clk raito change
* during this period, it may recognize it as signal unstable
* instead of no signal, and keep mute pattern for several
* seconds. Here keep hdmi output disabled for a few frames
* so let TV exit its still frame mode and not show pattern
*/
usleep_range(120000, 120010);
hdev->hwop.cntlddc(hdev, DDC_SCDC_DIV40_SCRAMB, 0);
hdev->div40 = 0;
}
static int hdmitx_is_hdmi_vmode(char *mode_name)
{
enum hdmi_vic vic = hdmitx_edid_vic_tab_map_vic(mode_name);
if (vic == 0)
return 0;
return 1;
}
static void hdmitx_late_resume(struct early_suspend *h)
{
const struct vinfo_s *info = hdmitx_get_current_vinfo();
struct hdmitx_dev *phdmi = (struct hdmitx_dev *)h->param;
/*open vpu clk*/
if (phdmi->hdmitx_clk_tree.hdmi_clk_vapb)
clk_prepare_enable(phdmi->hdmitx_clk_tree.hdmi_clk_vapb);
if (phdmi->hdmitx_clk_tree.hdmi_clk_vpu)
clk_prepare_enable(phdmi->hdmitx_clk_tree.hdmi_clk_vpu);
if (info && (hdmitx_is_hdmi_vmode(info->name) == 1))
phdmi->hwop.cntlmisc(&hdmitx_device, MISC_HPLL_FAKE, 0);
phdmi->hpd_lock = 0;
/* update status for hpd and switch/state */
hdmitx_device.hpd_state = !!(hdmitx_device.hwop.cntlmisc(&hdmitx_device,
MISC_HPD_GPI_ST, 0));
if (hdmitx_device.hpd_state)
hdmitx_device.already_used = 1;
pr_info("hdmitx hpd state: %d\n", hdmitx_device.hpd_state);
/*force to get EDID after resume for Amplifer Power case*/
if (hdmitx_device.hpd_state)
hdmitx_get_edid(phdmi);
hdmitx_notify_hpd(hdmitx_device.hpd_state,
hdmitx_device.edid_parsing ?
hdmitx_device.edid_ptr : NULL);
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
CONF_AUDIO_MUTE_OP, AUDIO_MUTE);
set_disp_mode_auto();
extcon_set_state_sync(hdmitx_extcon_hdmi, EXTCON_DISP_HDMI,
hdmitx_device.hpd_state);
extcon_set_state_sync(hdmitx_extcon_power, EXTCON_DISP_HDMI, 1);
extcon_set_state_sync(hdmitx_extcon_audio, EXTCON_DISP_HDMI,
hdmitx_device.hpd_state);
phdmi->hwop.cntl((struct hdmitx_dev *)h->param,
HDMITX_EARLY_SUSPEND_RESUME_CNTL, HDMITX_LATE_RESUME);
phdmi->hwop.cntlmisc(phdmi, MISC_SUSFLAG, 0);
}
static struct early_suspend hdmitx_early_suspend_handler = {
.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN - 10,
.suspend = hdmitx_early_suspend,
.resume = hdmitx_late_resume,
.param = &hdmitx_device,
};
#endif
/* 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(100000, 100010);
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;
}
#define INIT_FLAG_VDACOFF 0x1
/* unplug powerdown */
#define INIT_FLAG_POWERDOWN 0x2
#define INIT_FLAG_NOT_LOAD 0x80
static unsigned char init_flag;
#undef DISABLE_AUDIO
int get_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;
vic = hdmitx_edid_get_VIC(&hdmitx_device, mode, 1);
if (strncmp(mode, "2160p30hz", strlen("2160p30hz")) == 0)
vic = HDMI_4k2k_30;
else if (strncmp(mode, "2160p25hz", strlen("2160p25hz")) == 0)
vic = HDMI_4k2k_25;
else if (strncmp(mode, "2160p24hz", strlen("2160p24hz")) == 0)
vic = HDMI_4k2k_24;
else if (strncmp(mode, "smpte24hz", strlen("smpte24hz")) == 0)
vic = HDMI_4k2k_smpte_24;
else
;/* nothing */
if (strncmp(mode, "1080p60hz", strlen("1080p60hz")) == 0)
vic = HDMI_1080p60;
if (strncmp(mode, "1080p50hz", strlen("1080p50hz")) == 0)
vic = HDMI_1080p50;
if (vic != 0) {
hdmitx_device.mux_hpd_if_pin_high_flag = 1;
if (hdmitx_device.vic_count == 0) {
if (hdmitx_device.unplug_powerdown)
return 0;
}
}
hdmitx_device.cur_VIC = 0;
ret = hdmitx_set_display(&hdmitx_device, vic);
if (ret >= 0) {
hdmitx_device.hwop.cntl(&hdmitx_device,
HDMITX_AVMUTE_CNTL, AVMUTE_CLEAR);
hdmitx_device.cur_VIC = vic;
hdmitx_device.audio_param_update_flag = 1;
hdmitx_device.auth_process_timer = AUTH_PROCESS_TIME;
}
if (hdmitx_device.cur_VIC == 0) {
if (hdmitx_device.hpdmode == 2) {
/* edid will be read again when hpd is muxed and
* it is high
*/
hdmitx_edid_clear(&hdmitx_device);
hdmitx_device.mux_hpd_if_pin_high_flag = 0;
}
if (hdmitx_device.hwop.cntl) {
hdmitx_device.hwop.cntl(&hdmitx_device,
HDMITX_HWCMD_TURNOFF_HDMIHW,
(hdmitx_device.hpdmode == 2) ? 1 : 0);
}
}
return ret;
}
static void hdmitx_pre_display_init(void)
{
hdmitx_device.cur_VIC = 0;
hdmitx_device.auth_process_timer = AUTH_PROCESS_TIME;
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
CONF_AUDIO_MUTE_OP, AUDIO_MUTE);
/* msleep(10); */
hdmitx_device.hwop.cntlmisc(&hdmitx_device, MISC_TMDS_PHY_OP,
TMDS_PHY_DISABLE);
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
CONF_CLR_AVI_PACKET, 0);
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
CONF_CLR_VSDB_PACKET, 0);
}
/* 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, unsigned int frac)
{
struct frac_rate_table *fr = &fr_tab[0];
pr_info(SYS "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(SYS "recalc after %s %d %d, frac %d\n", info->name,
info->sync_duration_num, info->sync_duration_den, info->frac);
}
static void hdmi_physical_size_update(struct hdmitx_dev *hdev)
{
unsigned int width, height;
struct vinfo_s *info = NULL;
info = hdmitx_get_current_vinfo();
if (!info || !info->name) {
pr_info(SYS "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(SYS "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)
{
unsigned int i, j;
/*static hdr*/
info->hdr_info.hdr_support = (hdev->rxcap.hdr_sup_eotf_sdr << 0)
| (hdev->rxcap.hdr_sup_eotf_hdr << 1)
| (hdev->rxcap.hdr_sup_eotf_smpte_st_2084 << 2)
| (hdev->rxcap.hdr_sup_eotf_hlg << 3);
memcpy(info->hdr_info.rawdata, hdev->rxcap.hdr_rawdata, 7);
/*dynamic hdr*/
for (i = 0; i < 4; i++) {
if (hdev->rxcap.hdr_dynamic_info[i].type == 0) {
memset(&info->hdr_info.dynamic_info[i],
0, sizeof(struct hdr_dynamic));
continue;
}
info->hdr_info.dynamic_info[i].type =
hdev->rxcap.hdr_dynamic_info[i].type;
info->hdr_info.dynamic_info[i].of_len =
hdev->rxcap.hdr_dynamic_info[i].hd_len - 3;
info->hdr_info.dynamic_info[i].support_flags =
hdev->rxcap.hdr_dynamic_info[i].support_flags;
for (j = 0; j < hdev->rxcap.hdr_dynamic_info[i].hd_len - 3; j++)
info->hdr_info.dynamic_info[i].optional_fields[j] =
hdev->rxcap.hdr_dynamic_info[i].optional_fields[j];
}
/*hdr 10+*/
memcpy(&info->hdr_info.hdr10plus_info,
&hdev->rxcap.hdr10plus_info, sizeof(struct hdr10_plus_info));
info->hdr_info.colorimetry_support =
hdev->rxcap.colorimetry_data;
info->hdr_info.lumi_max = hdev->rxcap.hdr_lum_max;
info->hdr_info.lumi_avg = hdev->rxcap.hdr_lum_avg;
info->hdr_info.lumi_min = hdev->rxcap.hdr_lum_min;
pr_info(SYS "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();
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;
hdrinfo_to_vinfo(info, hdev);
rxlatency_to_vinfo(info, &hdev->rxcap);
hdmitx_vdev.dv_info = &hdmitx_device.rxcap.dv_info;
}
static void edidinfo_detach_to_vinfo(struct hdmitx_dev *hdev)
{
struct vinfo_s *info = NULL;
/* get current vinfo */
info = hdmitx_get_current_vinfo();
if (!info || !info->name)
return;
edidinfo_attach_to_vinfo(hdev);
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 = &hdmitx_device;
struct hdmi_format_para *para = NULL;
char mode[32];
static char fmt_attr[16] = {0};
unsigned char tmp;
enum hdmi_vic vic = 0;
/*STOP HDCP*/
if (hdev->hwop.am_hdmitx_hdcp_disable)
hdev->hwop.am_hdmitx_hdcp_disable();
memset(mode, 0, sizeof(mode));
hdev->ready = 0;
/* get current vinfo */
info = hdmitx_get_current_vinfo();
if (!info || !info->name)
return -1;
pr_info(SYS "get current mode: %s\n", info->name);
/* get current vinfo and refesh */
recalc_vinfo_sync_duration(info,
hdmitx_device.frac_rate_policy);
tmp = strlen(info->name);
if (tmp > sizeof(mode) - 1)
tmp = sizeof(mode) - 1;
strncpy(mode, info->name, tmp);
/* In the file hdmi_common/hdmi_parameters.c,
* the data array all_fmt_paras[] treat 2160p60hz and 2160p60hz420
* as two different modes, such Scrambler
* So if node "attr" contains 420, need append 420 to mode.
*/
if (strstr(hdev->fmt_attr, "420")) {
if (!strstr(mode, "420"))
strcat(mode, "420");
}
para = hdmi_get_fmt_name(mode, hdev->fmt_attr);
hdev->para = para;
vic = hdmitx_edid_get_VIC(hdev, mode, 1);
if ((hdev->cur_VIC != 0) &&
(hdev->cur_VIC == vic) &&
memcmp(fmt_attr,
hdev->fmt_attr,
sizeof(fmt_attr) == 0)) {
pr_info(SYS "[%s] ALREADY init VIC = %d\n",
__func__, vic);
hdev->output_blank_flag = 1;
hdev->ready = 1;
return 1;
}
memcpy(fmt_attr, hdev->fmt_attr, sizeof(fmt_attr));
hdev->cur_VIC = 0;
hdmitx_pre_display_init();
/* if vic is HDMI_Unknown, hdmitx_set_display will disable HDMI */
ret = hdmitx_set_display(hdev, vic);
if (ret >= 0) {
hdmitx_set_audio(hdev, &hdev->cur_audio_param);
hdev->hwop.cntl(hdev, HDMITX_AVMUTE_CNTL, AVMUTE_CLEAR);
hdev->cur_VIC = vic;
hdev->audio_param_update_flag = 1;
hdev->auth_process_timer = AUTH_PROCESS_TIME;
if (hdev->cedst_policy) {
cancel_delayed_work(&hdev->work_cedst);
queue_delayed_work(hdev->cedst_wq,
&hdev->work_cedst, 0);
}
/* enable drm hdcp*/
if (hdev->hwop.am_hdmitx_hdcp_enable)
hdev->hwop.am_hdmitx_hdcp_enable();
hdev->output_blank_flag = 1;
hdev->ready = 1;
}
return ret;
}
/*disp_mode attr*/
static ssize_t show_disp_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "VIC:%d\n",
hdmitx_device.cur_VIC);
return pos;
}
static ssize_t store_disp_mode(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
set_disp_mode(buf);
return count;
}
static ssize_t show_attr(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
if (!memcmp(hdmitx_device.fmt_attr, "default,", 7)) {
memset(hdmitx_device.fmt_attr, 0,
sizeof(hdmitx_device.fmt_attr));
hdmitx_fmt_attr(&hdmitx_device);
}
pos += snprintf(buf + pos, PAGE_SIZE, "%s", hdmitx_device.fmt_attr);
return pos;
}
ssize_t store_attr(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
strncpy(hdmitx_device.fmt_attr, buf, sizeof(hdmitx_device.fmt_attr));
hdmitx_device.fmt_attr[15] = '\0';
if (!memcmp(hdmitx_device.fmt_attr, "rgb", 3))
hdmitx_device.para->cs = COLORSPACE_RGB444;
else if (!memcmp(hdmitx_device.fmt_attr, "422", 3))
hdmitx_device.para->cs = COLORSPACE_YUV422;
else if (!memcmp(hdmitx_device.fmt_attr, "420", 3))
hdmitx_device.para->cs = COLORSPACE_YUV420;
else
hdmitx_device.para->cs = COLORSPACE_YUV444;
return count;
}
void setup_attr(const char *buf)
{
char attr[16] = {0};
memcpy(attr, buf, sizeof(attr));
memcpy(hdmitx_device.fmt_attr, attr, sizeof(hdmitx_device.fmt_attr));
}
EXPORT_SYMBOL(setup_attr);
void get_attr(char attr[16])
{
memcpy(attr, hdmitx_device.fmt_attr, sizeof(hdmitx_device.fmt_attr));
}
EXPORT_SYMBOL(get_attr);
/* for android application data exchange / swap */
static char *tmp_swap;
static DEFINE_MUTEX(mutex_swap);
static ssize_t store_swap(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
pr_info("store_swap: %s\n", buf);
mutex_lock(&mutex_swap);
kfree(tmp_swap);
tmp_swap = kzalloc(count + 1, GFP_KERNEL);
if (!tmp_swap) {
mutex_unlock(&mutex_swap);
return count;
}
memcpy(tmp_swap, buf, count);
tmp_swap[count] = '\0'; /* padding end string */
mutex_unlock(&mutex_swap);
return count;
}
static ssize_t show_swap(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i = 0;
int n = 0;
struct hdmitx_dev *hdev = &hdmitx_device;
struct rx_cap *prxcap = &hdev->rxcap;
struct hdcprp14_topo *topo14 = &hdev->topo_info->topo.topo14;
mutex_lock(&mutex_swap);
if (!tmp_swap ||
(!hdev->edid_parsing &&
!strstr(tmp_swap, "hdcp.topo"))) {
mutex_unlock(&mutex_swap);
return n;
}
/* VSD: Video Short Descriptor */
if (strstr(tmp_swap, "edid.vsd"))
for (i = 0; i < prxcap->vsd.len; i++)
n += snprintf(buf + n, PAGE_SIZE - n, "%02x",
prxcap->vsd.raw[i]);
/* ASD: Audio Short Descriptor */
if (strstr(tmp_swap, "edid.asd"))
for (i = 0; i < prxcap->asd.len; i++)
n += snprintf(buf + n, PAGE_SIZE - n, "%02x",
prxcap->asd.raw[i]);
/* CEC: Physical Address */
if (strstr(tmp_swap, "edid.cec"))
n += snprintf(buf + n, PAGE_SIZE - n, "%x%x%x%x",
hdev->hdmi_info.vsdb_phy_addr.a,
hdev->hdmi_info.vsdb_phy_addr.b,
hdev->hdmi_info.vsdb_phy_addr.c,
hdev->hdmi_info.vsdb_phy_addr.d);
/* HDCP TOPO */
if (strstr(tmp_swap, "hdcp.topo")) {
char *tmp = (char *)topo14;
pr_info("max_cascade_exceeded %d\n",
topo14->max_cascade_exceeded);
pr_info("depth %d\n", topo14->depth);
pr_info("max_devs_exceeded %d\n", topo14->max_devs_exceeded);
pr_info("device_count %d\n", topo14->device_count);
for (i = 0; i < sizeof(struct hdcprp14_topo); i++)
n += snprintf(buf + n, PAGE_SIZE - n, "%02x", tmp[i]);
}
kfree(tmp_swap);
tmp_swap = NULL;
mutex_unlock(&mutex_swap);
return n;
}
static ssize_t show_aud_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
return 0;
}
static ssize_t store_aud_mode(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
/* set_disp_mode(buf); */
struct hdmitx_audpara *audio_param =
&hdmitx_device.cur_audio_param;
if (strncmp(buf, "32k", 3) == 0) {
audio_param->sample_rate = FS_32K;
} else if (strncmp(buf, "44.1k", 5) == 0) {
audio_param->sample_rate = FS_44K1;
} else if (strncmp(buf, "48k", 3) == 0) {
audio_param->sample_rate = FS_48K;
} else {
hdmitx_device.force_audio_flag = 0;
return count;
}
audio_param->type = CT_PCM;
audio_param->channel_num = CC_2CH;
audio_param->sample_size = SS_16BITS;
hdmitx_device.audio_param_update_flag = 1;
hdmitx_device.force_audio_flag = 1;
return count;
}
static ssize_t show_edid(struct device *dev,
struct device_attribute *attr, char *buf)
{
return hdmitx_edid_dump(&hdmitx_device, buf, PAGE_SIZE);
}
static int dump_edid_data(unsigned int type, char *path)
{
struct file *filp = NULL;
loff_t pos = 0;
char line[128] = {0};
mm_segment_t old_fs = get_fs();
unsigned int i = 0, j = 0, k = 0, size = 0, block_cnt = 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 = hdmitx_device.EDID_buf[0x7e] + 1;
if (type == 1) {
/* dump as bin file*/
size = vfs_write(filp, hdmitx_device.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++) {
snprintf((char *)&line[k * 6], 7,
"0x%02x, ",
hdmitx_device.EDID_buf[i * 128 + j * 16 + k]);
}
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(unsigned int type, char *path)
{
struct file *filp = NULL;
loff_t pos = 0;
mm_segment_t old_fs = get_fs();
struct kstat stat;
unsigned int 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;
}
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(hdmitx_device.EDID_buf, buf, length);
kfree(buf);
filp_close(filp, NULL);
pr_info("[%s] %d bytes loaded from file %s\n", __func__, length, path);
hdmitx_edid_clear(&hdmitx_device);
hdmitx_edid_parse(&hdmitx_device);
pr_info("[%s] new edid loaded!\n", __func__);
PROCESS_END:
set_fs(old_fs);
return 0;
}
static ssize_t store_edid(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned int argn = 0;
char *p = NULL, *para = NULL, *argv[8] = {NULL};
unsigned int path_length = 0;
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 "EDID hash value:\n");
for (i = 0; i < 20; i++)
pr_info("%02x", hdmitx_device.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++) {
pr_info(EDID "%02x ",
hdmitx_device.EDID_buf[
block_idx * 128 + ii * 16 + jj]);
}
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++) {
pr_info(EDID "%02x ",
hdmitx_device.EDID_buf1[
block_idx * 128 + ii * 16 + jj]);
}
pr_info("\n");
}
pr_info("\n");
}
}
if (!strncmp(argv[0], "save", strlen("save"))) {
unsigned int 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;
}
static ssize_t show_rawedid(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
int i;
struct hdmitx_dev *hdev = &hdmitx_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(00 ff ff ff ff ff ff 00), 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 show_edid_parsing(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = &hdmitx_device;
if (hdmitx_edid_notify_ng(hdev->edid_ptr))
pos += snprintf(buf + pos, PAGE_SIZE, "ng\n");
else
pos += snprintf(buf + pos, PAGE_SIZE, "ok\n");
return pos;
}
/*
* sink_type attr
* sink, or repeater
*/
static ssize_t show_sink_type(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = &hdmitx_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;
}
/*
* hdcp_repeater attr
* For hdcp 22, hdcp_tx22 will write to store_hdcp_repeater
* For hdcp 14, directly get bcaps bit
*/
static ssize_t show_hdcp_repeater(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = &hdmitx_device;
if (hdev->hdcp_mode == 1)
hdev->hdcp_bcaps_repeater = hdev->hwop.cntlddc(hdev,
DDC_HDCP14_GET_BCAPS_RP, 0);
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdev->hdcp_bcaps_repeater);
return pos;
}
static ssize_t store_hdcp_repeater(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = &hdmitx_device;
if (hdev->hdcp_mode == 2)
hdev->hdcp_bcaps_repeater = (buf[0] == '1');
return count;
}
/*
* hdcp_topo_info attr
* For hdcp 22, hdcp_tx22 will write to store_hdcp_topo_info
* For hdcp 14, directly get from HW
*/
static ssize_t show_hdcp_topo_info(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = &hdmitx_device;
struct hdcprp_topo *topoinfo = hdev->topo_info;
if (!hdev->hdcp_mode) {
pos += snprintf(buf + pos, PAGE_SIZE, "hdcp mode: 0\n");
return pos;
}
if (!topoinfo)
return pos;
if (hdev->hdcp_mode == 1) {
memset(topoinfo, 0, sizeof(struct hdcprp_topo));
hdev->hwop.cntlddc(hdev, DDC_HDCP14_GET_TOPO_INFO,
(unsigned long)&topoinfo->topo.topo14);
}
pos += snprintf(buf + pos, PAGE_SIZE, "hdcp mode: %s\n",
hdev->hdcp_mode == 1 ? "14" : "22");
if (hdev->hdcp_mode == 2) {
topoinfo->hdcp_ver = HDCPVER_22;
pos += snprintf(buf + pos, PAGE_SIZE,
"max_devs_exceeded: %d\n",
topoinfo->topo.topo22.max_devs_exceeded);
pos += snprintf(buf + pos, PAGE_SIZE,
"max_cascade_exceeded: %d\n",
topoinfo->topo.topo22.max_cascade_exceeded);
pos += snprintf(buf + pos, PAGE_SIZE,
"v2_0_repeater_down: %d\n",
topoinfo->topo.topo22.v2_0_repeater_down);
pos += snprintf(buf + pos, PAGE_SIZE,
"v1_X_device_down: %d\n",
topoinfo->topo.topo22.v1_X_device_down);
pos += snprintf(buf + pos, PAGE_SIZE,
"device_count: %d\n",
topoinfo->topo.topo22.device_count);
pos += snprintf(buf + pos, PAGE_SIZE,
"depth: %d\n",
topoinfo->topo.topo22.depth);
return pos;
}
if (hdev->hdcp_mode == 1) {
topoinfo->hdcp_ver = HDCPVER_14;
pos += snprintf(buf + pos, PAGE_SIZE,
"max_devs_exceeded: %d\n",
topoinfo->topo.topo14.max_devs_exceeded);
pos += snprintf(buf + pos, PAGE_SIZE,
"max_cascade_exceeded: %d\n",
topoinfo->topo.topo14.max_cascade_exceeded);
pos += snprintf(buf + pos, PAGE_SIZE,
"device_count: %d\n",
topoinfo->topo.topo14.device_count);
pos += snprintf(buf + pos, PAGE_SIZE,
"depth: %d\n",
topoinfo->topo.topo14.depth);
return pos;
}
return pos;
}
static ssize_t store_hdcp_topo_info(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = &hdmitx_device;
struct hdcprp_topo *topoinfo = hdev->topo_info;
int cnt;
if (!topoinfo)
return count;
if (hdev->hdcp_mode == 2) {
memset(topoinfo, 0, sizeof(struct hdcprp_topo));
cnt = sscanf(buf, "%x %x %x %x %x %x",
(int *)&topoinfo->topo.topo22.max_devs_exceeded,
(int *)&topoinfo->topo.topo22.max_cascade_exceeded,
(int *)&topoinfo->topo.topo22.v2_0_repeater_down,
(int *)&topoinfo->topo.topo22.v1_X_device_down,
(int *)&topoinfo->topo.topo22.device_count,
(int *)&topoinfo->topo.topo22.depth);
if (cnt < 0)
return count;
}
return count;
}
static ssize_t show_hdcp22_type(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdmitx_device.hdcp22_type);
return pos;
}
static ssize_t store_hdcp22_type(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int type = 0;
struct hdmitx_dev *hdev = &hdmitx_device;
if (buf[0] == '1')
type = 1;
else
type = 0;
hdev->hdcp22_type = type;
pr_info("hdmitx: set hdcp22 content type %d\n", type);
hdev->hwop.cntlddc(hdev, DDC_HDCP_SET_TOPO_INFO, type);
return count;
}
static ssize_t show_hdcp22_base(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "0x%x\n", get_hdcp22_base());
return pos;
}
void hdmitx_audio_mute_op(unsigned int flag)
{
hdmitx_device.tx_aud_cfg = flag;
if (flag == 0)
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
CONF_AUDIO_MUTE_OP, AUDIO_MUTE);
else
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
CONF_AUDIO_MUTE_OP, AUDIO_UNMUTE);
}
EXPORT_SYMBOL(hdmitx_audio_mute_op);
void hdmitx_video_mute_op(unsigned int flag)
{
if (flag == 0)
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
CONF_VIDEO_MUTE_OP, VIDEO_MUTE);
else
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
CONF_VIDEO_MUTE_OP, VIDEO_UNMUTE);
}
EXPORT_SYMBOL(hdmitx_video_mute_op);
/*
* 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;
extcon_set_state_sync(hdmitx_extcon_hdr, EXTCON_DISP_HDMI, 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;
extcon_set_state_sync(hdmitx_extcon_hdr, EXTCON_DISP_HDMI, 0);
}
}
static void hdr_work_func(struct work_struct *work)
{
struct hdmitx_dev *hdev =
container_of(work, struct hdmitx_dev, work_hdr);
if (hdev->hdr_transfer_feature == T_BT709 &&
hdev->hdr_color_feature == C_BT709) {
unsigned char DRM_HB[3] = {0x87, 0x1, 26};
unsigned char DRM_DB[26] = {0x0};
pr_info("hdr_work_func: send zero DRM\n");
hdev->hwop.setpacket(HDMI_PACKET_DRM, DRM_DB, DRM_HB);
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
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("hdr_work_func: disable hdr10+ vsif\n");
hdev->hwop.setpacket(HDMI_PACKET_VEND, NULL, NULL);
hdr_status_pos = 0;
}
if (hdev->hdr_transfer_feature == T_BT709 &&
hdev->hdr_color_feature == C_BT709) {
pr_info("hdr_work_func: disable DRM\n");
hdev->hwop.setpacket(HDMI_PACKET_DRM, NULL, NULL);
hdev->hdmi_current_hdr_mode = 0;
hdmitx_sdr_hdr_uevent(hdev);
}
} else {
hdmitx_sdr_hdr_uevent(hdev);
}
}
/* Init DRM_DB[0] from Uboot status */
static void init_drm_db0(struct hdmitx_dev *hdev, unsigned char *dat)
{
static int once_flag = 1;
if (once_flag) {
once_flag = 0;
*dat = hdev->hwop.getstate(hdev, STAT_HDR_TYPE, 0);
}
}
#define GET_LOW8BIT(a) ((a) & 0xff)
#define GET_HIGH8BIT(a) (((a) >> 8) & 0xff)
struct master_display_info_s hsty_drm_config_data[8];
unsigned int hsty_drm_config_loc, hsty_drm_config_num;
struct master_display_info_s drm_config_data;
static void hdmitx_set_drm_pkt(struct master_display_info_s *data)
{
struct hdmitx_dev *hdev = &hdmitx_device;
unsigned char DRM_HB[3] = {0x87, 0x1, 26};
static unsigned char DRM_DB[26] = {0x0};
unsigned long flags = 0;
hdmi_debug();
spin_lock_irqsave(&hdev->edid_spinlock, flags);
if (data)
memcpy(&drm_config_data, data,
sizeof(struct master_display_info_s));
else
memset(&drm_config_data, 0,
sizeof(struct master_display_info_s));
if (hsty_drm_config_loc > 7)
hsty_drm_config_loc = 0;
memcpy(&hsty_drm_config_data[hsty_drm_config_loc++],
&drm_config_data, sizeof(struct master_display_info_s));
if (hsty_drm_config_num < 0xfffffff0)
hsty_drm_config_num++;
else
hsty_drm_config_num = 8;
init_drm_db0(hdev, &DRM_DB[0]);
if (hdr_status_pos == 4) {
/* zero hdr10+ VSIF being sent - disable it */
pr_info("hdmitx_set_drm_pkt: disable hdr10+ zero vsif\n");
hdev->hwop.setpacket(HDMI_PACKET_VEND, NULL, NULL);
hdr_status_pos = 0;
}
/*
*hdr_color_feature: bit 23-16: color_primaries
* 1:bt709 0x9:bt2020
*hdr_transfer_feature: bit 15-8: transfer_characteristic
* 1:bt709 0xe:bt2020-10 0x10:smpte-st-2084 0x12:hlg(todo)
*/
if (data) {
hdev->hdr_transfer_feature = (data->features >> 8) & 0xff;
hdev->hdr_color_feature = (data->features >> 16) & 0xff;
hdev->colormetry = (data->features >> 30) & 0x1;
}
if ((hdr_status_pos != 1) && (hdr_status_pos != 3))
pr_info("hdmitx_set_drm_pkt: tf=%d, cf=%d, colormetry=%d\n",
hdev->hdr_transfer_feature, hdev->hdr_color_feature,
hdev->colormetry);
hdr_status_pos = 1;
/* hdr10+ content on a hdr10 sink case */
if (hdev->hdr_transfer_feature == 0x30) {
if ((hdev->rxcap.hdr10plus_info.ieeeoui != 0x90848B) ||
(hdev->rxcap.hdr10plus_info.application_version != 1)) {
hdev->hdr_transfer_feature = T_SMPTE_ST_2084;
pr_info("hdmitx_set_drm_pkt: HDR10+ not supported, treat as hdr10\n");
}
}
if ((!data) || (!(hdev->rxcap.hdr_sup_eotf_smpte_st_2084) &&
!(hdev->rxcap.hdr_sup_eotf_hdr) &&
!(hdev->rxcap.hdr_sup_eotf_sdr) &&
!(hdev->rxcap.hdr_sup_eotf_hlg))) {
DRM_HB[1] = 0;
DRM_HB[2] = 0;
DRM_DB[0] = 0;
hdmitx_device.hwop.setpacket(HDMI_PACKET_DRM, NULL, NULL);
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
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("hdmitx_set_drm_pkt: HDR->SDR, DRM_DB[0]=%d\n",
DRM_DB[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) {
hdmitx_device.hwop.setpacket(HDMI_PACKET_DRM,
NULL, NULL);
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
CONF_AVI_BT2020, SET_AVI_BT2020);
} else if (hdev->sdr_hdr_feature == 1) {
memset(DRM_DB, 0, sizeof(DRM_DB));
hdev->hwop.setpacket(HDMI_PACKET_DRM,
DRM_DB, DRM_HB);
hdev->hwop.cntlconfig(&hdmitx_device,
CONF_AVI_BT2020, SET_AVI_BT2020);
} else {
DRM_DB[0] = 0x02; /* SMPTE ST 2084 */
hdmitx_device.hwop.setpacket(HDMI_PACKET_DRM,
DRM_DB, DRM_HB);
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
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_sup_eotf_smpte_st_2084) {
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_sup_eotf_hlg) {
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 */
hdmitx_device.hwop.setpacket(HDMI_PACKET_DRM,
DRM_DB, DRM_HB);
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
CONF_AVI_BT2020, SET_AVI_BT2020);
break;
case 2:
/*non standard*/
DRM_DB[0] = 0x02; /* no standard SMPTE ST 2084 */
hdmitx_device.hwop.setpacket(HDMI_PACKET_DRM,
DRM_DB, DRM_HB);
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
CONF_AVI_BT2020, CLR_AVI_BT2020);
break;
case 3:
/*HLG*/
DRM_DB[0] = 0x03;/* HLG is 0x03 */
hdmitx_device.hwop.setpacket(HDMI_PACKET_DRM,
DRM_DB, DRM_HB);
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
CONF_AVI_BT2020, SET_AVI_BT2020);
break;
case 0:
default:
/*other case*/
hdmitx_device.hwop.setpacket(HDMI_PACKET_DRM, NULL, NULL);
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
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);
}
struct hdr10plus_para hdr10p_config_data;
struct hdr10plus_para hsty_hdr10p_config_data[8];
unsigned int hsty_hdr10p_config_loc, hsty_hdr10p_config_num;
static void hdmitx_set_hdr10plus_pkt(unsigned int flag,
struct hdr10plus_para *data)
{
struct hdmitx_dev *hdev = &hdmitx_device;
unsigned char VEN_HB[3] = {0x81, 0x01, 0x1b};
unsigned char VEN_DB[27] = {0x00};
hdmi_debug();
if (hdev->bist_lock)
return;
if (data)
memcpy(&hdr10p_config_data, data,
sizeof(struct hdr10plus_para));
else
memset(&hdr10p_config_data, 0,
sizeof(struct hdr10plus_para));
if (hsty_hdr10p_config_loc > 7)
hsty_hdr10p_config_loc = 0;
memcpy(&hsty_hdr10p_config_data[hsty_hdr10p_config_loc++],
&hdr10p_config_data, sizeof(struct hdr10plus_para));
if (hsty_hdr10p_config_num < 0xfffffff0)
hsty_hdr10p_config_num++;
else
hsty_hdr10p_config_num = 8;
if (flag == HDR10_PLUS_ZERO_VSIF) {
/* needed during hdr10+ to sdr transition */
pr_info("hdmitx_set_hdr10plus_pkt: zero vsif\n");
hdev->hwop.setpacket(HDMI_PACKET_VEND, VEN_DB, VEN_HB);
hdev->hwop.cntlconfig(hdev, CONF_AVI_BT2020,
CLR_AVI_BT2020);
hdev->hdr10plus_feature = 0;
hdr_status_pos = 4;
return;
}
if ((!data) || (!flag)) {
pr_info("hdmitx_set_hdr10plus_pkt: null vsif\n");
hdev->hwop.setpacket(HDMI_PACKET_VEND, NULL, NULL);
hdev->hwop.cntlconfig(hdev, CONF_AVI_BT2020,
CLR_AVI_BT2020);
hdev->hdr10plus_feature = 0;
return;
}
if (hdev->hdr10plus_feature != 1)
pr_info("hdmitx_set_hdr10plus_pkt: flag = %d\n", 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);
hdev->hwop.setpacket(HDMI_PACKET_VEND, VEN_DB, VEN_HB);
hdev->hwop.cntlconfig(hdev, CONF_AVI_BT2020,
SET_AVI_BT2020);
}
#define EMP_FIRST 0x80
#define EMP_LAST 0x40
struct emp_debug_save emp_config_data;
static void hdmitx_set_emp_pkt(unsigned char *data, unsigned int type,
unsigned int size)
{
unsigned int number;
unsigned int remainder;
static unsigned char *virt_ptr;
unsigned char *virt_ptr_align32bit;
unsigned long phys_ptr;
unsigned int i;
struct hdmitx_dev *hdev = &hdmitx_device;
unsigned int ds_type = 0;
unsigned char afr = 0;
unsigned char vfr = 0;
unsigned char sync = 0;
unsigned char new = 0;
unsigned char end = 0;
unsigned int organzation_id = 0;
unsigned int data_set_tag = 0;
unsigned int data_set_length = 0;
hdmi_debug();
if (!data)
return;
emp_config_data.type = type;
emp_config_data.size = size;
if (size <= 128)
memcpy(emp_config_data.data, data, size);
else
memcpy(emp_config_data.data, data, 128);
if (hdmitx_device.chip_type < MESON_CPU_ID_G12A) {
pr_info("this chip doesn't support emp function\n");
return;
}
if (size <= 21) {
number = 1;
remainder = size;
} else {
number = ((size - 21) / 28) + 2;
remainder = (size - 21) % 28;
}
if (!virt_ptr) {
virt_ptr = kzalloc(sizeof(unsigned char) * (number + 0x1f),
GFP_KERNEL);
}
if (!virt_ptr)
return;
virt_ptr_align32bit = (unsigned char *)
((((unsigned long)virt_ptr) + 0x1f) & (~0x1f));
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 show_config(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
unsigned char *conf;
struct hdmitx_dev *hdev = &hdmitx_device;
pos += snprintf(buf + pos, PAGE_SIZE, "cur_VIC: %d\n", hdev->cur_VIC);
if (hdev->cur_video_param)
pos += snprintf(buf + pos, PAGE_SIZE,
"cur_video_param->VIC=%d\n",
hdev->cur_video_param->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 COLORSPACE_RGB444:
conf = "RGB";
break;
case COLORSPACE_YUV422:
conf = "422";
break;
case COLORSPACE_YUV444:
conf = "444";
break;
case 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 store_config(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};
pr_info("hdmitx: config: %s\n", buf);
if (strncmp(buf, "unplug_powerdown", 16) == 0) {
if (buf[16] == '0')
hdmitx_device.unplug_powerdown = 0;
else
hdmitx_device.unplug_powerdown = 1;
} else if (strncmp(buf, "3d", 2) == 0) {
/* Second, set 3D parameters */
if (strncmp(buf + 2, "tb", 2) == 0) {
hdmitx_device.flag_3dtb = 1;
hdmitx_device.flag_3dss = 0;
hdmitx_device.flag_3dfp = 0;
hdmi_set_3d(&hdmitx_device, T3D_TAB, 0);
} else if ((strncmp(buf + 2, "lr", 2) == 0) ||
(strncmp(buf + 2, "ss", 2) == 0)) {
unsigned long sub_sample_mode = 0;
hdmitx_device.flag_3dtb = 0;
hdmitx_device.flag_3dss = 1;
hdmitx_device.flag_3dfp = 0;
if (buf[2])
ret = kstrtoul(buf + 2, 10,
&sub_sample_mode);
/* side by side */
hdmi_set_3d(&hdmitx_device, T3D_SBS_HALF,
sub_sample_mode);
} else if (strncmp(buf + 2, "fp", 2) == 0) {
hdmitx_device.flag_3dtb = 0;
hdmitx_device.flag_3dss = 0;
hdmitx_device.flag_3dfp = 1;
hdmi_set_3d(&hdmitx_device, T3D_FRAME_PACKING, 0);
} else if (strncmp(buf + 2, "off", 3) == 0) {
hdmitx_device.flag_3dfp = 0;
hdmitx_device.flag_3dtb = 0;
hdmitx_device.flag_3dss = 0;
hdmi_set_3d(&hdmitx_device, 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) {
if (hdmitx_device.chip_type >= MESON_CPU_ID_G12A)
hdmitx_set_emp_pkt(NULL, 1, 1);
} else if (strncmp(buf, "hdr10+", 6) == 0) {
hdmitx_set_hdr10plus_pkt(1, &hdr_data);
}
return count;
}
void hdmitx_ext_set_audio_output(int enable)
{
hdmitx_audio_mute_op(enable);
}
int hdmitx_ext_get_audio_status(void)
{
return !!hdmitx_device.tx_aud_cfg;
}
void hdmitx_ext_set_i2s_mask(char ch_num, char ch_msk)
{
struct hdmitx_dev *hdev = &hdmitx_device;
static unsigned int update_flag = -1;
if (!((ch_num == 2) || (ch_num == 4) ||
(ch_num == 6) || (ch_num == 8))) {
hdev->aud_output_ch = 0;
if (update_flag != hdev->aud_output_ch) {
update_flag = hdev->aud_output_ch;
hdev->hdmi_ch = 0;
hdmitx_set_audio(hdev, &hdev->cur_audio_param);
}
}
if (ch_msk == 0)
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;
hdmitx_set_audio(hdev, &hdev->cur_audio_param);
}
}
char hdmitx_ext_get_i2s_mask(void)
{
struct hdmitx_dev *hdev = &hdmitx_device;
return hdev->aud_output_ch & 0xf;
}
static ssize_t show_vid_mute(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
atomic_read(&hdmitx_device.kref_video_mute));
return pos;
}
static ssize_t store_vid_mute(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
atomic_t kref_video_mute = hdmitx_device.kref_video_mute;
if (buf[0] == '1') {
atomic_inc(&kref_video_mute);
if (atomic_read(&kref_video_mute) == 1)
hdmitx_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))
hdmitx_video_mute_op(1);
}
}
hdmitx_device.kref_video_mute = kref_video_mute;
return count;
}
static ssize_t store_debug(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
hdmitx_device.hwop.debugfun(&hdmitx_device, buf);
return count;
}
/* support format lists */
const char *disp_mode_t[] = {
#if 1
"480i60hz",
"576i50hz",
#endif
"480p60hz",
"576p50hz",
"720p60hz",
"1080i60hz",
"1080p60hz",
"720p50hz",
"1080i50hz",
"1080p30hz",
"1080p50hz",
"1080p25hz",
"1080p24hz",
"2560x1080p50hz",
"2560x1080p60hz",
"2160p30hz",
"2160p25hz",
"2160p24hz",
"smpte24hz",
"smpte25hz",
"smpte30hz",
"smpte50hz",
"smpte60hz",
"2160p50hz",
"2160p60hz",
/* VESA modes */
"640x480p60hz",
"800x480p60hz",
"800x600p60hz",
"852x480p60hz",
"854x480p60hz",
"1024x600p60hz",
"1024x768p60hz",
"1152x864p75hz",
"1280x600p60hz",
"1280x768p60hz",
"1280x800p60hz",
"1280x960p60hz",
"1280x1024p60hz",
"1360x768p60hz",
"1366x768p60hz",
"1400x1050p60hz",
"1440x900p60hz",
"1440x2560p60hz",
"1600x900p60hz",
"1600x1200p60hz",
"1680x1050p60hz",
"1920x1200p60hz",
"2160x1200p90hz",
"2560x1080p60hz",
"2560x1440p60hz",
"2560x1600p60hz",
"3440x1440p60hz",
"2400x1200p90hz",
NULL
};
static int is_4k50_fmt(char *mode)
{
int i;
static char const *hdmi4k50[] = {
"2160p50hz",
"2160p60hz",
"smpte50hz",
"smpte60hz",
NULL
};
for (i = 0; hdmi4k50[i]; i++) {
if (strcmp(hdmi4k50[i], mode) == 0)
return 1;
}
return 0;
}
static int is_4k_fmt(char *mode)
{
int i;
static char const *hdmi4k[] = {
"2160p",
"smpte",
NULL
};
for (i = 0; hdmi4k[i]; i++) {
if (strstr(mode, hdmi4k[i]))
return 1;
}
return 0;
}
/* below items has feature limited, may need extra judgement */
static bool hdmitx_limited_1080p(void)
{
if (is_meson_gxl_package_805X())
return 1;
else if (is_meson_gxl_package_805Y())
return 1;
else
return 0;
}
/**/
static ssize_t show_disp_cap(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i, pos = 0;
const char *native_disp_mode =
hdmitx_edid_get_native_VIC(&hdmitx_device);
enum hdmi_vic vic;
char mode_tmp[32];
if (hdmitx_device.tv_no_edid) {
pos += snprintf(buf + pos, PAGE_SIZE, "null edid\n");
} else {
for (i = 0; disp_mode_t[i]; i++) {
memset(mode_tmp, 0, sizeof(mode_tmp));
strncpy(mode_tmp, disp_mode_t[i], 31);
if (hdmitx_limited_1080p() && is_4k_fmt(mode_tmp))
continue;
vic = hdmitx_edid_get_VIC(&hdmitx_device, mode_tmp, 0);
/* Handling only 4k420 mode */
if (vic == 0) {
if (is_4k50_fmt(mode_tmp)) {
strcat(mode_tmp, "420");
vic =
hdmitx_edid_get_VIC(&hdmitx_device,
mode_tmp, 0);
}
}
if (vic != 0) {
pos += snprintf(buf + pos, PAGE_SIZE, "%s",
disp_mode_t[i]);
if (native_disp_mode && (strcmp(
native_disp_mode,
disp_mode_t[i]) == 0)) {
pos += snprintf(buf + pos,
PAGE_SIZE,
"*\n");
} else {
pos += snprintf(buf + pos,
PAGE_SIZE,
"\n");
}
}
}
}
return pos;
}
static ssize_t show_preferred_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct rx_cap *prxcap = &hdmitx_device.rxcap;
pos += snprintf(buf + pos, PAGE_SIZE, "%s\n",
hdmitx_edid_vic_to_string(prxcap->preferred_mode));
return pos;
}
/* cea_cap, a clone of disp_cap */
static ssize_t show_cea_cap(struct device *dev,
struct device_attribute *attr, char *buf)
{
return show_disp_cap(dev, attr, buf);
}
static ssize_t show_vesa_cap(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i;
struct hdmi_format_para *para = NULL;
enum hdmi_vic *vesa_t = &hdmitx_device.rxcap.vesa_timing[0];
int pos = 0;
for (i = 0; vesa_t[i] && i < VESA_MAX_TIMING; i++) {
para = hdmi_get_fmt_paras(vesa_t[i]);
if (para && (para->vic >= HDMITX_VESA_OFFSET))
pos += snprintf(buf + pos,
PAGE_SIZE,
"%s\n",
para->name);
}
return pos;
}
/**/
static int local_support_3dfp(enum hdmi_vic vic)
{
switch (vic) {
case HDMI_1280x720p50_16x9:
case HDMI_1280x720p60_16x9:
case HDMI_1920x1080p24_16x9:
case HDMI_1920x1080p25_16x9:
case HDMI_1920x1080p30_16x9:
case HDMI_1920x1080p50_16x9:
case HDMI_1920x1080p60_16x9:
return 1;
default:
return 0;
}
}
static ssize_t show_disp_cap_3d(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i, pos = 0;
int j = 0;
enum hdmi_vic vic;
pos += snprintf(buf + pos, PAGE_SIZE, "3D support lists:\n");
for (i = 0; disp_mode_t[i]; i++) {
/* 3D is not supported under 4k modes */
if (strstr(disp_mode_t[i], "2160p") ||
strstr(disp_mode_t[i], "smpte"))
continue;
vic = hdmitx_edid_get_VIC(&hdmitx_device,
disp_mode_t[i], 0);
for (j = 0; j < hdmitx_device.rxcap.VIC_count; j++) {
if (vic == hdmitx_device.rxcap.VIC[j])
break;
}
pos += snprintf(buf + pos, PAGE_SIZE, "\n%s ",
disp_mode_t[i]);
if (local_support_3dfp(vic) &&
(hdmitx_device.rxcap.support_3d_format[
hdmitx_device.rxcap.VIC[j]].frame_packing == 1)) {
pos += snprintf(buf + pos, PAGE_SIZE,
"FramePacking ");
}
if (hdmitx_device.rxcap.support_3d_format[
hdmitx_device.rxcap.VIC[j]].top_and_bottom == 1) {
pos += snprintf(buf + pos, PAGE_SIZE,
"TopBottom ");
}
if (hdmitx_device.rxcap.support_3d_format[
hdmitx_device.rxcap.VIC[j]].side_by_side == 1) {
pos += snprintf(buf + pos, PAGE_SIZE,
"SidebySide ");
}
}
pos += snprintf(buf + pos, PAGE_SIZE, "\n");
return pos;
}
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 show_aud_cap(struct device *dev,
struct device_attribute *attr, char *buf)
{
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 = &hdmitx_device.rxcap;
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;
}
}
return pos;
}
/**/
static ssize_t show_hdmi_hdr_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = &hdmitx_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 show_dc_cap(struct device *dev,
struct device_attribute *attr, char *buf)
{
enum hdmi_vic vic = 0;
int pos = 0;
struct rx_cap *prxcap = &hdmitx_device.rxcap;
const struct dv_info *dv = &hdmitx_device.rxcap.dv_info;
const struct dv_info *dv2 = &hdmitx_device.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 {
vic = hdmitx_edid_get_VIC(&hdmitx_device, "2160p60hz420", 0);
if (vic != 0) {
pos += snprintf(buf + pos, PAGE_SIZE, "420,8bit\n");
goto next444;
}
vic = hdmitx_edid_get_VIC(&hdmitx_device, "2160p50hz420", 0);
if (vic != 0) {
pos += snprintf(buf + pos, PAGE_SIZE, "420,8bit\n");
goto next444;
}
vic = hdmitx_edid_get_VIC(&hdmitx_device, "smpte60hz420", 0);
if (vic != 0) {
pos += snprintf(buf + pos, PAGE_SIZE, "420,8bit\n");
goto next444;
}
vic = hdmitx_edid_get_VIC(&hdmitx_device, "smpte50hz420", 0);
if (vic != 0) {
pos += snprintf(buf + pos, PAGE_SIZE, "420,8bit\n");
goto next444;
}
}
next444:
if (prxcap->dc_y444) {
if ((prxcap->dc_36bit) || (dv->sup_10b_12b_444 == 0x2) ||
(dv2->sup_10b_12b_444 == 0x2))
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)) {
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))
pos += snprintf(buf + pos, PAGE_SIZE, "422,12bit\n");
if (prxcap->dc_30bit) {
pos += snprintf(buf + pos, PAGE_SIZE, "422,10bit\n");
pos += snprintf(buf + pos, PAGE_SIZE, "422,8bit\n");
goto nextrgb;
}
} 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,8bit\n");
}
nextrgb:
if ((prxcap->dc_36bit) || (dv->sup_10b_12b_444 == 0x2) ||
(dv2->sup_10b_12b_444 == 0x2))
pos += snprintf(buf + pos, PAGE_SIZE, "rgb,12bit\n");
if ((prxcap->dc_30bit) || (dv->sup_10b_12b_444 == 0x1) ||
(dv2->sup_10b_12b_444 == 0x1))
pos += snprintf(buf + pos, PAGE_SIZE, "rgb,10bit\n");
pos += snprintf(buf + pos, PAGE_SIZE, "rgb,8bit\n");
return pos;
}
static bool valid_mode;
static char cvalid_mode[32];
static bool pre_process_str(char *name)
{
int i;
unsigned int 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 show_valid_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
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 = hdmi_tst_fmt_name(cvalid_mode, cvalid_mode);
}
if (para) {
if (para->sname)
pr_info(SYS "sname = %s\n", para->sname);
else
pr_info(SYS "name = %s\n", para->name);
pr_info(SYS "char_clk = %d\n", para->tmds_clk);
pr_info(SYS "cd = %d\n", para->cd);
pr_info(SYS "cs = %d\n", para->cs);
}
valid_mode = hdmitx_edid_check_valid_mode(&hdmitx_device, para);
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n\r", valid_mode);
return pos;
}
static ssize_t store_valid_mode(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 show_allm_cap(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct rx_cap *prxcap = &hdmitx_device.rxcap;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n\r", prxcap->allm);
return pos;
}
static ssize_t show_allm_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = &hdmitx_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 store_allm_mode(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = &hdmitx_device;
pr_info("hdmitx: store allm_mode as %s\n", buf);
if (com_str(buf, "0")) {
// disable ALLM
hdev->allm_mode = 0;
hdmitx_construct_vsif(hdev, VT_ALLM, 0, NULL);
if (is_hdmi14_4k(hdev->cur_VIC))
hdmitx_construct_vsif(hdev, VT_HDMI14_4K, 1, NULL);
}
if (com_str(buf, "1")) {
hdev->allm_mode = 1;
hdmitx_construct_vsif(hdev, VT_ALLM, 1, NULL);
hdev->hwop.cntlconfig(hdev, CONF_CT_MODE, SET_CT_OFF);
}
if (com_str(buf, "-1"))
hdev->hwop.disablepacket(HDMI_PACKET_VEND);
return count;
}
static ssize_t show_contenttype_cap(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct rx_cap *prxcap = &hdmitx_device.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 show_contenttype_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = &hdmitx_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 store_contenttype_mode(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = &hdmitx_device;
pr_info("hdmitx: store contenttype_mode as %s\n", buf);
hdev->allm_mode = 0;
hdmitx_construct_vsif(hdev, VT_ALLM, 0, NULL);
if (is_hdmi14_4k(hdev->cur_VIC))
hdmitx_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 show_hdr_cap(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
unsigned int i, j;
struct rx_cap *prxcap = &hdmitx_device.rxcap;
int hdr10plugsupported = 0;
if ((prxcap->hdr10plus_info.ieeeoui == HDR10_PLUS_IEEE_OUI) &&
(prxcap->hdr10plus_info.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",
prxcap->hdr_sup_eotf_sdr);
pos += snprintf(buf + pos, PAGE_SIZE, " Traditional HDR: %d\n",
prxcap->hdr_sup_eotf_hdr);
pos += snprintf(buf + pos, PAGE_SIZE, " SMPTE ST 2084: %d\n",
prxcap->hdr_sup_eotf_smpte_st_2084);
pos += snprintf(buf + pos, PAGE_SIZE, " Hybrif Log-Gamma: %d\n",
prxcap->hdr_sup_eotf_hlg);
pos += snprintf(buf + pos, PAGE_SIZE, " Supported SMD type1: %d\n",
prxcap->hdr_sup_SMD_type1);
pos += snprintf(buf + pos, PAGE_SIZE, " Luminance Data\n");
pos += snprintf(buf + pos, PAGE_SIZE, " Max: %d\n",
prxcap->hdr_lum_max);
pos += snprintf(buf + pos, PAGE_SIZE, " Avg: %d\n",
prxcap->hdr_lum_avg);
pos += snprintf(buf + pos, PAGE_SIZE, " Min: %d\n\n",
prxcap->hdr_lum_min);
pos += snprintf(buf + pos, PAGE_SIZE, "HDR Dynamic Metadata:");
for (i = 0; i < 4; i++) {
if (prxcap->hdr_dynamic_info[i].type == 0)
continue;
pos += snprintf(buf + pos, PAGE_SIZE,
"\n metadata_version: %x\n",
prxcap->hdr_dynamic_info[i].type);
pos += snprintf(buf + pos, PAGE_SIZE,
" support_flags: %x\n",
prxcap->hdr_dynamic_info[i].support_flags);
pos += snprintf(buf + pos, PAGE_SIZE,
" optional_fields:");
for (j = 0; j <
(prxcap->hdr_dynamic_info[i].hd_len - 3); j++)
pos += snprintf(buf + pos, PAGE_SIZE, " %x",
prxcap->hdr_dynamic_info[i].optional_fields[j]);
}
pos += snprintf(buf + pos, PAGE_SIZE, "\n\ncolorimetry_data: %x\n",
prxcap->colorimetry_data);
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;
}
if (dv->block_flag != CORRECT) {
pos += snprintf(buf + pos, PAGE_SIZE,
"DolbyVision block is error\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 show_dv_cap(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
const struct dv_info *dv = &hdmitx_device.rxcap.dv_info;
if (dv->ieeeoui != DV_IEEE_OUI || hdmitx_device.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 show_dv_cap2(struct device *dev,
struct device_attribute *attr, char *buf)
{
const struct dv_info *dv2 = &hdmitx_device.rxcap.dv_info2;
return _show_dv_cap(dev, attr, buf, dv2);
}
static ssize_t show_aud_ch(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE,
"hdmi_channel = %d ch\n",
hdmitx_device.hdmi_ch ? hdmitx_device.hdmi_ch + 1 : 0);
return pos;
}
static ssize_t store_aud_ch(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
if (strncmp(buf, "6ch", 3) == 0)
hdmitx_device.hdmi_ch = 5;
else if (strncmp(buf, "8ch", 3) == 0)
hdmitx_device.hdmi_ch = 7;
else if (strncmp(buf, "2ch", 3) == 0)
hdmitx_device.hdmi_ch = 1;
else
return count;
hdmitx_device.audio_param_update_flag = 1;
hdmitx_device.force_audio_flag = 1;
return count;
}
/*
* 1: set avmute
* -1: clear avmute
* 0: off avmute
*/
static ssize_t store_avmute(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int cmd = OFF_AVMUTE;
static int mask0;
static int mask1;
static DEFINE_MUTEX(avmute_mutex);
pr_info("store_avmute %s\n", 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;
hdmitx_device.hwop.cntlmisc(&hdmitx_device, MISC_AVMUTE_OP, cmd);
mutex_unlock(&avmute_mutex);
return count;
}
static ssize_t show_avmute(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hdmitx_dev *hdev = &hdmitx_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;
}
/*
* 0: clear vic
*/
static ssize_t store_vic(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = &hdmitx_device;
if (strncmp(buf, "0", 1) == 0) {
hdev->hwop.cntlconfig(hdev, CONF_CLR_AVI_PACKET, 0);
hdev->hwop.cntlconfig(hdev, CONF_CLR_VSDB_PACKET, 0);
}
return count;
}
static ssize_t show_vic(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hdmitx_dev *hdev = &hdmitx_device;
enum hdmi_vic vic = 0;
int pos = 0;
vic = hdev->hwop.getstate(hdev, STAT_VIDEO_VIC, 0);
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n", vic);
return pos;
}
/*
* 1: enable hdmitx phy
* 0: disable hdmitx phy
*/
static ssize_t store_phy(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int cmd = TMDS_PHY_ENABLE;
if (strncmp(buf, "0", 1) == 0)
cmd = TMDS_PHY_DISABLE;
else if (strncmp(buf, "1", 1) == 0)
cmd = TMDS_PHY_ENABLE;
hdmitx_device.hwop.cntlmisc(&hdmitx_device, MISC_TMDS_PHY_OP, cmd);
return count;
}
static ssize_t show_phy(struct device *dev,
struct device_attribute *attr, char *buf)
{
return 0;
}
static ssize_t store_rxsense_policy(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int val = 0;
if (isdigit(buf[0])) {
val = buf[0] - '0';
if ((val == 0) || (val == 1))
hdmitx_device.rxsense_policy = val;
}
if (hdmitx_device.rxsense_policy)
queue_delayed_work(hdmitx_device.rxsense_wq,
&hdmitx_device.work_rxsense,
0);
else
cancel_delayed_work(&hdmitx_device.work_rxsense);
return count;
}
static ssize_t show_rxsense_policy(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdmitx_device.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 store_cedst_policy(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int val = 0;
struct hdmitx_dev *hdev = &hdmitx_device;
if (isdigit(buf[0])) {
val = buf[0] - '0';
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;
}
}
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 show_cedst_policy(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdmitx_device.cedst_policy);
return pos;
}
static ssize_t show_cedst_count(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct ced_cnt *ced = &hdmitx_device.ced_cnt;
struct scdc_locked_st *ch_st = &hdmitx_device.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 store_sspll(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int val = 0;
if (isdigit(buf[0])) {
val = buf[0] - '0';
pr_info(SYS "set sspll : %d\n", val);
if ((val == 0) || (val == 1))
hdmitx_device.sspll = val;
}
return count;
}
static ssize_t show_sspll(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdmitx_device.sspll);
return pos;
}
static ssize_t store_frac_rate(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int val = 0;
if (isdigit(buf[0])) {
val = buf[0] - '0';
pr_info(SYS "set frac_rate_policy as %d\n", val);
if ((val == 0) || (val == 1))
hdmitx_device.frac_rate_policy = val;
}
return count;
}
static ssize_t show_frac_rate(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdmitx_device.frac_rate_policy);
return pos;
}
static ssize_t store_hdcp_clkdis(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
hdmitx_device.hwop.cntlmisc(&hdmitx_device, MISC_HDCP_CLKDIS,
buf[0] == '1' ? 1 : 0);
return count;
}
static ssize_t show_hdcp_clkdis(struct device *dev,
struct device_attribute *attr, char *buf)
{
return 0;
}
static ssize_t store_hdcp_pwr(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
if (buf[0] == '1')
hdmitx_device.hdcp_tst_sig = 1;
return count;
}
static ssize_t show_hdcp_pwr(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
if (hdmitx_device.hdcp_tst_sig == 1) {
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdmitx_device.hdcp_tst_sig);
hdmitx_device.hdcp_tst_sig = 0;
}
return pos;
}
static ssize_t store_hdcp_byp(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
hdmitx_device.hwop.cntlmisc(&hdmitx_device, MISC_HDCP_CLKDIS,
buf[0] == '1' ? 1 : 0);
return count;
}
static ssize_t show_hdcp_lstore(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
/* if current TX is RP-TX, then return lstore as 00 */
/* hdcp_lstore is used under only TX */
if (hdmitx_device.repeater_tx == 1) {
pos += snprintf(buf + pos, PAGE_SIZE, "00\n");
return pos;
}
if (hdmitx_device.lstore < 0x10) {
hdmitx_device.lstore = 0;
if (hdmitx_device.hwop.cntlddc(&hdmitx_device,
DDC_HDCP_14_LSTORE, 0))
hdmitx_device.lstore += 1;
if (hdmitx_device.hwop.cntlddc(&hdmitx_device,
DDC_HDCP_22_LSTORE, 0))
hdmitx_device.lstore += 2;
}
if ((hdmitx_device.lstore & 0x3) == 0x3) {
pos += snprintf(buf + pos, PAGE_SIZE, "14+22\n");
} else {
if (hdmitx_device.lstore & 0x1)
pos += snprintf(buf + pos, PAGE_SIZE, "14\n");
if (hdmitx_device.lstore & 0x2)
pos += snprintf(buf + pos, PAGE_SIZE, "22\n");
if ((hdmitx_device.lstore & 0xf) == 0)
pos += snprintf(buf + pos, PAGE_SIZE, "00\n");
}
return pos;
}
static ssize_t store_hdcp_lstore(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
if (strncmp(buf, "0", 1) == 0)
hdmitx_device.lstore = 0x10;
if (strncmp(buf, "11", 2) == 0)
hdmitx_device.lstore = 0x11;
if (strncmp(buf, "12", 2) == 0)
hdmitx_device.lstore = 0x12;
if (strncmp(buf, "13", 2) == 0)
hdmitx_device.lstore = 0x13;
return count;
}
static int rptxlstore;
static ssize_t show_hdcp_rptxlstore(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
/* if current TX is not RP-TX, then return rptxlstore as 00 */
/* hdcp_rptxlstore is used under only RP-TX */
if (hdmitx_device.repeater_tx == 0) {
pos += snprintf(buf + pos, PAGE_SIZE, "00\n");
return pos;
}
if (rptxlstore < 0x10) {
rptxlstore = 0;
if (hdmitx_device.hwop.cntlddc(&hdmitx_device,
DDC_HDCP_14_LSTORE, 0))
rptxlstore += 1;
if (hdmitx_device.hwop.cntlddc(&hdmitx_device,
DDC_HDCP_22_LSTORE, 0))
rptxlstore += 2;
}
if (rptxlstore & 0x1)
pos += snprintf(buf + pos, PAGE_SIZE, "14\n");
if (rptxlstore & 0x2)
pos += snprintf(buf + pos, PAGE_SIZE, "22\n");
if ((rptxlstore & 0xf) == 0)
pos += snprintf(buf + pos, PAGE_SIZE, "00\n");
return pos;
}
static ssize_t store_hdcp_rptxlstore(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
if (strncmp(buf, "0", 1) == 0)
rptxlstore = 0x10;
if (strncmp(buf, "11", 2) == 0)
rptxlstore = 0x11;
if (strncmp(buf, "12", 2) == 0)
rptxlstore = 0x12;
if (strncmp(buf, "13", 2) == 0)
rptxlstore = 0x13;
return count;
}
static ssize_t show_div40(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdmitx_device.para->tmds_clk_div40);
return pos;
}
static ssize_t store_div40(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = &hdmitx_device;
hdev->hwop.cntlddc(hdev, DDC_SCDC_DIV40_SCRAMB, buf[0] == '1');
hdmitx_device.div40 = (buf[0] == '1');
return count;
}
static ssize_t show_hdcp_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
struct hdmitx_dev *hdev = &hdmitx_device;
unsigned int exe_type;
unsigned int result_type;
hdev->hwop.am_hdmitx_hdcp_result(&exe_type, &result_type);
switch (exe_type) {
case 1:
pos += snprintf(buf + pos, PAGE_SIZE, "14 : ");
break;
case 2:
pos += snprintf(buf + pos, PAGE_SIZE, "22 : ");
break;
default:
pos += snprintf(buf + pos, PAGE_SIZE, "off");
break;
}
if (exe_type == 1 ||
exe_type == 2) {
if (result_type == 1)
pos += snprintf(buf + pos, PAGE_SIZE, "succeed");
else
pos += snprintf(buf + pos, PAGE_SIZE, "fail");
}
return pos;
}
static ssize_t store_hdcp_mode(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
enum hdmi_vic vic =
hdmitx_device.hwop.getstate(&hdmitx_device, STAT_VIDEO_VIC, 0);
hdmitx_device.hwop.cntlddc(&hdmitx_device, DDC_HDCP_MUX_INIT, 1);
hdmitx_device.hwop.cntlddc(&hdmitx_device, DDC_HDCP_GET_AUTH, 0);
if (strncmp(buf, "0", 1) == 0) {
hdmitx_device.hdcp_mode = 0;
hdmitx_device.hwop.cntlddc(&hdmitx_device,
DDC_HDCP_OP, HDCP14_OFF);
hdmitx_hdcp_do_work(&hdmitx_device);
}
if (strncmp(buf, "1", 1) == 0) {
if ((vic == HDMI_576p50) ||
(vic == HDMI_576p50_16x9))
usleep_range(500000, 500010);
hdmitx_device.hdcp_mode = 1;
hdmitx_hdcp_do_work(&hdmitx_device);
hdmitx_device.hwop.cntlddc(&hdmitx_device,
DDC_HDCP_OP, HDCP14_ON);
}
if (strncmp(buf, "2", 1) == 0) {
hdmitx_device.hdcp_mode = 2;
hdmitx_hdcp_do_work(&hdmitx_device);
hdmitx_device.hwop.cntlddc(&hdmitx_device,
DDC_HDCP_MUX_INIT, 2);
}
return count;
}
static bool hdcp_sticky_mode;
static ssize_t show_hdcp_stickmode(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n", hdcp_sticky_mode);
return pos;
}
static ssize_t store_hdcp_stickmode(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
if (buf[0] == '0')
hdcp_sticky_mode = 0;
if (buf[0] == '1')
hdcp_sticky_mode = 1;
return count;
}
static unsigned char hdcp_sticky_step;
static ssize_t show_hdcp_stickstep(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%x\n", hdcp_sticky_step);
if (hdcp_sticky_step)
hdcp_sticky_step = 0;
return pos;
}
static ssize_t store_hdcp_stickstep(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
if (isdigit(buf[0]))
hdcp_sticky_step = buf[0] - '0';
return count;
}
/* Indicate whether a rptx under repeater */
static ssize_t show_repeater_tx(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
!!hdmitx_device.repeater_tx);
return pos;
}
#include <linux/amlogic/media/vout/hdmi_tx/hdmi_rptx.h>
void direct_hdcptx14_opr(enum rptx_hdcp14_cmd cmd, void *args)
{
int rst;
struct hdmitx_dev *hdev = &hdmitx_device;
switch (cmd) {
case RPTX_HDCP14_OFF:
hdev->hdcp_mode = 0;
hdev->hwop.cntlddc(hdev, DDC_HDCP_OP, HDCP14_OFF);
break;
case RPTX_HDCP14_ON:
hdev->hdcp_mode = 1;
hdev->hwop.cntlddc(hdev, DDC_HDCP_OP, HDCP14_ON);
break;
case RPTX_HDCP14_GET_AUTHST:
rst = hdev->hwop.cntlddc(hdev, DDC_HDCP_GET_AUTH, 0);
*(int *)args = rst;
break;
}
}
EXPORT_SYMBOL(direct_hdcptx14_opr);
static ssize_t store_hdcp_ctrl(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = &hdmitx_device;
if (hdev->hwop.cntlddc(hdev, DDC_HDCP_14_LSTORE, 0) == 0)
return count;
/* for repeater */
if (hdev->repeater_tx) {
dev_warn(dev, "hdmitx20: %s\n", buf);
if (strncmp(buf, "rstop", 5) == 0) {
if (strncmp(buf + 5, "14", 2) == 0)
hdev->hwop.cntlddc(hdev, DDC_HDCP_OP,
HDCP14_OFF);
if (strncmp(buf + 5, "22", 2) == 0)
hdev->hwop.cntlddc(hdev, DDC_HDCP_OP,
HDCP22_OFF);
hdev->hdcp_mode = 0;
hdmitx_hdcp_do_work(hdev);
}
return count;
}
/* for non repeater */
if (strncmp(buf, "stop", 4) == 0) {
dev_warn(dev, "hdmitx20: %s\n", buf);
if (strncmp(buf + 4, "14", 2) == 0)
hdev->hwop.cntlddc(hdev, DDC_HDCP_OP, HDCP14_OFF);
if (strncmp(buf + 4, "22", 2) == 0)
hdev->hwop.cntlddc(hdev, DDC_HDCP_OP, HDCP22_OFF);
hdev->hdcp_mode = 0;
hdmitx_hdcp_do_work(hdev);
}
return count;
}
static ssize_t show_hdcp_ctrl(struct device *dev,
struct device_attribute *attr, char *buf)
{
return 0;
}
static ssize_t show_hdcp_ksv_info(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0, i;
char bksv_buf[5];
hdmitx_device.hwop.cntlddc(&hdmitx_device, DDC_HDCP_GET_BKSV,
(unsigned long int)bksv_buf);
pos += snprintf(buf + pos, PAGE_SIZE, "HDCP14 BKSV: ");
for (i = 0; i < 5; i++) {
pos += snprintf(buf + pos, PAGE_SIZE, "%02x",
bksv_buf[i]);
}
pos += snprintf(buf + pos, PAGE_SIZE, " %s\n",
hdcp_ksv_valid(bksv_buf) ? "Valid" : "Invalid");
return pos;
}
/* Special FBC check */
static int check_fbc_special(unsigned char *edid_dat)
{
if ((edid_dat[250] == 0xfb) && (edid_dat[251] == 0x0c))
return 1;
else
return 0;
}
static ssize_t show_hdcp_ver(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
unsigned int ver = 0U;
if (check_fbc_special(&hdmitx_device.EDID_buf[0]) ||
check_fbc_special(&hdmitx_device.EDID_buf1[0])) {
pos += snprintf(buf + pos, PAGE_SIZE, "00\n\r");
return pos;
}
/* if TX don't have HDCP22 key, skip RX hdcp22 ver */
if (hdmitx_device.hwop.cntlddc(&hdmitx_device,
DDC_HDCP_22_LSTORE, 0) == 0)
goto next;
/* Detect RX support HDCP22 */
mutex_lock(&getedid_mutex);
ver = hdcp_rd_hdcp22_ver();
mutex_unlock(&getedid_mutex);
if (ver) {
pos += snprintf(buf + pos, PAGE_SIZE, "22\n\r");
pos += snprintf(buf + pos, PAGE_SIZE, "14\n\r");
return pos;
}
next: /* Detect RX support HDCP14 */
/* Here, must assume RX support HDCP14, otherwise affect 1A-03 */
pos += snprintf(buf + pos, PAGE_SIZE, "14\n\r");
return pos;
}
static ssize_t show_hpd_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d",
hdmitx_device.hpd_state);
return pos;
}
static ssize_t show_hdmitx_drm_flag(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d",
hdmitx_device.drm_feature);
return pos;
}
static ssize_t show_hdmi_used(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d",
hdmitx_device.already_used);
return pos;
}
static ssize_t show_fake_plug(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d", hdmitx_device.hpd_state);
}
static unsigned char fake_edid[1024];
static ssize_t store_fake_plug(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hdmitx_dev *hdev = &hdmitx_device;
char hex[] = "FF";
unsigned int res;
int i;
if (strncmp(buf, "1", 1) == 0) {
hdev->hpd_state = 1;
memset(fake_edid, 0, 1024);
if ((count >= 256 + 2) && (count <= 2048 + 2)) {
for (i = 0; i < (count - 2) / 2; i++) {
hex[0] = buf[2 + i * 2];
hex[1] = buf[3 + i * 2];
if (!kstrtouint(hex, 16, &res)) {
fake_edid[i] = res & 0xff;
} else {
memset(fake_edid, 0, 1024);
return count;
}
}
hdmitx_get_edid(hdev);
edidinfo_attach_to_vinfo(hdev);
hdmitx_notify_hpd(hdev->hpd_state, hdev->EDID_buf);
}
}
if (strncmp(buf, "0", 1) == 0)
hdev->hpd_state = 0;
extcon_set_state_sync(hdmitx_extcon_hdmi, EXTCON_DISP_HDMI,
hdev->hpd_state);
return count;
}
static ssize_t show_rhpd_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hdmitx_dev *hdev = &hdmitx_device;
int st;
st = hdev->hwop.cntlmisc(hdev, MISC_HPD_GPI_ST, 0);
return snprintf(buf, PAGE_SIZE, "%d", hdev->rhpd_state);
}
static ssize_t show_max_exceed_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hdmitx_dev *hdev = &hdmitx_device;
return snprintf(buf, PAGE_SIZE, "%d", hdev->hdcp_max_exceed_state);
}
static ssize_t show_hdmi_init(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos,
PAGE_SIZE,
"%d\n\r",
hdmitx_device.hdmi_init);
return pos;
}
static ssize_t show_ready(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\r\n",
hdmitx_device.ready);
return pos;
}
static ssize_t store_ready(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
if (strncmp(buf, "0", 1) == 0)
hdmitx_device.ready = 0;
if (strncmp(buf, "1", 1) == 0)
hdmitx_device.ready = 1;
return count;
}
static ssize_t show_support_3d(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pos += snprintf(buf + pos, PAGE_SIZE, "%d\n",
hdmitx_device.rxcap.threeD_present);
return pos;
}
#undef pr_fmt
#define pr_fmt(fmt) "" fmt
void print_drm_config_data(void)
{
enum hdmi_hdr_transfer hdr_transfer_feature;
enum hdmi_hdr_color hdr_color_feature;
unsigned int colormetry;
unsigned int hcnt, vcnt;
pr_info("***drm_config_data***\n");
hdr_transfer_feature = (drm_config_data.features >> 8) & 0xff;
hdr_color_feature = (drm_config_data.features >> 16) & 0xff;
colormetry = (drm_config_data.features >> 30) & 0x1;
pr_info("tf=%u, cf=%u, colormetry=%u\n",
hdr_transfer_feature, hdr_color_feature,
colormetry);
pr_info("primaries:\n");
for (vcnt = 0; vcnt < 3; vcnt++) {
for (hcnt = 0; hcnt < 2; hcnt++)
pr_info("%u, ", drm_config_data.primaries[vcnt][hcnt]);
pr_info("\n");
}
pr_info("white_point: ");
for (hcnt = 0; hcnt < 2; hcnt++)
pr_info("%u, ", drm_config_data.white_point[hcnt]);
pr_info("\n");
pr_info("luminance: ");
for (hcnt = 0; hcnt < 2; hcnt++)
pr_info("%u, ", drm_config_data.luminance[hcnt]);
pr_info("\n");
pr_info("max_content: %u, ", drm_config_data.max_content);
pr_info("max_frame_average: %u\n", drm_config_data.max_frame_average);
}
void print_vsif_config_data(void)
{
struct dv_vsif_para *data;
data = &vsif_debug_info.data;
pr_info("***vsif_config_data***\n");
pr_info("type: %u, tunnel: %u, sigsdr: %u\n",
vsif_debug_info.type,
vsif_debug_info.tunnel_mode,
vsif_debug_info.signal_sdr);
pr_info("dv_vsif_para:\n");
pr_info("ver: %u len: %u\n", data->ver, data->length);
pr_info("ll: %u dvsig: %u\n", data->vers.ver2.low_latency,
data->vers.ver2.dobly_vision_signal);
pr_info("bcMD: %u axMD: %u\n", data->vers.ver2.backlt_ctrl_MD_present,
data->vers.ver2.auxiliary_MD_present);
pr_info("PQhi: %u PQlow: %u\n", data->vers.ver2.eff_tmax_PQ_hi,
data->vers.ver2.eff_tmax_PQ_low);
pr_info("axrm: %u, axrv: %u, ", data->vers.ver2.auxiliary_runmode,
data->vers.ver2.auxiliary_runversion);
pr_info("axdbg: %u\n", data->vers.ver2.auxiliary_debug0);
}
void print_hdr10p_config_data(void)
{
unsigned int hcnt, vcnt;
unsigned char *tmp;
pr_info("***hdr10p_config_data***\n");
pr_info("appver: %u, tlum: %u, avgrgb: %u\n",
hdr10p_config_data.application_version,
hdr10p_config_data.targeted_max_lum,
hdr10p_config_data.average_maxrgb);
tmp = hdr10p_config_data.distribution_values;
pr_info("distribution_values:\n");
for (vcnt = 0; vcnt < 3; vcnt++) {
for (hcnt = 0; hcnt < 3; hcnt++)
pr_info("%u, ", tmp[vcnt * 3 + hcnt]);
pr_info("\n");
}
pr_info("nbca: %u, knpx: %u, knpy: %u\n",
hdr10p_config_data.num_bezier_curve_anchors,
hdr10p_config_data.knee_point_x,
hdr10p_config_data.knee_point_y);
tmp = hdr10p_config_data.bezier_curve_anchors;
pr_info("bezier_curve_anchors:\n");
for (vcnt = 0; vcnt < 3; vcnt++) {
for (hcnt = 0; hcnt < 3; hcnt++)
pr_info("%u, ", tmp[vcnt * 3 + hcnt]);
pr_info("\n");
}
pr_info("gof: %u, ndf: %u\n",
hdr10p_config_data.graphics_overlay_flag,
hdr10p_config_data.no_delay_flag);
}
void print_hdmiaud_config_data(void)
{
pr_info("***hdmiaud_config_data***\n");
pr_info("type: %u, chnum: %u, samrate: %u, samsize: %u\n",
hdmiaud_config_data.type,
hdmiaud_config_data.channel_num,
hdmiaud_config_data.sample_rate,
hdmiaud_config_data.sample_size);
}
void print_emp_config_data(void)
{
unsigned char *data;
unsigned int hcnt, vcnt, size;
data = emp_config_data.data;
pr_info("******emp_config_data******\n");
pr_info("type: %u, size: %u\n",
emp_config_data.type,
emp_config_data.size);
pr_info("data:\n");
size = emp_config_data.size;
for (vcnt = 0; vcnt < 8; vcnt++) {
for (hcnt = 0; hcnt < 16; hcnt++) {
if (vcnt * 16 + hcnt >= size)
break;
pr_info("%u, ", data[vcnt * 16 + hcnt]);
}
if (vcnt * 16 + hcnt < size)
pr_info("\n");
else
break;
}
pr_info("\n");
}
static ssize_t create_hdmitx_out_attr(char *buf)
{
unsigned int reg_val;
unsigned int reg_addr;
unsigned char *conf;
int pos = 0;
reg_addr = HDMITX_DWC_FC_AVICONF0;
reg_val = hdmitx_rd_reg(reg_addr);
switch (reg_val & 0x3) {
case 0:
conf = "RGB";
break;
case 1:
conf = "422";
break;
case 2:
conf = "444";
break;
case 3:
conf = "420";
}
pos += snprintf(buf + pos, PAGE_SIZE, "%s,", conf);
reg_addr = HDMITX_DWC_VP_PR_CD;
reg_val = hdmitx_rd_reg(reg_addr);
switch ((reg_val & 0xf0) >> 4) {
case 0:
case 4:
conf = "8bit";
break;
case 5:
conf = "10bit";
break;
case 6:
conf = "12bit";
break;
case 7:
conf = "16bit";
break;
default:
conf = "reserved";
}
pos += snprintf(buf + pos, PAGE_SIZE, "%s", conf);
return pos;
}
static ssize_t create_hdmitx_out_hdr(char *buf)
{
unsigned int reg_val, vsd_ieee_id[3];
unsigned int reg_addr;
unsigned char *conf;
int pos = 0;
if (hdr_status_pos == 2) {
reg_addr = HDMITX_DWC_FC_VSDIEEEID0;
reg_val = hdmitx_rd_reg(reg_addr);
vsd_ieee_id[0] = reg_val;
reg_addr = HDMITX_DWC_FC_VSDIEEEID1;
reg_val = hdmitx_rd_reg(reg_addr);
vsd_ieee_id[1] = reg_val;
reg_addr = HDMITX_DWC_FC_VSDIEEEID2;
reg_val = hdmitx_rd_reg(reg_addr);
vsd_ieee_id[2] = reg_val;
/*hdmi 1.4b VSIF only Support DolbyVision-Std*/
if ((vsd_ieee_id[0] == 0x03) &&
(vsd_ieee_id[1] == 0x0C) &&
(vsd_ieee_id[2] == 0x00)) {
pos += snprintf(buf + pos, PAGE_SIZE,
"DolbyVision-Std_hdmi 1.4b VSIF");
} else if ((vsd_ieee_id[0] == 0x46) &&
(vsd_ieee_id[1] == 0xD0) &&
(vsd_ieee_id[2] == 0x00)) {
reg_addr = HDMITX_DWC_FC_AVICONF0;
reg_val = hdmitx_rd_reg(reg_addr);
if ((reg_val & 0x3) == 0) {
/*RGB*/
reg_addr = HDMITX_DWC_FC_AVICONF2;
reg_val = hdmitx_rd_reg(reg_addr);
if (((reg_val & 0xc) >> 2) == 2)/*FULL*/
pos += snprintf(buf + pos, PAGE_SIZE,
"DolbyVision-Std");
else/*LIM*/
pos += snprintf(buf + pos, PAGE_SIZE,
"DolbyVision-Lowlatency");
} else if ((reg_val & 0x3) == 1) {
/*422*/
reg_addr = HDMITX_DWC_FC_AVICONF3;
reg_val = hdmitx_rd_reg(reg_addr);
if (((reg_val & 0xc) >> 2) == 0)/*LIM*/
pos += snprintf(buf + pos, PAGE_SIZE,
"DolbyVision-Lowlatency");
else/*FULL*/
pos += snprintf(buf + pos, PAGE_SIZE,
"DolbyVision-Std");
} else if ((reg_val & 0x3) == 2) {
/*444 only one probability: DolbyVision-Lowlatency*/
pos += snprintf(buf + pos, PAGE_SIZE,
"DolbyVision-Lowlatency");
}
} else {
pos += snprintf(buf + pos, PAGE_SIZE, "SDR");
}
} else {
reg_addr = HDMITX_DWC_FC_DRM_PB00;
reg_val = hdmitx_rd_reg(reg_addr);
switch (reg_val) {
case 0:
conf = "SDR";
break;
case 1:
conf = "HDR10-others";
break;
case 2:
conf = "HDR10-GAMMA_ST2084";
break;
case 3:
conf = "HDR10-GAMMA_HLG";
break;
default:
conf = "SDR";
}
pos += snprintf(buf + pos, PAGE_SIZE, "%s", conf);
}
return pos;
}
static ssize_t show_hdmi_config(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
enum hdmi_vic vic;
pr_info("************hdmi_config_info************\n");
pr_info("display_mode\nin:%s\t",
get_vout_mode_internal());
vic = hdmitx_device.hwop.getstate(&hdmitx_device, STAT_VIDEO_VIC, 0);
pr_info("out:%s\n", hdmitx_edid_vic_tab_map_string(vic));
pos = show_attr(dev, attr, buf);
buf[pos] = '\0';
pr_info("attr\nin:%s\t", buf);
pos = create_hdmitx_out_attr(buf);
buf[pos] = '\0';
pr_info("out:%s\n", buf);
pos = show_hdmi_hdr_status(dev, attr, buf);
buf[pos] = '\0';
pr_info("hdr_status\nin:%s\t", buf);
pos = create_hdmitx_out_hdr(buf);
buf[pos] = '\0';
pr_info("out:%s\n", buf);
pos = show_config(dev, attr, buf);
buf[pos] = '\0';
pr_info("\n******config******\n");
pr_info("%s\n", buf);
pr_info("******hdcp******\n");
pos = show_hdcp_mode(dev, attr, buf);
buf[pos] = '\0';
pr_info("hdcp_mode:%s\n", buf);
pos = show_hdcp_lstore(dev, attr, buf);
buf[pos] = '\0';
pr_info("hdcp_lstore:%s\n", buf);
pos = show_hdcp_ver(dev, attr, buf);
buf[pos] = '\0';
if (buf[0] == '2')
pr_info("Sink support hdcp22: yes\n");
else
pr_info("Sink support hdcp22: no\n");
pr_info("******scdc******\n");
pos = show_div40(dev, attr, buf);
buf[pos] = '\0';
pr_info("div40:%s\n", buf);
pr_info("******hdmi_pll******\n");
pos = show_sspll(dev, attr, buf);
buf[pos] = '\0';
pr_info("sspll:%s\n", buf);
pr_info("******dv vsif info config vs reg******\n");
print_vsif_config_data();
strcpy(buf, "vsif_info");
hdmitx_device.hwop.debugfun(&hdmitx_device, buf);
pr_info("******drm info config vs reg******\n");
print_drm_config_data();
strcpy(buf, "drm_info");
hdmitx_device.hwop.debugfun(&hdmitx_device, buf);
pr_info("******hdr10p vsif info config vs reg******\n");
print_hdr10p_config_data();
strcpy(buf, "vsif_info");
hdmitx_device.hwop.debugfun(&hdmitx_device, buf);
pr_info("******avi_info reg******\n");
strcpy(buf, "avi_info");
hdmitx_device.hwop.debugfun(&hdmitx_device, buf);
pr_info("******gcp_info reg******\n");
strcpy(buf, "gcp_info");
hdmitx_device.hwop.debugfun(&hdmitx_device, buf);
print_emp_config_data();
pr_info("******hdmiaud info config vs reg******\n");
print_hdmiaud_config_data();
pr_info("***aud_info in reg***\n");
strcpy(buf, "aud_info");
hdmitx_device.hwop.debugfun(&hdmitx_device, buf);
pr_info("***acr_info in reg***\n");
strcpy(buf, "acr_info");
hdmitx_device.hwop.debugfun(&hdmitx_device, buf);
pr_info("***aud_sample in reg***\n");
strcpy(buf, "aud_sample");
hdmitx_device.hwop.debugfun(&hdmitx_device, buf);
pr_info("***aud_chls in reg***\n");
strcpy(buf, "aud_chls");
hdmitx_device.hwop.debugfun(&hdmitx_device, buf);
memset(buf, 0, PAGE_SIZE);
return 0;
}
static ssize_t show_hdmirx_info(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pos = 0;
pr_info("************hdmirx_info************\n\n");
pos = show_hpd_state(dev, attr, buf);
buf[pos] = '\0';
pr_info("******hpd_edid_parsing******\n");
pr_info("hpd:%s\t", buf);
pos = show_edid_parsing(dev, attr, buf);
buf[pos] = '\0';
pr_info("edid_parsing:%s\n", buf);
pos = show_edid(dev, attr, buf);
buf[pos] = '\0';
pr_info("******edid******\n");
pr_info("%s\n", buf);
pos = show_dc_cap(dev, attr, buf);
buf[pos] = '\0';
pr_info("******dc_cap******\n%s\n", buf);
pos = show_disp_cap(dev, attr, buf);
buf[pos] = '\0';
pr_info("******disp_cap******\n%s\n", buf);
pos = show_dv_cap(dev, attr, buf);
buf[pos] = '\0';
pr_info("******dv_cap******\n%s\n", buf);
pos = show_hdr_cap(dev, attr, buf);
buf[pos] = '\0';
pr_info("******hdr_cap******\n%s\n", buf);
pos = show_sink_type(dev, attr, buf);
buf[pos] = '\0';
pr_info("******sink_type******\n%s\n", buf);
pos = show_aud_cap(dev, attr, buf);
buf[pos] = '\0';
pr_info("******aud_cap******\n%s\n", buf);
pos = show_aud_ch(dev, attr, buf);
buf[pos] = '\0';
pr_info("******aud_ch******\n%s\n", buf);
pos = show_rawedid(dev, attr, buf);
buf[pos] = '\0';
pr_info("******rawedid******\n%s\n", buf);
memset(buf, 0, PAGE_SIZE);
return 0;
}
void print_hsty_drm_config_data(void)
{
unsigned int hdr_transfer_feature;
unsigned int hdr_color_feature;
struct master_display_info_s *drmcfg;
unsigned int colormetry;
unsigned int hcnt, vcnt;
unsigned int arr_cnt, pr_loc;
unsigned int print_num;
pr_loc = hsty_drm_config_loc - 1;
if (hsty_drm_config_num > 8)
print_num = 8;
else
print_num = hsty_drm_config_num;
pr_info("******drm_config_data have trans %d times******\n",
hsty_drm_config_num);
for (arr_cnt = 0; arr_cnt < print_num; arr_cnt++) {
pr_info("***hsty_drm_config_data[%u]***\n", arr_cnt);
drmcfg = &hsty_drm_config_data[pr_loc];
hdr_transfer_feature = (drmcfg->features >> 8) & 0xff;
hdr_color_feature = (drmcfg->features >> 16) & 0xff;
colormetry = (drmcfg->features >> 30) & 0x1;
pr_info("tf=%u, cf=%u, colormetry=%u\n",
hdr_transfer_feature, hdr_color_feature,
colormetry);
pr_info("primaries:\n");
for (vcnt = 0; vcnt < 3; vcnt++) {
for (hcnt = 0; hcnt < 2; hcnt++)
pr_info("%u, ", drmcfg->primaries[vcnt][hcnt]);
pr_info("\n");
}
pr_info("white_point: ");
for (hcnt = 0; hcnt < 2; hcnt++)
pr_info("%u, ", drmcfg->white_point[hcnt]);
pr_info("\n");
pr_info("luminance: ");
for (hcnt = 0; hcnt < 2; hcnt++)
pr_info("%u, ", drmcfg->luminance[hcnt]);
pr_info("\n");
pr_info("max_content: %u, ", drmcfg->max_content);
pr_info("max_frame_average: %u\n", drmcfg->max_frame_average);
pr_loc = pr_loc > 0 ? pr_loc - 1 : 7;
}
}
void print_hsty_vsif_config_data(void)
{
struct dv_vsif_para *data;
unsigned int arr_cnt, pr_loc;
unsigned int print_num;
pr_loc = hsty_vsif_config_loc - 1;
if (hsty_vsif_config_num > 8)
print_num = 8;
else
print_num = hsty_vsif_config_num;
pr_info("******vsif_config_data have trans %d times******\n",
hsty_vsif_config_num);
for (arr_cnt = 0; arr_cnt < print_num; arr_cnt++) {
pr_info("***hsty_vsif_config_data[%u]***\n", arr_cnt);
data = &hsty_vsif_config_data[pr_loc].data;
pr_info("***vsif_config_data***\n");
pr_info("type: %u, tunnel: %u, sigsdr: %u\n",
hsty_vsif_config_data[pr_loc].type,
hsty_vsif_config_data[pr_loc].tunnel_mode,
hsty_vsif_config_data[pr_loc].signal_sdr);
pr_info("dv_vsif_para:\n");
pr_info("ver: %u len: %u\n",
data->ver, data->length);
pr_info("ll: %u dvsig: %u\n",
data->vers.ver2.low_latency,
data->vers.ver2.dobly_vision_signal);
pr_info("bcMD: %u axMD: %u\n",
data->vers.ver2.backlt_ctrl_MD_present,
data->vers.ver2.auxiliary_MD_present);
pr_info("PQhi: %u PQlow: %u\n",
data->vers.ver2.eff_tmax_PQ_hi,
data->vers.ver2.eff_tmax_PQ_low);
pr_info("axrm: %u, axrv: %u, ",
data->vers.ver2.auxiliary_runmode,
data->vers.ver2.auxiliary_runversion);
pr_info("axdbg: %u\n",
data->vers.ver2.auxiliary_debug0);
pr_loc = pr_loc > 0 ? pr_loc - 1 : 7;
}
}
void print_hsty_hdr10p_config_data(void)
{
struct hdr10plus_para *data;
unsigned int arr_cnt, pr_loc;
unsigned int hcnt, vcnt;
unsigned char *tmp;
unsigned int print_num;
pr_loc = hsty_hdr10p_config_loc - 1;
if (hsty_hdr10p_config_num > 8)
print_num = 8;
else
print_num = hsty_hdr10p_config_num;
pr_info("******hdr10p_config_data have trans %d times******\n",
hsty_hdr10p_config_num);
for (arr_cnt = 0; arr_cnt < print_num; arr_cnt++) {
pr_info("***hsty_hdr10p_config_data[%u]***\n", arr_cnt);
data = &hsty_hdr10p_config_data[pr_loc];
pr_info("appver: %u, tlum: %u, avgrgb: %u\n",
data->application_version,
data->targeted_max_lum,
data->average_maxrgb);
tmp = data->distribution_values;
pr_info("distribution_values:\n");
for (vcnt = 0; vcnt < 3; vcnt++) {
for (hcnt = 0; hcnt < 3; hcnt++)
pr_info("%u, ", tmp[vcnt * 3 + hcnt]);
pr_info("\n");
}
pr_info("nbca: %u, knpx: %u, knpy: %u\n",
data->num_bezier_curve_anchors,
data->knee_point_x,
data->knee_point_y);
tmp = data->bezier_curve_anchors;
pr_info("bezier_curve_anchors:\n");
for (vcnt = 0; vcnt < 3; vcnt++) {
for (hcnt = 0; hcnt < 3; hcnt++)
pr_info("%u, ", tmp[vcnt * 3 + hcnt]);
pr_info("\n");
}
pr_info("gof: %u, ndf: %u\n",
data->graphics_overlay_flag,
data->no_delay_flag);
pr_loc = pr_loc > 0 ? pr_loc - 1 : 7;
}
}
void print_hsty_hdmiaud_config_data(void)
{
struct hdmitx_audpara *data;
unsigned int arr_cnt, pr_loc;
unsigned int print_num;
pr_loc = hsty_hdmiaud_config_loc - 1;
if (hsty_hdmiaud_config_num > 8)
print_num = 8;
else
print_num = hsty_hdmiaud_config_num;
pr_info("******hdmitx_audpara have trans %d times******\n",
hsty_hdmiaud_config_num);
for (arr_cnt = 0; arr_cnt < print_num; arr_cnt++) {
pr_info("***hsty_hdmiaud_config_data[%u]***\n", arr_cnt);
data = &hsty_hdmiaud_config_data[pr_loc];
pr_info("type: %u, chnum: %u, samrate: %u, samsize: %u\n",
data->type, data->channel_num,
data->sample_rate, data->sample_size);
pr_loc = pr_loc > 0 ? pr_loc - 1 : 7;
}
}
#undef pr_fmt
#define pr_fmt(fmt) "hdmitx: " fmt
static ssize_t show_hdmi_hsty_config(struct device *dev,
struct device_attribute *attr, char *buf)
{
print_hsty_drm_config_data();
print_hsty_vsif_config_data();
print_hsty_hdr10p_config_data();
print_hsty_hdmiaud_config_data();
memset(buf, 0, PAGE_SIZE);
return 0;
}
static DEVICE_ATTR(disp_mode, 0664, show_disp_mode, store_disp_mode);
static DEVICE_ATTR(attr, 0664, show_attr, store_attr);
static DEVICE_ATTR(aud_mode, 0644, show_aud_mode, store_aud_mode);
static DEVICE_ATTR(vid_mute, 0644, show_vid_mute, store_vid_mute);
static DEVICE_ATTR(edid, 0644, show_edid, store_edid);
static DEVICE_ATTR(rawedid, 0444, show_rawedid, NULL);
static DEVICE_ATTR(sink_type, 0444, show_sink_type, NULL);
static DEVICE_ATTR(edid_parsing, 0444, show_edid_parsing, NULL);
static DEVICE_ATTR(config, 0664, show_config, store_config);
static DEVICE_ATTR(debug, 0200, NULL, store_debug);
static DEVICE_ATTR(disp_cap, 0444, show_disp_cap, NULL);
static DEVICE_ATTR(preferred_mode, 0444, show_preferred_mode, NULL);
static DEVICE_ATTR(cea_cap, 0444, show_cea_cap, NULL);
static DEVICE_ATTR(vesa_cap, 0444, show_vesa_cap, NULL);
static DEVICE_ATTR(aud_cap, 0444, show_aud_cap, NULL);
static DEVICE_ATTR(hdmi_hdr_status, 0444, show_hdmi_hdr_status, NULL);
static DEVICE_ATTR(hdr_cap, 0444, show_hdr_cap, NULL);
static DEVICE_ATTR(dv_cap, 0444, show_dv_cap, NULL);
static DEVICE_ATTR(dv_cap2, 0444, show_dv_cap2, NULL);
static DEVICE_ATTR(dc_cap, 0444, show_dc_cap, NULL);
static DEVICE_ATTR(valid_mode, 0664, show_valid_mode, store_valid_mode);
static DEVICE_ATTR(allm_cap, 0444, show_allm_cap, NULL);
static DEVICE_ATTR(allm_mode, 0664, show_allm_mode, store_allm_mode);
static DEVICE_ATTR(contenttype_cap, 0444, show_contenttype_cap, NULL);
static DEVICE_ATTR(contenttype_mode, 0664,
show_contenttype_mode, store_contenttype_mode);
static DEVICE_ATTR(aud_ch, 0664, show_aud_ch, store_aud_ch);
static DEVICE_ATTR(avmute, 0664, show_avmute, store_avmute);
static DEVICE_ATTR(swap, 0644, show_swap, store_swap);
static DEVICE_ATTR(vic, 0664, show_vic, store_vic);
static DEVICE_ATTR(phy, 0664, show_phy, store_phy);
static DEVICE_ATTR(sspll, 0664, show_sspll, store_sspll);
static DEVICE_ATTR(frac_rate_policy, 0664, show_frac_rate, store_frac_rate);
static DEVICE_ATTR(rxsense_policy, 0644, show_rxsense_policy,
store_rxsense_policy);
static DEVICE_ATTR(cedst_policy, 0664, show_cedst_policy, store_cedst_policy);
static DEVICE_ATTR(cedst_count, 0444, show_cedst_count, NULL);
static DEVICE_ATTR(hdcp_clkdis, 0664, show_hdcp_clkdis, store_hdcp_clkdis);
static DEVICE_ATTR(hdcp_pwr, 0664, show_hdcp_pwr, store_hdcp_pwr);
static DEVICE_ATTR(hdcp_byp, 0200, NULL, store_hdcp_byp);
static DEVICE_ATTR(hdcp_mode, 0664, show_hdcp_mode, store_hdcp_mode);
static DEVICE_ATTR(hdcp_lstore, 0664, show_hdcp_lstore, store_hdcp_lstore);
static DEVICE_ATTR(hdcp_rptxlstore, 0664, show_hdcp_rptxlstore,
store_hdcp_rptxlstore);
static DEVICE_ATTR(hdcp_repeater, 0644, show_hdcp_repeater,
store_hdcp_repeater);
static DEVICE_ATTR(hdcp_topo_info, 0644, show_hdcp_topo_info,
store_hdcp_topo_info);
static DEVICE_ATTR(hdcp22_type, 0644, show_hdcp22_type, store_hdcp22_type);
static DEVICE_ATTR(hdcp_stickmode, 0664, show_hdcp_stickmode,
store_hdcp_stickmode);
static DEVICE_ATTR(hdcp_stickstep, 0664, show_hdcp_stickstep,
store_hdcp_stickstep);
static DEVICE_ATTR(hdmi_repeater_tx, 0444, show_repeater_tx, NULL);
static DEVICE_ATTR(hdcp22_base, 0444, show_hdcp22_base, NULL);
static DEVICE_ATTR(div40, 0664, show_div40, store_div40);
static DEVICE_ATTR(hdcp_ctrl, 0664, show_hdcp_ctrl, store_hdcp_ctrl);
static DEVICE_ATTR(disp_cap_3d, 0444, show_disp_cap_3d, NULL);
static DEVICE_ATTR(hdcp_ksv_info, 0444, show_hdcp_ksv_info, NULL);
static DEVICE_ATTR(hdcp_ver, 0444, show_hdcp_ver, NULL);
static DEVICE_ATTR(hpd_state, 0444, show_hpd_state, NULL);
static DEVICE_ATTR(hdmitx_drm_flag, 0444, show_hdmitx_drm_flag, NULL);
static DEVICE_ATTR(hdmi_used, 0444, show_hdmi_used, NULL);
static DEVICE_ATTR(rhpd_state, 0444, show_rhpd_state, NULL);
static DEVICE_ATTR(max_exceed, 0444, show_max_exceed_state, NULL);
static DEVICE_ATTR(fake_plug, 0664, show_fake_plug, store_fake_plug);
static DEVICE_ATTR(hdmi_init, 0444, show_hdmi_init, NULL);
static DEVICE_ATTR(ready, 0664, show_ready, store_ready);
static DEVICE_ATTR(support_3d, 0444, show_support_3d, NULL);
static DEVICE_ATTR(hdmi_config_info, 0444, show_hdmi_config, NULL);
static DEVICE_ATTR(hdmi_rx_info, 0444, show_hdmirx_info, NULL);
static DEVICE_ATTR(hdmi_hsty_config_info, 0444, show_hdmi_hsty_config, NULL);
static int hdmitx_set_current_vmode(enum vmode_e mode)
{
if (mode != VMODE_HDMI)
return -1;
set_disp_mode_auto();
return 0;
}
static enum vmode_e hdmitx_validate_vmode(char *mode, unsigned int frac)
{
struct vinfo_s *info = hdmi_get_valid_vinfo(mode);
if (info) {
/* //remove frac support for vout api
*if (frac)
* hdmitx_device.frac_rate_policy = 1;
*else
* hdmitx_device.frac_rate_policy = 0;
*/
hdmitx_device.vinfo = info;
hdmitx_device.vinfo->info_3d = NON_3D;
if (hdmitx_device.flag_3dfp)
hdmitx_device.vinfo->info_3d = FP_3D;
if (hdmitx_device.flag_3dtb)
hdmitx_device.vinfo->info_3d = TB_3D;
if (hdmitx_device.flag_3dss)
hdmitx_device.vinfo->info_3d = SS_3D;
hdmitx_device.vinfo->vout_device = &hdmitx_vdev;
return VMODE_HDMI;
}
return VMODE_MAX;
}
static int hdmitx_vmode_is_supported(enum vmode_e mode)
{
if ((mode & VMODE_MODE_BIT_MASK) == VMODE_HDMI)
return true;
else
return false;
}
static int hdmitx_module_disable(enum vmode_e cur_vmod)
{
struct hdmitx_dev *hdev = &hdmitx_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);
hdmitx_disable_clk(hdev);
hdev->para = hdmi_get_fmt_name("invalid", hdev->fmt_attr);
hdmitx_validate_vmode("null", 0);
if (hdev->cedst_policy)
cancel_delayed_work(&hdev->work_cedst);
if (hdev->rxsense_policy)
queue_delayed_work(hdmitx_device.rxsense_wq,
&hdmitx_device.work_rxsense, 0);
return 0;
}
static int hdmitx_vout_state;
static int hdmitx_vout_set_state(int index)
{
hdmitx_vout_state |= (1 << index);
return 0;
}
static int hdmitx_vout_clr_state(int index)
{
hdmitx_vout_state &= ~(1 << index);
return 0;
}
static int hdmitx_vout_get_state(void)
{
return hdmitx_vout_state;
}
static int hdmitx_vout_get_disp_cap(char *buf)
{
return show_disp_cap(NULL, NULL, buf);
}
static void hdmitx_set_bist(unsigned int num)
{
if (hdmitx_device.hwop.debug_bist)
hdmitx_device.hwop.debug_bist(&hdmitx_device, 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,
.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
},
};
#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,
.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
},
};
#endif
#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(unsigned int 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(AUD "get FS_MAX\n");
return FS_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(unsigned int 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;
}
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 rx_cap *prxcap = &hdmitx_device.rxcap;
struct snd_pcm_substream *substream =
(struct snd_pcm_substream *)para;
struct hdmitx_audpara *audio_param =
&hdmitx_device.cur_audio_param;
enum hdmi_audio_fs n_rate = aud_samp_rate_map(substream->runtime->rate);
enum hdmi_audio_sampsize n_size =
aud_size_map(substream->runtime->sample_bits);
hdmitx_device.audio_param_update_flag = 0;
hdmitx_device.audio_notify_flag = 0;
if (audio_param->sample_rate != n_rate) {
audio_param->sample_rate = n_rate;
hdmitx_device.audio_param_update_flag = 1;
}
if (audio_param->type != cmd) {
audio_param->type = cmd;
hdmitx_device.audio_param_update_flag = 1;
}
if (audio_param->sample_size != n_size) {
audio_param->sample_size = n_size;
hdmitx_device.audio_param_update_flag = 1;
}
if (audio_param->channel_num !=
(substream->runtime->channels - 1)) {
audio_param->channel_num =
substream->runtime->channels - 1;
hdmitx_device.audio_param_update_flag = 1;
}
if (hdmitx_device.tx_aud_cfg == 2) {
/* 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)) {
hdmitx_device.hwop.cntlconfig(&hdmitx_device,
CONF_AUDIO_MUTE_OP, AUDIO_MUTE);
hdmitx_device.audio_param_update_flag = 0;
}
}
if (hdmitx_device.audio_param_update_flag == 0)
;
else
hdmitx_device.audio_notify_flag = 1;
if (!hdmitx_device.hdmi_audio_off_flag &&
hdmitx_device.audio_param_update_flag) {
/* plug-in & update audio param */
if (hdmitx_device.hpd_state == 1) {
hdmitx_set_audio(&hdmitx_device,
&hdmitx_device.cur_audio_param);
if ((hdmitx_device.audio_notify_flag == 1) ||
(hdmitx_device.audio_step == 1)) {
hdmitx_device.audio_notify_flag = 0;
hdmitx_device.audio_step = 0;
}
hdmitx_device.audio_param_update_flag = 0;
}
}
return 0;
}
void hdmi_tx_edid_proc(unsigned char *edid)
{
struct hdmitx_dev *hdev;
unsigned int edid_len;
hdev = &hdmitx_device;
edid_len = edid[126] + 1;
edid_len *= 128;
memcpy(hdev->EDID_buf, edid, edid_len);
memcpy(hdev->EDID_buf1, edid, edid_len);
hdmitx_edid_clear(hdev);
hdmitx_edid_parse(hdev);
hdmitx_edid_buf_compare_print(hdev);
}
static void hdmitx_get_edid(struct hdmitx_dev *hdev)
{
unsigned int num;
unsigned long flags = 0;
mutex_lock(&getedid_mutex);
/* TODO hdmitx_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 (fake_edid[0] == 0 && fake_edid[1] == 0xff) {
if (fake_edid[0x7e] < 4)
num = (fake_edid[0x7e] + 1) * 0x80;
else
num = 0x100;
memcpy(hdev->EDID_buf, fake_edid, num);
}
if (hdmitx_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 (!check_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 (hdmitx_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);
}
}
spin_lock_irqsave(&hdev->edid_spinlock, flags);
hdmitx_edid_clear(hdev);
hdmitx_edid_parse(hdev);
if (hdev->hdr_priority) { /* clear dv_info */
struct dv_info *dv = &hdev->rxcap.dv_info;
memset(dv, 0, sizeof(struct dv_info));
}
spin_unlock_irqrestore(&hdev->edid_spinlock, flags);
hdmitx_edid_buf_compare_print(hdev);
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);
extcon_set_state_sync(hdmitx_extcon_rxsense, EXTCON_DISP_HDMI, 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 */
extcon_set_state_sync(hdmitx_extcon_cedst, EXTCON_DISP_HDMI, 0);
extcon_set_state_sync(hdmitx_extcon_cedst, EXTCON_DISP_HDMI, ced);
queue_delayed_work(hdev->cedst_wq, &hdev->work_cedst, HZ);
}
bool is_tv_changed(void)
{
bool ret = false;
if (memcmp(hdmichecksum, hdmitx_device.rxcap.chksum, 10) &&
memcmp(emptychecksum, hdmitx_device.rxcap.chksum, 10) &&
memcmp(invalidchecksum, hdmichecksum, 10)) {
ret = true;
pr_info("hdmi crc is diff between uboot and kernel\n");
}
return ret;
}
EXPORT_SYMBOL(is_tv_changed);
static void hdmitx_hpd_plugin_handler(struct work_struct *work)
{
char bksv_buf[5];
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(SYS "plugin\n");
if (hdev->chip_type >= MESON_CPU_ID_G12A)
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_IEEEOUI)
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);
if (hdev->chip_type < MESON_CPU_ID_G12A)
hdev->hwop.cntlmisc(hdev, MISC_I2C_REACTIVE, 0);
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);
hdev->hwop.cntlddc(hdev, DDC_HDCP_GET_BKSV,
(unsigned long int)bksv_buf);
rx_set_receive_hdcp(bksv_buf, 1, 1, 0, 0);
}
info = hdmitx_get_current_vinfo();
if (info && (info->mode == VMODE_HDMI))
hdmitx_set_audio(hdev, &hdev->cur_audio_param);
hdev->hpd_state = 1;
hdmitx_notify_hpd(hdev->hpd_state,
hdev->edid_parsing ?
hdev->edid_ptr : NULL);
extcon_set_state_sync(hdmitx_extcon_hdmi, EXTCON_DISP_HDMI, 1);
extcon_set_state_sync(hdmitx_extcon_audio, EXTCON_DISP_HDMI, 1);
mutex_unlock(&setclk_mutex);
/* 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);
}
static void clear_rx_vinfo(struct hdmitx_dev *hdev)
{
struct vinfo_s *info = hdmitx_get_current_vinfo();
if (info) {
memset(&info->hdr_info, 0, sizeof(info->hdr_info));
memset(&info->rx_latency, 0, sizeof(info->rx_latency));
}
}
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;
}
hdev->hdcp_mode = 0;
hdev->hdcp_bcaps_repeater = 0;
hdev->hwop.cntlddc(hdev, DDC_HDCP_MUX_INIT, 1);
hdev->hwop.cntlddc(hdev, DDC_HDCP_OP, HDCP14_OFF);
hdev->hwop.cntlddc(hdev, DDC_GLITCH_FILTER_RESET, 0);
if (hdev->cedst_policy)
cancel_delayed_work(&hdev->work_cedst);
edidinfo_detach_to_vinfo(hdev);
pr_info(SYS "plugout\n");
if (!!(hdev->hwop.cntlmisc(hdev, MISC_HPD_GPI_ST, 0))) {
pr_info(SYS "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.cntlddc(hdev, DDC_HDCP_MUX_INIT, 1);
hdev->hwop.cntlddc(hdev, DDC_HDCP_OP, HDCP14_OFF);
hdev->hwop.cntlddc(hdev, DDC_HDCP_SET_TOPO_INFO, 0);
hdev->hwop.cntlmisc(hdev, MISC_TMDS_PHY_OP, TMDS_PHY_DISABLE);
hdev->hdmitx_event &= ~HDMI_TX_HPD_PLUGOUT;
hdev->hwop.cntlmisc(hdev, MISC_ESM_RESET, 0);
clear_rx_vinfo(hdev);
rx_edid_physical_addr(0, 0, 0, 0);
hdmitx_edid_clear(hdev);
hdmi_physical_size_update(hdev);
hdmitx_edid_ram_buffer_clear(hdev);
hdev->hpd_state = 0;
hdmitx_notify_hpd(hdev->hpd_state, NULL);
extcon_set_state_sync(hdmitx_extcon_hdmi, EXTCON_DISP_HDMI, 0);
extcon_set_state_sync(hdmitx_extcon_audio, EXTCON_DISP_HDMI, 0);
mutex_unlock(&setclk_mutex);
}
static void hdmitx_internal_intr_handler(struct work_struct *work)
{
struct hdmitx_dev *hdev = container_of((struct delayed_work *)work,
struct hdmitx_dev, work_internal_intr);
hdev->hwop.debugfun(hdev, "dumpintr");
}
int get_hpd_state(void)
{
int ret;
mutex_lock(&setclk_mutex);
ret = hdmitx_device.hpd_state;
mutex_unlock(&setclk_mutex);
return ret;
}
EXPORT_SYMBOL(get_hpd_state);
/******************************
* hdmitx kernel task
*******************************/
int tv_audio_support(int type, struct rx_cap *prxcap)
{
int i, audio_check = 0;
for (i = 0; i < prxcap->AUD_count; i++) {
if (prxcap->RxAudioCap[i].audio_format_code == type)
audio_check = 1;
}
return audio_check;
}
static int hdmi_task_handle(void *data)
{
struct vinfo_s *info = NULL;
struct hdmitx_dev *hdmitx_device = (struct hdmitx_dev *)data;
hdmitx_extcon_hdmi->state = !!(hdmitx_device->hwop.cntlmisc(
hdmitx_device, MISC_HPD_GPI_ST, 0));
hdmitx_device->hpd_state = hdmitx_extcon_hdmi->state;
hdmitx_notify_hpd(hdmitx_device->hpd_state, NULL);
if (hdmitx_device->hpd_state)
hdmitx_device->already_used = 1;
extcon_set_state_sync(hdmitx_extcon_power, EXTCON_DISP_HDMI,
hdmitx_device->hpd_state);
/* When init hdmi, clear the hdmitx module edid ram and edid buffer. */
hdmitx_edid_clear(hdmitx_device);
hdmitx_edid_ram_buffer_clear(hdmitx_device);
if (hdmitx_device->hpd_state) {
hdmitx_get_edid(hdmitx_device);
edidinfo_attach_to_vinfo(hdmitx_device);
}
INIT_WORK(&hdmitx_device->work_hdr, hdr_work_func);
hdmitx_device->hdmi_wq = alloc_workqueue(DEVICE_NAME,
WQ_HIGHPRI | WQ_CPU_INTENSIVE, 0);
INIT_DELAYED_WORK(&hdmitx_device->work_hpd_plugin,
hdmitx_hpd_plugin_handler);
INIT_DELAYED_WORK(&hdmitx_device->work_hpd_plugout,
hdmitx_hpd_plugout_handler);
INIT_DELAYED_WORK(&hdmitx_device->work_internal_intr,
hdmitx_internal_intr_handler);
/* for rx sense feature */
hdmitx_device->rxsense_wq = alloc_workqueue(hdmitx_extcon_rxsense->name,
WQ_SYSFS | WQ_FREEZABLE, 0);
INIT_DELAYED_WORK(&hdmitx_device->work_rxsense, hdmitx_rxsense_process);
/* for cedst feature */
hdmitx_device->cedst_wq = alloc_workqueue(hdmitx_extcon_cedst->name,
WQ_SYSFS | WQ_FREEZABLE, 0);
INIT_DELAYED_WORK(&hdmitx_device->work_cedst, hdmitx_cedst_process);
hdmitx_device->tx_aud_cfg = 1; /* default audio configure is on */
/*Direct Rander Management use another irq*/
if (hdmitx_device->drm_feature == 0)
hdmitx_device->hwop.setupirq(hdmitx_device);
/* Trigger HDMITX IRQ*/
hdmitx_device->hwop.cntlmisc(hdmitx_device, MISC_HPD_MUX_OP, PIN_MUX);
if (hdmitx_device->hwop.cntlmisc(hdmitx_device, MISC_HPD_GPI_ST, 0)) {
hdmitx_device->hwop.cntlmisc(hdmitx_device,
MISC_TRIGGER_HPD, 0);
hdmitx_device->already_used = 1;
}
hdmitx_device->hdmi_init = 1;
info = hdmitx_get_current_vinfo();
if (!info || !info->name)
return 0;
if (info->mode == VMODE_HDMI)
hdmitx_device->para = hdmi_get_fmt_name(info->name,
hdmitx_device->fmt_attr);
return 0;
}
/* Linux */
/*****************************
* 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,
};
struct hdmitx_dev *get_hdmitx_device(void)
{
return &hdmitx_device;
}
EXPORT_SYMBOL(get_hdmitx_device);
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(SYS "not find vendor name\n");
ret = of_property_read_u32(np, "vendor_id", &vend->vendor_id);
if (ret)
pr_info(SYS "not find vendor id\n");
ret = of_property_read_string(np, "product_desc",
(const char **)&vend->product_desc);
if (ret)
pr_info(SYS "not find product desc\n");
return 0;
}
static void hdmitx_fmt_attr(struct hdmitx_dev *hdev)
{
if (strlen(hdev->fmt_attr) >= 8)
return;
if ((hdev->para->cd == COLORDEPTH_RESERVED) &&
(hdev->para->cs == COLORSPACE_RESERVED)) {
strcpy(hdev->fmt_attr, "default");
} else {
memset(hdev->fmt_attr, 0, sizeof(hdev->fmt_attr));
switch (hdev->para->cs) {
case COLORSPACE_RGB444:
memcpy(hdev->fmt_attr, "rgb,", 5);
break;
case COLORSPACE_YUV422:
memcpy(hdev->fmt_attr, "422,", 5);
break;
case COLORSPACE_YUV444:
memcpy(hdev->fmt_attr, "444,", 5);
break;
case 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;
}
}
}
static void hdmitx_init_fmt_attr(struct hdmitx_dev *hdev)
{
if (strlen(hdev->fmt_attr) >= 8) {
pr_info(SYS "fmt_attr %s\n", hdev->fmt_attr);
return;
}
if ((hdev->para->cd == COLORDEPTH_RESERVED) &&
(hdev->para->cs == COLORSPACE_RESERVED)) {
strcpy(hdev->fmt_attr, "default");
} else {
hdmitx_fmt_attr(hdev);
}
}
/* for notify to cec */
static BLOCKING_NOTIFIER_HEAD(hdmitx_event_notify_list);
int hdmitx_event_notifier_regist(struct notifier_block *nb)
{
int ret = 0;
if (!nb)
return ret;
ret = blocking_notifier_chain_register(&hdmitx_event_notify_list, nb);
/* update status when register */
if (!ret && nb->notifier_call) {
hdmitx_notify_hpd(hdmitx_device.hpd_state,
hdmitx_device.edid_parsing ?
hdmitx_device.edid_ptr : NULL);
if (hdmitx_device.physical_addr != 0xffff)
hdmitx_event_notify(HDMITX_PHY_ADDR_VALID,
&hdmitx_device.physical_addr);
}
return ret;
}
EXPORT_SYMBOL(hdmitx_event_notifier_regist);
int hdmitx_event_notifier_unregist(struct notifier_block *nb)
{
int ret;
ret = blocking_notifier_chain_unregister(&hdmitx_event_notify_list, nb);
return ret;
}
EXPORT_SYMBOL(hdmitx_event_notifier_unregist);
void hdmitx_event_notify(unsigned long state, void *arg)
{
blocking_notifier_call_chain(&hdmitx_event_notify_list, state, arg);
}
void hdmitx_hdcp_status(int hdmi_authenticated)
{
extcon_set_state_sync(hdmitx_extcon_hdcp, EXTCON_DISP_HDMI,
hdmi_authenticated);
}
void hdmitx_extcon_register(struct platform_device *pdev, struct device *dev)
{
struct extcon_dev *edev;
int ret;
/*hdmitx extcon hdmi*/
edev = extcon_dev_allocate(hdmi_cable);
if (IS_ERR(edev))
return;
edev->dev.parent = dev;
edev->name = "hdmitx_extcon_hdmi";
dev_set_name(&edev->dev, "hdmi");
ret = extcon_dev_register(edev);
if (ret < 0) {
extcon_dev_free(edev);
return;
}
hdmitx_extcon_hdmi = edev;
/*hdmitx extcon audio*/
edev = extcon_dev_allocate(hdmi_cable);
if (IS_ERR(edev))
return;
edev->dev.parent = dev;
edev->name = "hdmitx_extcon_audio";
dev_set_name(&edev->dev, "hdmi_audio");
ret = extcon_dev_register(edev);
if (ret < 0) {
extcon_dev_free(edev);
return;
}
hdmitx_extcon_audio = edev;
/*hdmitx extcon power*/
edev = extcon_dev_allocate(hdmi_cable);
if (IS_ERR(edev))
return;
edev->dev.parent = dev;
edev->name = "hdmitx_extcon_power";
dev_set_name(&edev->dev, "hdmi_power");
ret = extcon_dev_register(edev);
if (ret < 0) {
extcon_dev_free(edev);
return;
}
hdmitx_extcon_power = edev;
/*hdmitx extcon hdr*/
edev = extcon_dev_allocate(hdmi_cable);
if (IS_ERR(edev))
return;
edev->dev.parent = dev;
edev->name = "hdmitx_extcon_hdr";
dev_set_name(&edev->dev, "hdmi_hdr");
ret = extcon_dev_register(edev);
if (ret < 0) {
extcon_dev_free(edev);
return;
}
hdmitx_extcon_hdr = edev;
/*hdmitx extcon CED */
edev = extcon_dev_allocate(hdmi_cable);
if (IS_ERR(edev))
return;
edev->dev.parent = dev;
edev->name = "hdmitx_extcon_cedst";
dev_set_name(&edev->dev, "hdmi_cedst");
ret = extcon_dev_register(edev);
if (ret < 0) {
extcon_dev_free(edev);
return;
}
hdmitx_extcon_cedst = edev;
/*hdmitx extcon rxsense*/
edev = extcon_dev_allocate(hdmi_cable);
if (IS_ERR(edev))
return;
edev->dev.parent = dev;
edev->name = "hdmitx_extcon_rxsense";
dev_set_name(&edev->dev, "hdmi_rxsense");
ret = extcon_dev_register(edev);
if (ret < 0) {
extcon_dev_free(edev);
return;
}
hdmitx_extcon_rxsense = edev;
/*hdmitx extcon hdcp*/
edev = extcon_dev_allocate(hdmi_cable);
if (IS_ERR(edev))
return;
edev->dev.parent = dev;
edev->name = "hdmitx_extcon_hdcp";
dev_set_name(&edev->dev, "hdcp");
ret = extcon_dev_register(edev);
if (ret < 0) {
extcon_dev_free(edev);
return;
}
hdmitx_extcon_hdcp = edev;
}
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;
info->auto_hdcp_ri_flag = 1;
info->hw_sha_calculator_flag = 1;
}
static int amhdmitx_device_init(struct hdmitx_dev *hdmi_dev)
{
if (!hdmi_dev)
return 1;
pr_info(SYS "Ver: %s\n", HDMITX_VER);
hdmi_dev->hdtx_dev = NULL;
hdmitx_device.physical_addr = 0xffff;
/* init para for NULL protection */
hdmitx_device.para = hdmi_get_fmt_name("invalid",
hdmitx_device.fmt_attr);
hdmitx_device.hdmi_last_hdr_mode = 0;
hdmitx_device.hdmi_current_hdr_mode = 0;
hdmitx_device.unplug_powerdown = 0;
hdmitx_device.vic_count = 0;
hdmitx_device.auth_process_timer = 0;
hdmitx_device.force_audio_flag = 0;
hdmitx_device.hdcp_mode = 0;
hdmitx_device.ready = 0;
hdmitx_device.rxsense_policy = 0; /* no RxSense by default */
/* enable or disable HDMITX SSPLL, enable by default */
hdmitx_device.sspll = 1;
/*
* 0, do not unmux hpd when off or unplug ;
* 1, unmux hpd when unplug;
* 2, unmux hpd when unplug or off;
*/
hdmitx_device.hpdmode = 1;
hdmitx_device.flag_3dfp = 0;
hdmitx_device.flag_3dss = 0;
hdmitx_device.flag_3dtb = 0;
if ((init_flag & INIT_FLAG_POWERDOWN) && (hdmitx_device.hpdmode == 2))
hdmitx_device.mux_hpd_if_pin_high_flag = 0;
else
hdmitx_device.mux_hpd_if_pin_high_flag = 1;
hdmitx_device.audio_param_update_flag = 0;
/* 1: 2ch */
hdmitx_device.hdmi_ch = 1;
hdmitx_device.topo_info =
kmalloc(sizeof(struct hdcprp_topo), GFP_KERNEL);
if (!hdmitx_device.topo_info)
pr_info("failed to alloc hdcp topo info\n");
hdmitx_init_parameters(&hdmitx_device.hdmi_info);
return 0;
}
static int amhdmitx_get_dt_info(struct platform_device *pdev)
{
int ret = 0;
#ifdef CONFIG_OF
int val;
phandle phandle;
struct device_node *init_data;
struct device_node *drm_node;
unsigned char *drm_status;
#endif
/* HDMITX pinctrl config for hdp and ddc*/
if (pdev->dev.pins) {
hdmitx_device.pdev = &pdev->dev;
hdmitx_device.pinctrl_default =
pinctrl_lookup_state(pdev->dev.pins->p, "default");
if (IS_ERR(hdmitx_device.pinctrl_default))
pr_info(SYS "no default of pinctrl state\n");
hdmitx_device.pinctrl_i2c =
pinctrl_lookup_state(pdev->dev.pins->p, "hdmitx_i2c");
if (IS_ERR(hdmitx_device.pinctrl_i2c))
pr_info(SYS "no hdmitx_i2c of pinctrl state\n");
pinctrl_select_state(pdev->dev.pins->p,
hdmitx_device.pinctrl_default);
}
#ifdef CONFIG_OF
if (pdev->dev.of_node) {
int dongle_mode = 0;
memset(&hdmitx_device.config_data, 0,
sizeof(struct hdmi_config_platform_data));
/* Get ic type information */
ret = of_property_read_u32(pdev->dev.of_node, "ic_type",
&hdmitx_device.chip_type);
/* Get dongle_mode information */
ret = of_property_read_u32(pdev->dev.of_node, "dongle_mode",
&dongle_mode);
hdmitx_device.dongle_mode = !!dongle_mode;
ret = of_property_read_u32(pdev->dev.of_node,
"repeater_tx", &val);
if (!ret)
hdmitx_device.repeater_tx = val;
if (hdmitx_device.repeater_tx == 1)
hdmitx_device.topo_info = kzalloc(
sizeof(*hdmitx_device.topo_info), GFP_KERNEL);
ret = of_property_read_u32(pdev->dev.of_node,
"cedst_en", &val);
if (!ret)
hdmitx_device.cedst_en = !!val;
/* Get vendor information */
ret = of_property_read_u32(pdev->dev.of_node,
"vend-data", &val);
if (ret == 0) {
phandle = val;
init_data = of_find_node_by_phandle(phandle);
hdmitx_device.config_data.vend_data = kzalloc(
sizeof(struct vendor_info_data), GFP_KERNEL);
ret =
get_dt_vend_init_data(init_data,
hdmitx_device.config_data.vend_data);
}
/* Get power control */
ret = of_property_read_u32(pdev->dev.of_node,
"pwr-ctrl", &val);
if (ret == 0) {
phandle = val;
init_data = of_find_node_by_phandle(phandle);
hdmitx_device.config_data.pwr_ctl = kzalloc((sizeof(
struct hdmi_pwr_ctl)) * HDMI_TX_PWR_CTRL_NUM,
GFP_KERNEL);
memset(hdmitx_device.config_data.pwr_ctl, 0,
sizeof(struct hdmi_pwr_ctl));
}
/* Get drm feature information */
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) {
if (memcmp(drm_status, "okay", 4) == 0)
hdmitx_device.drm_feature = 1;
else
hdmitx_device.drm_feature = 0;
}
}
}
#else
hdmi_pdata = pdev->dev.platform_data;
if (!hdmi_pdata)
r = -ENOENT;
#endif
hdmitx_device.irq_hpd = platform_get_irq_byname(pdev, "hdmitx_hpd");
if (hdmitx_device.irq_hpd == -ENXIO)
return -ENXIO;
pr_info(SYS "hpd irq = %d\n", hdmitx_device.irq_hpd);
return ret;
}
/*
* amhdmitx_clktree_probe
* get clktree info from dts
*/
static void amhdmitx_clktree_probe(struct device *hdmitx_dev)
{
struct clk *hdmi_clk_vapb, *hdmi_clk_vpu;
struct clk *hdcp22_tx_skp, *hdcp22_tx_esm;
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(SYS "vapb_clk failed to probe\n");
} else {
hdmitx_device.hdmitx_clk_tree.hdmi_clk_vapb = hdmi_clk_vapb;
clk_prepare_enable(hdmitx_device.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(SYS "vpu_clk failed to probe\n");
} else {
hdmitx_device.hdmitx_clk_tree.hdmi_clk_vpu = hdmi_clk_vpu;
clk_prepare_enable(hdmitx_device.hdmitx_clk_tree.hdmi_clk_vpu);
}
hdcp22_tx_skp = devm_clk_get(hdmitx_dev, "hdcp22_tx_skp");
if (IS_ERR(hdcp22_tx_skp))
pr_warn(SYS "hdcp22_tx_skp failed to probe\n");
else
hdmitx_device.hdmitx_clk_tree.hdcp22_tx_skp = hdcp22_tx_skp;
hdcp22_tx_esm = devm_clk_get(hdmitx_dev, "hdcp22_tx_esm");
if (IS_ERR(hdcp22_tx_esm))
pr_warn(SYS "hdcp22_tx_esm failed to probe\n");
else
hdmitx_device.hdmitx_clk_tree.hdcp22_tx_esm = hdcp22_tx_esm;
venci_top_gate = devm_clk_get(hdmitx_dev, "venci_top_gate");
if (IS_ERR(venci_top_gate))
pr_warn(SYS "venci_top_gate failed to probe\n");
else
hdmitx_device.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(SYS "venci_0_gate failed to probe\n");
else
hdmitx_device.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(SYS "venci_0_gate failed to probe\n");
else
hdmitx_device.hdmitx_clk_tree.venci_1_gate = venci_1_gate;
}
static int amhdmitx_probe(struct platform_device *pdev)
{
int r, ret = 0;
struct device *dev;
pr_info(SYS "amhdmitx_probe start\n");
amhdmitx_device_init(&hdmitx_device);
ret = amhdmitx_get_dt_info(pdev);
amhdmitx_clktree_probe(&pdev->dev);
r = alloc_chrdev_region(&hdmitx_device.hdmitx_id, 0, HDMI_TX_COUNT,
DEVICE_NAME);
cdev_init(&hdmitx_device.cdev, &amhdmitx_fops);
hdmitx_device.cdev.owner = THIS_MODULE;
r = cdev_add(&hdmitx_device.cdev, hdmitx_device.hdmitx_id,
HDMI_TX_COUNT);
hdmitx_class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(hdmitx_class)) {
unregister_chrdev_region(hdmitx_device.hdmitx_id,
HDMI_TX_COUNT);
return -1;
}
dev = device_create(hdmitx_class, NULL, hdmitx_device.hdmitx_id, NULL,
"amhdmitx%d", 0); /* kernel>=2.6.27 */
if (!dev) {
class_destroy(hdmitx_class);
r = -EEXIST;
return r;
}
hdmitx_device.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_aud_mode);
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_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_disp_cap_3d);
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_dv_cap);
ret = device_create_file(dev, &dev_attr_dv_cap2);
ret = device_create_file(dev, &dev_attr_aud_ch);
ret = device_create_file(dev, &dev_attr_avmute);
ret = device_create_file(dev, &dev_attr_swap);
ret = device_create_file(dev, &dev_attr_vic);
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_cedst_policy);
ret = device_create_file(dev, &dev_attr_cedst_count);
ret = device_create_file(dev, &dev_attr_hdcp_clkdis);
ret = device_create_file(dev, &dev_attr_hdcp_pwr);
ret = device_create_file(dev, &dev_attr_hdcp_ksv_info);
ret = device_create_file(dev, &dev_attr_hdcp_ver);
ret = device_create_file(dev, &dev_attr_hdcp_byp);
ret = device_create_file(dev, &dev_attr_hdcp_mode);
ret = device_create_file(dev, &dev_attr_hdcp_repeater);
ret = device_create_file(dev, &dev_attr_hdcp_topo_info);
ret = device_create_file(dev, &dev_attr_hdcp22_type);
ret = device_create_file(dev, &dev_attr_hdcp_stickmode);
ret = device_create_file(dev, &dev_attr_hdcp_stickstep);
ret = device_create_file(dev, &dev_attr_hdmi_repeater_tx);
ret = device_create_file(dev, &dev_attr_hdcp22_base);
ret = device_create_file(dev, &dev_attr_hdcp_lstore);
ret = device_create_file(dev, &dev_attr_hdcp_rptxlstore);
ret = device_create_file(dev, &dev_attr_div40);
ret = device_create_file(dev, &dev_attr_hdcp_ctrl);
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_rhpd_state);
ret = device_create_file(dev, &dev_attr_max_exceed);
ret = device_create_file(dev, &dev_attr_fake_plug);
ret = device_create_file(dev, &dev_attr_hdmi_init);
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_hdmi_config_info);
ret = device_create_file(dev, &dev_attr_hdmi_rx_info);
ret = device_create_file(dev, &dev_attr_hdmi_hsty_config_info);
ret = device_create_file(dev, &dev_attr_hdmitx_drm_flag);
#if defined(CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND) && !defined(CONFIG_AMLOGIC_DRM)
register_early_suspend(&hdmitx_early_suspend_handler);
#endif
hdmitx_device.nb.notifier_call = hdmitx_reboot_notifier;
register_reboot_notifier(&hdmitx_device.nb);
vsem_init_cfg(&hdmitx_device);
HDMITX_Meson_Init(&hdmitx_device);
hdmitx_device.hpd_state = !!(hdmitx_device.hwop.cntlmisc(
&hdmitx_device, MISC_HPD_GPI_ST, 0));
if (hdmitx_device.hpd_state)
hdmitx_device.already_used = 1;
vout_register_server(&hdmitx_vout_server);
#ifdef CONFIG_AMLOGIC_VOUT2_SERVE
vout2_register_server(&hdmitx_vout2_server);
#endif
#ifdef CONFIG_AMLOGIC_SND_SOC
aout_register_client(&hdmitx_notifier_nb_a);
#else
r = r ? (long int)&hdmitx_notifier_nb_a :
(long int)&hdmitx_notifier_nb_a;
#endif
hdmitx_extcon_register(pdev, dev);
/* update fmt_attr */
hdmitx_init_fmt_attr(&hdmitx_device);
hdmitx_device.task = kthread_run(hdmi_task_handle,
&hdmitx_device, "kthread_hdmi");
edidinfo_attach_to_vinfo(&hdmitx_device);
return r;
}
static int amhdmitx_remove(struct platform_device *pdev)
{
struct device *dev = hdmitx_device.hdtx_dev;
cancel_work_sync(&hdmitx_device.work_hdr);
if (hdmitx_device.hwop.uninit)
hdmitx_device.hwop.uninit(&hdmitx_device);
hdmitx_device.hpd_event = 0xff;
kthread_stop(hdmitx_device.task);
vout_unregister_server(&hdmitx_vout_server);
#ifdef CONFIG_AMLOGIC_VOUT2_SERVE
vout2_unregister_server(&hdmitx_vout2_server);
#endif
#ifdef 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_aud_mode);
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_preferred_mode);
device_remove_file(dev, &dev_attr_cea_cap);
device_remove_file(dev, &dev_attr_vesa_cap);
device_remove_file(dev, &dev_attr_disp_cap_3d);
device_remove_file(dev, &dev_attr_hdr_cap);
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_rhpd_state);
device_remove_file(dev, &dev_attr_max_exceed);
device_remove_file(dev, &dev_attr_hdmi_init);
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_vic);
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_cedst_policy);
device_remove_file(dev, &dev_attr_cedst_count);
device_remove_file(dev, &dev_attr_hdcp_pwr);
device_remove_file(dev, &dev_attr_div40);
device_remove_file(dev, &dev_attr_hdcp_repeater);
device_remove_file(dev, &dev_attr_hdcp_topo_info);
device_remove_file(dev, &dev_attr_hdcp22_type);
device_remove_file(dev, &dev_attr_hdcp_stickmode);
device_remove_file(dev, &dev_attr_hdcp_stickstep);
device_remove_file(dev, &dev_attr_hdmi_repeater_tx);
device_remove_file(dev, &dev_attr_hdcp22_base);
device_remove_file(dev, &dev_attr_swap);
device_remove_file(dev, &dev_attr_hdmi_hdr_status);
device_remove_file(dev, &dev_attr_hdmi_config_info);
device_remove_file(dev, &dev_attr_hdmi_rx_info);
device_remove_file(dev, &dev_attr_hdmi_hsty_config_info);
device_remove_file(dev, &dev_attr_hdmitx_drm_flag);
cdev_del(&hdmitx_device.cdev);
device_destroy(hdmitx_class, hdmitx_device.hdmitx_id);
class_destroy(hdmitx_class);
unregister_chrdev_region(hdmitx_device.hdmitx_id, HDMI_TX_COUNT);
return 0;
}
#ifdef CONFIG_PM
static int amhdmitx_suspend(struct platform_device *pdev,
pm_message_t state)
{
hdmitx_device.hwop.cntlddc(&hdmitx_device,
DDC_RESET_HDCP, 0);
return 0;
}
static int amhdmitx_resume(struct platform_device *pdev)
{
struct hdmitx_dev *hdev = &hdmitx_device;
hdev->hwop.cntlmisc(hdev, MISC_I2C_REACTIVE, 0);
return 0;
}
#endif
#ifdef CONFIG_OF
static const struct of_device_id meson_amhdmitx_dt_match[] = {
{
.compatible = "amlogic, amhdmitx",
},
{}
};
#else
#define meson_amhdmitx_dt_match NULL
#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 = meson_amhdmitx_dt_match,
#ifdef CONFIG_HIBERNATION
.pm = &amhdmitx_pm,
#endif
}
};
static int __init amhdmitx_init(void)
{
if (init_flag & INIT_FLAG_NOT_LOAD)
return 0;
if (platform_driver_register(&amhdmitx_driver))
return -ENODEV;
return 0;
}
static void __exit amhdmitx_exit(void)
{
platform_driver_unregister(&amhdmitx_driver);
}
subsys_initcall(amhdmitx_init);
module_exit(amhdmitx_exit);
MODULE_DESCRIPTION("AMLOGIC HDMI TX driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0.0");
MODULE_PARM_DESC(log_level, "\n log_level\n");
module_param(log_level, int, 0644);