diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index c7b7890f4aae..8bd9319f44d5 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -1613,7 +1613,7 @@ static int rockchip_drm_create_properties(struct drm_device *dev) struct rockchip_drm_private *private = dev->dev_private; prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, - "EOTF", 0, 5); + "EOTF", 0, HDMI_EOTF_DOVI); if (!prop) return -ENOMEM; private->eotf_prop = prop; @@ -1651,6 +1651,12 @@ static int rockchip_drm_create_properties(struct drm_device *dev) "PORT_ID", DRM_MODE_OBJECT_CRTC); private->port_id_prop = prop; + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "DOVI_INPUT_TYPE", 0, DOVI_ENHANCE_LAYER); + if (!prop) + return -ENOMEM; + private->dovi_input_type_prop = prop; + private->aclk_prop = drm_property_create_range(dev, 0, "ACLK", 0, UINT_MAX); private->bg_prop = drm_property_create_range(dev, 0, "BACKGROUND", 0, UINT_MAX); private->line_flag_prop = drm_property_create_range(dev, 0, "LINE_FLAG1", 0, UINT_MAX); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 6a07fd285386..9eef4e6b4ca8 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -31,7 +31,7 @@ #define ROCKCHIP_MAX_CONNECTOR 2 #define ROCKCHIP_MAX_CRTC 4 #define ROCKCHIP_MAX_LAYER 16 - +#define ROCKCHIP_MAX_DOVI_CORE 3 struct drm_device; struct drm_connector; @@ -61,6 +61,20 @@ struct iommu_domain; #define RK_IF_PROP_COLOR_FORMAT_CAPS "color_format_caps" #define RK_IF_PROP_ENCRYPTED "hdcp_encrypted" +/* + * This is extend by rockchip, the other EOTF is defined at hdmi.h + * + * enum hdmi_eotf { + * HDMI_EOTF_TRADITIONAL_GAMMA_SDR, + * HDMI_EOTF_TRADITIONAL_GAMMA_HDR, + * HDMI_EOTF_SMPTE_ST2084, + * HDMI_EOTF_BT_2100_HLG, + *}; + */ +#define HDMI_EOTF_HDR10PLUS 0x10 +#define HDMI_EOTF_HDRVIVID 0x11 +#define HDMI_EOTF_DOVI 0x12 + enum rockchip_drm_debug_category { VOP_DEBUG_PLANE = BIT(0), VOP_DEBUG_OVERLAY = BIT(1), @@ -158,6 +172,8 @@ struct rockchip_bcsh_state { struct rockchip_crtc { struct drm_crtc crtc; + /* @frme_count: the frame num of commit buf */ + u32 frame_count; #if defined(CONFIG_ROCKCHIP_DRM_DEBUG) /** * @vop_dump_status the status of vop dump control @@ -553,6 +569,7 @@ struct rockchip_drm_private { struct drm_property *eotf_prop; struct drm_property *async_commit_prop; struct drm_property *share_id_prop; + struct drm_property *dovi_input_type_prop; /* private connector prop */ struct drm_property *connector_id_prop; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index c583da73c5f0..d0cf023aff7a 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -70,7 +70,6 @@ #define VOP_FEATURE_INTERNAL_RGB BIT(1) #define VOP_FEATURE_ALPHA_SCALE BIT(2) #define VOP_FEATURE_HDR10 BIT(3) -#define VOP_FEATURE_NEXT_HDR BIT(4) #define VOP_FEATURE_DOVI BIT(4) /* a feature to splice two windows and two vps to support resolution > 4096 */ #define VOP_FEATURE_SPLICE BIT(5) @@ -141,6 +140,10 @@ enum vop2_win_dly_mode { VOP2_DLY_MODE_DEFAULT, /**< default mode */ VOP2_DLY_MODE_HISO_S, /** HDR in SDR out mode, as a SDR window */ VOP2_DLY_MODE_HIHO_H, /** HDR in HDR out mode, as a HDR window */ + VOP2_DLY_MODE_DOVI_IN_CORE1, /* dovi video input, as dovi core1 */ + VOP2_DLY_MODE_DOVI_IN_CORE2, /* dovi video input, as dovi core2 */ + VOP2_DLY_MODE_NONDOVI_IN_CORE1, /* ndovi video input, as dovi core1 */ + VOP2_DLY_MODE_NONDOVI_IN_CORE2, /* ndovi video input, as dovi core2 */ VOP2_DLY_MODE_MAX, }; @@ -615,11 +618,40 @@ struct hdrvivid_regs { #define RK_HDR_TYPE_MASK 0xff #define RK_HDR_PLAT_MASK (0xff << 8) +/* byte unit */ +#define VOP2_DOVI_CORE1_LUT_SIZE 5120 +#define VOP2_DOVI_TONE_SCA_AXI_TAB_SIZE (2560 * 4) + +/* word unit */ +#define DOVI_LUT_SIZE 1280 +#define DOVI_CORE1_SIZE 242 +#define DOVI_CORE2_SIZE 43 +#define DOVI_CORE3_SIZE 256 + +enum vop_dovi_input_type { + COMMON_LAYER = 0, + DOVI_BASE_LAYER = 1, + DOVI_ENHANCE_LAYER = 2, +}; + +struct dovi_regs { + uint32_t version; + uint32_t valid; + uint32_t input_mode; + uint32_t output_mode; + uint32_t core1_lut[DOVI_LUT_SIZE]; + uint32_t core2_lut[DOVI_LUT_SIZE]; + uint32_t core1[DOVI_CORE1_SIZE]; + uint32_t core2[DOVI_CORE2_SIZE]; + uint32_t core3[DOVI_CORE3_SIZE]; +}; + struct hdr_extend { uint32_t hdr_type; uint32_t length; union { struct hdrvivid_regs hdrvivid_data; + struct dovi_regs dovi_data; }; }; @@ -650,7 +682,7 @@ enum vop_hdr_format { HDR_HDR10PLUS = 8, RESERVED9 = 9, /* reserved for hdr hdr10+ */ RESERVED10 = 10, /* reserved for hdr hdr10+ */ - HDR_NEXT = 11, + HDR_DOVI = 11, RESERVED12 = 12, /* reserved for other dynamic hdr format */ RESERVED13 = 13, /* reserved for other dynamic hdr format */ HDR_FORMAT_MAX, @@ -883,6 +915,10 @@ struct vop2_video_port_regs { struct vop_reg dsp_x_mir_en; struct vop_reg post_dsp_out_r2y; struct vop_reg pre_scan_htiming; + struct vop_reg dovi_pre_scan_en; + struct vop_reg pre_scan_htiming1; + struct vop_reg pre_scan_htiming2; + struct vop_reg pre_scan_htiming3; struct vop_reg htotal_pw; struct vop_reg hact_st_end; struct vop_reg dsp_vtotal; @@ -905,6 +941,8 @@ struct vop2_video_port_regs { struct vop_reg dither_frc_2; struct vop_reg dither_up_en; struct vop_reg bg_dly; + struct vop_reg dp_line_end_mode; + struct vop_reg dp_bg_bottom_disable; struct vop_reg p2i_en; struct vop_reg dual_channel_en; @@ -1050,6 +1088,37 @@ struct vop2_power_domain_regs { struct vop_reg pmu_status; }; +struct vop2_dovi_regs { + /* common */ + struct vop_reg enable; + struct vop_reg interrupt_enable; + struct vop_reg interrupt_raw; + struct vop_reg metadata_program_st; + struct vop_reg metadata_program_end; + struct vop_reg metadata_copy_finish; + + /* core1 */ + struct vop_reg bypass_composer; + struct vop_reg bypass_csc; + struct vop_reg bypass_cvm; + struct vop_reg operating_mode; + struct vop_reg pixel_rate; + + /* core2 */ + struct vop_reg yuv2rgb_en; + struct vop_reg yuv422to444_en; + struct vop_reg yuv_swap; + struct vop_reg yuv422_en; + struct vop_reg dly_en; + + /* core1 and core2 */ + struct vop_reg lut_mst; + struct vop_reg lut_update; + + /* core3 */ + struct vop_reg output_mode; +}; + struct vop2_dsc_regs { /* DSC SYS CTRL */ struct vop_reg dsc_port_sel; @@ -1184,6 +1253,21 @@ struct vop2_win_data { const uint8_t dly[VOP2_DLY_MODE_MAX]; }; +struct vop2_dovi_core_data { + const uint8_t id; + const uint32_t ctrl_offset; + const uint32_t srange_offset; + const uint32_t srange_offset_from_core; + const struct vop2_dovi_regs *regs; +}; + +struct vop2_dovi_data { + const uint8_t nr_dovi_cores; + const uint8_t dovi_max_delay[2]; + const uint32_t enhance_layer_phy_id; + const struct vop2_dovi_core_data *dovi_core_data; +}; + struct dsc_error_info { u32 dsc_error_val; char dsc_error_info[50]; @@ -1481,6 +1565,10 @@ struct vop2_ctrl { struct vop_reg vp_intr_merge_en; struct vop_reg reg_done_frm; struct vop_reg cfg_done; + + struct vop_reg dovi_core1_en; + struct vop_reg dovi_core2_en; + struct vop_reg dovi_core3_en; }; struct vop_dump_regs { @@ -1529,6 +1617,7 @@ struct vop2_data { const struct vop2_esmart_lb_map *esmart_lb_mode_map; const struct vop_intr *axi_intr; const struct vop2_ctrl *ctrl; + const struct vop2_dovi_data *dovi; const struct vop2_dsc_data *dsc; const struct dsc_error_info *dsc_error_ecw; const struct dsc_error_info *dsc_error_buffer_flow; @@ -1581,6 +1670,9 @@ struct vop2_data { #define WB_YRGB_FIFO_FULL_INTR BIT(18) #define WB_COMPLETE_INTR BIT(19) #define MMU_EN_INTR BIT(20) +#define DOLBY_CORE1_INTR BIT(21) +#define DOLBY_CORE2_INTR BIT(22) +#define DOLBY_CORE3_INTR BIT(23) #define INTR_MASK (DSP_HOLD_VALID_INTR | FS_INTR | \ LINE_FLAG_INTR | BUS_ERROR_INTR | \ @@ -1590,7 +1682,8 @@ struct vop2_data { HWC_EMPTY_INTR | \ POST_BUF_EMPTY_INTR | \ DMA_FINISH_INTR | FS_FIELD_INTR | \ - FE_INTR | WB_COMPLETE_INTR | MMU_EN_INTR) + FE_INTR | WB_COMPLETE_INTR | MMU_EN_INTR | \ + DOLBY_CORE1_INTR | DOLBY_CORE2_INTR | DOLBY_CORE3_INTR) #define DSP_HOLD_VALID_INTR_EN(x) ((x) << 4) #define FS_INTR_EN(x) ((x) << 5) #define LINE_FLAG_INTR_EN(x) ((x) << 6) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index b7bebd22b543..196fb0384f84 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -370,6 +370,7 @@ struct vop2_plane_state { uint64_t color_key; unsigned long offset; int pdaf_data_type; + u8 dovi_input_type; bool async_commit; struct drm_property_blob *dci_data; @@ -539,6 +540,12 @@ struct vop2_wb { }; +struct vop2_dovi_core { + uint8_t id; + struct vop2 *vop2; + const struct vop2_dovi_regs *regs; +}; + struct vop2_dsc { uint8_t id; uint8_t max_slice_num; @@ -635,6 +642,19 @@ struct vop2_video_port { */ int hdr_en; + /** + * @dovi_hdr_mode: Set when use dovi. + */ + bool dovi_hdr_mode; + /** + * @dovi_hdr_en: Set when dovi enabled. + */ + bool dovi_hdr_en; + /** + * @dovi_hdr_in: Set when dovi input. + */ + bool dovi_hdr_in; + /** * ----------------- * | | | @@ -722,6 +742,11 @@ struct vop2_video_port { */ struct rockchip_gem_object *hdr_lut_gem_obj; + /** + * @dovi_lut_gem_obj: gem obj to store dovi lut + */ + struct rockchip_gem_object *dovi_lut_gem_obj; + /** * @cubic_lut: cubic look up table */ @@ -856,6 +881,7 @@ struct vop2 { u32 version; struct device *dev; struct drm_device *drm_dev; + struct vop2_dovi_core dovi_cores[ROCKCHIP_MAX_DOVI_CORE]; struct vop2_dsc dscs[ROCKCHIP_MAX_CRTC]; struct vop2_video_port vps[ROCKCHIP_MAX_CRTC]; struct vop2_wb wb; @@ -962,6 +988,9 @@ struct vop2 { struct clk *hclk; struct clk *aclk; struct clk *pclk; + struct clk *aclk_dovi; + struct clk *aclk_div2_src; + struct clk *aclk_root; struct reset_control *ahb_rst; struct reset_control *axi_rst; struct csu_clk *csu_aclk; @@ -1175,6 +1204,11 @@ static inline bool is_vop3(struct vop2 *vop2) return true; } +static inline bool vop2_is_dovi_mode(struct vop2_video_port *vp) +{ + return vp->dovi_hdr_mode; +} + static bool vop2_soc_is_rk3566(void) { return soc_is_rk3566(); @@ -3302,6 +3336,19 @@ static void vop2_setup_csc_mode(struct vop2_video_port *vp, vpstate->r2y_en = 0; vpstate->csc_mode = 0; + /** + * DOVI core1 input format must YUV422, VOP win will do: + * [YUV420/422 -> YUV444], [YUV444 -> YUV422] -> core1 + * DOVI core2 input format must RGB + */ + if (vop2_is_dovi_mode(vp)) { + if (vpstate->dovi_input_type && !is_input_yuv) + drm_err(vp->vop2, "DOVI core1 input format must YUV format\n"); + if (!vpstate->dovi_input_type && is_input_yuv) + drm_err(vp->vop2, "DOVI core2 input format must RGB format\n"); + return; + } + if (is_vop3(vp->vop2)) { if (vpstate->hdr_in) { if (is_input_yuv) { @@ -4719,6 +4766,379 @@ static void vop2_power_off_all_pd(struct vop2 *vop2) } } +static bool vop2_check_dovi_core_enabled(int id, u32 valid) +{ + if (valid & (BIT(id - 1))) + return true; + + return false; +} + +/* The dovi always trigger error interrupt, so it's disabled by default */ +static void __maybe_unused vop2_enable_dovi_sys_irqs(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + const struct vop_intr *intr = vp_data->intr; + uint32_t irqs = DOLBY_CORE1_INTR | DOLBY_CORE2_INTR | DOLBY_CORE3_INTR; + struct vop2_dovi_core *dovi_core; + int i = 0; + + VOP_INTR_SET_TYPE(vop2, intr, clear, irqs, 1); + VOP_INTR_SET_TYPE(vop2, intr, enable, irqs, 1); + + for (i = 0; i < vop2_data->dovi->nr_dovi_cores; i++) { + dovi_core = &vop2->dovi_cores[i]; + /* enable memtadata program error and unmatched frame detect error */ + VOP_MODULE_SET(vop2, dovi_core, interrupt_enable, 3); + } +} + +static int vop2_hdr_get_eotf_by_output_mode(int output_mode) +{ + switch (output_mode) { + case HDR_HDR10: + return HDMI_EOTF_SMPTE_ST2084; + case HDR_HDRVIVID: + return HDMI_EOTF_HDRVIVID; + case HDR_HDR10PLUS: + return HDMI_EOTF_HDR10PLUS; + case HDR_DOVI: + return HDMI_EOTF_DOVI; + case HDR_NONE: + default: + return HDMI_EOTF_TRADITIONAL_GAMMA_SDR; + } +} + +static void vop2_dovi_enable(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + struct vop2_dovi_core *dovi_core; + struct dovi_regs *dovi_data; + struct hdr_extend *hdr_data; + int i = 0; + + if (vp->dovi_hdr_en) + return; + + hdr_data = (struct hdr_extend *)vcstate->hdr_ext_data->data; + if (!hdr_data) + return; + dovi_data = &hdr_data->dovi_data; + for (i = 0; i < vop2_data->dovi->nr_dovi_cores; i++) { + dovi_core = &vop2->dovi_cores[i]; + + if (vop2_check_dovi_core_enabled(dovi_core->id, dovi_data->valid)) + VOP_MODULE_SET(vop2, dovi_core, enable, 1); + } + + vp->dovi_hdr_en = true; + DRM_DEV_INFO(vop2->dev, "vp%d dovi enabled\n", vp->id); +} + +static int vop2_dovi_init(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct hdr_extend *hdr_data; + unsigned long aclk_rate; + int ret = 0; + + if (!vcstate || !vcstate->hdr_ext_data) + return 0; + + hdr_data = (struct hdr_extend *)vcstate->hdr_ext_data->data; + if (!hdr_data || hdr_data->hdr_type != HDR_DOVI) + return 0; + + aclk_rate = clk_get_rate(vop2->aclk); + if (!vp->dovi_lut_gem_obj) { + vp->dovi_lut_gem_obj = + rockchip_gem_create_object(vop2->drm_dev, + VOP2_DOVI_TONE_SCA_AXI_TAB_SIZE * 2, + true, 0); + + if (IS_ERR(vp->dovi_lut_gem_obj)) { + drm_err(vop2, "create dovi lut obj failed\n"); + return 0; + } + } + + ret = clk_prepare_enable(vop2->aclk_dovi); + if (ret < 0) + drm_err(vop2, "failed to enable aclk_dovi - %d\n", ret); + + ret = clk_set_parent(vop2->aclk, vop2->aclk_div2_src); + if (ret < 0) { + drm_err(vop2, "failed to set parent(%s) for %s\n", + __clk_get_name(vop2->aclk_div2_src), + __clk_get_name(vop2->aclk)); + } + clk_set_rate(vop2->aclk_dovi, aclk_rate); + /* vop2_enable_dovi_sys_irqs(crtc); */ + + vp->dovi_hdr_mode = true; + vop2_dovi_enable(crtc); + + return 0; +} + +static void vop2_dovi_pre_disable(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + struct vop2_dovi_core *dovi_core; + int i; + + if (!vp->dovi_hdr_en) + return; + + VOP_MODULE_SET(vop2, vp, dp_line_end_mode, 0); + VOP_MODULE_SET(vop2, vp, dp_bg_bottom_disable, 0); + VOP_MODULE_SET(vop2, vp, dovi_pre_scan_en, 0); + for (i = 0; i < vop2_data->dovi->nr_dovi_cores; i++) { + dovi_core = &vop2->dovi_cores[i]; + VOP_MODULE_SET(vop2, dovi_core, enable, 0); + VOP_MODULE_SET(vop2, dovi_core, lut_update, 0); + if (dovi_core->id == 2) + VOP_MODULE_SET(vop2, dovi_core, dly_en, 0); + } + VOP_CTRL_SET(vop2, lut_dma_en, 0); +} + +static void vop2_dovi_post_disable(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + int ret; + + if (!vp->dovi_hdr_en) + return; + + ret = clk_set_parent(vop2->aclk, vop2->aclk_root); + if (ret < 0) + drm_err(vop2, + "failed to set aclk vop parent back to aclk vop root\n"); + clk_disable_unprepare(vop2->aclk_dovi); + if (vp->dovi_lut_gem_obj) + rockchip_gem_free_object(&vp->dovi_lut_gem_obj->base); + vp->dovi_lut_gem_obj = NULL; + vp->dovi_hdr_mode = false; + vp->dovi_hdr_en = false; + vp->dovi_hdr_in = false; + drm_info(vop2, "vp%d dovi disabled\n", vp->id); +} + +static u32 vop2_read_dovi_metadata_copy_finish(struct vop2_dovi_core *dovi_core) +{ + struct vop2 *vop2 = dovi_core->vop2; + + return VOP_MODULE_GET(vop2, dovi_core, metadata_copy_finish); +} + +static void vop2_wait_for_dovi_metadata_copy_finish(struct vop2_dovi_core *dovi_core) +{ + struct vop2 *vop2 = dovi_core->vop2; + bool finish; + int ret; + + ret = readx_poll_timeout_atomic(vop2_read_dovi_metadata_copy_finish, + dovi_core, finish, finish == true, 0, 10 * 1000); + if (ret) + DRM_DEV_DEBUG(vop2->dev, "Wait dovi%d metadata copy finish timeout: 0x%x\n", + dovi_core->id, vop2_readl(vop2, dovi_core->regs->metadata_copy_finish.offset)); +} + + +static u32 vop2_read_dovi_core_enable(struct vop2_dovi_core *dovi_core) +{ + struct vop2 *vop2 = dovi_core->vop2; + + return VOP_MODULE_GET(vop2, dovi_core, enable); +} + +static void vop2_wait_for_dovi_core_enabled(struct vop2_dovi_core *dovi_core) +{ + struct vop2 *vop2 = dovi_core->vop2; + bool enable; + int ret; + + ret = readx_poll_timeout_atomic(vop2_read_dovi_core_enable, + dovi_core, enable, enable == true, 0, 10 * 1000); + if (ret) + DRM_DEV_DEBUG(vop2->dev, "Wait dovi%d metadata copy finish timeout: 0x%x\n", + dovi_core->id, vop2_readl(vop2, dovi_core->regs->metadata_copy_finish.offset)); +} + +static void vop2_load_dovi_coe_table(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct dovi_regs *dovi_reg_data; + struct hdr_extend *hdr_data; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_dovi_core_data *dovi_core_data; + struct vop2_dovi_core *dovi_core; + u32 *dovi_lut_kvaddr; + u32 dovi_lut_mst; + int i = 0, j = 0; + u32 offset = vp->rockchip_crtc.frame_count % 2 ? 0 : VOP2_DOVI_TONE_SCA_AXI_TAB_SIZE; + + if (!vop2_is_dovi_mode(vp)) + return; + + if (!vcstate->hdr_ext_data) + return; + + hdr_data = (struct hdr_extend *)vcstate->hdr_ext_data->data; + dovi_reg_data = &hdr_data->dovi_data; + vcstate->eotf = vop2_hdr_get_eotf_by_output_mode(dovi_reg_data->output_mode); + + for (i = 0; i < vop2_data->dovi->nr_dovi_cores; i++) { + dovi_core = &vop2->dovi_cores[i]; + + if (!vop2_check_dovi_core_enabled(dovi_core->id, dovi_reg_data->valid)) + continue; + + vop2_wait_for_dovi_core_enabled(dovi_core); + /* + * The metadata_copy_finish maybe cleared by vop2_dovi_hanle_irqs, + * but vop2_dovi_hanle_irqs is disabled now. + */ + vop2_wait_for_dovi_metadata_copy_finish(dovi_core); + VOP_MODULE_SET(vop2, dovi_core, metadata_copy_finish, 1); + } + + dovi_lut_kvaddr = (u32 *)vp->dovi_lut_gem_obj->kvaddr + offset / 4; + memcpy(dovi_lut_kvaddr, &dovi_reg_data->core1_lut, VOP2_DOVI_CORE1_LUT_SIZE); + dovi_lut_kvaddr += VOP2_DOVI_CORE1_LUT_SIZE / 4; + memcpy(dovi_lut_kvaddr, &dovi_reg_data->core2_lut, VOP2_DOVI_CORE1_LUT_SIZE); + + dovi_lut_mst = vp->dovi_lut_gem_obj->dma_addr + offset; + for (i = 0; i < vop2_data->dovi->nr_dovi_cores; i++) { + dovi_core = &vop2->dovi_cores[i]; + + if (!vop2_check_dovi_core_enabled(dovi_core->id, dovi_reg_data->valid)) + continue; + + if (dovi_core->id == 1) + VOP_MODULE_SET(vop2, dovi_core, lut_mst, dovi_lut_mst); + + if (dovi_core->id == 2) + VOP_MODULE_SET(vop2, dovi_core, lut_mst, dovi_lut_mst + VOP2_DOVI_CORE1_LUT_SIZE); + } + + VOP_CTRL_SET(vop2, lut_dma_en, 1); + + for (i = 0; i < vop2_data->dovi->nr_dovi_cores; i++) { + dovi_core = &vop2->dovi_cores[i]; + + if (!vop2_check_dovi_core_enabled(dovi_core->id, dovi_reg_data->valid)) + continue; + + VOP_MODULE_SET(vop2, dovi_core, lut_update, 1); + } + + for (i = 0; i < vop2_data->dovi->nr_dovi_cores; i++) { + u32 *core_data = NULL; + int size; + int offset; + + dovi_core_data = &vop2_data->dovi->dovi_core_data[i]; + dovi_core = &vop2->dovi_cores[i]; + + if (!vop2_check_dovi_core_enabled(dovi_core->id, dovi_reg_data->valid)) + continue; + + if (dovi_core->id == 1) { + core_data = (u32 *)&dovi_reg_data->core1; + offset = dovi_core_data->srange_offset_from_core >> 2; + size = DOVI_CORE1_SIZE - offset; + } else if (dovi_core->id == 2) { + core_data = (u32 *)&dovi_reg_data->core2; + offset = dovi_core_data->srange_offset_from_core >> 2; + size = DOVI_CORE2_SIZE - offset; + size -= 2; /* core2 last 2 word is reserved */ + } else if (dovi_core->id == 3) { + core_data = (u32 *)&dovi_reg_data->core3; + offset = dovi_core_data->srange_offset_from_core >> 2; + size = DOVI_CORE3_SIZE - offset; + } + + if (!core_data) + continue; + + VOP_MODULE_SET(vop2, dovi_core, metadata_program_st, 1); + vop2_writel(vop2, dovi_core_data->ctrl_offset, core_data[1]); + /* write regs start from SRANGE_REGISTER */ + core_data += offset; + for (j = 0; j < size; j++) + vop2_writel(vop2, dovi_core_data->srange_offset + (j << 2), *(core_data++)); + VOP_MODULE_SET(vop2, dovi_core, metadata_program_end, 1); + + if (dovi_core->id == 2) { + if (dovi_reg_data->input_mode != HDR_DOVI) + VOP_MODULE_SET(vop2, dovi_core, dly_en, 1); + else + VOP_MODULE_SET(vop2, dovi_core, dly_en, 0); + } + } +} + +static void vop2_dovi_mode_config(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; + u16 hsync_len = (adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start) >> 1; + u16 hdisplay = (adjusted_mode->crtc_hdisplay) >> 1; + u16 hact_st = (adjusted_mode->crtc_htotal - adjusted_mode->crtc_hsync_start) >> 1; + u16 hact_end = hact_st + hdisplay; + u16 hfp = (adjusted_mode->crtc_hsync_start - adjusted_mode->crtc_hdisplay) >> 1; + u16 htotal; + + u16 vdisplay = adjusted_mode->crtc_vdisplay; + u16 vtotal = adjusted_mode->crtc_vtotal; + u16 vsync_len = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start; + u16 vact_st = adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vsync_start; + u16 vact_end = vact_st + vdisplay; + u32 val = 0; + int dovi_max_delay = vp->dovi_hdr_in ? + vop2_data->dovi->dovi_max_delay[0] : vop2_data->dovi->dovi_max_delay[1]; + + if (!vop2_is_dovi_mode(vp)) { + VOP_MODULE_SET(vop2, vp, dovi_pre_scan_en, 0); + return; + } + + hact_end = hact_end + dovi_max_delay; + val = hact_st << 16 | hact_end; + VOP_MODULE_SET(vop2, vp, pre_scan_htiming1, val); + + hfp = hfp >= 20 ? hfp : 20; + htotal = hact_end + hfp; + val = htotal << 16 | hsync_len; + VOP_MODULE_SET(vop2, vp, pre_scan_htiming, val); + + val = vtotal << 16 | vsync_len; + VOP_MODULE_SET(vop2, vp, pre_scan_htiming2, val); + + val = vact_st << 16 | vact_end; + VOP_MODULE_SET(vop2, vp, pre_scan_htiming3, val); + + VOP_MODULE_SET(vop2, vp, dovi_pre_scan_en, 1); +} + static void vop2_disable(struct drm_crtc *crtc) { struct vop2_video_port *vp = to_vop2_video_port(crtc); @@ -5182,7 +5602,9 @@ static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, if (vp_data->feature & VOP_FEATURE_VIVID_HDR) VOP_MODULE_SET(vop2, vp, hdr_lut_update_en, 0); + vop2_dovi_pre_disable(crtc); vop2_disable_all_planes_for_crtc(crtc); + vop2_dovi_post_disable(crtc); if (vop2->dscs[vcstate->dsc_id].enabled && vop2->dscs[vcstate->dsc_id].attach_vp_id == vp->id && @@ -5282,6 +5704,7 @@ static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, vcstate->output_type = 0; vp->splice_mode_right = false; vp->loader_protect = false; + vp->enabled_win_mask = 0; splice_vp->splice_mode_right = false; memset(&vp->active_tv_state, 0, sizeof(vp->active_tv_state)); vop2_unlock(vop2); @@ -6829,6 +7252,11 @@ static int vop2_atomic_plane_set_property(struct drm_plane *plane, return ret; } + if (property == private->dovi_input_type_prop) { + vpstate->dovi_input_type = val; + return 0; + } + DRM_ERROR("failed to set vop2 plane property id:%d, name:%s\n", property->base.id, property->name); @@ -6881,6 +7309,11 @@ static int vop2_atomic_plane_get_property(struct drm_plane *plane, return 0; } + if (property == private->dovi_input_type_prop) { + *val = vpstate->dovi_input_type; + return 0; + } + DRM_ERROR("failed to get vop2 plane property id:%d, name:%s\n", property->base.id, property->name); @@ -7326,6 +7759,27 @@ static int vop2_crtc_loader_protect(struct drm_crtc *crtc, bool on, void *data) return 0; } +static const char *hdr_to_string(int eotf) +{ + switch (eotf) { + case HDMI_EOTF_TRADITIONAL_GAMMA_HDR: + return "GAMMA HDR"; + case HDMI_EOTF_SMPTE_ST2084: + return "HDR10"; + case HDMI_EOTF_BT_2100_HLG: + return "HLG"; + case HDMI_EOTF_HDR10PLUS: + return "HDR10PLUS"; + case HDMI_EOTF_HDRVIVID: + return "HDRVIVID"; + case HDMI_EOTF_DOVI: + return "DOVI"; + case HDMI_EOTF_TRADITIONAL_GAMMA_SDR: + default: + return "SDR"; + } +} + #define DEBUG_PRINT(args...) \ do { \ if (s) \ @@ -7360,10 +7814,9 @@ static int vop2_plane_info_dump(struct seq_file *s, struct drm_plane *plane) &fb->format->format, rockchip_drm_modifier_to_string(fb->modifier), pstate->pixel_blend_mode, vpstate->global_alpha); DEBUG_PRINT("\tcolor: %s[%d] color-encoding[%s] color-range[%s]\n", - vpstate->eotf ? "HDR" : "SDR", vpstate->eotf, + hdr_to_string(vpstate->eotf), vpstate->eotf, rockchip_drm_get_color_encoding_name(pstate->color_encoding), rockchip_drm_get_color_range_name(pstate->color_range)); - DEBUG_PRINT("\trotate: xmirror: %d ymirror: %d rotate_90: %d rotate_270: %d\n", vpstate->xmirror_en, vpstate->ymirror_en, vpstate->rotate_90_en, vpstate->rotate_270_en); @@ -7435,7 +7888,7 @@ static int vop2_crtc_debugfs_dump(struct drm_crtc *crtc, struct seq_file *s) DEBUG_PRINT("\toverlay_mode[%d] output_mode[%x] ", state->yuv_overlay, state->output_mode); DEBUG_PRINT("%s[%d] color-encoding[%s] color-range[%s]\n", - state->eotf ? "HDR" : "SDR", state->eotf, + hdr_to_string(state->eotf), state->eotf, rockchip_drm_get_color_encoding_name(state->color_encoding), rockchip_drm_get_color_range_name(state->color_range)); DEBUG_PRINT(" Display mode: %dx%d%s%d\n", @@ -9821,6 +10274,8 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_sta } if (is_vop3(vop2)) vop3_setup_pipe_dly(vp, NULL); + if (vp_data->feature & VOP_FEATURE_DOVI) + vop2_dovi_init(crtc); vop2_crtc_setup_output_mode(crtc); @@ -9964,6 +10419,26 @@ static void vop2_update_post_csc_info(struct vop2_video_port *vp, memset(&vp->csc_info, 0, sizeof(struct post_csc)); } +/* + * Check hdr_ext_data switch from valid to NULL or + * NULL to valid. + * This is used to check a switch from dynamic HDR2SDR + * or SDR2HDR output mode switch. + */ +static bool vop_hdr_ext_data_switch(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) || (!new_blob && old_blob)) + return false; + + return true; +} + static int vop2_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) { @@ -9978,6 +10453,7 @@ static int vop2_crtc_atomic_check(struct drm_crtc *crtc, struct rockchip_crtc_state *new_vcstate = to_rockchip_crtc_state(new_crtc_state); struct rockchip_crtc_state *old_vcstate = to_rockchip_crtc_state(old_crtc_state); struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; + bool hdr_ext_data_change; if (vop2_has_feature(vop2, VOP_FEATURE_SPLICE)) { if (adjusted_mode->hdisplay > VOP2_MAX_VP_OUTPUT_WIDTH) { @@ -10002,6 +10478,11 @@ static int vop2_crtc_atomic_check(struct drm_crtc *crtc, else vp->acm_state_changed = false; + hdr_ext_data_change = !vop_hdr_ext_data_switch(old_crtc_state, new_crtc_state) | + new_crtc_state->active_changed; + if (hdr_ext_data_change) + new_crtc_state->mode_changed = true; + return 0; } @@ -10235,6 +10716,29 @@ static void vop3_setup_dynamic_hdr(struct vop2_video_port *vp, uint8_t win_phys_ } } +static void vop2_setup_hdr_dovi(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct dovi_regs *dovi_data; + struct hdr_extend *hdr_data; + + if (!vop2_data->dovi->nr_dovi_cores || !vcstate || !vcstate->hdr_ext_data) + return; + + hdr_data = (struct hdr_extend *)vcstate->hdr_ext_data->data; + if (!hdr_data || hdr_data->hdr_type != HDR_DOVI) + return; + + dovi_data = &hdr_data->dovi_data; + if (dovi_data->input_mode == HDR_DOVI) + vp->dovi_hdr_in = true; + else + vp->dovi_hdr_in = false; +} + static void vop2_setup_hdr10(struct vop2_video_port *vp, uint8_t win_phys_id) { struct vop2 *vop2 = vp->vop2; @@ -10549,7 +11053,7 @@ static void vop2_setup_alpha(struct vop2_video_port *vp, int mixer_id; int phys_id; uint32_t offset; - int i; + int i, begin_layer = 0; bool bottom_layer_alpha_en = false; u32 dst_global_alpha = 0xff; @@ -10590,8 +11094,24 @@ static void vop2_setup_alpha(struct vop2_video_port *vp, vp->hdr10_at_splice_mode && vp->id == 0) mixer_id++;/* fixed path for rk3588: layer1 -> hdr10_1 */ + /* + * vp0 begin from mix0, mix0 input is layer0 and layer1, we need to use + * layer1 format to config mix0, the layer0 alpha[bottom_layer_alpha_en is true], + * will use the following formulas: Cd = Cs + (1 - As) * Cd * Agd to do overlay; + * vp1/2/3 layer0 will enter first mix src layer, so we need to init it. + * + * rk3588 hdr splice mode, vp1 hdr layer will be insert to layer1[vp0], + * so we need to ignore layer0 and begin from layer1 to config mix for vp1. + */ + if (vp->id == 0 || vp->hdr10_at_splice_mode) + begin_layer = 1; + + /* dovi core1 base layer and enhance layer no need to do overlay */ + if (vop2_is_dovi_mode(vp)) + begin_layer = 2; + alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */ - for (i = 1; i < vp->nr_layers; i++) { + for (i = begin_layer; i < vp->nr_layers; i++) { zpos = &vop2_zpos[i]; win = vop2_find_win_by_phys_id(vop2, zpos->win_phys_id); if (win->splice_mode_right) @@ -10609,7 +11129,7 @@ static void vop2_setup_alpha(struct vop2_video_port *vp, pixel_alpha_en = is_alpha_support(fb->format->format); alpha_config.src_premulti_en = premulti_en; - if (bottom_layer_alpha_en && i == 1) { + if (bottom_layer_alpha_en && i == begin_layer && vp->id == 0) {/* Cd = Cs + (1 - As) * Cd * Agd */ /** * The data from cluster mix is always premultiplied alpha; * cluster layer or esmart layer[premulti_en = 1] @@ -10638,6 +11158,13 @@ static void vop2_setup_alpha(struct vop2_video_port *vp, } vop2_parse_alpha(&alpha_config, &alpha); + /* + * The first UI enter dovi core2 no need to do alpha blending, but + * the alpha value need to transfer to next mix and enter core2. + */ + if (vop2_is_dovi_mode(vp) && i == begin_layer) + alpha.src_color_ctrl.bits.alpha_en = false; + offset = (mixer_id + i - 1) * 0x10; vop2_writel(vop2, src_color_ctrl_offset + offset, alpha.src_color_ctrl.val); vop2_writel(vop2, dst_color_ctrl_offset + offset, alpha.dst_color_ctrl.val); @@ -10645,7 +11172,7 @@ static void vop2_setup_alpha(struct vop2_video_port *vp, vop2_writel(vop2, dst_alpha_ctrl_offset + offset, alpha.dst_alpha_ctrl.val); } - if (bottom_layer_alpha_en || vp->hdr_en) { + if (bottom_layer_alpha_en || vp->hdr_en || (vop2_is_dovi_mode(vp) && vp->nr_layers > 1)) { /* Transfer pixel alpha to hdr mix */ alpha_config.src_premulti_en = premulti_en; alpha_config.dst_premulti_en = true; @@ -10653,6 +11180,16 @@ static void vop2_setup_alpha(struct vop2_video_port *vp, alpha_config.src_glb_alpha_value = 0xff; alpha_config.dst_glb_alpha_value = 0xff; vop2_parse_alpha(&alpha_config, &alpha); + if (vop2_is_dovi_mode(vp)) { + /* dovi core2 output must be no pre mul alpha + * color_mode = ALPHA_SRC_NO_PRE_MUL && factor_mode = ALPHA_ONE is + * roughly equal to color_mode = ALPHA_SRC_PRE_MUL && factor_mode = ALPHA_NO_SATURATION, + * but the secondary is more correctly. + */ + alpha.src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL; + alpha.src_color_ctrl.bits.factor_mode = ALPHA_SRC_GLOBAL; + alpha.src_color_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION; + } VOP_MODULE_SET(vop2, vp, hdr_src_color_ctrl, alpha.src_color_ctrl.val); @@ -10968,6 +11505,13 @@ static u16 vop2_calc_bg_ovl_and_port_mux(struct vop2_video_port *vp) used_layers += 1; if (vop2->vps[0].hdr10_at_splice_mode && i == 1) used_layers -= 1; + /* + * At RK3588 dovi mode, layer1 always used by enhance layer, + * so the used_layers at least 3 layers, include: + * base layer[video], enhance layer[reserved] and UI layer. + */ + if (vop2_is_dovi_mode(prev_vp) && used_layers < 3) + used_layers++; } /* * when a window move from vp0 to vp1, or vp0 to vp2, @@ -11216,6 +11760,12 @@ static void vop2_setup_dly_for_vp(struct vop2_video_port *vp) pre_scan_dly = (pre_scan_dly << 16) | hsync_len; VOP_MODULE_SET(vop2, vp, bg_dly, bg_dly); + /* Disable bg bottom overlay */ + if (vop2_is_dovi_mode(vp)) { + VOP_MODULE_SET(vop2, vp, dp_line_end_mode, 1); + VOP_MODULE_SET(vop2, vp, dp_bg_bottom_disable, 1); + } + /* will be rewrite at dovi_mode_config when at dovi mode */ VOP_MODULE_SET(vop2, vp, pre_scan_htiming, pre_scan_dly); } @@ -11249,6 +11799,18 @@ static void vop2_setup_dly_for_window(struct vop2_video_port *vp, const struct v } else if (vp->hdr_in && vp->hdr_out && vpstate->hdr_in) { dly = win->dly[VOP2_DLY_MODE_HIHO_H]; dly -= vp->bg_ovl_dly; + } else if (vop2_is_dovi_mode(vp)) { + if (vp->dovi_hdr_in) { + if (vpstate->dovi_input_type) + dly = win->dly[VOP2_DLY_MODE_DOVI_IN_CORE1]; + else + dly = win->dly[VOP2_DLY_MODE_DOVI_IN_CORE2]; + } else { + if (vpstate->dovi_input_type) + dly = win->dly[VOP2_DLY_MODE_NONDOVI_IN_CORE1]; + else + dly = win->dly[VOP2_DLY_MODE_NONDOVI_IN_CORE2]; + } } else { dly = win->dly[VOP2_DLY_MODE_DEFAULT]; } @@ -11498,6 +12060,33 @@ static void vop2_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_stat vp->nr_layers = nr_layers; sort(vop2_zpos, nr_layers, sizeof(vop2_zpos[0]), vop2_zpos_cmp, NULL); + /* + * At RK3588 dovi mode, the layer0 and layer1 is used for dovi layer, + * layer0 for base layer, layer1 for enhance layer, the enhance layer + * is option, but it's always occupy this layer, and the other layer + * must be assigned from layer2, so copy the other layers to &vop_zpos[2]. + */ + if (vop2->version == VOP_VERSION_RK3588 && + vop2_is_dovi_mode(vp) && vp->nr_layers > 1) { + const struct vop2_data *vop2_data = vop2->data; + struct vop2_zpos *vop2_zpos_tmp; + int i = 0; + + vop2_zpos_tmp = kmalloc_array(nr_layers - 1, sizeof(struct vop2_zpos), GFP_KERNEL); + if (!vop2_zpos_tmp) + goto dovi_err; + + /* Insert esmart3 as core1 enhance layer to zpos1 */ + memcpy(vop2_zpos_tmp, &vop2_zpos[1], (nr_layers - 1) * sizeof(struct vop2_zpos)); + vp->nr_layers++; + vop2_zpos[1].zpos = 1; + vop2_zpos[1].win_phys_id = vop2_data->dovi->enhance_layer_phy_id; + memcpy(&vop2_zpos[2], vop2_zpos_tmp, (nr_layers - 1) * sizeof(struct vop2_zpos)); + for (i = 0; i < nr_layers - 1; i++) + vop2_zpos[2 + i].zpos = 2 + i; + + kfree(vop2_zpos_tmp); + } if (!vp->hdr10_at_splice_mode) { if (is_vop3(vop2)) { @@ -11514,7 +12103,10 @@ static void vop2_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_stat vop3_setup_alpha(vp, vop2_zpos); vop3_setup_pipe_dly(vp, vop2_zpos); } else { - vop2_setup_hdr10(vp, vop2_zpos[0].win_phys_id); + if (!vop2_is_dovi_mode(vp)) + vop2_setup_hdr10(vp, vop2_zpos[0].win_phys_id); + else + vop2_setup_hdr_dovi(crtc); vop2_setup_alpha(vp, vop2_zpos); vop2_setup_dly_for_vp(vp); vop2_setup_dly_for_window(vp, vop2_zpos); @@ -11529,7 +12121,8 @@ static void vop2_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_stat vop2_setup_port_mux(vp); if (!vp->hdr10_at_splice_mode) vop2_setup_layer_mixer_for_vp(splice_vp, vop2_zpos_splice); - vop2_setup_hdr10(splice_vp, vop2_zpos_splice[0].win_phys_id); + if (!vop2_is_dovi_mode(vp)) + vop2_setup_hdr10(splice_vp, vop2_zpos_splice[0].win_phys_id); vop2_setup_alpha(splice_vp, vop2_zpos_splice); vop2_setup_dly_for_vp(splice_vp); vop2_setup_dly_for_window(splice_vp, vop2_zpos_splice); @@ -11570,6 +12163,7 @@ static void vop2_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_stat } } +dovi_err: if (vcstate->splice_mode) kfree(vop2_zpos_splice); out: @@ -11971,6 +12565,11 @@ static void vop2_cfg_update(struct drm_crtc *crtc, if (vp_data->feature & VOP_FEATURE_OVERSCAN) vop2_post_config(crtc); + if (vop2_is_dovi_mode(vp) && vp->enabled_win_mask) { + vop2_dovi_mode_config(crtc); + vop2_load_dovi_coe_table(crtc); + } + spin_unlock(&vop2->reg_lock); if (vp_data->feature & VOP_FEATURE_POST_CSC) { @@ -12069,7 +12668,9 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_stat struct vop2_wb *wb = &vop2->wb; struct drm_writeback_connector *wb_conn = &wb->conn; struct drm_connector_state *conn_state = wb_conn->base.state; + bool wb_mode = conn_state && conn_state->writeback_job && conn_state->writeback_job->fb; bool wb_oneframe_mode = VOP_MODULE_GET(vop2, wb, one_frame_mode); + bool dovi_mode = vop2_is_dovi_mode(vp) && vp->enabled_win_mask; #if defined(CONFIG_ROCKCHIP_DRM_DEBUG) if (vp->rockchip_crtc.vop_dump_status == DUMP_KEEP || @@ -12079,7 +12680,13 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_stat } #endif - if (conn_state && conn_state->writeback_job && conn_state->writeback_job->fb && !wb_oneframe_mode) { + if ((wb_mode && !wb_oneframe_mode) || dovi_mode) { + /** + * Avoid commit time close to vsync when enable writeback or dovi mode. + * For writeback may be lost writeback frame when close to vsync, + * For dovi mode may be appear dovi config and plane config take effect + * at different frame. + */ u16 vtotal = VOP_MODULE_GET(vop2, vp, dsp_vtotal); u32 current_line = vop2_read_vcnt(vp); @@ -12156,6 +12763,7 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_stat if (vp->mcu_timing.mcu_pix_total) VOP_MODULE_SET(vop2, vp, mcu_hold_mode, 0); + vp->rockchip_crtc.frame_count++; spin_unlock_irqrestore(&vop2->irq_lock, flags); /* @@ -12730,6 +13338,35 @@ static void vop2_wb_handler(struct vop2_video_port *vp) spin_unlock_irqrestore(&wb->job_lock, flags); } +static void vop2_dovi_hanle_irqs(struct drm_crtc *crtc, uint32_t active_irqs) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + struct vop2_dovi_core *dovi_core; + u32 val = 0; + + if (active_irqs & DOLBY_CORE1_INTR) { + dovi_core = &vop2->dovi_cores[0]; + val = VOP_MODULE_GET(vop2, dovi_core, interrupt_raw); + VOP_MODULE_SET(vop2, dovi_core, interrupt_raw, val); + drm_dbg(vop2, "dovi core1 irq: 0x%x\n", val); + } + + if (active_irqs & DOLBY_CORE2_INTR) { + dovi_core = &vop2->dovi_cores[1]; + val = VOP_MODULE_GET(vop2, dovi_core, interrupt_raw); + VOP_MODULE_SET(vop2, dovi_core, interrupt_raw, val); + drm_dbg(vop2, "dovi core2 irq: 0x%x\n", val); + } + + if (active_irqs & DOLBY_CORE3_INTR) { + dovi_core = &vop2->dovi_cores[2]; + val = VOP_MODULE_GET(vop2, dovi_core, interrupt_raw); + VOP_MODULE_SET(vop2, dovi_core, interrupt_raw, val); + drm_dbg(vop2, "dovi core3 irq: 0x%x\n", val); + } +} + static void vop2_dsc_isr(struct vop2 *vop2) { const struct vop2_data *vop2_data = vop2->data; @@ -12859,6 +13496,12 @@ static irqreturn_t vop2_isr(int irq, void *data) ret = IRQ_HANDLED; } + if (active_irqs & (DOLBY_CORE1_INTR | DOLBY_CORE2_INTR | DOLBY_CORE3_INTR)) { + vop2_dovi_hanle_irqs(crtc, active_irqs); + active_irqs &= ~(DOLBY_CORE1_INTR | DOLBY_CORE2_INTR | DOLBY_CORE3_INTR); + ret = IRQ_HANDLED; + } + if (active_irqs & POST_BUF_EMPTY_INTR) { vop2_handle_post_buf_empty(crtc); DRM_DEV_ERROR_RATELIMITED(vop2->dev, "POST_BUF_EMPTY irq err at vp%d\n", vp->id); @@ -13212,6 +13855,9 @@ static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win, unsigned lon else drm_object_attach_property(&win->base.base, private->share_id_prop, win->base.base.id); + + drm_object_attach_property(&win->base.base, private->dovi_input_type_prop, 0); + if (win->supported_rotations) drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0, DRM_MODE_ROTATE_0 | win->supported_rotations); @@ -13412,7 +14058,7 @@ static int vop2_crtc_create_feature_property(struct vop2 *vop2, struct drm_crtc static const struct drm_prop_enum_list props[] = { { ROCKCHIP_DRM_CRTC_FEATURE_ALPHA_SCALE, "ALPHA_SCALE" }, { ROCKCHIP_DRM_CRTC_FEATURE_HDR10, "HDR10" }, - { ROCKCHIP_DRM_CRTC_FEATURE_NEXT_HDR, "NEXT_HDR" }, + { ROCKCHIP_DRM_CRTC_FEATURE_DOVI, "DOVI" }, { ROCKCHIP_DRM_CRTC_FEATURE_VIVID_HDR, "VIVID_HDR" }, }; @@ -13420,8 +14066,8 @@ static int vop2_crtc_create_feature_property(struct vop2 *vop2, struct drm_crtc feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_ALPHA_SCALE); if (vp_data->feature & VOP_FEATURE_HDR10) feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_HDR10); - if (vp_data->feature & VOP_FEATURE_NEXT_HDR) - feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_NEXT_HDR); + if (vp_data->feature & VOP_FEATURE_DOVI) + feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_DOVI); if (vp_data->feature & VOP_FEATURE_VIVID_HDR) feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_VIVID_HDR); @@ -13817,7 +14463,7 @@ static int vop2_create_crtc(struct vop2 *vop2, uint8_t enabled_vp_mask) "Failed to init %s with SR helpers %d, ignoring\n", crtc->name, ret); - if (vp_data->feature & VOP_FEATURE_VIVID_HDR) + if (vp_data->feature & (VOP_FEATURE_VIVID_HDR | VOP_FEATURE_DOVI)) vop2_crtc_create_hdr_property(vop2, crtc); if (vp_data->feature & VOP_FEATURE_POST_ACM) vop2_crtc_create_post_acm_property(vop2, crtc); @@ -13950,6 +14596,22 @@ static int vop2_pd_data_init(struct vop2 *vop2) return 0; } +static void vop2_dovi_data_init(struct vop2 *vop2) +{ + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_dovi_core_data *dovi_core_data; + struct vop2_dovi_core *dovi_core; + int i; + + for (i = 0; i < vop2_data->dovi->nr_dovi_cores; i++) { + dovi_core = &vop2->dovi_cores[i]; + dovi_core_data = &vop2_data->dovi->dovi_core_data[i]; + dovi_core->id = dovi_core_data->id; + dovi_core->regs = dovi_core_data->regs; + dovi_core->vop2 = vop2; + } +} + static void vop2_dsc_data_init(struct vop2 *vop2) { const struct vop2_data *vop2_data = vop2->data; @@ -14668,6 +15330,24 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) return PTR_ERR(vop2->pclk); } + vop2->aclk_dovi = devm_clk_get_optional(vop2->dev, "aclk_dovi"); + if (IS_ERR(vop2->aclk_dovi)) { + DRM_DEV_ERROR(vop2->dev, "failed to get aclk dovi source\n"); + return PTR_ERR(vop2->aclk_dovi); + } + + vop2->aclk_div2_src = devm_clk_get_optional(vop2->dev, "aclk_vop_div2_src"); + if (IS_ERR(vop2->aclk_div2_src)) { + DRM_DEV_ERROR(vop2->dev, "failed to get aclk div2 src\n"); + return PTR_ERR(vop2->aclk_div2_src); + } + + vop2->aclk_root = devm_clk_get_optional(vop2->dev, "aclk_vop_root"); + if (IS_ERR(vop2->aclk_root)) { + DRM_DEV_ERROR(vop2->dev, "failed to get aclk vop root\n"); + return PTR_ERR(vop2->aclk_root); + } + vop2->ahb_rst = devm_reset_control_get_optional(vop2->dev, "ahb"); if (IS_ERR(vop2->ahb_rst)) { DRM_DEV_ERROR(vop2->dev, "failed to get ahb reset\n"); @@ -14776,6 +15456,7 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) if (ret) return ret; + vop2_dovi_data_init(vop2); vop2_dsc_data_init(vop2); registered_num_crtcs = vop2_create_crtc(vop2, enabled_vp_mask); diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c index f353c0acd91e..1da6bc02ee18 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c @@ -680,6 +680,10 @@ static const int rk3568_vop_intrs[] = { POST_BUF_EMPTY_INTR, FS_FIELD_INTR, DSP_HOLD_VALID_INTR, + 0, 0, 0, 0, 0, + DOLBY_CORE1_INTR, + DOLBY_CORE2_INTR, + DOLBY_CORE3_INTR, }; static const struct vop_intr rk3568_vp0_intr = { @@ -722,6 +726,88 @@ static const struct vop_intr rk3588_vp3_intr = { .clear = VOP_REG_MASK(RK3588_VP3_INT_CLR, 0xffff, 0), }; +static const struct vop2_dovi_regs rk3588_vop_dovi_core1_regs = { + .enable = VOP_REG(RK3568_OVL_CTRL, 0x1, 8), + .interrupt_raw = VOP_REG(RK3588_DOLBY_CORE1_INTR_RAW_REG, 0xf, 0), + .interrupt_enable = VOP_REG(RK3588_DOLBY_CORE1_INTR_ENABLE_REG, 0xf, 0), + .metadata_program_st = VOP_REG(RK3588_DOLBY_CORE1_METADATA_PROGRAM_ST, 0x1, 0), + .metadata_program_end = VOP_REG(RK3588_DOLBY_CORE1_METADATA_PROGRAM_END, 0x1, 0), + .metadata_copy_finish = VOP_REG(RK3588_DOLBY_CORE1_INTR_RAW_REG, 0x1, 2), + + .bypass_composer = VOP_REG(RK3588_DOLBY_CORE1_CONTROL_REG, 0x1, 0), + .bypass_csc = VOP_REG(RK3588_DOLBY_CORE1_CONTROL_REG, 0x1, 1), + .bypass_cvm = VOP_REG(RK3588_DOLBY_CORE1_CONTROL_REG, 0x1, 2), + .operating_mode = VOP_REG(RK3588_DOLBY_CORE1_CONTROL_REG, 0x1, 3), + .pixel_rate = VOP_REG(RK3588_DOLBY_CORE1_CONTROL_REG, 0xf, 4), + + .lut_update = VOP_REG(RK3568_OVL_CTRL, 0x1, 11), + .lut_mst = VOP_REG(RK3588_DOLBY_LUT_MST, 0xffffffff, 0), +}; + +static const struct vop2_dovi_regs rk3588_vop_dovi_core2_regs = { + .enable = VOP_REG(RK3568_OVL_CTRL, 0x1, 9), + .interrupt_raw = VOP_REG(RK3588_DOLBY_CORE2_INTR_RAW_REG, 0xf, 0), + .interrupt_enable = VOP_REG(RK3588_DOLBY_CORE2_INTR_ENABLE_REG, 0xf, 0), + .metadata_program_st = VOP_REG(RK3588_DOLBY_CORE2_METADATA_PROGRAM_ST, 0x1, 0), + .metadata_program_end = VOP_REG(RK3588_DOLBY_CORE2_METADATA_PROGRAM_END, 0x1, 0), + .metadata_copy_finish = VOP_REG(RK3588_DOLBY_CORE2_INTR_RAW_REG, 0x1, 2), + + .bypass_cvm = VOP_REG(RK3588_DOLBY_CORE2_CONTROL_REG, 0x1, 0), + .yuv2rgb_en = VOP_REG(RK3588_DOLBY_CORE2_CONTROL_REG, 0x1, 1), + .yuv422to444_en = VOP_REG(RK3588_DOLBY_CORE2_CONTROL_REG, 0x1, 2), + .yuv_swap = VOP_REG(RK3568_OVL_CTRL, 0x1, 6), + .yuv422_en = VOP_REG(RK3568_OVL_CTRL, 0x1, 7), + + .lut_update = VOP_REG(RK3568_OVL_CTRL, 0x1, 12), + .lut_mst = VOP_REG(RK3568_HDR_LUT_MST, 0xffffffff, 0), + + .dly_en = VOP_REG(RK3568_OVL_CTRL, 0x1, 13), +}; + +static const struct vop2_dovi_regs rk3588_vop_dovi_core3_regs = { + .enable = VOP_REG(RK3568_OVL_CTRL, 0x1, 10), + .interrupt_raw = VOP_REG(RK3588_DOLBY_CORE3_INTR_RAW_REG, 0xf, 0), + .interrupt_enable = VOP_REG(RK3588_DOLBY_CORE3_INTR_ENABLE_REG, 0xf, 0), + .metadata_program_st = VOP_REG(RK3588_DOLBY_CORE3_METADATA_PROGRAM_ST, 0x1, 0), + .metadata_program_end = VOP_REG(RK3588_DOLBY_CORE3_METADATA_PROGRAM_END, 0x1, 0), + .metadata_copy_finish = VOP_REG(RK3588_DOLBY_CORE3_INTR_RAW_REG, 0x1, 2), + + .output_mode = VOP_REG(RK3588_DOLBY_CORE3_CONTROL_REG, 0xf, 0), +}; + +static const struct vop2_dovi_core_data rk3588_vop_dovi_core_data[3] = { + { + .id = 1, + .ctrl_offset = RK3588_DOLBY_CORE1_CONTROL_REG, + .srange_offset = RK3588_DOLBY_CORE1_SRANGE_REG, + .srange_offset_from_core = 0x18, + .regs = &rk3588_vop_dovi_core1_regs, + }, + + { + .id = 2, + .ctrl_offset = RK3588_DOLBY_CORE2_CONTROL_REG, + .srange_offset = RK3588_DOLBY_CORE2_SRANGE_REG, + .srange_offset_from_core = 0x18, + .regs = &rk3588_vop_dovi_core2_regs, + }, + + { + .id = 3, + .ctrl_offset = RK3588_DOLBY_CORE3_CONTROL_REG, + .srange_offset = RK3588_DOLBY_CORE3_SRANGE_REG, + .srange_offset_from_core = 0x18, + .regs = &rk3588_vop_dovi_core3_regs, + }, +}; + +static const struct vop2_dovi_data rk3588_vop_dovi_data = { + .nr_dovi_cores = ROCKCHIP_MAX_DOVI_CORE, + .dovi_max_delay = { 54, 24 }, + .enhance_layer_phy_id = ROCKCHIP_VOP2_ESMART3, + .dovi_core_data = rk3588_vop_dovi_core_data, +}; + static const struct vop2_dsc_regs rk3588_vop_dsc_8k_regs = { /* DSC SYS CTRL */ .dsc_port_sel = VOP_REG(RK3588_DSC_8K_SYS_CTRL, 0x3, 0), @@ -1863,11 +1949,13 @@ static const struct vop2_video_port_regs rk3588_vop_vp0_regs = { .dclk_core_div = VOP_REG(RK3568_VP0_CLK_CTRL, 0x3, 0), .dclk_out_div = VOP_REG(RK3568_VP0_CLK_CTRL, 0x3, 2), .pre_scan_htiming = VOP_REG(RK3568_VP0_PRE_SCAN_HTIMING, 0x1fff1fff, 0), + .dovi_pre_scan_en = VOP_REG(RK3568_VP0_PRE_SCAN_HTIMING, 0x1, 15), + .pre_scan_htiming1 = VOP_REG(RK3588_VP0_PRE_SCAN_HTIMING1, 0x1fff1fff, 0), + .pre_scan_htiming2 = VOP_REG(RK3588_VP0_PRE_SCAN_HTIMING2, 0x1fff1fff, 0), + .pre_scan_htiming3 = VOP_REG(RK3588_VP0_PRE_SCAN_HTIMING3, 0x1fff1fff, 0), + .dp_line_end_mode = VOP_REG(RK3568_VP0_BG_MIX_CTRL, 0x1, 4), + .dp_bg_bottom_disable = VOP_REG(RK3568_VP0_BG_MIX_CTRL, 0x1, 5), .bg_dly = VOP_REG(RK3568_VP0_BG_MIX_CTRL, 0xff, 24), - .hpost_st_end = VOP_REG(RK3568_VP0_POST_DSP_HACT_INFO, 0x1fff1fff, 0), - .vpost_st_end = VOP_REG(RK3568_VP0_POST_DSP_VACT_INFO, 0x1fff1fff, 0), - .post_scl_factor = VOP_REG(RK3568_VP0_POST_SCL_FACTOR_YRGB, 0xffffffff, 0), - .post_scl_ctrl = VOP_REG(RK3568_VP0_POST_SCL_CTRL, 0x3, 0), .htotal_pw = VOP_REG(RK3568_VP0_DSP_HTOTAL_HS_END, 0xffffffff, 0), .hact_st_end = VOP_REG(RK3568_VP0_DSP_HACT_ST_END, 0xffffffff, 0), .dsp_vtotal = VOP_REG(RK3568_VP0_DSP_VTOTAL_VS_END, 0x1fff, 16), @@ -1965,10 +2053,6 @@ static const struct vop2_video_port_regs rk3588_vop_vp1_regs = { .dclk_out_div = VOP_REG(RK3568_VP1_CLK_CTRL, 0x3, 2), .pre_scan_htiming = VOP_REG(RK3568_VP1_PRE_SCAN_HTIMING, 0x1fff1fff, 0), .bg_dly = VOP_REG(RK3568_VP1_BG_MIX_CTRL, 0xff, 24), - .hpost_st_end = VOP_REG(RK3568_VP1_POST_DSP_HACT_INFO, 0x1fff1fff, 0), - .vpost_st_end = VOP_REG(RK3568_VP1_POST_DSP_VACT_INFO, 0x1fff1fff, 0), - .post_scl_factor = VOP_REG(RK3568_VP1_POST_SCL_FACTOR_YRGB, 0xffffffff, 0), - .post_scl_ctrl = VOP_REG(RK3568_VP1_POST_SCL_CTRL, 0x3, 0), .htotal_pw = VOP_REG(RK3568_VP1_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), .hact_st_end = VOP_REG(RK3568_VP1_DSP_HACT_ST_END, 0x1fff1fff, 0), .dsp_vtotal = VOP_REG(RK3568_VP1_DSP_VTOTAL_VS_END, 0x1fff, 16), @@ -2062,10 +2146,6 @@ static const struct vop2_video_port_regs rk3588_vop_vp2_regs = { .dclk_out_div = VOP_REG(RK3568_VP2_CLK_CTRL, 0x3, 2), .pre_scan_htiming = VOP_REG(RK3568_VP2_PRE_SCAN_HTIMING, 0x1fff1fff, 0), .bg_dly = VOP_REG(RK3568_VP2_BG_MIX_CTRL, 0xff, 24), - .hpost_st_end = VOP_REG(RK3568_VP2_POST_DSP_HACT_INFO, 0x1fff1fff, 0), - .vpost_st_end = VOP_REG(RK3568_VP2_POST_DSP_VACT_INFO, 0x1fff1fff, 0), - .post_scl_factor = VOP_REG(RK3568_VP2_POST_SCL_FACTOR_YRGB, 0xffffffff, 0), - .post_scl_ctrl = VOP_REG(RK3568_VP2_POST_SCL_CTRL, 0x3, 0), .htotal_pw = VOP_REG(RK3568_VP2_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), .hact_st_end = VOP_REG(RK3568_VP2_DSP_HACT_ST_END, 0x1fff1fff, 0), .dsp_vtotal = VOP_REG(RK3568_VP2_DSP_VTOTAL_VS_END, 0x1fff, 16), @@ -2129,10 +2209,6 @@ static const struct vop2_video_port_regs rk3588_vop_vp3_regs = { .dclk_out_div = VOP_REG(RK3568_VP3_CLK_CTRL, 0x3, 2), .pre_scan_htiming = VOP_REG(RK3588_VP3_PRE_SCAN_HTIMING, 0x1fff1fff, 0), .bg_dly = VOP_REG(RK3588_VP3_BG_MIX_CTRL, 0xff, 24), - .hpost_st_end = VOP_REG(RK3588_VP3_POST_DSP_HACT_INFO, 0x1fff1fff, 0), - .vpost_st_end = VOP_REG(RK3588_VP3_POST_DSP_VACT_INFO, 0x1fff1fff, 0), - .post_scl_factor = VOP_REG(RK3588_VP3_POST_SCL_FACTOR_YRGB, 0xffffffff, 0), - .post_scl_ctrl = VOP_REG(RK3588_VP3_POST_SCL_CTRL, 0x3, 0), .htotal_pw = VOP_REG(RK3588_VP3_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), .hact_st_end = VOP_REG(RK3588_VP3_DSP_HACT_ST_END, 0x1fff1fff, 0), .dsp_vtotal = VOP_REG(RK3588_VP3_DSP_VTOTAL_VS_END, 0x1fff, 16), @@ -2174,7 +2250,7 @@ static const struct vop2_video_port_data rk3588_vop_video_ports[] = { .lut_dma_rid = 0xd, .soc_id = { 0x3588, 0x3588 }, .feature = VOP_FEATURE_OUTPUT_10BIT | VOP_FEATURE_ALPHA_SCALE | - VOP_FEATURE_HDR10 | VOP_FEATURE_NEXT_HDR, + VOP_FEATURE_HDR10 | VOP_FEATURE_DOVI, .gamma_lut_len = 1024, .cubic_lut_len = 729, /* 9x9x9 */ .dclk_max = 2400000000, @@ -4203,7 +4279,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { BIT(ROCKCHIP_VOP_VP2) | BIT(ROCKCHIP_VOP_VP3), .max_upscale_factor = 4, .max_downscale_factor = 4, - .dly = { 4, 26, 29 }, + .dly = { 4, 26, 29, 4, 35, 3, 5 }, .type = DRM_PLANE_TYPE_OVERLAY, .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER_MAIN | WIN_FEATURE_SPLICE_LEFT, }, @@ -4257,7 +4333,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { BIT(ROCKCHIP_VOP_VP2) | BIT(ROCKCHIP_VOP_VP3), .max_upscale_factor = 4, .max_downscale_factor = 4, - .dly = { 4, 26, 29 }, + .dly = { 4, 26, 29, 4, 35, 3, 5 }, .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER_MAIN, }, @@ -4311,7 +4387,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { BIT(ROCKCHIP_VOP_VP2) | BIT(ROCKCHIP_VOP_VP3), .max_upscale_factor = 4, .max_downscale_factor = 4, - .dly = { 4, 26, 29 }, + .dly = { 4, 26, 29, 4, 35, 3, 5 }, .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER_MAIN | WIN_FEATURE_SPLICE_LEFT, }, @@ -4364,7 +4440,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { BIT(ROCKCHIP_VOP_VP2) | BIT(ROCKCHIP_VOP_VP3), .max_upscale_factor = 4, .max_downscale_factor = 4, - .dly = { 4, 26, 29 }, + .dly = { 4, 26, 29, 4, 35, 3, 5 }, .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER_MAIN, }, @@ -4418,7 +4494,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { BIT(ROCKCHIP_VOP_VP2) | BIT(ROCKCHIP_VOP_VP3), .max_upscale_factor = 8, .max_downscale_factor = 8, - .dly = { 23, 45, 48 }, + .dly = { 23, 45, 48, 23, 54, 22, 24 }, .feature = WIN_FEATURE_SPLICE_LEFT | WIN_FEATURE_MULTI_AREA, }, @@ -4448,7 +4524,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { BIT(ROCKCHIP_VOP_VP2) | BIT(ROCKCHIP_VOP_VP3), .max_upscale_factor = 8, .max_downscale_factor = 8, - .dly = { 23, 45, 48 }, + .dly = { 23, 45, 48, 23, 54, 22, 24 }, .feature = WIN_FEATURE_SPLICE_LEFT | WIN_FEATURE_MULTI_AREA, }, @@ -4477,7 +4553,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { BIT(ROCKCHIP_VOP_VP2) | BIT(ROCKCHIP_VOP_VP3), .max_upscale_factor = 8, .max_downscale_factor = 8, - .dly = { 23, 45, 48 }, + .dly = { 23, 45, 48, 23, 54, 22, 24 }, .feature = WIN_FEATURE_MULTI_AREA, }, @@ -4506,7 +4582,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { BIT(ROCKCHIP_VOP_VP2) | BIT(ROCKCHIP_VOP_VP3), .max_upscale_factor = 8, .max_downscale_factor = 8, - .dly = { 23, 45, 48 }, + .dly = { 23, 45, 48, 23, 54, 22, 24 }, .feature = WIN_FEATURE_MULTI_AREA, }, }; @@ -4801,6 +4877,9 @@ static const struct vop2_ctrl rk3588_vop_ctrl = { .wb_dma_finish_and_en = VOP_REG(RK3588_SYS_VAR_FREQ_CTRL, 0x1, 3), .ovl_cfg_done_port = VOP_REG(RK3568_OVL_CTRL, 0x3, 30), .ovl_port_mux_cfg_done_imd = VOP_REG(RK3568_OVL_CTRL, 0x1, 28), + .dovi_core3_en = VOP_REG(RK3568_OVL_CTRL, 0x1, 10), + .dovi_core2_en = VOP_REG(RK3568_OVL_CTRL, 0x1, 9), + .dovi_core1_en = VOP_REG(RK3568_OVL_CTRL, 0x1, 8), .ovl_port_mux_cfg = VOP_REG(RK3568_OVL_PORT_SEL, 0xffff, 0), .if_ctrl_cfg_done_imd = VOP_REG(RK3568_DSP_IF_POL, 0x1, 28), .version = VOP_REG(RK3568_VERSION_INFO, 0xffff, 16), @@ -5243,6 +5322,7 @@ static const struct vop2_data rk3588_vop = { .vo1_grf = &rk3588_vo1_grf_ctrl, .axi_intr = rk3568_vop_axi_intr, .nr_axi_intr = ARRAY_SIZE(rk3568_vop_axi_intr), + .dovi = &rk3588_vop_dovi_data, .dsc = rk3588_vop_dsc_data, .dsc_error_ecw = dsc_ecw, .dsc_error_buffer_flow = dsc_buffer_flow, diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h index 287424106ba9..83d3163cea96 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h @@ -1142,6 +1142,9 @@ #define RK3576_VP0_POST_CRC 0xC28 #define RK3568_VP0_DSP_BG 0xC2C #define RK3568_VP0_PRE_SCAN_HTIMING 0xC30 +#define RK3588_VP0_PRE_SCAN_HTIMING1 0xC34 +#define RK3588_VP0_PRE_SCAN_HTIMING2 0xC38 +#define RK3588_VP0_PRE_SCAN_HTIMING3 0xC3c #define RK3568_VP0_POST_DSP_HACT_INFO 0xC34 #define RK3568_VP0_POST_DSP_VACT_INFO 0xC38 #define RK3568_VP0_POST_SCL_FACTOR_YRGB 0xC3C @@ -1326,6 +1329,7 @@ #define RK3568_OVL_CTRL 0x600 #define RK3568_OVL_LAYER_SEL 0x604 #define RK3568_OVL_PORT_SEL 0x608 +#define RK3588_DOLBY_LUT_MST 0x60c #define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL 0x610 #define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL 0x614 #define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL 0x618 @@ -1781,6 +1785,28 @@ #define RK3568_HDR_OETF_DX_POW1 0x2200 #define RK3568_HDR_OETF_XN1 0x2300 +/* DOLBY register definition */ +#define RK3588_DOLBY_CORE1_CONTROL_REG 0x3004 +#define RK3588_DOLBY_CORE1_METADATA_PROGRAM_ST 0x3008 +#define RK3588_DOLBY_CORE1_METADATA_PROGRAM_END 0x300C +#define RK3588_DOLBY_CORE1_INTR_RAW_REG 0x3010 +#define RK3588_DOLBY_CORE1_INTR_ENABLE_REG 0x3014 +#define RK3588_DOLBY_CORE1_SRANGE_REG 0x3018 + +#define RK3588_DOLBY_CORE2_CONTROL_REG 0x3404 +#define RK3588_DOLBY_CORE2_METADATA_PROGRAM_ST 0x3408 +#define RK3588_DOLBY_CORE2_METADATA_PROGRAM_END 0x340C +#define RK3588_DOLBY_CORE2_INTR_RAW_REG 0x3410 +#define RK3588_DOLBY_CORE2_INTR_ENABLE_REG 0x3414 +#define RK3588_DOLBY_CORE2_SRANGE_REG 0x3418 + +#define RK3588_DOLBY_CORE3_CONTROL_REG 0x3504 +#define RK3588_DOLBY_CORE3_METADATA_PROGRAM_ST 0x3508 +#define RK3588_DOLBY_CORE3_METADATA_PROGRAM_END 0x350C +#define RK3588_DOLBY_CORE3_INTR_RAW_REG 0x3510 +#define RK3588_DOLBY_CORE3_INTR_ENABLE_REG 0x3514 +#define RK3588_DOLBY_CORE3_SRANGE_REG 0x3518 + /* DSC register definition */ #define RK3588_DSC_8K_PPS0_3 0x4000 #define RK3588_DSC_8K_CTRL0 0x40A0 diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h index 553b5af92420..200584764a65 100644 --- a/include/uapi/drm/rockchip_drm.h +++ b/include/uapi/drm/rockchip_drm.h @@ -90,7 +90,6 @@ enum drm_rockchip_gem_cpu_acquire_type { enum rockchip_crtc_feture { ROCKCHIP_DRM_CRTC_FEATURE_ALPHA_SCALE, ROCKCHIP_DRM_CRTC_FEATURE_HDR10, - ROCKCHIP_DRM_CRTC_FEATURE_NEXT_HDR, ROCKCHIP_DRM_CRTC_FEATURE_DOVI, ROCKCHIP_DRM_CRTC_FEATURE_VIVID_HDR, };