From e606ea2bbebb689f0bc26fafba0ea4b10df1c0c2 Mon Sep 17 00:00:00 2001 From: Zhang Yubing Date: Wed, 31 Aug 2022 15:08:06 +0800 Subject: [PATCH] drm/rockchip: vop3: add support hdrvivid Signed-off-by: Zhang Yubing Change-Id: I741ed74b38ea431e03bacacf1300793a9e2660f5 --- drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 + drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 91 ++++ drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 506 +++++++++++++++++-- drivers/gpu/drm/rockchip/rockchip_vop2_reg.c | 34 +- drivers/gpu/drm/rockchip/rockchip_vop_reg.h | 45 ++ 5 files changed, 643 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 3dd30880f6a2..34f12d8eae10 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -239,6 +239,7 @@ struct rockchip_crtc_state { struct drm_dsc_picture_parameter_set pps; struct rockchip_dsc_sink_cap dsc_sink_cap; struct rockchip_hdr_state hdr; + struct drm_property_blob *hdr_ext_data; int request_refresh_rate; int max_refresh_rate; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index 903503e3eea0..925af3a400e6 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -48,6 +48,7 @@ /* a feature to splice two windows and two vps to support resolution > 4096 */ #define VOP_FEATURE_SPLICE BIT(5) #define VOP_FEATURE_OVERSCAN BIT(6) +#define VOP_FEATURE_VIVID_HDR BIT(7) #define VOP_FEATURE_OUTPUT_10BIT VOP_FEATURE_OUTPUT_RGB10 @@ -508,6 +509,84 @@ struct vop_hdr_table { const uint32_t *sdr2hdr_st2084oetf_xn; }; +#define RK_HDRVIVID_TONE_SCA_TAB_LENGTH 257 +#define RK_HDRVIVID_GAMMA_CURVE_LENGTH 81 +#define RK_HDRVIVID_GAMMA_MDFVALUE_LENGTH 9 +#define RK_SDR2HDR_INVGAMMA_CURVE_LENGTH 69 +#define RK_SDR2HDR_INVGAMMA_S_IDX_LENGTH 6 +#define RK_SDR2HDR_INVGAMMA_C_IDX_LENGTH 6 +#define RK_SDR2HDR_SMGAIN_LENGTH 64 +#define RK_HDRVIVID_TONE_SCA_AXI_TAB_LENGTH 264 + +struct hdrvivid_regs { + uint32_t sdr2hdr_ctrl; + uint32_t sdr2hdr_coe0; + uint32_t sdr2hdr_coe1; + uint32_t sdr2hdr_csc_coe00_01; + uint32_t sdr2hdr_csc_coe02_10; + uint32_t sdr2hdr_csc_coe11_12; + uint32_t sdr2hdr_csc_coe20_21; + uint32_t sdr2hdr_csc_coe22; + uint32_t hdrvivid_ctrl; + uint32_t hdr_pq_gamma; + uint32_t hlg_rfix_scalefac; + uint32_t hlg_maxluma; + uint32_t hlg_r_tm_lin2non; + uint32_t hdr_csc_coe00_01; + uint32_t hdr_csc_coe02_10; + uint32_t hdr_csc_coe11_12; + uint32_t hdr_csc_coe20_21; + uint32_t hdr_csc_coe22; + uint32_t hdr_tone_sca[RK_HDRVIVID_TONE_SCA_TAB_LENGTH]; + uint32_t hdrgamma_curve[RK_HDRVIVID_GAMMA_CURVE_LENGTH]; + uint32_t hdrgamma_mdfvalue[RK_HDRVIVID_GAMMA_MDFVALUE_LENGTH]; + uint32_t sdrinvgamma_curve[RK_SDR2HDR_INVGAMMA_CURVE_LENGTH]; + uint32_t sdrinvgamma_startidx[RK_SDR2HDR_INVGAMMA_S_IDX_LENGTH]; + uint32_t sdrinvgamma_changeidx[RK_SDR2HDR_INVGAMMA_C_IDX_LENGTH]; + uint32_t sdr_smgain[RK_SDR2HDR_SMGAIN_LENGTH]; + uint32_t hdr_mode; + uint32_t tone_sca_axi_tab[RK_HDRVIVID_TONE_SCA_AXI_TAB_LENGTH]; +}; + +struct hdr_extend { + uint32_t hdr_type; + uint32_t length; + union { + struct hdrvivid_regs hdrvivid_data; + }; +}; + +enum _vop_hdrvivid_mode { + PQHDR2HDR_WITH_DYNAMIC, + PQHDR2SDR_WITH_DYNAMIC, + HLG2PQHDR_WITH_DYNAMIC, + HLG2SDR_WITH_DYNAMIC, + HLG2PQHDR_WITHOUT_DYNAMIC, + HLG2SDR_WITHOUT_DYNAMIC, + HDR_BYPASS, + HDR102SDR, + SDR2PQ, + SDR2HLG, +}; + +enum vop_hdr_format { + HDR_NONE = 0, + HDR_HDR10 = 1, + HDR_HGGSTATIC = 2, + RESERVED3 = 3, /* reserved for more future static hdr format */ + RESERVED4 = 4, /* reserved for more future static hdr format */ + HDR_HDRVIVID = 5, + RESERVED6 = 6, /* reserved for hdr vivid */ + RESERVED7 = 7, /* reserved for hdr vivid */ + HDR_HDR10PLUS = 8, + RESERVED9 = 9, /* reserved for hdr hdr10+ */ + RESERVED10 = 10, /* reserved for hdr hdr10+ */ + HDR_NEXT = 11, + RESERVED12 = 12, /* reserved for other dynamic hdr format */ + RESERVED13 = 13, /* reserved for other dynamic hdr format */ + HDR_FORMAT_MAX, +}; + enum { VOP_CSC_Y2R_BT601, VOP_CSC_Y2R_BT709, @@ -709,6 +788,13 @@ struct vop2_video_port_regs { struct vop_reg hdr_lut_update_en; struct vop_reg hdr_lut_mode; struct vop_reg hdr_lut_mst; + struct vop_reg hdr_lut_fetch_done; + struct vop_reg hdr_vivid_en; + struct vop_reg hdr_vivid_bypass_en; + struct vop_reg hdr_vivid_path_mode; + struct vop_reg hdr_vivid_dstgamut; + struct vop_reg sdr2hdr_en; + struct vop_reg sdr2hdr_dstmode; struct vop_reg sdr2hdr_eotf_en; struct vop_reg sdr2hdr_r2r_en; struct vop_reg sdr2hdr_r2r_mode; @@ -967,6 +1053,11 @@ struct vop2_video_port_data { unsigned long dclk_max; struct vop_rect max_output; const u8 pre_scan_max_dly[4]; + const u8 hdrvivid_dly[10]; + const u8 sdr2hdr_dly; + const u8 layer_mix_dly; + const u8 hdr_mix_dly; + const u8 win_dly; const struct vop_intr *intr; const struct vop_hdr_table *hdr_table; const struct vop2_video_port_regs *regs; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 36ae75111f1b..2da7d069438c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -685,6 +685,11 @@ struct vop2_video_port { */ struct rockchip_gem_object *cubic_lut_gem_obj; + /** + * @hdr_lut_gem_obj: gem obj to store hdr lut + */ + struct rockchip_gem_object *hdr_lut_gem_obj; + /** * @cubic_lut: cubic look up table */ @@ -725,6 +730,13 @@ struct vop2_video_port { */ struct drm_property *min_refresh_rate_prop; + /** + * @hdr_ext_data_prop: hdr extend data interaction with userspace + */ + struct drm_property *hdr_ext_data_prop; + + bool hdr_ext_data_change; + int hdrvivid_mode; /** * @primary_plane_phy_id: vp primary plane phy id, the primary plane * will be used to show uboot logo and kernel logo @@ -2754,29 +2766,49 @@ static void vop2_setup_csc_mode(struct vop2_video_port *vp, vpstate->r2y_en = 0; vpstate->csc_mode = 0; - /* hdr2sdr and sdr2hdr will do csc itself */ - if (vpstate->hdr2sdr_en) { - /* - * This is hdr2sdr enabled plane - * If it's RGB layer do hdr2sdr, we need to do r2y before send to hdr2sdr, - * because hdr2sdr only support yuv input. - */ - if (!is_input_yuv) { - vpstate->r2y_en = 1; - vpstate->csc_mode = vop2_convert_csc_mode(output_csc, CSC_10BIT_DEPTH); + if (is_vop3(vp->vop2)) { + if (vpstate->hdr_in) { + if (is_input_yuv) { + vpstate->y2r_en = 1; + vpstate->csc_mode = vop2_convert_csc_mode(input_csc, + CSC_13BIT_DEPTH); + } + return; + } else if (vp->sdr2hdr_en) { + if (is_input_yuv) { + vpstate->y2r_en = 1; + vpstate->csc_mode = vop2_convert_csc_mode(input_csc, + csc_y2r_bit_depth); + } + return; } - return; - } else if (!vpstate->hdr_in && vp->sdr2hdr_en) { - /* - * This is sdr2hdr enabled plane - * If it's YUV layer do sdr2hdr, we need to do y2r before send to sdr2hdr, - * because sdr2hdr only support rgb input. - */ - if (is_input_yuv) { - vpstate->y2r_en = 1; - vpstate->csc_mode = vop2_convert_csc_mode(input_csc, csc_y2r_bit_depth); + } else { + /* hdr2sdr and sdr2hdr will do csc itself */ + if (vpstate->hdr2sdr_en) { + /* + * This is hdr2sdr enabled plane + * If it's RGB layer do hdr2sdr, we need to do r2y before send to hdr2sdr, + * because hdr2sdr only support yuv input. + */ + if (!is_input_yuv) { + vpstate->r2y_en = 1; + vpstate->csc_mode = vop2_convert_csc_mode(output_csc, + CSC_10BIT_DEPTH); + } + return; + } else if (!vpstate->hdr_in && vp->sdr2hdr_en) { + /* + * This is sdr2hdr enabled plane + * If it's YUV layer do sdr2hdr, we need to do y2r before send to sdr2hdr, + * because sdr2hdr only support rgb input. + */ + if (is_input_yuv) { + vpstate->y2r_en = 1; + vpstate->csc_mode = vop2_convert_csc_mode(input_csc, + csc_y2r_bit_depth); + } + return; } - return; } if (is_input_yuv && !is_output_yuv) { @@ -7454,6 +7486,23 @@ static int vop2_zpos_cmp(const void *a, const void *b) return pa->plane->base.id - pb->plane->base.id; } +static bool vop3_hdr_ext_data_equal(struct drm_crtc_state *old_state, + struct drm_crtc_state *new_state) +{ + struct rockchip_crtc_state *new_vcstate = to_rockchip_crtc_state(new_state); + struct rockchip_crtc_state *old_vcstate = to_rockchip_crtc_state(old_state); + struct drm_property_blob *new_blob = new_vcstate->hdr_ext_data; + struct drm_property_blob *old_blob = old_vcstate->hdr_ext_data; + + if (!old_blob || !new_blob) + return old_blob == new_blob; + + if (old_blob->length != new_blob->length) + return false; + + return !memcmp(old_blob->data, new_blob->data, old_blob->length); +} + static int vop2_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) { @@ -7481,9 +7530,219 @@ static int vop2_crtc_atomic_check(struct drm_crtc *crtc, else vp->refresh_rate_change = false; + vp->hdr_ext_data_change = !vop3_hdr_ext_data_equal(crtc->state, crtc_state) | + crtc_state->active_changed; + return 0; } +static void vop3_disable_dynamic_hdr(struct vop2_video_port *vp, uint8_t win_phys_id) +{ + struct vop2 *vop2 = vp->vop2; + struct vop2_win *win = vop2_find_win_by_phys_id(vop2, win_phys_id); + struct drm_plane *plane = &win->base; + struct drm_plane_state *pstate = plane->state; + struct vop2_plane_state *vpstate = to_vop2_plane_state(pstate); + + VOP_MODULE_SET(vop2, vp, hdr10_en, 0); + VOP_MODULE_SET(vop2, vp, hdr_vivid_en, 0); + VOP_MODULE_SET(vop2, vp, hdr_vivid_bypass_en, 0); + VOP_MODULE_SET(vop2, vp, sdr2hdr_en, 0); + VOP_MODULE_SET(vop2, vp, sdr2hdr_path_en, 0); + + vp->hdr_en = false; + vp->hdr_in = false; + vp->hdr_out = false; + vp->sdr2hdr_en = false; + vpstate->hdr_in = false; + vpstate->hdr2sdr_en = false; +} + +static void vop3_setup_hdrvivid(struct vop2_video_port *vp, uint8_t win_phys_id) +{ + struct vop2 *vop2 = vp->vop2; + struct vop2_win *win = vop2_find_win_by_phys_id(vop2, win_phys_id); + struct drm_plane *plane = &win->base; + struct drm_plane_state *pstate = plane->state; + struct vop2_plane_state *vpstate = to_vop2_plane_state(pstate); + struct drm_crtc_state *cstate = vp->rockchip_crtc.crtc.state; + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(cstate); + unsigned long win_mask = vp->win_mask; + int phys_id; + struct hdrvivid_regs *hdrvivid_data; + struct hdr_extend *hdr_data; + bool have_sdr_layer = false; + uint32_t hdr_mode; + int i; + u32 *tone_lut_kvaddr; + dma_addr_t tone_lut_mst; + + vp->hdr_en = false; + vp->hdr_in = false; + vp->hdr_out = false; + vp->sdr2hdr_en = false; + vpstate->hdr_in = false; + vpstate->hdr2sdr_en = false; + + hdr_data = (struct hdr_extend *)vcstate->hdr_ext_data->data; + hdrvivid_data = &hdr_data->hdrvivid_data; + + hdr_mode = hdrvivid_data->hdr_mode; + + if (hdr_mode > SDR2HLG) { + DRM_ERROR("Invalid HDR mode:%d, beyond the mode range\n", hdr_mode); + return; + } + + if (hdr_mode <= HDR102SDR && vpstate->eotf != HDMI_EOTF_SMPTE_ST2084 && + vpstate->eotf != HDMI_EOTF_BT_2100_HLG) { + DRM_ERROR("Invalid HDR mode:%d, mismatch plane eotf:%d\n", hdr_mode, + vpstate->eotf); + return; + } + + vp->hdrvivid_mode = hdr_mode; + vcstate->yuv_overlay = false; + + if (hdr_mode <= HDR102SDR) { + vp->hdr_en = true; + vp->hdr_in = true; + vpstate->hdr_in = true; + } else { + vp->sdr2hdr_en = true; + } + + /* + * To confirm whether need to enable sdr2hdr. + */ + for_each_set_bit(phys_id, &win_mask, ROCKCHIP_MAX_LAYER) { + win = vop2_find_win_by_phys_id(vop2, phys_id); + plane = &win->base; + pstate = plane->state; + vpstate = to_vop2_plane_state(pstate); + + /* skip inactive plane */ + if (!vop2_plane_active(pstate)) + continue; + + if (vpstate->eotf != HDMI_EOTF_SMPTE_ST2084 && + vpstate->eotf != HDMI_EOTF_BT_2100_HLG) { + have_sdr_layer = true; + break; + } + } + + if (hdr_mode == PQHDR2SDR_WITH_DYNAMIC || hdr_mode == HLG2SDR_WITH_DYNAMIC || + hdr_mode == HLG2SDR_WITHOUT_DYNAMIC || hdr_mode == HDR102SDR) { + vpstate->hdr2sdr_en = true; + } else { + vp->hdr_out = true; + if (have_sdr_layer) + vp->sdr2hdr_en = true; + } + + /** + * Config hdr ctrl registers + */ + vop2_writel(vop2, RK3528_SDR2HDR_CTRL, hdrvivid_data->sdr2hdr_ctrl); + vop2_writel(vop2, RK3528_HDRVIVID_CTRL, hdrvivid_data->hdrvivid_ctrl); + + VOP_MODULE_SET(vop2, vp, hdr10_en, vp->hdr_en); + if (vp->hdr_en) { + VOP_MODULE_SET(vop2, vp, hdr_vivid_en, (hdr_mode == HDR_BYPASS) ? 0 : 1); + VOP_MODULE_SET(vop2, vp, hdr_vivid_path_mode, + (hdr_mode == HDR102SDR) ? PQHDR2SDR_WITH_DYNAMIC : hdr_mode); + VOP_MODULE_SET(vop2, vp, hdr_vivid_bypass_en, (hdr_mode == HDR_BYPASS) ? 1 : 0); + } else { + VOP_MODULE_SET(vop2, vp, hdr_vivid_en, 0); + } + VOP_MODULE_SET(vop2, vp, sdr2hdr_en, vp->sdr2hdr_en); + VOP_MODULE_SET(vop2, vp, sdr2hdr_path_en, vp->sdr2hdr_en); + + vop2_writel(vop2, RK3528_SDR_CFG_COE0, hdrvivid_data->sdr2hdr_coe0); + vop2_writel(vop2, RK3528_SDR_CFG_COE1, hdrvivid_data->sdr2hdr_coe1); + vop2_writel(vop2, RK3528_SDR_CSC_COE00_01, hdrvivid_data->sdr2hdr_csc_coe00_01); + vop2_writel(vop2, RK3528_SDR_CSC_COE02_10, hdrvivid_data->sdr2hdr_csc_coe02_10); + vop2_writel(vop2, RK3528_SDR_CSC_COE11_12, hdrvivid_data->sdr2hdr_csc_coe11_12); + vop2_writel(vop2, RK3528_SDR_CSC_COE20_21, hdrvivid_data->sdr2hdr_csc_coe20_21); + vop2_writel(vop2, RK3528_SDR_CSC_COE22, hdrvivid_data->sdr2hdr_csc_coe22); + + vop2_writel(vop2, RK3528_HDR_PQ_GAMMA, hdrvivid_data->hdr_pq_gamma); + vop2_writel(vop2, RK3528_HLG_RFIX_SCALEFAC, hdrvivid_data->hlg_rfix_scalefac); + vop2_writel(vop2, RK3528_HLG_MAXLUMA, hdrvivid_data->hlg_maxluma); + vop2_writel(vop2, RK3528_HLG_R_TM_LIN2NON, hdrvivid_data->hlg_r_tm_lin2non); + + vop2_writel(vop2, RK3528_HDR_CSC_COE00_01, hdrvivid_data->hdr_csc_coe00_01); + vop2_writel(vop2, RK3528_HDR_CSC_COE02_10, hdrvivid_data->hdr_csc_coe02_10); + vop2_writel(vop2, RK3528_HDR_CSC_COE11_12, hdrvivid_data->hdr_csc_coe11_12); + vop2_writel(vop2, RK3528_HDR_CSC_COE20_21, hdrvivid_data->hdr_csc_coe20_21); + vop2_writel(vop2, RK3528_HDR_CSC_COE22, hdrvivid_data->hdr_csc_coe22); + + tone_lut_kvaddr = (u32 *)vp->hdr_lut_gem_obj->kvaddr; + tone_lut_mst = vp->hdr_lut_gem_obj->dma_addr; + + for (i = 0; i < RK_HDRVIVID_TONE_SCA_AXI_TAB_LENGTH; i++) + *tone_lut_kvaddr++ = hdrvivid_data->tone_sca_axi_tab[i]; + + VOP_MODULE_SET(vop2, vp, lut_dma_rid, vp->lut_dma_rid - vp->id); + VOP_MODULE_SET(vop2, vp, hdr_lut_mode, 1); + VOP_MODULE_SET(vop2, vp, hdr_lut_mst, tone_lut_mst); + VOP_MODULE_SET(vop2, vp, hdr_lut_update_en, 1); + VOP_CTRL_SET(vop2, lut_dma_en, 1); + + for (i = 0; i < RK_HDRVIVID_GAMMA_CURVE_LENGTH; i++) + vop2_writel(vop2, RK3528_HDRGAMMA_CURVE + i * 4, hdrvivid_data->hdrgamma_curve[i]); + + for (i = 0; i < RK_HDRVIVID_GAMMA_MDFVALUE_LENGTH; i++) + vop2_writel(vop2, RK3528_HDRGAMMA_MDFVALUE + i * 4, + hdrvivid_data->hdrgamma_mdfvalue[i]); + + for (i = 0; i < RK_SDR2HDR_INVGAMMA_CURVE_LENGTH; i++) + vop2_writel(vop2, RK3528_SDRINVGAMMA_CURVE + i * 4, + hdrvivid_data->sdrinvgamma_curve[i]); + + for (i = 0; i < RK_SDR2HDR_INVGAMMA_S_IDX_LENGTH; i++) + vop2_writel(vop2, RK3528_SDRINVGAMMA_STARTIDX + i * 4, + hdrvivid_data->sdrinvgamma_startidx[i]); + + for (i = 0; i < RK_SDR2HDR_INVGAMMA_C_IDX_LENGTH; i++) + vop2_writel(vop2, RK3528_SDRINVGAMMA_CHANGEIDX + i * 4, + hdrvivid_data->sdrinvgamma_changeidx[i]); + + for (i = 0; i < RK_SDR2HDR_SMGAIN_LENGTH; i++) + vop2_writel(vop2, RK3528_SDR_SMGAIN + i * 4, hdrvivid_data->sdr_smgain[i]); +} + +static void vop3_setup_dynamic_hdr(struct vop2_video_port *vp, uint8_t win_phys_id) +{ + struct drm_crtc_state *cstate = vp->rockchip_crtc.crtc.state; + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(cstate); + struct hdr_extend *hdr_data; + uint32_t hdr_format; + + /* If hdr extend data is null, exit hdr mode */ + if (!vcstate->hdr_ext_data) { + vop3_disable_dynamic_hdr(vp, win_phys_id); + return; + } + + /* If hdr ext data is not change, do nothing here */ + if (!vp->hdr_ext_data_change) + return; + + hdr_data = (struct hdr_extend *)vcstate->hdr_ext_data->data; + hdr_format = hdr_data->hdr_type; + + switch (hdr_format) { + case HDR_HDRVIVID: + vop3_setup_hdrvivid(vp, win_phys_id); + break; + default: + DRM_DEBUG("unsupprot hdr format:%u\n", hdr_format); + break; + } +} + static void vop2_setup_hdr10(struct vop2_video_port *vp, uint8_t win_phys_id) { struct vop2 *vop2 = vp->vop2; @@ -8025,7 +8284,7 @@ static void vop3_setup_alpha(struct vop2_video_port *vp, vop2_writel(vop2, dst_alpha_ctrl_offset + offset, alpha.dst_alpha_ctrl.val); } - if (vp_data->feature & VOP_FEATURE_HDR10) { + if (vp_data->feature & (VOP_FEATURE_HDR10 | VOP_FEATURE_VIVID_HDR)) { src_color_ctrl_offset = ovl_regs->hdr_mix_regs->src_color_ctrl.offset; dst_color_ctrl_offset = ovl_regs->hdr_mix_regs->dst_color_ctrl.offset; src_alpha_ctrl_offset = ovl_regs->hdr_mix_regs->src_alpha_ctrl.offset; @@ -8418,6 +8677,107 @@ static void vop2_crtc_update_vrr(struct drm_crtc *crtc) rockchip_connector_update_vfp_for_vrr(crtc, adjust_mode, new_vfp); } +/* + * For vop3 video port0, if hdr_vivid is not enable, the pipe delay time as follow: + * win_dly + config_win_dly + layer_mix_dly + sdr2hdr_dly + * hdr_mix_dly = config_bg_dly + * + * if hdr_vivid is enable, the hdr layer's pipe delay time as follow: + * win_dly + config_win_dly +hdrvivid_dly + hdr_mix_dly = config_bg_dly + * + * If hdrvivid and sdr2hdr bot enable, the time arrivr hdr_mix should be the same: + * win_dly + config_win_dly0 + hdrvivid_dly = win_dly + config_win_dly1 + laer_mix_dly + + * sdr2hdr_dly + * + * For vop3 video port1, the pipe delay time as follow: + * win_dly + config_win_dly + layer_mix_dly = config_bg_dly + * + * Here, win_dly, layer_mix_dly, sdr2hdr_dly, hdr_mix_dly, hdrvivid_dly is the hardware + * delay cycles. Config_win_dly and config_bg_dly is the register value that we can config. + * Different hdr vivid mode have different hdrvivid_dly. For sdr2hdr_dly, only sde2hdr + * enable, it will delay, otherwise, the sdr2hdr_dly is 0. + * + * For default, the config_win_dly will be 0, it just user to make the pipe to arrive + * hdr_mix at the same time. + */ +static void vop3_setup_pipe_dly(struct vop2_video_port *vp, const struct vop2_zpos *vop2_zpos) +{ + struct vop2 *vop2 = vp->vop2; + struct drm_crtc *crtc = &vp->rockchip_crtc.crtc; + const struct vop2_zpos *zpos; + struct drm_plane *plane; + struct vop2_plane_state *vpstate; + struct vop2_win *win; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; + u16 hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; + u16 hdisplay = adjusted_mode->crtc_hdisplay; + int bg_dly = 0x0; + int dly = 0x0; + int hdr_win_dly; + int sdr_win_dly; + int sdr2hdr_dly; + int pre_scan_dly; + int i; + + /** + * config bg dly, select the max delay num of hdrvivid and sdr2hdr module + * as the increase value of bg delay num. If hdrvivid and sdr2hdr is not + * work, the default bg_dly is 0x10. and the default win delay num is 0. + */ + if ((vp->hdr_en || vp->sdr2hdr_en) && + (vp->hdrvivid_mode >= 0 && vp->hdrvivid_mode <= SDR2HLG)) { + /* set sdr2hdr_dly to 0 if sdr2hdr is disable */ + sdr2hdr_dly = vp->sdr2hdr_en ? vp_data->sdr2hdr_dly : 0; + + /* set the max delay pipe's config_win_dly as 0 */ + if (vp_data->hdrvivid_dly[vp->hdrvivid_mode] >= + sdr2hdr_dly + vp_data->layer_mix_dly) { + bg_dly = vp_data->win_dly + vp_data->hdrvivid_dly[vp->hdrvivid_mode] + + vp_data->hdr_mix_dly; + hdr_win_dly = 0; + sdr_win_dly = vp_data->hdrvivid_dly[vp->hdrvivid_mode] - + vp_data->layer_mix_dly - sdr2hdr_dly; + } else { + bg_dly = vp_data->win_dly + vp_data->layer_mix_dly + sdr2hdr_dly + + vp_data->hdr_mix_dly; + hdr_win_dly = sdr2hdr_dly + vp_data->layer_mix_dly - + vp_data->hdrvivid_dly[vp->hdrvivid_mode]; + sdr_win_dly = 0; + } + } else { + bg_dly = vp_data->win_dly + vp_data->layer_mix_dly + vp_data->hdr_mix_dly; + sdr_win_dly = 0; + } + + pre_scan_dly = bg_dly + (hdisplay >> 1) - 1; + pre_scan_dly = (pre_scan_dly << 16) | hsync_len; + VOP_MODULE_SET(vop2, vp, bg_dly, bg_dly); + VOP_MODULE_SET(vop2, vp, pre_scan_htiming, pre_scan_dly); + + /** + * config win dly + */ + if (!vop2_zpos) + return; + + for (i = 0; i < vp->nr_layers; i++) { + zpos = &vop2_zpos[i]; + win = vop2_find_win_by_phys_id(vop2, zpos->win_phys_id); + plane = &win->base; + vpstate = to_vop2_plane_state(plane->state); + + if ((vp->hdr_en || vp->sdr2hdr_en) && + (vp->hdrvivid_mode >= 0 && vp->hdrvivid_mode <= SDR2HLG)) { + dly = vpstate->hdr_in ? hdr_win_dly : sdr_win_dly; + } + if (vop2_cluster_window(win)) + dly |= dly << 8; + + VOP_CTRL_SET(vop2, win_dly[win->phys_id], dly); + } +} + static void vop2_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { struct vop2_video_port *vp = to_vop2_video_port(crtc); @@ -8545,13 +8905,18 @@ static void vop2_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state vop2_setup_layer_mixer_for_vp(vp, vop2_zpos); } } - vop2_setup_hdr10(vp, vop2_zpos[0].win_phys_id); - if (is_vop3(vop2)) + + if (is_vop3(vop2)) { + vop3_setup_dynamic_hdr(vp, vop2_zpos[0].win_phys_id); vop3_setup_alpha(vp, vop2_zpos); - else + vop3_setup_pipe_dly(vp, vop2_zpos); + } else { + vop2_setup_hdr10(vp, vop2_zpos[0].win_phys_id); vop2_setup_alpha(vp, vop2_zpos); - vop2_setup_dly_for_vp(vp); - vop2_setup_dly_for_window(vp, vop2_zpos); + vop2_setup_dly_for_vp(vp); + vop2_setup_dly_for_window(vp, vop2_zpos); + } + if (vcstate->splice_mode) {/* Fixme for VOP3 8K */ splice_vp->nr_layers = splice_nr_layers; @@ -8570,13 +8935,15 @@ static void vop2_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state vp->hdr10_at_splice_mode) rk3588_vop2_setup_hdr10_splice_layer_mixer(crtc, vop2_zpos, vop2_zpos_splice); } - } else { - if (!is_vop3(vop2)) + if (!is_vop3(vop2)) { vop2_calc_bg_ovl_and_port_mux(vp); - vop2_setup_dly_for_vp(vp); - if (vcstate->splice_mode) - vop2_setup_dly_for_vp(splice_vp); + vop2_setup_dly_for_vp(vp); + if (vcstate->splice_mode) + vop2_setup_dly_for_vp(splice_vp); + } else { + vop3_setup_pipe_dly(vp, NULL); + } } /* The pre alpha overlay of Cluster still need process in one win mode. */ @@ -9065,11 +9432,48 @@ static int vop2_crtc_atomic_get_property(struct drm_crtc *crtc, return 0; } + if (property == vp->hdr_ext_data_prop) + return 0; + DRM_ERROR("failed to get vop2 crtc property: %s\n", property->name); return -EINVAL; } +/* copied from drm_atomic.c */ +static int +vop2_atomic_replace_property_blob_from_id(struct drm_device *dev, + struct drm_property_blob **blob, + uint64_t blob_id, + ssize_t expected_size, + ssize_t expected_elem_size, + bool *replaced) +{ + struct drm_property_blob *new_blob = NULL; + + if (blob_id != 0) { + new_blob = drm_property_lookup_blob(dev, blob_id); + if (new_blob == NULL) + return -EINVAL; + + if (expected_size > 0 && + new_blob->length != expected_size) { + drm_property_blob_put(new_blob); + return -EINVAL; + } + if (expected_elem_size > 0 && + new_blob->length % expected_elem_size != 0) { + drm_property_blob_put(new_blob); + return -EINVAL; + } + } + + *replaced |= drm_property_replace_blob(blob, new_blob); + drm_property_blob_put(new_blob); + + return 0; +} + static int vop2_crtc_atomic_set_property(struct drm_crtc *crtc, struct drm_crtc_state *state, struct drm_property *property, @@ -9080,6 +9484,8 @@ static int vop2_crtc_atomic_set_property(struct drm_crtc *crtc, struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state); struct drm_mode_config *mode_config = &drm_dev->mode_config; struct vop2_video_port *vp = to_vop2_video_port(crtc); + bool replaced = false; + int ret; if (property == mode_config->tv_left_margin_property) { vcstate->left_margin = val; @@ -9127,6 +9533,15 @@ static int vop2_crtc_atomic_set_property(struct drm_crtc *crtc, return 0; } + if (property == vp->hdr_ext_data_prop) { + ret = vop2_atomic_replace_property_blob_from_id(drm_dev, + &vcstate->hdr_ext_data, + val, + -1, -1, + &replaced); + return ret; + } + DRM_ERROR("failed to set vop2 crtc property %s\n", property->name); return -EINVAL; @@ -9742,6 +10157,22 @@ static int vop2_crtc_create_vrr_property(struct vop2 *vop2, struct drm_crtc *crt return 0; } +static int vop2_crtc_create_hdr_property(struct vop2 *vop2, struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct drm_property *prop; + + prop = drm_property_create(vop2->drm_dev, DRM_MODE_PROP_BLOB, "HDR_EXT_DATA", 0); + if (!prop) { + DRM_DEV_ERROR(vop2->dev, "create hdr ext data prop for vp%d failed\n", vp->id); + return -ENOMEM; + } + vp->hdr_ext_data_prop = prop; + drm_object_attach_property(&crtc->base, vp->hdr_ext_data_prop, 0); + + return 0; +} + #define RK3566_MIRROR_PLANE_MASK (BIT(ROCKCHIP_VOP2_CLUSTER1) | BIT(ROCKCHIP_VOP2_ESMART1) | \ BIT(ROCKCHIP_VOP2_SMART1)) @@ -9961,6 +10392,15 @@ static int vop2_create_crtc(struct vop2 *vop2) "Failed to init %s with SR helpers %d, ignoring\n", crtc->name, ret); + if (vp_data->feature & VOP_FEATURE_VIVID_HDR) { + vop2_crtc_create_hdr_property(vop2, crtc); + vp->hdr_lut_gem_obj = rockchip_gem_create_object(vop2->drm_dev, + RK_HDRVIVID_TONE_SCA_AXI_TAB_LENGTH * 4, true, 0); + if (IS_ERR(vp->hdr_lut_gem_obj)) { + DRM_ERROR("create hdr lut obj failed\n"); + return -ENOMEM; + } + } registered_num_crtcs++; } @@ -10026,6 +10466,8 @@ static void vop2_destroy_crtc(struct drm_crtc *crtc) struct vop2_video_port *vp = to_vop2_video_port(crtc); drm_self_refresh_helper_cleanup(crtc); + if (vp->hdr_lut_gem_obj) + rockchip_gem_free_object(&vp->hdr_lut_gem_obj->base); of_node_put(crtc->port); diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c index 63d8b8af82fa..37d0517662d8 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c @@ -811,6 +811,19 @@ static const struct vop2_video_port_regs rk3528_vop_vp0_regs = { .hdr_dst_color_ctrl = VOP_REG(RK3528_HDR_DST_COLOR_CTRL, 0xffffffff, 0), .hdr_src_alpha_ctrl = VOP_REG(RK3528_HDR_SRC_ALPHA_CTRL, 0xffffffff, 0), .hdr_dst_alpha_ctrl = VOP_REG(RK3528_HDR_DST_ALPHA_CTRL, 0xffffffff, 0), + .hdr_lut_update_en = VOP_REG(RK3568_HDR_LUT_CTRL, 0x1, 0), + .hdr_lut_mode = VOP_REG(RK3568_HDR_LUT_CTRL, 0x1, 1), + .hdr_lut_mst = VOP_REG(RK3568_HDR_LUT_MST, 0xffffffff, 0), + .hdr_lut_fetch_done = VOP_REG(RK3528_HDR_LUT_STATUS, 0x1, 0), + .hdr10_en = VOP_REG(RK3568_OVL_CTRL, 0x1, 4), + .sdr2hdr_path_en = VOP_REG(RK3568_OVL_CTRL, 0x1, 5), + .sdr2hdr_en = VOP_REG(RK3568_SDR2HDR_CTRL, 0x1, 0), + .sdr2hdr_bypass_en = VOP_REG(RK3568_SDR2HDR_CTRL, 0x1, 2), + .sdr2hdr_dstmode = VOP_REG(RK3568_SDR2HDR_CTRL, 0x1, 3), + .hdr_vivid_en = VOP_REG(RK3528_HDRVIVID_CTRL, 0x1, 0), + .hdr_vivid_bypass_en = VOP_REG(RK3528_HDRVIVID_CTRL, 0x1, 2), + .hdr_vivid_path_mode = VOP_REG(RK3528_HDRVIVID_CTRL, 0x7, 3), + .hdr_vivid_dstgamut = VOP_REG(RK3528_HDRVIVID_CTRL, 0x1, 6), }; static const struct vop2_video_port_regs rk3528_vop_vp1_regs = { @@ -862,6 +875,13 @@ static const struct vop2_video_port_regs rk3528_vop_vp1_regs = { .layer_sel = VOP_REG(RK3528_OVL_PORT1_LAYER_SEL, 0xffff, 0), }; +static const struct vop3_ovl_mix_regs rk3528_vop_hdr_mix_regs = { + .src_color_ctrl = VOP_REG(RK3528_HDR_SRC_COLOR_CTRL, 0xffffffff, 0), + .dst_color_ctrl = VOP_REG(RK3528_HDR_DST_COLOR_CTRL, 0xffffffff, 0), + .src_alpha_ctrl = VOP_REG(RK3528_HDR_SRC_ALPHA_CTRL, 0xffffffff, 0), + .dst_alpha_ctrl = VOP_REG(RK3528_HDR_DST_ALPHA_CTRL, 0xffffffff, 0), +}; + static const struct vop3_ovl_mix_regs rk3528_vop_vp0_layer_mix_regs = { .src_color_ctrl = VOP_REG(RK3528_OVL_PORT0_MIX0_SRC_COLOR_CTRL, 0xffffffff, 0), .dst_color_ctrl = VOP_REG(RK3528_OVL_PORT0_MIX0_DST_COLOR_CTRL, 0xffffffff, 0), @@ -878,6 +898,7 @@ static const struct vop3_ovl_mix_regs rk3528_vop_vp1_layer_mix_regs = { static const struct vop3_ovl_regs rk3528_vop_vp0_ovl_regs = { .layer_mix_regs = &rk3528_vop_vp0_layer_mix_regs, + .hdr_mix_regs = &rk3528_vop_hdr_mix_regs, }; static const struct vop3_ovl_regs rk3528_vop_vp1_ovl_regs = { @@ -889,10 +910,14 @@ static const struct vop2_video_port_data rk3528_vop_video_ports[] = { .id = 0, .soc_id = { 0x3528, 0x3528 }, .lut_dma_rid = 14, - .feature = VOP_FEATURE_ALPHA_SCALE | VOP_FEATURE_OVERSCAN, + .feature = VOP_FEATURE_ALPHA_SCALE | VOP_FEATURE_OVERSCAN | VOP_FEATURE_VIVID_HDR, .gamma_lut_len = 1024, .max_output = { 4096, 4096 }, - .pre_scan_max_dly = { 43, 53, 53, 42 }, + .hdrvivid_dly = {17, 29, 32, 44, 15, 38, 1, 29, 0, 0}, + .sdr2hdr_dly = 21, + .layer_mix_dly = 6, + .hdr_mix_dly = 2, + .win_dly = 8, .intr = &rk3568_vp0_intr, .regs = &rk3528_vop_vp0_regs, .ovl_regs = &rk3528_vop_vp0_ovl_regs, @@ -903,6 +928,11 @@ static const struct vop2_video_port_data rk3528_vop_video_ports[] = { .feature = VOP_FEATURE_ALPHA_SCALE | VOP_FEATURE_OVERSCAN, .max_output = { 720, 576 }, .pre_scan_max_dly = { 37, 40, 40, 40 }, + .hdrvivid_dly = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .sdr2hdr_dly = 0, + .layer_mix_dly = 2, + .hdr_mix_dly = 0, + .win_dly = 8, .intr = &rk3568_vp1_intr, .regs = &rk3528_vop_vp1_regs, .ovl_regs = &rk3528_vop_vp1_ovl_regs, diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h index 7476dd7c8cb1..1363764a40bb 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h @@ -1694,4 +1694,49 @@ #define RK3588_PMU_BISR_CON3 0x20C #define RK3588_PMU_BISR_STATUS5 0x294 +/* RK3528 HDR register definition */ +#define RK3528_HDR_LUT_CTRL 0x2000 +#define RK3528_HDR_LUT_MST 0x2004 +#define RK3528_HDR_LUT_STATUS 0x2008 +#define RK3528_SDR2HDR_CTRL 0x2010 +#define RK3528_SDR_CFG_COE0 0x2014 +#define RK3528_SDR_CFG_COE1 0x2018 +#define RK3528_SDR_CSC_COE00_01 0x201C +#define RK3528_SDR_CSC_COE02_10 0x2020 +#define RK3528_SDR_CSC_COE11_12 0x2024 +#define RK3528_SDR_CSC_COE20_21 0x2028 +#define RK3528_SDR_CSC_COE22 0x202C +#define RK3528_HDRVIVID_CTRL 0x2040 +#define RK3528_HDR_PQ_GAMMA 0x2044 +#define RK3528_HLG_RFIX_SCALEFAC 0x2048 +#define RK3528_HLG_MAXLUMA 0x204C +#define RK3528_HLG_R_TM_LIN2NON 0x2050 +#define RK3528_HDR_CSC_COE00_01 0x2054 +#define RK3528_HDR_CSC_COE02_10 0x2058 +#define RK3528_HDR_CSC_COE11_12 0x205C +#define RK3528_HDR_CSC_COE20_21 0x2060 +#define RK3528_HDR_CSC_COE22 0x2064 +#define RK3528_INK_CFG 0x2080 +#define RK3528_INK_POINT0_CFG 0x2084 +#define RK3528_INK_POINT1_CFG 0x2088 +#define RK3528_INK_POINT0_R0 0x208C +#define RK3528_INK_POINT0_G0 0x2090 +#define RK3528_INK_POINT0_B0 0x2094 +#define RK3528_INK_POINT0_R1 0x2098 +#define RK3528_INK_POINT0_G1 0x209C +#define RK3528_INK_POINT0_B1 0x20A0 +#define RK3528_INK_POINT1_R0 0x20A4 +#define RK3528_INK_POINT1_G0 0x20A8 +#define RK3528_INK_POINT1_B0 0x20AC +#define RK3528_INK_POINT1_R1 0x20B0 +#define RK3528_INK_POINT1_G1 0x20B4 +#define RK3528_INK_POINT1_B1 0x20B8 +#define RK3528_HDR_TONE_SCA 0x213C +#define RK3528_HDRGAMMA_CURVE 0x2540 +#define RK3528_HDRGAMMA_MDFVALUE 0x2690 +#define RK3528_SDRINVGAMMA_CURVE 0x2700 +#define RK3528_SDRINVGAMMA_STARTIDX 0x2820 +#define RK3528_SDRINVGAMMA_CHANGEIDX 0x2840 +#define RK3528_SDR_SMGAIN 0x2900 + #endif /* _ROCKCHIP_VOP_REG_H */