blob: d1d786aca45350cc5225c256bf8e93b22f0164a7 [file]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/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 "hdmi_tx_module.h"
#include "hw/common.h"
#define to_hdmitx21_dev(x) container_of(x, struct hdmitx_dev, tx_comm)
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->tx_comm.fmt_para;
hdmi_avi_infoframe_init(info);
info->colorspace = para->cs;
/* underscan */
info->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
if (para->timing.v_active <= 576)
info->colorimetry = HDMI_COLORIMETRY_ITU_601;
else
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*/
if (!hdev->frl_rate) /* TODO, clear under FRL */
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 = &hdev->tx_comm.fmt_para;
int ret = -1;
if (videocode >= HDMITX_VESA_OFFSET) {
param->cs = HDMI_COLORSPACE_RGB;
param->cd = COLORDEPTH_24B;
HDMITX_INFO("VESA only support RGB format\n");
}
if (hdev->tx_hw.base.setdispmode(&hdev->tx_hw.base) >= 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->tx_comm.rxcap)) {
HDMITX_INFO("Sink is DVI device\n");
hdmitx_hw_cntl_config(&hdev->tx_hw.base,
CONF_HDMI_DVI_MODE, DVI_MODE);
} else {
HDMITX_INFO("Sink is HDMI device\n");
hdmitx_hw_cntl_config(&hdev->tx_hw.base,
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) {
if (!hdev->frl_rate) /* TODO */
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->tx_comm.allm_mode) {
hdmitx_common_setup_vsif_packet(&hdev->tx_comm, VT_ALLM, 1, NULL);
hdmitx_hw_cntl_config(&hdev->tx_hw.base, CONF_CT_MODE, SET_CT_OFF);
} else {
hdmitx_hw_cntl_config(&hdev->tx_hw.base, CONF_CT_MODE,
hdev->tx_comm.ct_mode | hdev->it_content << 4);
}
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_rawset(NULL, NULL);
return;
}
if (hdev->tx_comm.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 {
HDMITX_DEBUG_VIDEO("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);
}