drm/msm/dp: configure quantization ranges as per video format
As per CEA-861-F, use Limited Range levels if a CE video format
pxl data is encoded using RGB color space. Use Full Range levels
for a IT video format.
Change-Id: I254a0e4b5c72d9dd783a539c10c339d50a409ab0
Signed-off-by: Narender Ankam <nankam@codeaurora.org>
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index a3985c6..0a8fc78 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -2960,7 +2960,7 @@
*
* Returns the HDMI Video ID (VIC) of the mode or 0 if it isn't one.
*/
-static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match)
+u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match)
{
u8 vic;
@@ -2982,6 +2982,7 @@
}
return 0;
}
+EXPORT_SYMBOL(drm_match_hdmi_mode);
static bool drm_valid_hdmi_vic(u8 vic)
{
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 922df7e..3229987 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -59,6 +59,7 @@
int dp_display_get_num_of_displays(void);
int dp_display_get_displays(void **displays, int count);
bool dp_connector_mode_needs_full_range(void *display);
+bool dp_connector_mode_is_cea_mode(void *display);
enum sde_csc_type dp_connector_get_csc_type(struct drm_connector *conn,
void *data);
#endif /* _DP_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 1ffb0e2..442179f 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -134,6 +134,8 @@
!!(drm_mode->flags & DRM_MODE_FLAG_NHSYNC);
dp_mode->flags = drm_mode->flags;
+
+ dp_mode->timing.par = drm_mode->picture_aspect_ratio;
}
static void convert_to_drm_mode(const struct dp_display_mode *dp_mode,
@@ -174,6 +176,8 @@
drm_mode->flags = flags;
drm_mode->flags |= (dp_mode->flags & SDE_DRM_MODE_FLAG_FMT_MASK);
+ drm_mode->picture_aspect_ratio = dp_mode->timing.par;
+
drm_mode->type = 0x48;
drm_mode_set_name(drm_mode);
}
@@ -474,6 +478,45 @@
return false;
}
+bool dp_connector_mode_is_cea_mode(void *data)
+{
+ struct dp_display *display = data;
+ struct dp_bridge *bridge;
+ struct dp_display_mode *mode;
+ struct drm_display_mode drm_mode;
+ struct dp_panel_info *timing;
+ bool is_ce_mode = false;
+
+ if (!display) {
+ pr_err("invalid input\n");
+ return false;
+ }
+
+ bridge = display->bridge;
+ if (!bridge) {
+ pr_err("invalid bridge data\n");
+ return false;
+ }
+
+ mode = &bridge->dp_mode;
+ timing = &mode->timing;
+
+ if (timing->h_active == 640 &&
+ timing->v_active == 480)
+ is_ce_mode = false;
+
+ convert_to_drm_mode(mode, &drm_mode);
+ drm_mode.flags &= ~SDE_DRM_MODE_FLAG_FMT_MASK;
+
+ if (drm_match_cea_mode(&drm_mode) || drm_match_hdmi_mode(&drm_mode))
+ is_ce_mode = true;
+
+ pr_debug("%s: %s : %s video format\n", __func__,
+ drm_mode.name, is_ce_mode ? "CE" : "IT");
+
+ return is_ce_mode;
+}
+
enum sde_csc_type dp_connector_get_csc_type(struct drm_connector *conn,
void *data)
{
@@ -500,10 +543,10 @@
return SDE_CSC_RGB2YUV_2020L;
else if (dp_connector_mode_needs_full_range(data)
|| conn->yuv_qs)
- return SDE_CSC_RGB2YUV_601FR;
+ return SDE_CSC_RGB2YUV_709FR;
error:
- return SDE_CSC_RGB2YUV_601L;
+ return SDE_CSC_RGB2YUV_709L;
}
enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index 57f63a8..1a87a96 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -47,6 +47,7 @@
u32 pixel_clk_khz;
u32 bpp;
u32 out_format;
+ enum hdmi_picture_aspect par;
};
struct dp_display_mode {
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 32478a7..9ef04a8 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -768,6 +768,28 @@
return c_conn->ops.mode_needs_full_range(c_conn->display);
}
+bool sde_connector_mode_is_cea_mode(struct drm_connector *connector)
+{
+ struct sde_connector *c_conn;
+
+ if (!connector) {
+ SDE_ERROR("invalid argument\n");
+ return false;
+ }
+
+ c_conn = to_sde_connector(connector);
+
+ if (!c_conn->display) {
+ SDE_ERROR("invalid argument\n");
+ return false;
+ }
+
+ if (!c_conn->ops.mode_is_cea_mode)
+ return false;
+
+ return c_conn->ops.mode_is_cea_mode(c_conn->display);
+}
+
static void sde_connector_destroy(struct drm_connector *connector)
{
struct sde_connector *c_conn;
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 2543822..3fb4531 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -192,6 +192,14 @@
bool (*mode_needs_full_range)(void *display);
/**
+ * mode_is_cea_mode - is this mode a CE video format
+ * or IT video format.
+ * @display: Pointer to private display structure
+ * Returns: true or false based on CE or IT video format
+ */
+ bool (*mode_is_cea_mode)(void *display);
+
+ /**
* clk_ctrl - perform clk enable/disable on the connector
* @handle: Pointer to clk handle
* @type: Type of clks
@@ -728,6 +736,14 @@
bool sde_connector_mode_needs_full_range(struct drm_connector *connector);
/**
+ * sde_connector_mode_is_cea_mode - query if this mode is
+ * CE or IT video format
+ * @connector: Pointer to drm connector object
+ * Returns: true OR false based on CE or IT video format mode
+ */
+bool sde_connector_mode_is_cea_mode(struct drm_connector *connector);
+
+/**
* sde_connector_get_csc_type - query csc type
* to be used for the connector
* @connector: Pointer to drm connector object
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 0956c01..3e625fe 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -234,6 +234,30 @@
{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
},
+
+ [SDE_CSC_RGB2RGB_L] = {
+ {
+ TO_S15D16(0x01b7), TO_S15D16(0x0000), TO_S15D16(0x0000),
+ TO_S15D16(0x0000), TO_S15D16(0x01b7), TO_S15D16(0x0000),
+ TO_S15D16(0x0000), TO_S15D16(0x0000), TO_S15D16(0x01b7),
+ },
+ { 0x0, 0x0, 0x0,},
+ { 0x0040, 0x0040, 0x0040,},
+ { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+ { 0x40, 0x3ac, 0x40, 0x3ac, 0x40, 0x3ac,},
+ },
+
+ [SDE_CSC_RGB2RGB_FR] = {
+ {
+ TO_S15D16(0x0200), TO_S15D16(0x0000), TO_S15D16(0x0000),
+ TO_S15D16(0x0000), TO_S15D16(0x0200), TO_S15D16(0x0000),
+ TO_S15D16(0x0000), TO_S15D16(0x0000), TO_S15D16(0x0200),
+ },
+ { 0x0, 0x0, 0x0,},
+ { 0x0, 0x0, 0x0,},
+ { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+ { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+ }
};
/**
@@ -4198,8 +4222,10 @@
phys = sde_enc->phys_encs[i];
if (phys) {
mode = &phys->cached_mode;
- mode_is_yuv = (mode->private_flags &
- MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420);
+ mode_is_yuv = ((mode->private_flags &
+ MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) ||
+ (mode->private_flags &
+ MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422));
}
/**
* Check the CSC matrix type to which the
@@ -5269,7 +5295,8 @@
}
sde_enc = to_sde_encoder_virt(encoder);
- if (!SDE_FORMAT_IS_YUV(format)) {
+ if ((output_type == CDM_CDWN_OUTPUT_WB) &&
+ !SDE_FORMAT_IS_YUV(format)) {
SDE_DEBUG_ENC(sde_enc, "[cdm_disable fmt:%x]\n",
format->base.pixel_format);
@@ -5329,13 +5356,21 @@
*/
if (output_type == CDM_CDWN_OUTPUT_HDMI) {
- if (connector && connector->yuv_qs)
- csc_type = SDE_CSC_RGB2YUV_709FR;
- else if (connector &&
- sde_connector_mode_needs_full_range(connector))
- csc_type = SDE_CSC_RGB2YUV_709FR;
- else
- csc_type = SDE_CSC_RGB2YUV_709L;
+ if (SDE_FORMAT_IS_YUV(format)) {
+ if (connector && connector->yuv_qs)
+ csc_type = SDE_CSC_RGB2YUV_709FR;
+ else if (connector &&
+ sde_connector_mode_needs_full_range(connector))
+ csc_type = SDE_CSC_RGB2YUV_709FR;
+ else
+ csc_type = SDE_CSC_RGB2YUV_709L;
+ } else if (connector &&
+ sde_connector_mode_is_cea_mode(connector)) {
+ csc_type = SDE_CSC_RGB2RGB_L;
+ } else {
+ csc_type = SDE_CSC_RGB2RGB_FR;
+ }
+
} else if (output_type == CDM_CDWN_OUTPUT_WB) {
csc_type = SDE_CSC_RGB2YUV_601L;
}
@@ -5368,3 +5403,21 @@
}
}
}
+
+void sde_encoder_phys_destroy_cdm(struct sde_encoder_phys *phys_enc)
+{
+ struct drm_encoder *encoder = phys_enc->parent;
+ struct sde_encoder_virt *sde_enc = NULL;
+ struct sde_hw_cdm *hw_cdm = phys_enc->hw_cdm;
+
+ if (!encoder) {
+ SDE_ERROR("invalid encoder\n");
+ return;
+ }
+ sde_enc = to_sde_encoder_virt(encoder);
+
+ SDE_DEBUG_ENC(sde_enc, "[cdm_disable]\n");
+
+ if (hw_cdm && hw_cdm->ops.disable)
+ hw_cdm->ops.disable(hw_cdm);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index b1547ab..d5e9f4a 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -498,6 +498,8 @@
const struct sde_format *format, u32 output_type,
struct sde_rect *roi);
+void sde_encoder_phys_destroy_cdm(struct sde_encoder_phys *phys_enc);
+
/**
* sde_encoder_helper_trigger_flush - control flush helper function
* This helper function may be optionally specified by physical
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index a1c3bfa..bd0868b 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -794,6 +794,8 @@
fmt = sde_get_sde_format(DRM_FORMAT_YUV420);
else if (mode.private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR422)
fmt = sde_get_sde_format(DRM_FORMAT_NV61);
+ else if (mode.private_flags & MSM_MODE_FLAG_COLOR_FORMAT_RGB444)
+ fmt = sde_get_sde_format(DRM_FORMAT_RGB888);
if (fmt) {
struct sde_rect hdmi_roi;
@@ -830,6 +832,7 @@
}
vid_enc = to_sde_encoder_phys_vid(phys_enc);
+ sde_encoder_phys_destroy_cdm(phys_enc);
SDE_DEBUG_VIDENC(vid_enc, "\n");
kfree(vid_enc);
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
index a223e77..86661983 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-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
@@ -226,21 +226,29 @@
u32 opmode = 0;
u32 csc = 0;
- if (!SDE_FORMAT_IS_YUV(fmt))
+ if ((cdm->output_type == CDM_CDWN_OUTPUT_WB) &&
+ !SDE_FORMAT_IS_YUV(fmt))
return -EINVAL;
if (cdm->output_type == CDM_CDWN_OUTPUT_HDMI) {
if (fmt->chroma_sample == SDE_CHROMA_H1V2)
return -EINVAL; /*unsupported format */
- opmode = BIT(0);
- opmode |= (fmt->chroma_sample << 1);
+ if (fmt->chroma_sample == SDE_CHROMA_RGB) {
+ opmode = 0;
+ } else {
+ opmode = BIT(0);
+ opmode |= (fmt->chroma_sample << 1);
+ }
cdm_cfg.intf_en = true;
} else {
opmode = 0;
cdm_cfg.wb_en = true;
}
- csc |= BIT(2);
+ if (fmt->chroma_sample == SDE_CHROMA_RGB)
+ csc &= ~BIT(2);
+ else
+ csc |= BIT(2);
csc &= ~BIT(1);
csc |= BIT(0);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index 322613d..db84ba4 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -400,6 +400,8 @@
SDE_CSC_RGB2YUV_709FR,
SDE_CSC_RGB2YUV_2020L,
SDE_CSC_RGB2YUV_2020FR,
+ SDE_CSC_RGB2RGB_L,
+ SDE_CSC_RGB2RGB_FR,
SDE_MAX_CSC
};
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 977e3a5..df4eaec 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -1320,6 +1320,7 @@
.cont_splash_config = NULL,
.get_panel_vfp = NULL,
.mode_needs_full_range = dp_connector_mode_needs_full_range,
+ .mode_is_cea_mode = dp_connector_mode_is_cea_mode,
.get_csc_type = dp_connector_get_csc_type,
};
static const struct sde_connector_ops ext_bridge_ops = {
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index b5c134a..c363e3b 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -458,6 +458,7 @@
int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
u8 drm_match_cea_mode(const struct drm_display_mode *to_match);
+u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match);
enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code);
bool drm_detect_hdmi_monitor(struct edid *edid);
bool drm_detect_monitor_audio(struct edid *edid);