blob: 64ccd44c683a059a688c0f5c50ae57f9f1178b03 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/mm.h>
#include <linux/major.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/amlogic/media/vout/hdmi_tx21/hdmi_info_global.h>
#include <linux/amlogic/media/vout/hdmi_tx21/hdmi_tx_module.h>
#include "hw/common.h"
static void hdmitx_set_spd_info(struct hdmitx_dev *hdmitx_device);
static void hdmi_set_vend_spec_infofram(struct hdmitx_dev *hdev,
enum hdmi_vic videocode);
static void construct_avi_packet(struct hdmitx_dev *hdev)
{
struct hdmi_avi_infoframe *info = &hdev->infoframes.avi.avi;
struct hdmi_format_para *para = hdev->para;
int ret;
ret = hdmi_avi_infoframe_init(info);
if (ret)
pr_info("init avi infoframe failed\n");
info->colorspace = para->cs;
info->scan_mode = HDMI_SCAN_MODE_NONE;
info->colorimetry = HDMI_COLORIMETRY_ITU_709;
info->picture_aspect = HDMI_PICTURE_ASPECT_16_9;
info->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
info->itc = 0;
info->extended_colorimetry = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
info->quantization_range = HDMI_QUANTIZATION_RANGE_LIMITED;
info->nups = HDMI_NUPS_UNKNOWN;
info->video_code = para->timing.vic;
if (para->timing.vic == HDMI_95_3840x2160p30_16x9 ||
para->timing.vic == HDMI_94_3840x2160p25_16x9 ||
para->timing.vic == HDMI_93_3840x2160p24_16x9 ||
para->timing.vic == HDMI_98_4096x2160p24_256x135)
/*HDMI Spec V1.4b P151*/
info->video_code = 0;
info->ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
info->content_type = HDMI_CONTENT_TYPE_GRAPHICS;
info->pixel_repeat = 0;
if (para->timing.pi_mode == 0) { /* interlaced modes */
if (para->timing.h_active == 1440)
info->pixel_repeat = 1;
if (para->timing.h_active == 2880)
info->pixel_repeat = 3;
}
info->top_bar = 0;
info->bottom_bar = 0;
info->left_bar = 0;
info->right_bar = 0;
hdmi_avi_infoframe_set(info);
}
/************************************
* hdmitx protocol level interface
*************************************/
/*
* HDMI Identifier = HDMI_IEEE_OUI 0x000c03
* If not, treated as a DVI Device
*/
static int is_dvi_device(struct rx_cap *prxcap)
{
if (prxcap->ieeeoui != HDMI_IEEE_OUI)
return 1;
else
return 0;
}
int hdmitx21_set_display(struct hdmitx_dev *hdev, enum hdmi_vic videocode)
{
struct hdmi_format_para *param = NULL;
enum hdmi_vic vic;
int ret = -1;
vic = hdev->hwop.getstate(hdev, STAT_VIDEO_VIC, 0);
if (hdev->vend_id_hit)
pr_info(VID "special tv detected\n");
pr_info(VID "already init VIC = %d Now VIC = %d\n",
vic, videocode);
if (vic != HDMI_0_UNKNOWN && vic == videocode)
hdev->cur_VIC = vic;
param = hdev->para;
if (!param) {
pr_info("%s[%d]\n", __func__, __LINE__);
return ret;
}
if (param->cs == HDMI_COLORSPACE_YUV444)
if (!(hdev->rxcap.native_Mode & (1 << 5))) {
param->cs = HDMI_COLORSPACE_YUV422;
pr_info("change cs from 444 to 422\n");
}
if (param->cs == HDMI_COLORSPACE_YUV422)
if (!(hdev->rxcap.native_Mode & (1 << 4))) {
param->cs = HDMI_COLORSPACE_RGB;
pr_info("change cs from 422 to rgb\n");
}
/* For Y420 modes */
switch (videocode) {
case HDMI_96_3840x2160p50_16x9:
case HDMI_97_3840x2160p60_16x9:
case HDMI_101_4096x2160p50_256x135:
case HDMI_102_4096x2160p60_256x135:
//param->cs = COLORSPACE_YUV420; /* TODO */
break;
default:
break;
}
if (param->cs == HDMI_COLORSPACE_RGB)
pr_info(VID "rx edid only support RGB format\n");
if (videocode >= HDMITX_VESA_OFFSET) {
hdev->para->cs = HDMI_COLORSPACE_RGB;
hdev->para->cd = COLORDEPTH_24B;
pr_info("hdmitx: VESA only support RGB format\n");
}
if (hdev->hwop.setdispmode(hdev) >= 0) {
construct_avi_packet(hdev);
/* HDMI CT 7-33 DVI Sink, no HDMI VSDB nor any
* other VSDB, No GB or DI expected
* TMDS_MODE[hdmi_config]
* 0: DVI Mode 1: HDMI Mode
*/
if (is_dvi_device(&hdev->rxcap)) {
pr_info(VID "Sink is DVI device\n");
hdev->hwop.cntlconfig(hdev,
CONF_HDMI_DVI_MODE, DVI_MODE);
} else {
pr_info(VID "Sink is HDMI device\n");
hdev->hwop.cntlconfig(hdev,
CONF_HDMI_DVI_MODE, HDMI_MODE);
}
if (videocode == HDMI_95_3840x2160p30_16x9 ||
videocode == HDMI_94_3840x2160p25_16x9 ||
videocode == HDMI_93_3840x2160p24_16x9 ||
videocode == HDMI_98_4096x2160p24_256x135)
hdmi_set_vend_spec_infofram(hdev, videocode);
else if ((!hdev->flag_3dfp) && (!hdev->flag_3dtb) &&
(!hdev->flag_3dss))
hdmi_set_vend_spec_infofram(hdev, 0);
else
;
if (hdev->allm_mode) {
hdmitx21_construct_vsif(hdev, VT_ALLM, 1, NULL);
hdev->hwop.cntlconfig(hdev, CONF_CT_MODE,
SET_CT_OFF);
}
hdev->hwop.cntlconfig(hdev, CONF_CT_MODE,
hdev->ct_mode);
ret = 0;
}
hdmitx_set_spd_info(hdev);
return ret;
}
static void hdmi_set_vend_spec_infofram(struct hdmitx_dev *hdev,
enum hdmi_vic videocode)
{
int i;
u8 db[28];
u8 *ven_db = &db[1];
u8 ven_hb[3];
ven_hb[0] = 0x81;
ven_hb[1] = 0x01;
ven_hb[2] = 0x5;
if (videocode == 0) { /* For non-4kx2k mode setting */
hdmi_vend_infoframe_set(NULL);
return;
}
if (hdev->rxcap.dv_info.block_flag == CORRECT ||
hdev->dv_src_feature == 1) { /* For dolby */
return;
}
for (i = 0; i < 0x6; i++)
ven_db[i] = 0;
ven_db[0] = GET_OUI_BYTE0(HDMI_IEEE_OUI);
ven_db[1] = GET_OUI_BYTE1(HDMI_IEEE_OUI);
ven_db[2] = GET_OUI_BYTE2(HDMI_IEEE_OUI);
ven_db[3] = 0x00; /* 4k x 2k Spec P156 */
if (videocode == HDMI_95_3840x2160p30_16x9) {
ven_db[3] = 0x20;
ven_db[4] = 0x1;
} else if (videocode == HDMI_94_3840x2160p25_16x9) {
ven_db[3] = 0x20;
ven_db[4] = 0x2;
} else if (videocode == HDMI_93_3840x2160p24_16x9) {
ven_db[3] = 0x20;
ven_db[4] = 0x3;
} else if (videocode == HDMI_98_4096x2160p24_256x135) {
ven_db[3] = 0x20;
ven_db[4] = 0x4;
} else {
;
}
hdmi_vend_infoframe_rawset(ven_hb, db);
}
int hdmi21_set_3d(struct hdmitx_dev *hdev, int type, u32 param)
{
int i;
u8 db[28];
u8 *ven_db = &db[1];
u8 ven_hb[3];
struct hdmi_vendor_infoframe *info;
info = &hdev->infoframes.vend.vendor.hdmi;
ven_hb[0] = 0x81;
ven_hb[1] = 0x01;
ven_hb[2] = 0x6;
if (type == T3D_DISABLE) {
hdmi_vend_infoframe_rawset(ven_hb, db);
} else {
for (i = 0; i < 0x6; i++)
ven_db[i] = 0;
ven_db[0] = GET_OUI_BYTE0(HDMI_IEEE_OUI);
ven_db[1] = GET_OUI_BYTE1(HDMI_IEEE_OUI);
ven_db[2] = GET_OUI_BYTE2(HDMI_IEEE_OUI);
ven_db[3] = 0x40;
ven_db[4] = type << 4;
ven_db[5] = param << 4;
hdmi_vend_infoframe_rawset(ven_hb, db);
}
return 0;
}
/* Set Source Product Descriptor InfoFrame
*/
static void hdmitx_set_spd_info(struct hdmitx_dev *hdev)
{
u8 spd_db[28] = {0x00};
u32 len = 0;
struct vendor_info_data *vend_data;
if (hdev->config_data.vend_data) {
vend_data = hdev->config_data.vend_data;
} else {
pr_info(VID "packet: can\'t get vendor data\n");
return;
}
if (vend_data->vendor_name) {
len = strlen(vend_data->vendor_name);
strncpy(&spd_db[0], vend_data->vendor_name,
(len > 8) ? 8 : len);
}
if (vend_data->product_desc) {
len = strlen(vend_data->product_desc);
strncpy(&spd_db[8], vend_data->product_desc,
(len > 16) ? 16 : len);
}
spd_db[24] = 0x1;
// TODO hdev->hwop.setinfoframe(HDMI_INFOFRAME_TYPE_SPD, SPD_HB);
}
static void fill_hdmi4k_vsif_data(enum hdmi_vic vic, u8 *db,
u8 *hb)
{
if (!db || !hb)
return;
if (vic == HDMI_95_3840x2160p30_16x9)
db[4] = 0x1;
else if (vic == HDMI_94_3840x2160p25_16x9)
db[4] = 0x2;
else if (vic == HDMI_93_3840x2160p24_16x9)
db[4] = 0x3;
else if (vic == HDMI_98_4096x2160p24_256x135)
db[4] = 0x4;
else
return;
hb[0] = 0x81;
hb[1] = 0x01;
hb[2] = 0x5;
db[3] = 0x20;
}
int hdmitx21_construct_vsif(struct hdmitx_dev *hdev, enum vsif_type type,
int on, void *param)
{
u8 hb[3] = {0x81, 0x1, 0};
u8 len = 0; /* hb[2] = len */
u8 vsif_db[28] = {0}; /* to be fulfilled */
u8 *db = &vsif_db[1]; /* to be fulfilled */
u32 ieeeoui = 0;
struct hdmi_vendor_infoframe *info;
info = &hdev->infoframes.vend.vendor.hdmi;
if (!hdev || type >= VT_MAX)
return 0;
switch (type) {
case VT_DEFAULT:
break;
case VT_HDMI14_4K:
ieeeoui = HDMI_IEEE_OUI;
len = 5;
if (_is_hdmi14_4k(hdev->cur_VIC)) {
fill_hdmi4k_vsif_data(hdev->cur_VIC, db, hb);
hdmitx21_set_avi_vic(0);
}
break;
case VT_ALLM:
ieeeoui = HDMI_FORUM_IEEE_OUI;
len = 5;
db[3] = 0x1; /* Fixed value */
if (on) {
db[4] |= 1 << 1; /* set bit1, ALLM_MODE */
if (_is_hdmi14_4k(hdev->cur_VIC))
hdmitx21_set_avi_vic(hdev->cur_VIC);
} else {
db[4] &= ~(1 << 1); /* clear bit1, ALLM_MODE */
/* still send out HS_VSIF, no set AVI.VIC = 0 */
}
break;
default:
break;
}
hb[2] = len;
db[0] = GET_OUI_BYTE0(ieeeoui);
db[1] = GET_OUI_BYTE1(ieeeoui);
db[2] = GET_OUI_BYTE2(ieeeoui);
hdmi_vend_infoframe_rawset(hb, vsif_db);
return 1;
}