From 7c79cabfdf98929dc449ce56772106480704f2ae Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Wed, 26 Jul 2017 20:11:32 -0700 Subject: [PATCH] drm/msm: change CSC matrix selection logic for CDM block CDM block is always using a limited quantization range matrix. This can be overridden to use a full range matrix if the sink supports override capability or the mode is a non-CEA mode. Adjust the matrix selection logic to accommodate these conditions. Change-Id: I708412a923fb0d47e798f35ebe14b4c2f1a72fc9 Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c | 30 ++++++++++++++++ drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h | 13 +++++++ .../drm/msm/hdmi-staging/sde_hdmi_bridge.c | 35 +++++++++++++++++-- .../gpu/drm/msm/hdmi-staging/sde_hdmi_util.h | 2 ++ drivers/gpu/drm/msm/sde/sde_connector.c | 22 ++++++++++++ drivers/gpu/drm/msm/sde/sde_connector.h | 15 ++++++++ drivers/gpu/drm/msm/sde/sde_encoder.c | 23 ++++++++++-- drivers/gpu/drm/msm/sde/sde_kms.c | 1 + 8 files changed, 136 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index f1c44b30575f..0949b55a6d87 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -2364,6 +2364,36 @@ int sde_hdmi_pre_kickoff(struct drm_connector *connector, return 0; } +bool sde_hdmi_mode_needs_full_range(void *display) +{ + struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display; + struct drm_display_mode *mode; + u32 mode_fmt_flags = 0; + u32 cea_mode; + + if (!hdmi_display) { + SDE_ERROR("invalid input\n"); + return false; + } + + mode = &hdmi_display->mode; + /* Cache the format flags before clearing */ + mode_fmt_flags = mode->flags; + /** + * Clear the RGB/YUV format flags before calling upstream API + * as the API also compares the flags and then returns a mode + */ + mode->flags &= ~SDE_DRM_MODE_FLAG_FMT_MASK; + cea_mode = drm_match_cea_mode(mode); + /* Restore the format flags */ + mode->flags = mode_fmt_flags; + + if (cea_mode > SDE_HDMI_VIC_640x480) + return false; + + return true; +} + int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display) { struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display; diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index f2dd5351913b..441bb3a6dfe8 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -482,6 +482,14 @@ int sde_hdmi_pre_kickoff(struct drm_connector *connector, void *display, struct msm_display_kickoff_params *params); +/* + * sde_hdmi_mode_needs_full_range - does mode need full range + * quantization + * @display: Pointer to private display structure + * Returns: true or false based on mode + */ +bool sde_hdmi_mode_needs_full_range(void *display); + #else /*#ifdef CONFIG_DRM_SDE_HDMI*/ static inline u32 sde_hdmi_get_num_of_displays(void) @@ -596,5 +604,10 @@ static inline int sde_hdmi_set_property(struct drm_connector *connector, return 0; } +static inline bool sde_hdmi_mode_needs_full_range(void *display) +{ + return false; +} + #endif /*#else of CONFIG_DRM_SDE_HDMI*/ #endif /* _SDE_HDMI_H_ */ diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index 17ef86f1ba4f..d9a8feb14a23 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -582,18 +582,49 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) } static void _sde_hdmi_bridge_set_avi_infoframe(struct hdmi *hdmi, - const struct drm_display_mode *mode) + struct drm_display_mode *mode) { u8 avi_iframe[HDMI_AVI_INFOFRAME_BUFFER_SIZE] = {0}; u8 *avi_frame = &avi_iframe[HDMI_INFOFRAME_HEADER_SIZE]; u8 checksum; u32 reg_val; + u32 mode_fmt_flags = 0; struct hdmi_avi_infoframe info; + struct drm_connector *connector; + if (!hdmi || !mode) { + SDE_ERROR("invalid input\n"); + return; + } + + connector = hdmi->connector; + + if (!connector) { + SDE_ERROR("invalid input\n"); + return; + } + + /* Cache the format flags before clearing */ + mode_fmt_flags = mode->flags; + /** + * Clear the RGB/YUV format flags before calling upstream API + * as the API also compares the flags and then returns a mode + */ + mode->flags &= ~SDE_DRM_MODE_FLAG_FMT_MASK; drm_hdmi_avi_infoframe_from_display_mode(&info, mode); + /* Restore the format flags */ + mode->flags = mode_fmt_flags; - if (mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) + if (mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420) { info.colorspace = HDMI_COLORSPACE_YUV420; + /** + * If sink supports quantization select, + * override to full range + */ + if (connector->yuv_qs) + info.ycc_quantization_range = + HDMI_YCC_QUANTIZATION_RANGE_FULL; + } hdmi_avi_infoframe_pack(&info, avi_iframe, sizeof(avi_iframe)); checksum = avi_iframe[HDMI_INFOFRAME_HEADER_SIZE - 1]; diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h index 1d89ae222a7b..f18b44f51ff9 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h @@ -97,6 +97,8 @@ #define HDMI_GET_MSB(x)(x >> 8) #define HDMI_GET_LSB(x)(x & 0xff) +#define SDE_HDMI_VIC_640x480 0x1 + /* * Bits 1:0 in HDMI_HW_DDC_CTRL that dictate how the HDCP 2.2 RxStatus will be * read by the hardware diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index 6cc54d15beb2..d79361bbe265 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -90,6 +90,28 @@ int sde_connector_pre_kickoff(struct drm_connector *connector) return rc; } +bool sde_connector_mode_needs_full_range(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_needs_full_range) + return false; + + return c_conn->ops.mode_needs_full_range(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 8257f29bd4b8..73a20133a944 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.h +++ b/drivers/gpu/drm/msm/sde/sde_connector.h @@ -135,6 +135,14 @@ struct sde_connector_ops { int (*pre_kickoff)(struct drm_connector *connector, void *display, struct msm_display_kickoff_params *params); + + /** + * mode_needs_full_range - does the mode need full range + * quantization + * @display: Pointer to private display structure + * Returns: true or false based on whether full range is needed + */ + bool (*mode_needs_full_range)(void *display); }; /** @@ -327,5 +335,12 @@ int sde_connector_get_info(struct drm_connector *connector, */ int sde_connector_pre_kickoff(struct drm_connector *connector); +/** + * sde_connector_mode_needs_full_range - query quantization type + * for the connector mode + * @connector: Pointer to drm connector object + * Returns: true OR false based on connector mode + */ +bool sde_connector_mode_needs_full_range(struct drm_connector *connector); #endif /* _SDE_CONNECTOR_H_ */ diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index 97c9f8baea6d..baf0d0bf1f0b 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -1417,6 +1417,7 @@ void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc, struct sde_encoder_virt *sde_enc = NULL; struct sde_hw_cdm *hw_cdm = phys_enc->hw_cdm; struct sde_hw_cdm_cfg *cdm_cfg = &phys_enc->cdm_cfg; + struct drm_connector *connector = phys_enc->connector; int ret; u32 csc_type = 0; @@ -1476,10 +1477,26 @@ void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc, cdm_cfg->h_cdwn_type, cdm_cfg->v_cdwn_type); - if (output_type == CDM_CDWN_OUTPUT_HDMI) - csc_type = SDE_CSC_RGB2YUV_601FR; - else if (output_type == CDM_CDWN_OUTPUT_WB) + /** + * Choose CSC matrix based on following rules: + * 1. If connector supports quantization select, + * pick Full-Range for better quality. + * 2. If non-CEA mode, then pick Full-Range as per CEA spec + * 3. Otherwise, pick Limited-Range as all other CEA modes + * need a limited range + */ + + if (output_type == CDM_CDWN_OUTPUT_HDMI) { + if (connector && connector->yuv_qs) + csc_type = SDE_CSC_RGB2YUV_601FR; + else if (connector && + sde_connector_mode_needs_full_range(connector)) + csc_type = SDE_CSC_RGB2YUV_601FR; + else + csc_type = SDE_CSC_RGB2YUV_601L; + } else if (output_type == CDM_CDWN_OUTPUT_WB) { csc_type = SDE_CSC_RGB2YUV_601L; + } if (hw_cdm && hw_cdm->ops.setup_csc_data) { ret = hw_cdm->ops.setup_csc_data(hw_cdm, diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index a84d65195363..eaa1eca8fc8e 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -594,6 +594,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .set_property = sde_hdmi_set_property, .get_property = sde_hdmi_get_property, .pre_kickoff = sde_hdmi_pre_kickoff, + .mode_needs_full_range = sde_hdmi_mode_needs_full_range }; struct msm_display_info info = {0}; struct drm_encoder *encoder;