summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorAbhinav Kumar <abhinavk@codeaurora.org>2017-07-26 20:11:32 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2017-08-10 17:39:57 -0700
commit7c79cabfdf98929dc449ce56772106480704f2ae (patch)
treecb56c718637d9046fd87272616db7946f8a90826 /drivers/gpu
parent39b5f76de03e17a0b80cb94a92496b1f95bf32b0 (diff)
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 <abhinavk@codeaurora.org>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c30
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h13
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c35
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h2
-rw-r--r--drivers/gpu/drm/msm/sde/sde_connector.c22
-rw-r--r--drivers/gpu/drm/msm/sde/sde_connector.h15
-rw-r--r--drivers/gpu/drm/msm/sde/sde_encoder.c23
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.c1
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;