drm/msm/dp: Enable YUV422/YUV420 format
Select YUV422/YUV420 format as desired output for
current resolution if supported.
Chooses best resolution as per below priority order
1. RGB + DC
2. YUV422 + DC
3. YUV420 + DC
4. RGB
5. YUV420
Change-Id: I50dfd4767eecacddcb208c3d2e63638845cc9b2a
Signed-off-by: Narender Ankam <nankam@codeaurora.org>
Signed-off-by: Chirag Khurana <ckhurana@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index 5396a35..94392d3 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. 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 version 2 and
@@ -19,6 +19,7 @@
#include "dp_catalog.h"
#include "dp_reg.h"
+#include "msm_kms.h"
#define DP_GET_MSB(x) (x >> 8)
#define DP_GET_LSB(x) (x & 0xff)
@@ -394,7 +395,8 @@
return dp_read(catalog, io_data, DP_HDCP_STATUS);
}
-static void dp_catalog_panel_setup_infoframe_sdp(struct dp_catalog_panel *panel)
+static void dp_catalog_panel_setup_hdr_infoframe_sdp(
+ struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct drm_msm_ext_hdr_metadata *hdr;
@@ -501,7 +503,7 @@
DUMP_PREFIX_NONE, 16, 4, buf, off, false);
}
-static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
+static void dp_catalog_panel_setup_hdr_vsc_sdp(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
@@ -627,8 +629,8 @@
cfg2 |= BIT(15) | BIT(16);
dp_write(catalog, io_data, MMSS_DP_SDP_CFG2, cfg2);
- dp_catalog_panel_setup_vsc_sdp(panel);
- dp_catalog_panel_setup_infoframe_sdp(panel);
+ dp_catalog_panel_setup_hdr_vsc_sdp(panel);
+ dp_catalog_panel_setup_hdr_infoframe_sdp(panel);
/* indicates presence of VSC (BIT(6) of MISC1) */
misc |= BIT(14);
@@ -772,6 +774,7 @@
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
+ misc_val = cc;
misc_val |= (tb << 5);
misc_val |= BIT(0); /* Configure clock to synchronous mode */
@@ -780,7 +783,7 @@
}
static void dp_catalog_ctrl_config_msa(struct dp_catalog_ctrl *ctrl,
- u32 rate, u32 stream_rate_khz)
+ u32 rate, u32 stream_rate_khz, u32 out_format)
{
u32 pixel_m, pixel_n;
u32 mvid, nvid;
@@ -815,6 +818,9 @@
pr_debug("rate = %d\n", rate);
+ if (out_format == MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)
+ mvid *= 2;
+
if (link_rate_hbr2 == rate)
nvid *= 2;
@@ -1319,6 +1325,158 @@
return ret;
}
+static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
+{
+ struct dp_catalog_private *catalog;
+ struct dp_io_data *io_data;
+ u32 header, parity, data;
+ u8 bpc, off = 0;
+ u8 buf[SZ_128];
+
+ if (!panel) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ catalog = dp_catalog_get_priv(panel);
+ io_data = catalog->io.dp_link;
+
+ /* HEADER BYTE 1 */
+ header = panel->vsc_sdp_data.vsc_header_byte1;
+ parity = dp_header_get_parity(header);
+ data = ((header << HEADER_BYTE_1_BIT)
+ | (parity << PARITY_BYTE_1_BIT));
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_0, data);
+ memcpy(buf + off, &data, sizeof(data));
+ off += sizeof(data);
+
+ /* HEADER BYTE 2 */
+ header = panel->vsc_sdp_data.vsc_header_byte2;
+ parity = dp_header_get_parity(header);
+ data = ((header << HEADER_BYTE_2_BIT)
+ | (parity << PARITY_BYTE_2_BIT));
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_1, data);
+
+ /* HEADER BYTE 3 */
+ header = panel->vsc_sdp_data.vsc_header_byte3;
+ parity = dp_header_get_parity(header);
+ data = ((header << HEADER_BYTE_3_BIT)
+ | (parity << PARITY_BYTE_3_BIT));
+ data |= dp_read(catalog, io_data, MMSS_DP_GENERIC0_1);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_1, data);
+ memcpy(buf + off, &data, sizeof(data));
+ off += sizeof(data);
+
+ data = 0;
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_2, data);
+ memcpy(buf + off, &data, sizeof(data));
+ off += sizeof(data);
+
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_3, data);
+ memcpy(buf + off, &data, sizeof(data));
+ off += sizeof(data);
+
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_4, data);
+ memcpy(buf + off, &data, sizeof(data));
+ off += sizeof(data);
+
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_5, data);
+ memcpy(buf + off, &data, sizeof(data));
+ off += sizeof(data);
+
+ switch (panel->vsc_sdp_data.bpc) {
+ case 10:
+ bpc = BIT(1);
+ break;
+ case 8:
+ default:
+ bpc = BIT(0);
+ break;
+ }
+
+ data = (panel->vsc_sdp_data.colorimetry & 0xF) |
+ ((panel->vsc_sdp_data.pixel_encoding & 0xF) << 4) |
+ (bpc << 8) |
+ ((panel->vsc_sdp_data.dynamic_range & 0x1) << 15) |
+ ((panel->vsc_sdp_data.content_type & 0x7) << 16);
+
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_6, data);
+ memcpy(buf + off, &data, sizeof(data));
+ off += sizeof(data);
+
+ data = 0;
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_7, data);
+ memcpy(buf + off, &data, sizeof(data));
+ off += sizeof(data);
+
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_8, data);
+ memcpy(buf + off, &data, sizeof(data));
+ off += sizeof(data);
+
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_9, data);
+ memcpy(buf + off, &data, sizeof(data));
+ off += sizeof(data);
+
+ print_hex_dump(KERN_DEBUG, "[drm-dp] VSC: ",
+ DUMP_PREFIX_NONE, 16, 4, buf, off, false);
+}
+
+static void dp_catalog_panel_config_vsc_sdp(struct dp_catalog_panel *panel,
+ bool en)
+{
+ struct dp_catalog_private *catalog;
+ struct dp_io_data *io_data;
+ u32 cfg, cfg2, misc;
+
+ if (!panel) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ catalog = dp_catalog_get_priv(panel);
+ io_data = catalog->io.dp_link;
+
+ cfg = dp_read(catalog, io_data, MMSS_DP_SDP_CFG);
+ cfg2 = dp_read(catalog, io_data, MMSS_DP_SDP_CFG2);
+ misc = dp_read(catalog, io_data, DP_MISC1_MISC0);
+
+ if (en) {
+ /* GEN0_SDP_EN */
+ cfg |= BIT(17);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG, cfg);
+
+ /* GENERIC0_SDPSIZE */
+ cfg2 |= BIT(16);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG2, cfg2);
+
+ dp_catalog_panel_setup_vsc_sdp(panel);
+
+ /* indicates presence of VSC (BIT(6) of MISC1) */
+ misc |= BIT(14);
+
+ pr_debug("Enabled\n");
+ } else {
+ /* GEN0_SDP_EN */
+ cfg &= ~BIT(17);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG, cfg);
+
+ /* GENERIC0_SDPSIZE */
+ cfg2 &= ~BIT(16);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG2, cfg2);
+
+ /* switch back to MSA */
+ misc &= ~BIT(14);
+
+ pr_debug("Disabled\n");
+ }
+
+ pr_debug("misc settings = 0x%x\n", misc);
+ dp_write(catalog, io_data, DP_MISC1_MISC0, misc);
+
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG3, 0x01);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG3, 0x00);
+}
+
/* panel related catalog functions */
static int dp_catalog_panel_timing_cfg(struct dp_catalog_panel *panel)
{
@@ -1762,6 +1920,7 @@
.config_hdr = dp_catalog_panel_config_hdr,
.tpg_config = dp_catalog_panel_tpg_cfg,
.config_spd = dp_catalog_panel_config_spd,
+ .config_vsc_sdp = dp_catalog_panel_config_vsc_sdp,
};
if (!dev || !parser) {
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index dbcbd8b..12dce42 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. 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 version 2 and
@@ -64,6 +64,22 @@
struct drm_msm_ext_hdr_metadata hdr_meta;
};
+struct dp_catalog_vsc_sdp_data {
+ u32 vsc_header_byte0;
+ u32 vsc_header_byte1;
+ u32 vsc_header_byte2;
+ u32 vsc_header_byte3;
+
+ u32 bpc;
+
+ u32 version;
+ u32 length;
+ u32 pixel_encoding;
+ u32 colorimetry;
+ u32 dynamic_range;
+ u32 content_type;
+};
+
struct dp_catalog_aux {
u32 data;
u32 isr;
@@ -94,7 +110,7 @@
void (*mainlink_ctrl)(struct dp_catalog_ctrl *ctrl, bool enable);
void (*config_misc)(struct dp_catalog_ctrl *ctrl, u32 cc, u32 tb);
void (*config_msa)(struct dp_catalog_ctrl *ctrl, u32 rate,
- u32 stream_rate_khz);
+ u32 stream_rate_khz, u32 out_format);
void (*set_pattern)(struct dp_catalog_ctrl *ctrl, u32 pattern);
void (*reset)(struct dp_catalog_ctrl *ctrl);
void (*usb_reset)(struct dp_catalog_ctrl *ctrl, bool flip);
@@ -159,6 +175,7 @@
u8 *spd_vendor_name;
u8 *spd_product_description;
+ struct dp_catalog_vsc_sdp_data vsc_sdp_data;
struct dp_catalog_hdr_data hdr_data;
/* TPG */
@@ -174,6 +191,7 @@
void (*config_hdr)(struct dp_catalog_panel *panel, bool en);
void (*tpg_config)(struct dp_catalog_panel *panel, bool enable);
void (*config_spd)(struct dp_catalog_panel *panel);
+ void (*config_vsc_sdp)(struct dp_catalog_panel *panel, bool en);
};
struct dp_catalog {
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index befbc5c..43548a0 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. 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 version 2 and
@@ -18,6 +18,7 @@
#include <linux/completion.h>
#include <linux/delay.h>
+#include "msm_kms.h"
#include "dp_ctrl.h"
#define DP_KHZ_TO_HZ 1000
@@ -149,9 +150,16 @@
{
u32 config = 0, tbd;
u8 *dpcd = ctrl->panel->dpcd;
+ u32 out_format = ctrl->panel->pinfo.out_format;
config |= (2 << 13); /* Default-> LSCLK DIV: 1/4 LCLK */
- config |= (0 << 11); /* RGB */
+
+ if (out_format & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)
+ config |= (1 << 11); /* YUV420 */
+ else if (out_format & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422)
+ config |= (2 << 11); /* YUV422 */
+ else
+ config |= (0 << 11); /* RGB */
/* Scrambler reset enable */
if (dpcd[DP_EDP_CONFIGURATION_CAP] & DP_ALTERNATE_SCRAMBLER_RESET_CAP)
@@ -178,6 +186,20 @@
ctrl->catalog->config_ctrl(ctrl->catalog, config);
}
+static void dp_ctrl_misc_ctrl(struct dp_ctrl_private *ctrl)
+{
+ u32 out_format = ctrl->panel->pinfo.out_format;
+ u32 cc, tb;
+
+ tb = ctrl->link->get_test_bits_depth(ctrl->link,
+ ctrl->panel->pinfo.bpp);
+ cc = ctrl->link->get_colorimetry_config(ctrl->link);
+ if (out_format == MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422)
+ cc |= (0x01 << 1); /* Set YUV422 */
+
+ ctrl->catalog->config_misc(ctrl->catalog, cc, tb);
+}
+
/**
* dp_ctrl_configure_source_params() - configures DP transmitter source params
* @ctrl: Display Port Driver data
@@ -187,17 +209,13 @@
*/
static void dp_ctrl_configure_source_params(struct dp_ctrl_private *ctrl)
{
- u32 cc, tb;
ctrl->catalog->lane_mapping(ctrl->catalog);
ctrl->catalog->mainlink_ctrl(ctrl->catalog, true);
dp_ctrl_config_ctrl(ctrl);
+ dp_ctrl_misc_ctrl(ctrl);
- tb = ctrl->link->get_test_bits_depth(ctrl->link,
- ctrl->panel->pinfo.bpp);
- cc = ctrl->link->get_colorimetry_config(ctrl->link);
- ctrl->catalog->config_misc(ctrl->catalog, cc, tb);
ctrl->panel->timing_cfg(ctrl->panel);
}
@@ -251,7 +269,8 @@
{
u32 const multiplier = 1000000;
u64 pclk, lclk;
- u8 bpp, ln_cnt;
+ u32 bpp;
+ u8 ln_cnt;
int run_idx = 0;
u32 lwidth, h_blank;
u32 fifo_empty = 0;
@@ -309,6 +328,8 @@
int min_hblank_tmp = 0;
bool extra_req_bytes_is_neg = false;
struct dp_panel_info *pinfo = &ctrl->panel->pinfo;
+ int div = 0;
+ u32 out_format = pinfo->out_format;
u8 dp_brute_force = 1;
u64 brute_force_threshold = 10;
@@ -317,10 +338,26 @@
ln_cnt = ctrl->link->link_params.lane_count;
bpp = pinfo->bpp;
- lwidth = pinfo->h_active;
- h_blank = pinfo->h_back_porch + pinfo->h_front_porch +
- pinfo->h_sync_width;
- pclk = pinfo->pixel_clk_khz * 1000;
+ if (out_format == MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422) {
+ switch (pinfo->bpp) {
+ case 24:
+ bpp = 16;
+ break;
+ case 30:
+ bpp = 20;
+ break;
+ default:
+ bpp = 16;
+ break;
+ };
+ } else if (out_format == MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) {
+ div = 1;
+ }
+
+ lwidth = pinfo->h_active >> div;
+ h_blank = (pinfo->h_back_porch + pinfo->h_front_porch +
+ pinfo->h_sync_width) >> div;
+ pclk = (pinfo->pixel_clk_khz * 1000) >> div;
boundary_moderation_en = 0;
upper_bdry_cnt = 0;
@@ -1201,7 +1238,7 @@
ctrl->dp_ctrl.push_idle(&ctrl->dp_ctrl);
ctrl->dp_ctrl.reset(&ctrl->dp_ctrl);
- ctrl->pixel_rate = ctrl->panel->pinfo.pixel_clk_khz;
+ ctrl->pixel_rate = ctrl->panel->get_pixel_clk(ctrl->panel);
do {
if (ret == -EAGAIN) {
@@ -1229,7 +1266,9 @@
ctrl->catalog->config_msa(ctrl->catalog,
drm_dp_bw_code_to_link_rate(
- ctrl->link->link_params.bw_code), ctrl->pixel_rate);
+ ctrl->link->link_params.bw_code),
+ ctrl->pixel_rate,
+ ctrl->panel->pinfo.out_format);
reinit_completion(&ctrl->idle_comp);
@@ -1374,7 +1413,7 @@
drm_dp_link_rate_to_bw_code(rate);
ctrl->link->link_params.lane_count =
ctrl->panel->link_info.num_lanes;
- ctrl->pixel_rate = ctrl->panel->pinfo.pixel_clk_khz;
+ ctrl->pixel_rate = ctrl->panel->get_pixel_clk(ctrl->panel);
}
pr_debug("bw_code=%d, lane_count=%d, pixel_rate=%d\n",
@@ -1395,7 +1434,8 @@
while (--link_train_max_retries && !atomic_read(&ctrl->aborted)) {
ctrl->catalog->config_msa(ctrl->catalog,
drm_dp_bw_code_to_link_rate(
- ctrl->link->link_params.bw_code), ctrl->pixel_rate);
+ ctrl->link->link_params.bw_code),
+ ctrl->pixel_rate, ctrl->panel->pinfo.out_format);
rc = dp_ctrl_setup_main_link(ctrl, true);
if (!rc)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 9ae0482..eb40715 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. 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 version 2 and
@@ -1023,6 +1023,8 @@
{
const u32 num_components = 3, default_bpp = 24;
struct dp_display_private *dp;
+ u32 pixel_clk_khz = 0;
+ u32 rate_ratio = RGB_24BPP_TMDS_CHAR_RATE_RATIO;
if (!dp_display) {
pr_err("invalid input\n");
@@ -1031,13 +1033,43 @@
dp = container_of(dp_display, struct dp_display_private, dp_display);
mutex_lock(&dp->session_lock);
- mode->timing.bpp =
- dp_display->connector->display_info.bpc * num_components;
+ if (mode->timing.out_format == MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) {
+ mode->timing.bpp =
+ dp_display->connector->display_info.y420_bpc *
+ num_components;
+ rate_ratio = YUV420_24BPP_TMDS_CHAR_RATE_RATIO;
+ } else {
+ mode->timing.bpp =
+ dp_display->connector->display_info.bpc *
+ num_components;
+ }
+
+ pixel_clk_khz = mode->timing.pixel_clk_khz / rate_ratio;
+
if (!mode->timing.bpp)
mode->timing.bpp = default_bpp;
mode->timing.bpp = dp->panel->get_mode_bpp(dp->panel,
- mode->timing.bpp, mode->timing.pixel_clk_khz);
+ mode->timing.bpp, pixel_clk_khz);
+
+ /* Refactor bits per pixel for YUV422 format */
+ if (mode->timing.out_format == MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422) {
+ switch (mode->timing.bpp) {
+ case 18:
+ mode->timing.bpp = 24;
+ break;
+ case 24:
+ mode->timing.bpp = 30;
+ break;
+ case 30:
+ mode->timing.bpp = 36;
+ break;
+ default:
+ mode->timing.bpp = 30;
+ break;
+ };
+ pr_debug("YCC422 bpp = %d\n", mode->timing.bpp);
+ }
dp->panel->pinfo = mode->timing;
dp->panel->init(dp->panel);
@@ -1287,12 +1319,14 @@
return 0;
}
-static int dp_display_validate_mode(struct dp_display *dp, u32 mode_pclk_khz)
+static int dp_display_get_dc_support(struct dp_display *dp,
+ u32 mode_pclk_khz, u32 out_format, bool dc_enable)
{
- const u32 num_components = 3, default_bpp = 24;
struct dp_display_private *dp_display;
struct drm_dp_link *link_info;
- u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
+ u32 mode_rate_khz = 0, supported_rate_khz = 0;
+ u32 default_bpp = 24, max_supported_bpp = 30;
+ u32 rate_ratio = RGB_24BPP_TMDS_CHAR_RATE_RATIO;
if (!dp || !mode_pclk_khz || !dp->connector) {
pr_err("invalid params\n");
@@ -1302,7 +1336,51 @@
dp_display = container_of(dp, struct dp_display_private, dp_display);
link_info = &dp_display->panel->link_info;
- mode_bpp = dp->connector->display_info.bpc * num_components;
+ if (out_format & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)
+ rate_ratio = YUV420_24BPP_TMDS_CHAR_RATE_RATIO;
+
+ mode_pclk_khz /= rate_ratio;
+
+ mode_rate_khz = mode_pclk_khz * default_bpp;
+ if (dc_enable)
+ mode_rate_khz = mode_pclk_khz * max_supported_bpp;
+
+ supported_rate_khz = link_info->num_lanes * link_info->rate * 8;
+
+ if (mode_rate_khz > supported_rate_khz)
+ return false;
+
+ return true;
+}
+
+static int dp_display_validate_mode(struct dp_display *dp,
+ u32 mode_pclk_khz, u32 flags)
+{
+ const u32 num_components = 3, default_bpp = 24;
+ struct dp_display_private *dp_display;
+ struct drm_dp_link *link_info;
+ u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
+
+ if (!dp || !dp->connector) {
+ pr_err("invalid params\n");
+ return -EINVAL;
+ }
+
+ if (!mode_pclk_khz) {
+ pr_err("invalid mode_pclk_khz\n");
+ return -EINVAL;
+ }
+
+ dp_display = container_of(dp, struct dp_display_private, dp_display);
+ link_info = &dp_display->panel->link_info;
+
+ if ((flags & SDE_DRM_MODE_FLAG_FMT_MASK) ==
+ DRM_MODE_FLAG_SUPPORTS_YUV420)
+ mode_bpp =
+ dp->connector->display_info.y420_bpc * num_components;
+ else
+ mode_bpp = dp->connector->display_info.bpc * num_components;
+
if (!mode_bpp)
mode_bpp = default_bpp;
@@ -1412,6 +1490,7 @@
g_dp_display->set_mode = dp_display_set_mode;
g_dp_display->validate_mode = dp_display_validate_mode;
g_dp_display->get_modes = dp_display_get_modes;
+ g_dp_display->get_dc_support = dp_display_get_dc_support;
g_dp_display->prepare = dp_display_prepare;
g_dp_display->unprepare = dp_display_unprepare;
g_dp_display->request_irq = dp_request_irq;
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 6b83368..533bc6c 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -35,9 +35,12 @@
int (*set_mode)(struct dp_display *dp_display,
struct dp_display_mode *mode);
- int (*validate_mode)(struct dp_display *dp_display, u32 mode_pclk_khz);
+ int (*validate_mode)(struct dp_display *dp_display,
+ u32 mode_pclk_khz, u32 flags);
int (*get_modes)(struct dp_display *dp_display,
struct dp_display_mode *dp_mode);
+ int (*get_dc_support)(struct dp_display *dp_display,
+ u32 mode_pclk_khz, u32 out_format, bool dc_enable);
int (*prepare)(struct dp_display *dp_display);
int (*unprepare)(struct dp_display *dp_display);
int (*request_irq)(struct dp_display *dp_display);
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index fdd40a7..5f06009 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -31,11 +31,82 @@
HDR_ENABLE
};
+static int get_sink_dc_support(struct dp_display *dp,
+ struct drm_display_mode *mode)
+{
+ int dc_format = 0;
+ struct drm_connector *connector = dp->connector;
+
+ if ((mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV420) &&
+ (connector->display_info.edid_hdmi_dc_modes
+ & DRM_EDID_YCBCR420_DC_30))
+ if (dp->get_dc_support(dp, mode->clock,
+ MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420, true))
+ dc_format |= MSM_MODE_FLAG_YUV420_DC_ENABLE;
+
+ if ((mode->flags & DRM_MODE_FLAG_SUPPORTS_RGB) &&
+ (connector->display_info.edid_hdmi_dc_modes
+ & DRM_EDID_HDMI_DC_30))
+ if (dp->get_dc_support(dp, mode->clock,
+ MSM_MODE_FLAG_COLOR_FORMAT_RGB444, true))
+ dc_format |= MSM_MODE_FLAG_RGB444_DC_ENABLE;
+
+ if ((mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV422) &&
+ (connector->display_info.edid_hdmi_dc_modes
+ & DRM_EDID_HDMI_DC_30))
+ if (dp->get_dc_support(dp, mode->clock,
+ MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422, false))
+ dc_format |= MSM_MODE_FLAG_YUV422_DC_ENABLE;
+
+ return dc_format;
+}
+
+static u32 choose_best_format(struct dp_display *dp,
+ struct drm_display_mode *mode)
+{
+ /*
+ * choose priority:
+ * 1. DC + RGB
+ * 2. DC + YUV422
+ * 3. DC + YUV420
+ * 4. RGB
+ * 5. YUV420
+ */
+ int dc_format;
+
+ dc_format = get_sink_dc_support(dp, mode);
+ if (dc_format & MSM_MODE_FLAG_RGB444_DC_ENABLE)
+ return (MSM_MODE_FLAG_COLOR_FORMAT_RGB444
+ | MSM_MODE_FLAG_RGB444_DC_ENABLE);
+ else if (dc_format & MSM_MODE_FLAG_YUV422_DC_ENABLE)
+ return (MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422
+ | MSM_MODE_FLAG_YUV422_DC_ENABLE);
+ else if (dc_format & MSM_MODE_FLAG_YUV420_DC_ENABLE)
+ return (MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420
+ | MSM_MODE_FLAG_YUV420_DC_ENABLE);
+ else if (mode->flags & DRM_MODE_FLAG_SUPPORTS_RGB)
+ return MSM_MODE_FLAG_COLOR_FORMAT_RGB444;
+ else if (mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV420)
+ return MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420;
+
+ pr_err("Can't get available best display format\n");
+
+ return MSM_MODE_FLAG_COLOR_FORMAT_RGB444;
+}
+
static void convert_to_dp_mode(const struct drm_display_mode *drm_mode,
struct dp_display_mode *dp_mode, struct dp_display *dp)
{
memset(dp_mode, 0, sizeof(*dp_mode));
+ dp_mode->timing.out_format = MSM_MODE_FLAG_COLOR_FORMAT_RGB444;
+ if (drm_mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422)
+ dp_mode->timing.out_format =
+ MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422;
+ else if (drm_mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)
+ dp_mode->timing.out_format =
+ MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420;
+
dp_mode->timing.h_active = drm_mode->hdisplay;
dp_mode->timing.h_back_porch = drm_mode->htotal - drm_mode->hsync_end;
dp_mode->timing.h_sync_width = drm_mode->htotal -
@@ -61,6 +132,8 @@
dp_mode->timing.h_active_low =
!!(drm_mode->flags & DRM_MODE_FLAG_NHSYNC);
+
+ dp_mode->flags = drm_mode->flags;
}
static void convert_to_drm_mode(const struct dp_display_mode *dp_mode,
@@ -99,6 +172,7 @@
flags |= DRM_MODE_FLAG_PVSYNC;
drm_mode->flags = flags;
+ drm_mode->flags |= (dp_mode->flags & SDE_DRM_MODE_FLAG_FMT_MASK);
drm_mode->type = 0x48;
drm_mode_set_name(drm_mode);
@@ -153,6 +227,7 @@
bridge->id, rc);
dp->unprepare(dp);
}
+
}
static void dp_bridge_enable(struct drm_bridge *drm_bridge)
@@ -272,6 +347,15 @@
convert_to_dp_mode(mode, &dp_mode, dp);
convert_to_drm_mode(&dp_mode, adjusted_mode);
+
+ /* Clear the private flags before assigning new one */
+ adjusted_mode->private_flags = 0;
+
+ adjusted_mode->private_flags |=
+ choose_best_format(dp, adjusted_mode);
+ SDE_DEBUG("Adjusted mode private flags: 0x%x\n",
+ adjusted_mode->private_flags);
+
end:
return ret;
}
@@ -574,6 +658,8 @@
{
struct dp_display *dp_disp;
struct dp_debug *debug;
+ u32 pclk = 0;
+ u32 rate_ratio = RGB_24BPP_TMDS_CHAR_RATE_RATIO;
if (!mode || !display) {
pr_err("invalid params\n");
@@ -585,7 +671,23 @@
mode->vrefresh = drm_mode_vrefresh(mode);
- if (mode->clock > dp_disp->max_pclk_khz)
+ if ((mode->flags & SDE_DRM_MODE_FLAG_FMT_MASK) ==
+ DRM_MODE_FLAG_SUPPORTS_YUV420) {
+ rate_ratio = YUV420_24BPP_TMDS_CHAR_RATE_RATIO;
+ } else if ((mode->flags & DRM_MODE_FLAG_SUPPORTS_RGB) &&
+ (mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV420)) {
+ if (mode->clock > dp_disp->max_pclk_khz) {
+ /* Clear RGB and YUV422 support flags */
+ mode->flags &= ~DRM_MODE_FLAG_SUPPORTS_RGB;
+ mode->flags &= ~DRM_MODE_FLAG_SUPPORTS_YUV422;
+ /* Only YUV420 format is now supported */
+ rate_ratio = YUV420_24BPP_TMDS_CHAR_RATE_RATIO;
+ }
+ }
+
+ pclk = mode->clock / rate_ratio;
+
+ if (pclk > dp_disp->max_pclk_khz)
return MODE_BAD;
if (debug->debug_en && (mode->hdisplay != debug->hdisplay ||
@@ -594,5 +696,5 @@
mode->picture_aspect_ratio != debug->aspect_ratio))
return MODE_BAD;
- return dp_disp->validate_mode(dp_disp, mode->clock);
+ return dp_disp->validate_mode(dp_disp, pclk, mode->flags);
}
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index 7132699..756298f 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. 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 version 2 and
@@ -14,6 +14,7 @@
#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
+#include "msm_kms.h"
#include "dp_panel.h"
#define DP_PANEL_DEFAULT_BPP 24
@@ -43,6 +44,17 @@
ITU_R_BT_2020_RGB,
};
+enum dp_panel_hdr_yuv_colorimetry {
+ ITU_R_BT_601,
+ ITU_R_BT_709,
+ xvYCC_601,
+ xvYCC_709,
+ sYCC_601,
+ ADOBE_YCC_601,
+ ITU_R_BT_2020_YcCBcCRc,
+ ITU_R_BT_2020_YCBCR,
+};
+
enum dp_panel_hdr_dynamic_range {
VESA,
CEA,
@@ -537,6 +549,50 @@
panel->catalog->tpg_config(catalog, true);
}
+static int dp_panel_setup_vsc_sdp(struct dp_panel *dp_panel)
+{
+ struct dp_catalog_panel *catalog;
+ struct dp_panel_private *panel;
+ struct dp_panel_info *pinfo;
+ int rc = 0;
+
+ if (!dp_panel) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+ catalog = panel->catalog;
+ pinfo = &panel->dp_panel.pinfo;
+
+ memset(&catalog->vsc_sdp_data, 0, sizeof(catalog->vsc_sdp_data));
+
+ if (pinfo->out_format & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) {
+ catalog->vsc_sdp_data.vsc_header_byte0 = 0x00;
+ catalog->vsc_sdp_data.vsc_header_byte1 = 0x07;
+ catalog->vsc_sdp_data.vsc_header_byte2 = 0x05;
+ catalog->vsc_sdp_data.vsc_header_byte3 = 0x13;
+
+ /* VSC SDP Payload for DB16 */
+ catalog->vsc_sdp_data.pixel_encoding = YCbCr420;
+ catalog->vsc_sdp_data.colorimetry = ITU_R_BT_709;
+
+ /* VSC SDP Payload for DB17 */
+ catalog->vsc_sdp_data.dynamic_range = CEA;
+
+ /* VSC SDP Payload for DB18 */
+ catalog->vsc_sdp_data.content_type = GRAPHICS;
+
+ catalog->vsc_sdp_data.bpc = pinfo->bpp / 3;
+
+ if (panel->catalog->config_vsc_sdp)
+ panel->catalog->config_vsc_sdp(catalog, true);
+ }
+end:
+ return rc;
+}
+
static int dp_panel_timing_cfg(struct dp_panel *dp_panel)
{
int rc = 0;
@@ -596,11 +652,37 @@
catalog->dp_active = data;
panel->catalog->timing_cfg(catalog);
+ dp_panel_setup_vsc_sdp(dp_panel);
panel->panel_on = true;
end:
return rc;
}
+static u32 dp_panel_get_pixel_clk(struct dp_panel *dp_panel)
+{
+ struct dp_panel_private *panel;
+ struct dp_panel_info *pinfo;
+ u32 pixel_clk_khz = 0;
+ u32 rate_ratio = RGB_24BPP_TMDS_CHAR_RATE_RATIO;
+
+ if (!dp_panel) {
+ pr_err("invalid input\n");
+ return 0;
+ }
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+ pinfo = &panel->dp_panel.pinfo;
+
+ pixel_clk_khz = pinfo->pixel_clk_khz;
+
+ if (pinfo->out_format == MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)
+ rate_ratio = YUV420_24BPP_TMDS_CHAR_RATE_RATIO;
+
+ pixel_clk_khz /= rate_ratio;
+
+ return pixel_clk_khz;
+}
+
static int dp_panel_edid_register(struct dp_panel_private *panel)
{
int rc = 0;
@@ -619,6 +701,16 @@
sde_edid_deinit((void **)&panel->dp_panel.edid_ctrl);
}
+static inline char *get_out_format(u32 out_format)
+{
+ if (out_format == MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)
+ return "Y420";
+ else if (out_format == MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422)
+ return "Y422";
+ else
+ return "RGB";
+}
+
static int dp_panel_init_panel_info(struct dp_panel *dp_panel)
{
int rc = 0;
@@ -648,6 +740,7 @@
pinfo->v_front_porch,
pinfo->v_sync_width);
pr_info("pixel clock (KHz)=(%d)\n", pinfo->pixel_clk_khz);
+ pr_info("%s\n", get_out_format(pinfo->out_format));
pr_info("bpp = %d\n", pinfo->bpp);
pr_info("active low (h|v)=(%d|%d)\n", pinfo->h_active_low,
pinfo->v_active_low);
@@ -856,6 +949,7 @@
dp_panel->spd_config = dp_panel_spd_config;
dp_panel->setup_hdr = dp_panel_setup_hdr;
dp_panel->hdr_supported = dp_panel_hdr_supported;
+ dp_panel->get_pixel_clk = dp_panel_get_pixel_clk;
dp_panel_edid_register(panel);
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index 6c2e186..564ace3 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. 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 version 2 and
@@ -28,6 +28,7 @@
DP_LANE_COUNT_4 = 4,
};
+
#define DP_MAX_DOWNSTREAM_PORTS 0x10
struct dp_panel_info {
@@ -45,11 +46,13 @@
u32 refresh_rate;
u32 pixel_clk_khz;
u32 bpp;
+ u32 out_format;
};
struct dp_display_mode {
struct dp_panel_info timing;
u32 capabilities;
+ u32 flags;
};
struct dp_panel_in {
@@ -94,6 +97,7 @@
void (*tpg_config)(struct dp_panel *dp_panel, bool enable);
int (*spd_config)(struct dp_panel *dp_panel);
bool (*hdr_supported)(struct dp_panel *dp_panel);
+ u32 (*get_pixel_clk)(struct dp_panel *dp_panel);
};
/**
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index 25523b0..828e0f6 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@@ -26,6 +26,9 @@
#define MAX_PLANE 4
+#define RGB_24BPP_TMDS_CHAR_RATE_RATIO 1
+#define YUV420_24BPP_TMDS_CHAR_RATE_RATIO 2
+
/**
* Device Private DRM Mode Flags
* drm_mode->private_flags