blob: 31e95177e920bbb23c6bff8b9daccd0d65b6de6e [file]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <drm/drm_atomic_helper.h>
#include <drm/drm_connector.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_hdcp.h>
#include <media/cec.h>
#include "mtk_drm_ddp.h"
#include "mtk_drm_drv.h"
#include "mtk_drm_gem.h"
#include "mtk_drm_fb.h"
#include "mtk_hdmi.h"
#include "hdmictrl.h"
#include "hdmi_ctrl.h"
#include "hdmihdcp.h"
struct drm_device *hdmi_drm_dev;
struct drm_connector *hdmi_drm_connector;
struct drm_connector_state *hdmi_drm_state;
struct drm_encoder *hdmi_encoder;
struct drm_display_mode mtk_drm_hdmi_mode;
struct drm_property *prop_hdcp_status;
struct drm_property *prop_hdcp_tx_ver;
struct drm_property *prop_hdcp_rx_ver;
static const struct drm_display_mode mtk_drm_hdmi_default_mode = {
.clock = 1500,
.hdisplay = 1920,
.hsync_start = 2008,
.hsync_end = 2052,
.htotal = 2250,
.vdisplay = 1080,
.vsync_start = 1084,
.vsync_end = 1089,
.vtotal = 1125,
};
#define MAX_VIC 120
static int timing;
static int vic2timing[MAX_VIC] = {
0xff,// 1 640x480 59.94
0x02,// 2 720x480 59.94
0x02,// 3 720x480 59.94
0x04,// 4 1280x720 60
0x06,// 5 1920x1080 60 int
0x00,// 6 1440x480 59.94 int
0x00,// 7 1440x480 59.94 int
0xff,// 8 1440x240 60.054
0xff,// 9 1440x240 60.054
0x00,// 10 2880x480 59.94 int
0x00,// 11 2880x480 59.94 int
0xff,// 12 2880x240 60.054
0xff,// 13 2880x240 60.054
0x02,// 14 1440x480 59.94
0x02,// 15 1440x480 59.94
0x0d,// 16 1920x1080 60
0x03,// 17 720x576 50
0x03,// 18 720x576 50
0x05,// 19 1280x720 50
0x07,// 20 1920x1080 50 int
0x01,// 21 1440x576 50 int
0x01,// 22 1440x576 50 int
0xff,// 23 1440x288 50
0xff,// 24 1440x288 50
0x01,// 25 2880x576 50 int
0x01,// 26 2880x576 50 int
0xff,// 27 2880x288 50
0xff,// 28 2880x288 50
0x03,// 29 1440x576 50
0x03,// 30 1440x576 50
0x0e,// 31 1920x1080 50
0x0a,// 32 1920x1080 24
0x09,// 33 1920x1080 25
0x08,// 34 1920x1080 30
0x02,// 35 2880x480 59.94
0x02,// 36 2880x480 59.94
0x03,// 37 2880x576 50
0x03,// 38 2880x576 50
0x07,// 39 1920x1080 50 int
0x07,// 40 1920x1080 100 int
0x05,// 41 1280x720 100
0x03,// 42 720x576 100
0x03,// 43 720x576 100
0x01,// 44 1440x576 100 int
0x01,// 45 1440x576 100 int
0x06,// 46 1920x1080 120 int
0x04,// 47 1280x720 120
0x02,// 48 720x480 119.88
0x02,// 49 720x480 119.88
0x00,// 50 1440x480 119.88 int
0x00,// 51 1440x480 119.88 int
0x03,// 52 720x576 200
0x03,// 53 720x576 200
0x01,// 54 1440x576 200 int
0x01,// 55 1440x576 200 int
0x02,// 56 720x480 239.76
0x02,// 57 720x480 239.76
0x00,// 58 1440x480 239.76 int
0x00,// 59 1440x480 239.76 int
0xff,// 60 1280x720 24
0xff,// 61 1280x720 25
0xff,// 62 1280x720 30
0x0d,// 63 1920x1080 120
0x0e,// 64 1920x1080 100
0xff,// 65 1280x720 24
0xff,// 66 1280x720 25
0xff,// 67 1280x720 30
0x05,// 68 1280x720 50
0x04,// 69 1280x720 60
0x05,// 70 1280x720 100
0x04,// 71 1280x720 120
0x0a,// 72 1920x1080 24
0x09,// 73 1920x1080 25
0x08,// 74 1920x1080 30
0x0e,// 75 1920x1080 50
0x0d,// 76 1920x1080 60
0x0e,// 77 1920x1080 100
0x0d,// 78 1920x1080 120
0xff,// 79 1680x720 24
0xff,// 80 1680x720 25
0xff,// 81 1680x720 30
0xff,// 82 1680x720 50
0xff,// 83 1680x720 60
0xff,// 84 1680x720 100
0xff,// 85 1680x720 120
0xff,// 86 2560x1080 24
0xff,// 87 2560x1080 25
0xff,// 88 2560x1080 30
0xff,// 89 2560x1080 50
0xff,// 90 2560x1080 60
0xff,// 91 2560x1080 100
0xff,// 92 2560x1080 120
0x16,// 93 3840x2160 24
0x17,// 94 3840x2160 25
0x19,// 95 3840x2160 30
0x1c,// 96 3840x2160 50
0x1b,// 97 3840x2160 60
0x1a,// 98 4090x2160 24
0xff,// 99 4090x2160 25
0xff,// 100 4090x2160 30
0x1e,// 101 4090x2160 50
0x1d,// 102 4090x2160 60
0x16,// 103 3840x2160 24
0x17,// 104 3840x2160 25
0x19,// 105 3840x2160 30
0x1e,// 106 3840x2160 50
0x1d,// 107 3840x2160 60
0xff,// 108 1280x720 48
0xff,// 109 1280x720 48
0xff,// 110 1680x720 48
0xff,// 111 1920x1080 48
0xff,// 112 1920x1080 48
0xff,// 113 2560x1080 48
0xff,// 114 3840x2160 48
0xff,// 115 4096x2160 48
0xff,// 116 3840x2160 48
0xff,// 117 3840x2160 100
0xff,// 118 3840x2160 120
0xff,// 119 3840x2160 100
0xff // 120 3840x2160 120
};
u32 mtk_hdmi_display_mode_to_hdmi_timing(struct drm_display_mode *mode)
{
struct hdmi_avi_infoframe frame;
u32 hdmi_timing;
DRM_DEBUG_DRIVER("\n");
frame.video_code = drm_match_cea_mode(mode);
if (frame.video_code < 1 || frame.video_code > MAX_VIC) {
pr_info("video_code valied %d\n", __LINE__);
return 0xff;
}
hdmi_timing = vic2timing[frame.video_code-1];
DRM_DEBUG_DRIVER("VIC %d; timing 0x%x\n", frame.video_code, hdmi_timing);
return hdmi_timing;
}
void mtk_hdmi_notify_hpd_irq_event(void)
{
pr_info("%s 0x%p\n", __func__, hdmi_drm_dev);
if (hdmi_drm_dev)
drm_helper_hpd_irq_event(hdmi_drm_dev);
}
static void mtk_hdmi_encoder_destroy(struct drm_encoder *encoder)
{
DRM_DEBUG_DRIVER("\n");
drm_encoder_cleanup(encoder);
}
static bool mtk_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
DRM_DEBUG_DRIVER("\n");
return true;
}
static void mtk_hdmi_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct hdmi_avi_infoframe frame;
DRM_DEBUG_DRIVER("\n");
pr_info("mode/adjust hactive %d/%d\n", mode->hdisplay, adjusted_mode->hdisplay);
pr_info("mode/adjust vactive %d/%d\n", mode->vdisplay, adjusted_mode->vdisplay);
pr_info("mode/adjust clock %d/%d\n", mode->clock, adjusted_mode->clock);
frame.video_code = drm_match_cea_mode(mode);
if (frame.video_code < 1 || frame.video_code > MAX_VIC) {
pr_info("video_code valied %d\n", __LINE__);
return;
}
timing = vic2timing[frame.video_code-1];
pr_info("VIC %d; timing 0x%x\n", frame.video_code, timing);
drm_mode_copy(&mtk_drm_hdmi_mode, adjusted_mode);
}
static void mtk_hdmi_encoder_disable(struct drm_encoder *encoder)
{
DRM_DEBUG_DRIVER("\n");
}
static void mtk_hdmi_encoder_enable(struct drm_encoder *encoder)
{
// int ret = 0;
DRM_DEBUG_DRIVER("\n");
pr_info("%s %d\n", __func__, timing);
hdmi_video_config(timing);
/*
* drm_connector_set_link_status_property(hdmi_drm_connector,
* DRM_MODE_LINK_STATUS_GOOD);
*/
/*
* hdmi_enablehdcp(0x1);
* ret = drm_connector_attach_content_protection_property(hdmi_drm_connector,
* hdcp2_version_flag);
* if (ret) {
* pr_info("drm_connector_attach_content_protection_property error\n");
* }
*/
}
static int mtk_hdmi_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
return 0;
}
static enum drm_connector_status mtk_hdmi_connector_detect(
struct drm_connector *connector, bool force)
{
enum drm_connector_status hdmi_plug_status;
DRM_DEBUG_DRIVER("\n");
if (hdmi_hotplugstate == HDMI_STATE_HOT_PLUG_OUT)
hdmi_plug_status = connector_status_disconnected;
else
hdmi_plug_status = connector_status_connected;
pr_info("%s %d %d\n",
__func__,
hdmi_hotplugstate,
hdmi_plug_status);
return hdmi_plug_status;
}
static int mtk_hdmi_connector_get_modes(struct drm_connector *connector)
{
int ret;
struct edid *edid = NULL;
DRM_DEBUG_DRIVER("\n");
pr_info("%s edid %d\n", __func__, sizeof(struct edid));
edid = (struct edid *)drm_bEdidData;
drm_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
return ret;
}
static struct drm_encoder *mtk_hdmi_connector_best_encoder(
struct drm_connector *connector)
{
return hdmi_encoder;
}
enum drm_mode_status mtk_hdmi_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct hdmi_avi_infoframe frame;
DRM_DEBUG_DRIVER("\n");
if (mode == NULL)
return -1;
frame.video_code = drm_match_cea_mode(mode);
if ((frame.video_code < 1) || (frame.video_code > MAX_VIC)) {
pr_info("video_code valied %d\n", __LINE__);
return MODE_NOMODE;
}
timing = vic2timing[frame.video_code-1];
if ((timing == HDMI_VIDEO_1920x1080p_24Hz) && (mode->clock != 74250))
timing = HDMI_VIDEO_1920x1080p_23Hz;
else if ((timing == HDMI_VIDEO_1920x1080p_30Hz) && (mode->clock != 74250))
timing = HDMI_VIDEO_1920x1080p_29Hz;
else if ((timing == HDMI_VIDEO_3840x2160P_24HZ) && (mode->clock != 297000))
timing = HDMI_VIDEO_3840x2160P_23_976HZ;
else if ((timing == HDMI_VIDEO_3840x2160P_30HZ) && (mode->clock != 297000))
timing = HDMI_VIDEO_3840x2160P_29_97HZ;
else if ((timing == HDMI_VIDEO_1280x720p_60Hz) && (mode->clock != 74250))
timing = HDMI_VIDEO_1280x720p_59_94Hz;
else if ((timing == HDMI_VIDEO_1920x1080p_60Hz) && (mode->clock != 148500))
timing = HDMI_VIDEO_1920x1080p_59_94Hz;
else if ((timing == HDMI_VIDEO_3840x2160P_60HZ) && (mode->clock != 594000))
timing = HDMI_VIDEO_3840x2160P_59_94HZ;
else if ((timing == HDMI_VIDEO_4096x2160P_60HZ) && (mode->clock != 594000))
timing = HDMI_VIDEO_4096x2160P_59_94HZ;
pr_info("mode valied VIC %d; timing 0x%x\n", frame.video_code, timing);
if (mode && ((timing == 0xff) || (frame.video_code == 0x0) || (frame.video_code > 120)))
return MODE_NOMODE;
pr_info("mode valied\n");
return MODE_OK;
}
void mtk_attach_colorspace_property(struct drm_connector *connector)
{
if (!drm_mode_create_hdmi_colorspace_property(connector))
drm_object_attach_property(&connector->base,
connector->colorspace_property, 0);
}
static const struct drm_prop_enum_list hdcp_status_props[] = {
{ dsHDCP_STATUS_UNPOWERED, "HDCP_UNPWR"},
{ dsHDCP_STATUS_UNAUTHENTICATED, "HDCP_UNAUTH"},
{ dsHDCP_STATUS_AUTHENTICATED, "HDCP_AUTH_DONE"},
{ (dsHDCP_STATUS_AUTHENTICATIONFAILURE), "HDCP_AUTH_FAIL"},
{ (dsHDCP_STATUS_INPROGRESS), "HDCP_DOING"},
{ dsHDCP_STATUS_PORTDISABLED, "HDCP_PLUG_OUT"},
{ dsHDCP_STATUS_MAX, "HDCP_MAX"},
};
static const struct drm_prop_enum_list hdcp_ver_props[] = {
{ dsHDCP_VERSION_1X, "HDCP_1x"},
{ dsHDCP_VERSION_2X, "HDCP_2x"},
{ dsHDCP_VERSION_MAX, "HDCP_non"},
};
static void mtk_hdmi_add_properties(struct drm_connector *connector)
{
pr_info("%s %d\n", __func__, __LINE__);
mtk_attach_colorspace_property(connector);
drm_connector_attach_content_type_property(connector);
drm_connector_attach_max_bpc_property(connector, 8, 12);
/* create hdcp info property */
prop_hdcp_status = drm_property_create_enum(connector->dev, 0, "hdcp_status",
hdcp_status_props, ARRAY_SIZE(hdcp_status_props));
if (!prop_hdcp_status) {
pr_info("cannot create hdcp status property\n");
return;
}
pr_info("create hdcp status property success\n");
drm_object_attach_property(&connector->base, prop_hdcp_status, 0);
prop_hdcp_tx_ver = drm_property_create_enum(connector->dev, 0, "hdcp_tx_ver",
hdcp_ver_props, ARRAY_SIZE(hdcp_ver_props));
if (!prop_hdcp_tx_ver) {
pr_info("cannot create hdcp tx ver property\n");
return;
}
pr_info("create hdcp tx ver property success\n");
drm_object_attach_property(&connector->base, prop_hdcp_tx_ver, 0);
prop_hdcp_rx_ver = drm_property_create_enum(connector->dev, 0, "hdcp_rx_ver",
hdcp_ver_props, ARRAY_SIZE(hdcp_ver_props));
if (!prop_hdcp_rx_ver) {
pr_info("cannot create hdcp rx ver property\n");
return;
}
pr_info("create hdcp rx ver property success\n");
drm_object_attach_property(&connector->base, prop_hdcp_rx_ver, 0);
}
static int hdmi_conn_atomic_get_property(
struct drm_connector *conn,
const struct drm_connector_state *state,
struct drm_property *property,
uint64_t *val)
{
if (property == prop_hdcp_status) {
*val = hdmi_get_hdcp_status();
} else if (property == prop_hdcp_tx_ver) {
if (hdcp2_version_flag == true)
*val = dsHDCP_VERSION_2X;
else
*val = dsHDCP_VERSION_1X;
} else if (property == prop_hdcp_rx_ver) {
if (hdcp2_version_flag == true)
*val = dsHDCP_VERSION_2X;
else
*val = dsHDCP_VERSION_1X;
} else {
pr_info("[hdmi_tx]un-support property\n");
return -EINVAL;
}
return 0;
}
/*
* static void mtk_connector_reset(struct drm_connector *connector)
* {
* struct drm_connector_state *state;
*
* pr_info("connector reset %p\n", connector->state);
*
* if (connector->state) {
* __drm_atomic_helper_plane_destroy_state(connector->state);
*
* state = to_mtk_plane_state(connector->state);
* memset(state, 0, sizeof(*state));
* } else {
* state = kzalloc(sizeof(*state), GFP_KERNEL);
* if (!state)
* return;
* connector->state = &state->base;
* }
* }
*/
static const struct drm_encoder_funcs mtk_hdmi_encoder_funcs = {
.destroy = mtk_hdmi_encoder_destroy,
};
static const struct drm_encoder_helper_funcs mtk_hdmi_encoder_helper_funcs = {
.mode_fixup = mtk_hdmi_encoder_mode_fixup,
.mode_set = mtk_hdmi_encoder_mode_set,
.disable = mtk_hdmi_encoder_disable,
.enable = mtk_hdmi_encoder_enable,
.atomic_check = mtk_hdmi_atomic_check,
};
static const struct drm_connector_funcs mtk_hdmi_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.detect = mtk_hdmi_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_get_property = hdmi_conn_atomic_get_property,
};
static const struct drm_connector_helper_funcs
mtk_hdmi_connector_helper_funcs = {
.get_modes = mtk_hdmi_connector_get_modes,
.best_encoder = mtk_hdmi_connector_best_encoder,
.mode_valid = mtk_hdmi_connector_mode_valid,
};
void mtk_drm_hdmi_enc_init(struct drm_device *drm_dev)
{
int ret;
struct device *dev = drm_dev->dev;
DRM_DEBUG_DRIVER("%s\n", dev_name(dev));
hdmi_encoder = devm_kzalloc(dev, sizeof(*hdmi_encoder), GFP_KERNEL);
if (!hdmi_encoder) {
DRM_INFO("%s alloc hdmi_encoder fail\n", dev_name(dev));
return;
}
ret = drm_encoder_init(drm_dev, hdmi_encoder, &mtk_hdmi_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
if (ret) {
DRM_DEV_INFO(dev, "Failed to initialize decoder: %d\n", ret);
}
drm_encoder_helper_add(hdmi_encoder, &mtk_hdmi_encoder_helper_funcs);
hdmi_encoder->possible_crtcs = 1;
}
void mtk_drm_hdmi_conn_init(struct drm_device *drm_dev)
{
int ret;
struct device *dev = drm_dev->dev;
DRM_DEBUG_DRIVER("%s %s\n", dev_name(dev), dev_name(drm_dev->dev));
hdmi_drm_connector = devm_kzalloc(dev, sizeof(*hdmi_drm_connector), GFP_KERNEL);
if (!hdmi_drm_connector) {
DRM_INFO("%s alloc hdmi_connector fail\n", dev_name(dev));
return;
}
hdmi_drm_state = kzalloc(sizeof(*hdmi_drm_state), GFP_KERNEL);
if (!hdmi_drm_state) {
DRM_INFO("%s alloc hdmi_drm_state fail\n", dev_name(dev));
return;
}
__drm_atomic_helper_connector_reset(hdmi_drm_connector,
hdmi_drm_state);
ret = drm_connector_init(drm_dev, hdmi_drm_connector, &mtk_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA);
if (ret) {
DRM_ERROR("Failed to connector init to drm\n");
}
drm_connector_helper_add(hdmi_drm_connector,
&mtk_hdmi_connector_helper_funcs);
hdmi_drm_connector->dpms = DRM_MODE_DPMS_OFF;
hdmi_drm_connector->ycbcr_420_allowed = true;
hdmi_drm_connector->polled = DRM_CONNECTOR_POLL_HPD;
mtk_hdmi_add_properties(hdmi_drm_connector);
DRM_INFO("drm_connector_attach_encoder pre\n");
ret = drm_connector_attach_encoder(hdmi_drm_connector, hdmi_encoder);
if (ret) {
DRM_INFO("Failed to attach encoder\n");
}
DRM_DEBUG_DRIVER("connector %d encoder %d\n", hdmi_drm_connector->base.id,
hdmi_encoder->base.id);
}
void mtk_drm_hdmi_enc_conn_init(struct drm_device *drm_dev)
{
DRM_DEBUG_DRIVER("0x%p\n", drm_dev);
hdmi_drm_dev = drm_dev;
mtk_drm_hdmi_enc_init(drm_dev);
mtk_drm_hdmi_conn_init(drm_dev);
}