From d0e8f558bc58984f8f4508133143cc9ca77c8b0d Mon Sep 17 00:00:00 2001 From: Algea Cao Date: Mon, 24 Feb 2025 17:09:02 +0800 Subject: [PATCH] drm/rockchip: vop2: Support hdmi qms-vrr Change-Id: Icc2931661b50a3a8168a98b983a676a74a9ba9da Signed-off-by: Algea Cao --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 553 +++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 54 ++ drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 131 ++++- 3 files changed, 721 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index e75d5bbaa88d..8ca0dbd9d15d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -78,6 +78,502 @@ static const struct drm_driver rockchip_drm_driver; static unsigned int drm_debug; module_param_named(debug, drm_debug, int, 0600); +static const u16 tfr_vrefresh_table[TFR_MAX] = { + [TFR_QMSVRR_INACTIVE] = 0, + [TFR_23P97] = 2397, + [TFR_24] = 2400, + [TFR_25] = 2500, + [TFR_29P97] = 2997, + [TFR_30] = 3000, + [TFR_47P95] = 4795, + [TFR_48] = 4800, + [TFR_50] = 5000, + [TFR_59P94] = 5994, + [TFR_60] = 6000, + [TFR_100] = 10000, + [TFR_119P88] = 11988, + [TFR_120] = 12000, +}; + +/* BRR 720p60hz */ +static const struct mvrr_const_val const_hdmi720p60_6000 = { + .vrefresh_khz = 6000, + .vtotal_fixed = 750, +}; + +/** + * @vrefresh_khz: qms-vrr target refresh rate is 59.94Hz + * @vtotal_fixed: When switch to target refresh rate, vtotal is 750 + * @bit_len: frac_array's bit length + * @frac_array: Sources may also alternate between two sequential values + * of actual vtotal to better approximate the target refresh + * rate when target vtotal is fractional. For this example, + * the source vtotal would vary between 750 and 751. + * The value in frac_array indicates the order in which the + * vtotal changes during this process. Each bit of 0 indicates + * that the current frame vtotal is 750, and each bit of 1 + * indicates that the current frame vtotal is 751. + * Take 0x3f(00111111B) as an example, it represents a vtotal + * of 750 for the first and second frames, and 751 for the + * remaining six frames. 0xe3 and 0xfe also have the same meaning. + * The current frac_array represents the value of vtotal for 24 + * consecutive frames. + */ +static const struct mvrr_const_val const_hdmi720p60_5994 = { + .vrefresh_khz = 5994, + .vtotal_fixed = 750, /* 0.75 */ + .bit_len = 24, + .frac_array = {0x3f, 0xe3, 0xfe}, +}; + +static const struct mvrr_const_val const_hdmi720p60_5000 = { + .vrefresh_khz = 5000, + .vtotal_fixed = 900, +}; + +static const struct mvrr_const_val const_hdmi720p60_4800 = { + .vrefresh_khz = 4800, + .vtotal_fixed = 937, /* 0.5 */ + .bit_len = 8, + .frac_array = {0x3c}, +}; + +static const struct mvrr_const_val const_hdmi720p60_4795 = { + .vrefresh_khz = 4795, + .vtotal_fixed = 938, /* 0.4375 */ + .bit_len = 16, + .frac_array = {0x1e, 0x0e}, +}; + +static const struct mvrr_const_val const_hdmi720p60_3000 = { + .vrefresh_khz = 3000, + .vtotal_fixed = 1500, +}; + +static const struct mvrr_const_val const_hdmi720p60_2997 = { + .vrefresh_khz = 2997, + .vtotal_fixed = 1501, /* 0.5 */ + .bit_len = 56, + .frac_array = {0x1f, 0xe0, 0x3f, 0x80, 0xfe, 0x03, 0xf8}, +}; + +static const struct mvrr_const_val const_hdmi720p60_2500 = { + .vrefresh_khz = 2500, + .vtotal_fixed = 1800, +}; + +static const struct mvrr_const_val const_hdmi720p60_2400 = { + .vrefresh_khz = 2400, + .vtotal_fixed = 1875, +}; + +static const struct mvrr_const_val const_hdmi720p60_2397 = { + .vrefresh_khz = 2397, + .vtotal_fixed = 1876, /* 0.875 */ + .bit_len = 40, + .frac_array = {0x1f, 0xff, 0xff, 0xff, 0xfc}, +}; + +/* BRR 720p120hz */ +static const struct mvrr_const_val const_hdmi720p120_12000 = { + .vrefresh_khz = 12000, + .vtotal_fixed = 750, +}; + +static const struct mvrr_const_val const_hdmi720p120_11988 = { + .vrefresh_khz = 11988, + .vtotal_fixed = 750, /* 0.75 */ + .bit_len = 24, + .frac_array = {0x3f, 0xe3, 0xfe}, +}; + +static const struct mvrr_const_val const_hdmi720p120_10000 = { + .vrefresh_khz = 10000, + .vtotal_fixed = 900, +}; + +static const struct mvrr_const_val const_hdmi720p120_6000 = { + .vrefresh_khz = 6000, + .vtotal_fixed = 1500, +}; + +static const struct mvrr_const_val const_hdmi720p120_5994 = { + .vrefresh_khz = 5994, + .vtotal_fixed = 1501, /* 0.5 */ + .bit_len = 56, + .frac_array = {0x0f, 0xe0, 0x3f, 0x80, 0xfe, 0x03, 0xf8}, +}; + +static const struct mvrr_const_val const_hdmi720p120_5000 = { + .vrefresh_khz = 5000, + .vtotal_fixed = 1800, +}; + +static const struct mvrr_const_val const_hdmi720p120_4800 = { + .vrefresh_khz = 4800, + .vtotal_fixed = 1875, +}; + +static const struct mvrr_const_val const_hdmi720p120_4795 = { + .vrefresh_khz = 4795, + .vtotal_fixed = 1876, /* 0.875 */ + .bit_len = 40, + .frac_array = {0x1f, 0xff, 0xff, 0xff, 0xfc}, +}; + +static const struct mvrr_const_val const_hdmi720p120_3000 = { + .vrefresh_khz = 3000, + .vtotal_fixed = 3000, +}; + +static const struct mvrr_const_val const_hdmi720p120_2997 = { + .vrefresh_khz = 2997, + .vtotal_fixed = 3003, +}; + +static const struct mvrr_const_val const_hdmi720p120_2500 = { + .vrefresh_khz = 2500, + .vtotal_fixed = 3600, +}; + +static const struct mvrr_const_val const_hdmi720p120_2400 = { + .vrefresh_khz = 2400, + .vtotal_fixed = 3750, +}; + +static const struct mvrr_const_val const_hdmi720p120_2397 = { + .vrefresh_khz = 2397, + .vtotal_fixed = 3753, /* 0.75 */ + .bit_len = 88, + .frac_array = { + 0x03, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xe0 + }, +}; + +/* BRR 1080p60hz */ +static const struct mvrr_const_val const_hdmi1080p60_6000 = { + .vrefresh_khz = 6000, + .vtotal_fixed = 1125, +}; + +static const struct mvrr_const_val const_hdmi1080p60_5994 = { + .vrefresh_khz = 5994, + .vtotal_fixed = 1126, /* 0.125 */ + .bit_len = 24, + .frac_array = {0x00, 0x38, 0x00}, +}; + +static const struct mvrr_const_val const_hdmi1080p60_5000 = { + .vrefresh_khz = 5000, + .vtotal_fixed = 1350, +}; + +static const struct mvrr_const_val const_hdmi1080p60_4800 = { + .vrefresh_khz = 4800, + .vtotal_fixed = 1406, /* 0.25 */ + .bit_len = 16, + .frac_array = {0x03, 0xc0}, +}; + +static const struct mvrr_const_val const_hdmi1080p60_4795 = { + .vrefresh_khz = 4795, + .vtotal_fixed = 1407, /* 0.65625 */ + .bit_len = 32, + .frac_array = {0x1f, 0xf1, 0x7f, 0xf0}, +}; + +static const struct mvrr_const_val const_hdmi1080p60_3000 = { + .vrefresh_khz = 3000, + .vtotal_fixed = 2250, +}; + +static const struct mvrr_const_val const_hdmi1080p60_2997 = { + .vrefresh_khz = 2997, + .vtotal_fixed = 2252, /* 0.25 */ + .bit_len = 56, + .frac_array = {0x00, 0x3f, 0x80, 0x00, 0x03, 0xf8, 0x00}, +}; + +static const struct mvrr_const_val const_hdmi1080p60_2500 = { + .vrefresh_khz = 2500, + .vtotal_fixed = 2700, +}; + +static const struct mvrr_const_val const_hdmi1080p60_2400 = { + .vrefresh_khz = 2400, + .vtotal_fixed = 2812, /* 0.5 */ + .bit_len = 24, + .frac_array = {0x03, 0xff, 0xc0}, +}; + +static const struct mvrr_const_val const_hdmi1080p60_2397 = { + .vrefresh_khz = 2397, + .vtotal_fixed = 2815, /* 0.3125 */ + .bit_len = 128, + .frac_array = { + 0x00, 0x7f, 0x80, 0x00, 0x3f, 0xc0, 0x00, 0x0f, + 0xf0, 0x00, 0x07, 0xf8, 0x00, 0x01, 0xfe, 0x00, + }, +}; + +/* BRR 1080p120hz */ +static const struct mvrr_const_val const_hdmi1080p120_12000 = { + .vrefresh_khz = 12000, + .vtotal_fixed = 1125, +}; + +static const struct mvrr_const_val const_hdmi1080p120_11988 = { + .vrefresh_khz = 11988, + .vtotal_fixed = 1126, /* 0.125 */ + .bit_len = 24, + .frac_array = {0x00, 0x38, 0x00}, +}; + +static const struct mvrr_const_val const_hdmi1080p120_10000 = { + .vrefresh_khz = 10000, + .vtotal_fixed = 1350, +}; + +static const struct mvrr_const_val const_hdmi1080p120_6000 = { + .vrefresh_khz = 6000, + .vtotal_fixed = 2250, +}; + +static const struct mvrr_const_val const_hdmi1080p120_5994 = { + .vrefresh_khz = 5994, + .vtotal_fixed = 2252, /* 0.25 */ + .bit_len = 56, + .frac_array = {0x00, 0x3f, 0x80, 0x00, 0x03, 0xf8, 0x00}, +}; + +static const struct mvrr_const_val const_hdmi1080p120_5000 = { + .vrefresh_khz = 5000, + .vtotal_fixed = 2700, +}; + +static const struct mvrr_const_val const_hdmi1080p120_4800 = { + .vrefresh_khz = 4800, + .vtotal_fixed = 2812, /* 0.5 */ + .bit_len = 24, + .frac_array = {0x03, 0xff, 0xc0}, +}; + +static const struct mvrr_const_val const_hdmi1080p120_4795 = { + .vrefresh_khz = 4795, + .vtotal_fixed = 2812, /* 0.3125 */ + .bit_len = 48, + .frac_array = {0x00, 0x3f, 0x80, 0x00, 0x3f, 0xc0}, +}; + +static const struct mvrr_const_val const_hdmi1080p120_3000 = { + .vrefresh_khz = 3000, + .vtotal_fixed = 4500, +}; + +static const struct mvrr_const_val const_hdmi1080p120_2997 = { + .vrefresh_khz = 2997, + .vtotal_fixed = 4504, /* 0.5 */ + .bit_len = 32, + .frac_array = {0x00, 0xff, 0xff, 0x00}, +}; + +static const struct mvrr_const_val const_hdmi1080p120_2500 = { + .vrefresh_khz = 2500, + .vtotal_fixed = 5400, +}; + +static const struct mvrr_const_val const_hdmi1080p120_2400 = { + .vrefresh_khz = 2400, + .vtotal_fixed = 5625, +}; + +static const struct mvrr_const_val const_hdmi1080p120_2397 = { + .vrefresh_khz = 2397, + .vtotal_fixed = 5630, /* 0.625 */ + .bit_len = 48, + .frac_array = {0x00, 0x7f, 0xff, 0xff, 0xfe, 0x00}, +}; + +/* BRR 2160p60hz */ +static const struct mvrr_const_val const_hdmi2160p60_6000 = { + .vrefresh_khz = 6000, + .vtotal_fixed = 2250, +}; + +static const struct mvrr_const_val const_hdmi2160p60_5994 = { + .vrefresh_khz = 5994, + .vtotal_fixed = 2252, /* 0.25 */ + .bit_len = 56, + .frac_array = {0x00, 0x3f, 0x80, 0x00, 0x03, 0xf8, 0x00}, +}; + +static const struct mvrr_const_val const_hdmi2160p60_5000 = { + .vrefresh_khz = 5000, + .vtotal_fixed = 2700, +}; + +static const struct mvrr_const_val const_hdmi2160p60_4800 = { + .vrefresh_khz = 4800, + .vtotal_fixed = 2812, /* 0.5 */ + .bit_len = 24, + .frac_array = {0x03, 0xff, 0xc0}, +}; + +static const struct mvrr_const_val const_hdmi2160p60_4795 = { + .vrefresh_khz = 4795, + .vtotal_fixed = 2815, /* 0.3125 */ + .bit_len = 48, + .frac_array = {0x00, 0x3f, 0x80, 0x00, 0x3f, 0xc0}, +}; + +static const struct mvrr_const_val const_hdmi2160p60_3000 = { + .vrefresh_khz = 3000, + .vtotal_fixed = 4500, +}; + +static const struct mvrr_const_val const_hdmi2160p60_2997 = { + .vrefresh_khz = 2997, + .vtotal_fixed = 4504, /* 0.5 */ + .bit_len = 32, + .frac_array = {0x00, 0xff, 0xff, 0x00}, +}; + +static const struct mvrr_const_val const_hdmi2160p60_2500 = { + .vrefresh_khz = 2500, + .vtotal_fixed = 5400, +}; + +static const struct mvrr_const_val const_hdmi2160p60_2400 = { + .vrefresh_khz = 2400, + .vtotal_fixed = 5625, +}; + +static const struct mvrr_const_val const_hdmi2160p60_2397 = { + .vrefresh_khz = 2397, + .vtotal_fixed = 5630, /* 0.625 */ + .bit_len = 48, + .frac_array = {0x00, 0x3f, 0xff, 0xff, 0xff, 0x00}, +}; + +const struct mvrr_const_st const_hdmi1080p60_val = { + .brr_vic = HDMI_16_1920x1080P60_16x9, + .val = { + &const_hdmi1080p60_6000, + &const_hdmi1080p60_5994, + &const_hdmi1080p60_5000, + &const_hdmi1080p60_4800, + &const_hdmi1080p60_4795, + &const_hdmi1080p60_3000, + &const_hdmi1080p60_2997, + &const_hdmi1080p60_2500, + &const_hdmi1080p60_2400, + &const_hdmi1080p60_2397, + NULL, + }, +}; + +const struct mvrr_const_st const_hdmi1080p120_val = { + .brr_vic = HDMI_63_1920x1080P120_16x9, + .val = { + &const_hdmi1080p120_12000, + &const_hdmi1080p120_11988, + &const_hdmi1080p120_10000, + &const_hdmi1080p120_6000, + &const_hdmi1080p120_5994, + &const_hdmi1080p120_5000, + &const_hdmi1080p120_4800, + &const_hdmi1080p120_4795, + &const_hdmi1080p120_3000, + &const_hdmi1080p120_2997, + &const_hdmi1080p120_2500, + &const_hdmi1080p120_2400, + &const_hdmi1080p120_2397, + NULL, + }, +}; + +const struct mvrr_const_st const_hdmi720p60_val = { + .brr_vic = HDMI_4_1280x720P60_16x9, + .val = { + &const_hdmi720p60_6000, + &const_hdmi720p60_5994, + &const_hdmi720p60_5000, + &const_hdmi720p60_4800, + &const_hdmi720p60_4795, + &const_hdmi720p60_3000, + &const_hdmi720p60_2997, + &const_hdmi720p60_2500, + &const_hdmi720p60_2400, + &const_hdmi720p60_2397, + NULL, + }, +}; + +const struct mvrr_const_st const_hdmi720p120_val = { + .brr_vic = HDMI_47_1280x720P120_16x9, + .val = { + &const_hdmi720p120_12000, + &const_hdmi720p120_11988, + &const_hdmi720p120_10000, + &const_hdmi720p120_6000, + &const_hdmi720p120_5994, + &const_hdmi720p120_5000, + &const_hdmi720p120_4800, + &const_hdmi720p120_4795, + &const_hdmi720p120_3000, + &const_hdmi720p120_2997, + &const_hdmi720p120_2500, + &const_hdmi720p120_2400, + &const_hdmi720p120_2397, + NULL, + }, +}; + +const struct mvrr_const_st const_hdmi2160p60_val = { + .brr_vic = HDMI_97_3840x2160P60_16x9, + .val = { + &const_hdmi2160p60_6000, + &const_hdmi2160p60_5994, + &const_hdmi2160p60_5000, + &const_hdmi2160p60_4800, + &const_hdmi2160p60_4795, + &const_hdmi2160p60_3000, + &const_hdmi2160p60_2997, + &const_hdmi2160p60_2500, + &const_hdmi2160p60_2400, + &const_hdmi2160p60_2397, + NULL, + }, +}; + +/* The vtotal parameters of 4096x2160p60hz are the same as 3840x2160p60hz */ +const struct mvrr_const_st const_hdmismpte60_val = { + .brr_vic = HDMI_102_4096x2160P60_256x135, + .val = { + &const_hdmi2160p60_6000, + &const_hdmi2160p60_5994, + &const_hdmi2160p60_5000, + &const_hdmi2160p60_4800, + &const_hdmi2160p60_4795, + &const_hdmi2160p60_3000, + &const_hdmi2160p60_2997, + &const_hdmi2160p60_2500, + &const_hdmi2160p60_2400, + &const_hdmi2160p60_2397, + NULL, + }, +}; + +const struct mvrr_const_st *qms_const[] = { + &const_hdmi1080p60_val, + &const_hdmi1080p120_val, + &const_hdmi2160p60_val, + &const_hdmi720p60_val, + &const_hdmi720p120_val, + &const_hdmismpte60_val, + NULL, +}; + static inline bool rockchip_drm_debug_enabled(enum rockchip_drm_debug_category category) { return unlikely(drm_debug & category); @@ -1259,6 +1755,63 @@ void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc) priv->crtc_funcs[pipe] = NULL; } +u16 rockchip_hdmi_vrr_tfr_match_to_vrefresh(u8 tfr) +{ + if (tfr < 0 || tfr >= TFR_MAX) { + DRM_ERROR("qms-vrr tfr is out of range\n"); + return 0; + } + + return tfr_vrefresh_table[tfr]; +} + +const struct +mvrr_const_val *rockchip_hdmi_vrr_get_vrrconf_mconst(enum hdmi_brr_vic brr_vic, u16 vrefresh_khz) +{ + const struct mvrr_const_st **table_vic = NULL; + const struct mvrr_const_val *const *table_val = NULL; + + for (table_vic = qms_const; table_vic; table_vic++) { + if ((*table_vic)->brr_vic == brr_vic) { + table_val = (*table_vic)->val; + for (; table_val; table_val++) { + if ((*table_val)->vrefresh_khz == vrefresh_khz) + break; + } + break; + } + } + + if (!table_val) { + DRM_ERROR("%s[%d] not find brr_vic: %d vrefresh_khz: %d\n", + __func__, __LINE__, brr_vic, vrefresh_khz); + return NULL; + } + + return *table_val; +} + +u16 rockchip_hdmi_vrr_calc_new_vtotal(const struct mvrr_const_val *mvrr, u32 frame_cnt) +{ + u32 pos; + u16 vtotal = 0; + + if (!mvrr) + return vtotal; + + vtotal = mvrr->vtotal_fixed; + /* if bit_len is 0, then means there is no fraction */ + if (!mvrr->bit_len) + return vtotal; + + /* calculate the fraction number */ + pos = frame_cnt % mvrr->bit_len; + if (mvrr->frac_array[pos / 8] & (1 << (7 - (pos % 8)))) + vtotal++; + + return vtotal; +} + /* * a high frequency of page faults will follow up, if * there is a iommu fault, so it's better to limit the diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 30082987aac5..ddc85452c002 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -266,6 +266,14 @@ struct post_sharp { u32 regs[SHARP_REG_LENGTH / 4]; }; +struct rockchip_hdmi_vrr_state { + bool refresh_rate_ready_to_change; + bool m_const; + u8 next_tfr_val; + unsigned int vrr_frame_cnt; + const struct mvrr_const_val *mconst_val; +}; + struct rockchip_crtc_state { struct drm_crtc_state base; int vp_id; @@ -344,6 +352,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 rockchip_hdmi_vrr_state hdmi_vrr; struct drm_property_blob *hdr_ext_data; struct drm_property_blob *acm_lut_data; struct drm_property_blob *post_csc_data; @@ -494,6 +503,47 @@ struct next_hdr_sink_data { struct ver_12_v2 ver_12_v2; } __packed; +/* refer to HDMI2.1B P453 */ +enum TARGET_FRAME_RATE { + TFR_QMSVRR_INACTIVE = 0, + TFR_23P97, + TFR_24, + TFR_25, + TFR_29P97, + TFR_30, + TFR_47P95, + TFR_48, + TFR_50, + TFR_59P94, + TFR_60, + TFR_100, + TFR_119P88, + TFR_120, + TFR_MAX, +}; + +enum hdmi_brr_vic { + HDMI_16_1920x1080P60_16x9 = 16, + HDMI_63_1920x1080P120_16x9 = 63, + HDMI_4_1280x720P60_16x9 = 4, + HDMI_47_1280x720P120_16x9 = 47, + HDMI_97_3840x2160P60_16x9 = 97, + HDMI_102_4096x2160P60_256x135 = 102, +}; + +struct mvrr_const_val { + /* unit: 100 6000, 5994, 5000, 3000, 2997, 2500, 2400, 2397 */ + u16 vrefresh_khz; + u16 vtotal_fixed; /* vtotal_fixed is mutex with bit_len */ + u8 bit_len; /* current max value is 16 * 8, 128 */ + u8 frac_array[16]; +}; + +struct mvrr_const_st { + enum hdmi_brr_vic brr_vic; /* the vic of brr */ + const struct mvrr_const_val *val[]; +}; + /* * Rockchip drm private crtc funcs. * @loader_protect: protect loader logo crtc's power @@ -721,6 +771,10 @@ __printf(3, 4) void rockchip_drm_dbg_thread_info(const struct device *dev, enum rockchip_drm_debug_category category, const char *format, ...); +u16 rockchip_hdmi_vrr_tfr_match_to_vrefresh(u8 tfr); +const struct +mvrr_const_val *rockchip_hdmi_vrr_get_vrrconf_mconst(enum hdmi_brr_vic brr_vic, u16 vrefresh_khz); +u16 rockchip_hdmi_vrr_calc_new_vtotal(const struct mvrr_const_val *mvrr, u32 frame_cnt); extern struct platform_driver cdn_dp_driver; extern struct platform_driver dw_hdmi_rockchip_pltfm_driver; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 11c9fbac60f7..e6a26e75deb8 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -5955,6 +5955,9 @@ static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, vcstate->splice_mode = false; vcstate->output_flags = 0; vcstate->output_type = 0; + vcstate->hdmi_vrr.m_const = 0; + vcstate->hdmi_vrr.next_tfr_val = 0; + vcstate->hdmi_vrr.refresh_rate_ready_to_change = false; vp->splice_mode_right = false; vp->loader_protect = false; vp->enabled_win_mask = 0; @@ -10682,10 +10685,15 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_sta VOP_MODULE_SET(vop2, vp, dsp_vtotal, vtotal); VOP_MODULE_SET(vop2, vp, dsp_vs_end, vsync_len); - /** - * when display interface support vrr, config vtotal valid immediately + /* + * when display interface support vrr, config vtotal + * valid immediately except HDMI QMS-VRR. HDMI QMS-VRR + * requires cfg done to accurately handle the vrr process. + * Fixme: HDMI GAMING-VRR needs to config vtotal valid immediately, + * the next version will be implemented. */ - if (vcstate->max_refresh_rate && vcstate->min_refresh_rate) + if (vcstate->max_refresh_rate && vcstate->min_refresh_rate && + !output_if_is_hdmi(vcstate->output_if)) VOP_MODULE_SET(vop2, vp, sw_dsp_vtotal_imd, 1); snprintf(clk_name, sizeof(clk_name), "dclk_out%d", vp->id); @@ -12502,22 +12510,13 @@ static void vop2_crtc_hfp_seamless_switch(struct drm_crtc *crtc) VOP_MODULE_SET(vop2, vp, htotal_pw, (new_htotal << 16) | hsync_len); } -static void vop2_crtc_vfp_seamless_switch(struct drm_crtc *crtc) +static void +vop2_crtc_update_vrr_timing(struct drm_crtc *crtc, unsigned int new_vtotal, unsigned int new_vfp) { struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); struct vop2_video_port *vp = to_vop2_video_port(crtc); struct vop2 *vop2 = vp->vop2; struct drm_display_mode *adjust_mode = &crtc->state->adjusted_mode; - unsigned int vrefresh; - unsigned int new_vtotal, vfp, new_vfp; - - DRM_DEV_INFO(vop2->dev, "change refresh rate by changing vfp\n"); - vrefresh = drm_mode_vrefresh(adjust_mode); - - /* calculate new vfp for new refresh rate */ - new_vtotal = adjust_mode->vtotal * vrefresh / vcstate->request_refresh_rate; - vfp = adjust_mode->vsync_start - adjust_mode->vdisplay; - new_vfp = vfp + new_vtotal - adjust_mode->vtotal; /* config vop2 vtotal register */ VOP_MODULE_SET(vop2, vp, dsp_vtotal, new_vtotal); @@ -12539,13 +12538,81 @@ static void vop2_crtc_vfp_seamless_switch(struct drm_crtc *crtc) rockchip_connector_update_vfp_for_vrr(crtc, adjust_mode, new_vfp); } +static void vop2_crtc_vfp_seamless_switch(struct drm_crtc *crtc) +{ + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + struct drm_display_mode *adjust_mode = &crtc->state->adjusted_mode; + struct rockchip_hdmi_vrr_state *hdmi_vrr = &vcstate->hdmi_vrr; + unsigned int vrefresh, vrefresh_khz; + unsigned int new_vtotal, vfp, new_vfp; + u8 brr_vic; + + DRM_DEV_DEBUG(vop2->dev, "change refresh rate by changing vfp\n"); + /* + * If next_tfr_val is no zero, current mode is hdmi qms-vrr. + * If next_tfr_val is 0, it indicates that current mode is hdmi + * gaming-vrr/fva, or eDP/DSI vrr. + */ + if (hdmi_vrr->next_tfr_val) { + brr_vic = drm_match_cea_mode(adjust_mode); + if (!brr_vic) { + DRM_ERROR("qms vrr can't support resolution:\n"); + DRM_ERROR(DRM_MODE_FMT "\n", DRM_MODE_ARG(adjust_mode)); + return; + } + + vrefresh_khz = rockchip_hdmi_vrr_tfr_match_to_vrefresh(hdmi_vrr->next_tfr_val); + if (!vrefresh_khz) { + DRM_ERROR("qms vrr unsupported tfr:%d\n", hdmi_vrr->next_tfr_val); + return; + } + + hdmi_vrr->mconst_val = rockchip_hdmi_vrr_get_vrrconf_mconst(brr_vic, vrefresh_khz); + if (!hdmi_vrr->mconst_val) { + DRM_ERROR("qms vrr can't find mconst_val\n"); + return; + } + + hdmi_vrr->vrr_frame_cnt = 0; + new_vtotal = rockchip_hdmi_vrr_calc_new_vtotal(hdmi_vrr->mconst_val, + hdmi_vrr->vrr_frame_cnt); + if (!new_vtotal) { + DRM_ERROR("qms vrr invalid vtotal\n"); + return; + } + hdmi_vrr->vrr_frame_cnt++; + vfp = adjust_mode->vsync_start - adjust_mode->vdisplay; + new_vfp = vfp + new_vtotal - adjust_mode->vtotal; + hdmi_vrr->refresh_rate_ready_to_change = false; + } else { + vrefresh = drm_mode_vrefresh(adjust_mode); + + /* calculate new vfp for new refresh rate */ + new_vtotal = adjust_mode->vtotal * vrefresh / vcstate->request_refresh_rate; + vfp = adjust_mode->vsync_start - adjust_mode->vdisplay; + new_vfp = vfp + new_vtotal - adjust_mode->vtotal; + } + vop2_crtc_update_vrr_timing(crtc, new_vtotal, new_vfp); +} + static void vop2_crtc_update_vrr(struct drm_crtc *crtc) { struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); struct vop2_video_port *vp = to_vop2_video_port(crtc); - if (!vp->refresh_rate_change) - return; + /* + * In hdmi qms vrr scenarios, it is possible to switch + * timing several frames after refresh rate is configured + */ + if (output_if_is_hdmi(vcstate->output_if)) { + if (!vcstate->hdmi_vrr.refresh_rate_ready_to_change) + return; + } else { + if (!vp->refresh_rate_change) + return; + } if (!vcstate->min_refresh_rate || !vcstate->max_refresh_rate) return; @@ -12722,7 +12789,7 @@ static void vop2_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_stat vop2_wait_for_scan_timing_max_to_assigned_line(vp, current_line, assigned_line); } - if (vop2->version == VOP_VERSION_RK3588) + if (vop2->version == VOP_VERSION_RK3588 || vop2->version == VOP_VERSION_RK3576) vop2_crtc_update_vrr(crtc); /* Process cluster sub windows overlay. */ @@ -14249,6 +14316,34 @@ static void vop2_dsc_isr(struct vop2 *vop2) } } +static void vop2_frac_mvrr_update(struct drm_crtc *crtc, struct vop2_video_port *vp) +{ + struct rockchip_crtc_state *vcstate; + struct drm_display_mode *adjust_mode; + unsigned int new_vtotal, vfp, new_vfp; + + vcstate = to_rockchip_crtc_state(crtc->state); + /* + * When hdmi qms-vrr is enabled and M_VRR(The difference between + * current refresh rate vfp and the vfp of the base fresh rate) + * is fractional. Sources should alternate between two sequential + * values of M_VRR to better approximate the fractional M_VRR. + */ + if (vcstate->hdmi_vrr.next_tfr_val && vcstate->hdmi_vrr.m_const) { + adjust_mode = &crtc->state->adjusted_mode; + new_vtotal = rockchip_hdmi_vrr_calc_new_vtotal(vcstate->hdmi_vrr.mconst_val, + vcstate->hdmi_vrr.vrr_frame_cnt); + if (!new_vtotal) { + DRM_ERROR("qms vrr invalid vtotal\n"); + } else { + vfp = adjust_mode->vsync_start - adjust_mode->vdisplay; + new_vfp = vfp + new_vtotal - adjust_mode->vtotal; + vop2_crtc_update_vrr_timing(crtc, new_vtotal, new_vfp); + vcstate->hdmi_vrr.vrr_frame_cnt++; + } + } +} + static irqreturn_t vop2_isr(int irq, void *data) { struct vop2 *vop2 = data; @@ -14340,6 +14435,7 @@ static irqreturn_t vop2_isr(int irq, void *data) drm_crtc_handle_vblank(crtc); vop2_handle_vblank(vop2, crtc); } + vop2_frac_mvrr_update(crtc, vp); active_irqs &= ~FS_FIELD_INTR; ret = IRQ_HANDLED; } @@ -14544,6 +14640,7 @@ static irqreturn_t vop3_vp_isr(int irq, void *data) rockchip_drm_dbg(vop2->dev, VOP_DEBUG_VSYNC, "vsync_vp%d", vp->id); drm_crtc_handle_vblank(crtc); vop2_handle_vblank(vop2, crtc); + vop2_frac_mvrr_update(crtc, vp); active_irqs &= ~FS_FIELD_INTR; ret = IRQ_HANDLED; }