diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 0949b55a6d87..ab8e7a0ed6a5 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -2394,6 +2394,40 @@ bool sde_hdmi_mode_needs_full_range(void *display) return true; } +enum sde_csc_type sde_hdmi_get_csc_type(struct drm_connector *conn, + void *display) +{ + struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display; + struct sde_connector_state *c_state; + struct drm_msm_ext_panel_hdr_ctrl *hdr_ctrl; + struct drm_msm_ext_panel_hdr_metadata *hdr_meta; + + if (!hdmi_display || !conn) { + SDE_ERROR("invalid input\n"); + goto error; + } + + c_state = to_sde_connector_state(conn->state); + + if (!c_state) { + SDE_ERROR("invalid input\n"); + goto error; + } + + hdr_ctrl = &c_state->hdr_ctrl; + hdr_meta = &hdr_ctrl->hdr_meta; + + if ((hdr_ctrl->hdr_state == HDR_ENABLE) + && (hdr_meta->eotf != 0)) + return SDE_CSC_RGB2YUV_2020L; + else if (sde_hdmi_mode_needs_full_range(hdmi_display) + || conn->yuv_qs) + return SDE_CSC_RGB2YUV_601FR; + +error: + return SDE_CSC_RGB2YUV_601L; +} + 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 441bb3a6dfe8..e80fc66a6244 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -490,6 +490,15 @@ int sde_hdmi_pre_kickoff(struct drm_connector *connector, */ bool sde_hdmi_mode_needs_full_range(void *display); +/* + * sde_hdmi_get_csc_type - returns the CSC type to be + * used based on state of HDR playback + * @conn: Pointer to DRM connector + * @display: Pointer to private display structure + * Returns: true or false based on mode + */ +enum sde_csc_type sde_hdmi_get_csc_type(struct drm_connector *conn, + void *display); #else /*#ifdef CONFIG_DRM_SDE_HDMI*/ static inline u32 sde_hdmi_get_num_of_displays(void) @@ -609,5 +618,11 @@ static inline bool sde_hdmi_mode_needs_full_range(void *display) return false; } +enum sde_csc_type sde_hdmi_get_csc_type(struct drm_connector *conn, + void *display) +{ + return 0; +} + #endif /*#else of CONFIG_DRM_SDE_HDMI*/ #endif /* _SDE_HDMI_H_ */ diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index d79361bbe265..5fa4c21060f9 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; } +enum sde_csc_type sde_connector_get_csc_type(struct drm_connector *conn) +{ + struct sde_connector *c_conn; + + if (!conn) { + SDE_ERROR("invalid argument\n"); + return -EINVAL; + } + + c_conn = to_sde_connector(conn); + + if (!c_conn->display) { + SDE_ERROR("invalid argument\n"); + return -EINVAL; + } + + if (!c_conn->ops.get_csc_type) + return SDE_CSC_RGB2YUV_601L; + + return c_conn->ops.get_csc_type(conn, c_conn->display); +} + bool sde_connector_mode_needs_full_range(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 73a20133a944..b76ce0aaf577 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.h +++ b/drivers/gpu/drm/msm/sde/sde_connector.h @@ -143,6 +143,16 @@ struct sde_connector_ops { * Returns: true or false based on whether full range is needed */ bool (*mode_needs_full_range)(void *display); + + /** + * get_csc_type - returns the CSC type to be used + * by the CDM block based on HDR state + * @connector: Pointer to drm connector structure + * @display: Pointer to private display structure + * Returns: type of CSC matrix to be used + */ + enum sde_csc_type (*get_csc_type)(struct drm_connector *connector, + void *display); }; /** @@ -342,5 +352,14 @@ int sde_connector_pre_kickoff(struct drm_connector *connector); * Returns: true OR false based on connector mode */ bool sde_connector_mode_needs_full_range(struct drm_connector *connector); + +/** + * sde_connector_get_csc_type - query csc type + * to be used for the connector + * @connector: Pointer to drm connector object + * Returns: csc type based on connector HDR state + */ +enum sde_csc_type sde_connector_get_csc_type(struct drm_connector *conn); + #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 baf0d0bf1f0b..23fb79241d84 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -80,6 +80,42 @@ static struct sde_csc_cfg sde_csc_10bit_convert[SDE_MAX_CSC] = { { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, }, + + [SDE_CSC_RGB2YUV_709L] = { + { + TO_S15D16(0x005d), TO_S15D16(0x013a), TO_S15D16(0x0020), + TO_S15D16(0xffcc), TO_S15D16(0xff53), TO_S15D16(0x00e1), + TO_S15D16(0x00e1), TO_S15D16(0xff34), TO_S15D16(0xffeb), + }, + { 0x0, 0x0, 0x0,}, + { 0x0040, 0x0200, 0x0200,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + { 0x0040, 0x03ac, 0x0040, 0x03c0, 0x0040, 0x03c0,}, + }, + + [SDE_CSC_RGB2YUV_2020L] = { + { + TO_S15D16(0x0073), TO_S15D16(0x0129), TO_S15D16(0x001a), + TO_S15D16(0xffc1), TO_S15D16(0xff5e), TO_S15D16(0x00e0), + TO_S15D16(0x00e0), TO_S15D16(0xff32), TO_S15D16(0xffee), + }, + { 0x0, 0x0, 0x0,}, + { 0x0040, 0x0200, 0x0200,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + { 0x0040, 0x03ac, 0x0040, 0x03c0, 0x0040, 0x03c0,}, + }, + + [SDE_CSC_RGB2YUV_2020FR] = { + { + TO_S15D16(0x0086), TO_S15D16(0x015b), TO_S15D16(0x001e), + TO_S15D16(0xffb9), TO_S15D16(0xff47), TO_S15D16(0x0100), + TO_S15D16(0x0100), TO_S15D16(0xff15), TO_S15D16(0xffeb), + }, + { 0x0, 0x0, 0x0,}, + { 0x0, 0x0200, 0x0200,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, + }, }; /** @@ -826,7 +862,12 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc; struct sde_encoder_phys *phys; + struct drm_connector *conn_mas = NULL; unsigned int i; + enum sde_csc_type conn_csc; + struct drm_display_mode *mode; + struct sde_hw_cdm *hw_cdm; + int mode_is_yuv = 0; int rc; if (!drm_enc) { @@ -846,11 +887,46 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc) } if (sde_enc->cur_master && sde_enc->cur_master->connector) { - rc = sde_connector_pre_kickoff(sde_enc->cur_master->connector); + conn_mas = sde_enc->cur_master->connector; + rc = sde_connector_pre_kickoff(conn_mas); if (rc) - SDE_ERROR_ENC(sde_enc, "kickoff conn%d failed rc %d\n", - sde_enc->cur_master->connector->base.id, - rc); + SDE_ERROR_ENC(sde_enc, + "kickoff conn%d failed rc %d\n", + conn_mas->base.id, + rc); + + for (i = 0; i < sde_enc->num_phys_encs; i++) { + phys = sde_enc->phys_encs[i]; + if (phys) { + mode = &phys->cached_mode; + mode_is_yuv = (mode->private_flags & + MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420); + } + /** + * Check the CSC matrix type to which the + * CDM CSC matrix should be updated to based + * on the connector HDR state + */ + conn_csc = sde_connector_get_csc_type(conn_mas); + if (phys && mode_is_yuv) { + if (phys->enc_cdm_csc != conn_csc) { + hw_cdm = phys->hw_cdm; + rc = hw_cdm->ops.setup_csc_data(hw_cdm, + &sde_csc_10bit_convert[conn_csc]); + + if (rc) + SDE_ERROR_ENC(sde_enc, + "CSC setup failed rc %d\n", + rc); + SDE_DEBUG_ENC(sde_enc, + "updating CSC %d to %d\n", + phys->enc_cdm_csc, + conn_csc); + phys->enc_cdm_csc = conn_csc; + + } + } + } } } @@ -1507,6 +1583,9 @@ void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc, } } + /* Cache the CSC default matrix type */ + phys_enc->enc_cdm_csc = csc_type; + if (hw_cdm && hw_cdm->ops.setup_cdwn) { ret = hw_cdm->ops.setup_cdwn(hw_cdm, cdm_cfg); if (ret < 0) { diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h index 20f125155de3..aec844d640bd 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h @@ -174,6 +174,7 @@ enum sde_intr_idx { * @split_role: Role to play in a split-panel configuration * @intf_mode: Interface mode * @intf_idx: Interface index on sde hardware + * @enc_cdm_csc: Cached CSC type of CDM block * @enc_spinlock: Virtual-Encoder-Wide Spin Lock for IRQ purposes * @enable_state: Enable state tracking * @vblank_refcount: Reference count of vblank request @@ -201,6 +202,7 @@ struct sde_encoder_phys { enum sde_enc_split_role split_role; enum sde_intf_mode intf_mode; enum sde_intf intf_idx; + enum sde_csc_type enc_cdm_csc; spinlock_t *enc_spinlock; enum sde_enc_enable_state enable_state; atomic_t vblank_refcount; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h index 1edeff6a7aec..92dd829eee3e 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h @@ -344,6 +344,9 @@ enum sde_3d_blend_mode { enum sde_csc_type { SDE_CSC_RGB2YUV_601L, SDE_CSC_RGB2YUV_601FR, + SDE_CSC_RGB2YUV_709L, + SDE_CSC_RGB2YUV_2020L, + SDE_CSC_RGB2YUV_2020FR, SDE_MAX_CSC }; diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index eaa1eca8fc8e..cdf67c0aa864 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -594,7 +594,8 @@ 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 + .mode_needs_full_range = sde_hdmi_mode_needs_full_range, + .get_csc_type = sde_hdmi_get_csc_type }; struct msm_display_info info = {0}; struct drm_encoder *encoder;