drm/rockchip: vop2: support vrr by changing dclk and hfp

Change-Id: Ib4237693add871c9fe9f5a848cffcfaee242b69e
Signed-off-by: Zhang Yubing <yubing.zhang@rock-chips.com>
This commit is contained in:
Zhang Yubing
2024-01-08 20:06:22 +08:00
parent 62646c7ab1
commit 8ebfee0d1b
4 changed files with 162 additions and 10 deletions

View File

@@ -134,6 +134,12 @@ enum rockchip_drm_vop_aclk_mode {
ROCKCHIP_VOP_ACLK_MAX_MODE,
};
enum rockchip_drm_vrr_type {
ROCKCHIP_VRR_VFP_MODE = 0,
ROCKCHIP_VRR_HFP_MODE = 1,
ROCKCHIP_VRR_DCLK_MODE = 2,
};
struct rockchip_drm_sub_dev {
struct list_head list;
struct drm_connector *connector;
@@ -347,6 +353,7 @@ struct rockchip_crtc_state {
int min_refresh_rate;
int shift_x;
int shift_y;
int vrr_type;
};
#define to_rockchip_crtc_state(s) \

View File

@@ -1315,6 +1315,7 @@ struct vop2_video_port_data {
char id;
uint8_t splice_vp_id;
uint16_t lut_dma_rid;
uint32_t dclk_switch_id;
uint32_t feature;
uint64_t soc_id[VOP2_SOC_VARIANT];
uint16_t gamma_lut_len;

View File

@@ -582,6 +582,7 @@ struct vop2_video_port {
struct vop2 *vop2;
struct reset_control *dclk_rst;
struct clk *dclk;
struct clk *dclk_switch;
struct clk *dclk_parent;
uint8_t id;
bool layer_sel_update;
@@ -631,6 +632,12 @@ struct vop2_video_port {
*/
bool has_dci_enabled_win;
/**
* @dclk_switch_enabled: a new refresh rate is config by userspace and the
* vrr type is switch dclk.
*/
bool dclk_switch_enabled;
/**
* @bg_ovl_dly: The timing delay from background layer
* to overlay module.
@@ -5179,6 +5186,10 @@ static void vop2_disable(struct drm_crtc *crtc)
struct vop2 *vop2 = vp->vop2;
clk_disable_unprepare(vp->dclk);
if (vp->dclk_switch_enabled) {
vp->dclk_switch_enabled = false;
clk_disable_unprepare(vp->dclk_switch);
}
if (--vop2->enable_count > 0)
return;
@@ -11983,28 +11994,127 @@ out:
kfree(vop2_zpos_splice_hdr);
}
static void vop2_crtc_update_vrr(struct drm_crtc *crtc)
static struct clk *vop2_get_switch_dclk(struct vop2_video_port *vp)
{
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];
struct vop2_video_port *dclk_switch_vp;
uint32_t dclk_switch_id;
if (!vp_data->dclk_switch_id)
return NULL;
dclk_switch_id = ffs(vp_data->dclk_switch_id) - 1;
if (dclk_switch_id >= vop2_data->nr_vps || dclk_switch_id >= ARRAY_SIZE(vop2->vps))
return NULL;
dclk_switch_vp = &vop2->vps[dclk_switch_id];
return dclk_switch_vp->dclk;
}
static void vop2_crtc_dclk_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 vop2_clk *dclk;
char clk_name[32];
unsigned int dclk_src;
unsigned int vrefresh;
unsigned int new_vtotal, vfp, new_vfp;
u64 dclk_rate;
int ret;
if (!vp->refresh_rate_change)
return;
DRM_DEV_INFO(vop2->dev, "change refresh rate by changing dclk\n");
if (!vcstate->min_refresh_rate || !vcstate->max_refresh_rate)
return;
if (!vp->dclk_switch) {
vp->dclk_switch = vop2_get_switch_dclk(vp);
if (!vp->dclk_switch)
return;
}
if (vcstate->request_refresh_rate < vcstate->min_refresh_rate ||
vcstate->request_refresh_rate > vcstate->max_refresh_rate) {
DRM_ERROR("invalid rate:%d\n", vcstate->request_refresh_rate);
dclk_src = VOP_MODULE_GET(vop2, vp, dclk_src_sel);
snprintf(clk_name, sizeof(clk_name), "dclk%d", vp->id);
dclk = vop2_clk_get(vop2, clk_name);
if (!dclk) {
DRM_DEV_INFO(vop2->dev, "can't find dclk%d\n", vp->id);
return;
}
vrefresh = drm_mode_vrefresh(adjust_mode);
DRM_DEV_INFO(vop2->dev, "origin fresh rate:%u, request fresh rate:%u\n",
vrefresh, vcstate->request_refresh_rate);
if (vp->loader_protect && !dclk->rate)
dclk->rate = clk_get_rate(vp->dclk);
if (!vp->dclk_switch_enabled) {
vp->dclk_switch_enabled = true;
ret = clk_prepare_enable(vp->dclk_switch);
if (ret < 0)
DRM_DEV_ERROR(vop2->dev, "failed to enable dclk switch %d\n", ret);
}
dclk_rate = (unsigned long long)dclk->rate * vcstate->request_refresh_rate;
do_div(dclk_rate, vrefresh);
DRM_DEV_INFO(vop2->dev, "origin dclk rate:%lu, request dclk rate:%llu\n",
dclk->rate, dclk_rate);
if (dclk_rate > (VOP2_MAX_DCLK_RATE * 1000))
return;
if (dclk_src) {
rockchip_drm_dclk_set_rate(vop2->version, vp->dclk, dclk_rate);
DRM_DEV_INFO(vop2->dev, "set %s to %llu, get %lu\n",
__clk_get_name(vp->dclk), dclk_rate, clk_get_rate(vp->dclk));
} else {
rockchip_drm_dclk_set_rate(vop2->version, vp->dclk_switch, dclk_rate);
DRM_DEV_INFO(vop2->dev, "set %s to %llu, get %lu\n",
__clk_get_name(vp->dclk_switch), dclk_rate,
clk_get_rate(vp->dclk_switch));
}
VOP_MODULE_SET(vop2, vp, dclk_src_sel, dclk_src ? 0 : 1);
}
static void vop2_crtc_hfp_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;
u32 hsync_len;
unsigned int vrefresh;
unsigned int new_htotal, hfp, new_hfp;
DRM_DEV_INFO(vop2->dev, "change refresh rate by changing hfp\n");
vrefresh = drm_mode_vrefresh(adjust_mode);
/* calculate new hfp for new refresh rate */
new_htotal = adjust_mode->htotal * vrefresh / vcstate->request_refresh_rate;
hfp = adjust_mode->hsync_start - adjust_mode->hdisplay;
new_hfp = hfp + new_htotal - adjust_mode->htotal;
DRM_DEV_INFO(vop2->dev, "origin fresh rate:%u, request fresh rate:%u\n",
vrefresh, vcstate->request_refresh_rate);
DRM_DEV_INFO(vop2->dev, "origin hfp:%u, request hfp:%u\n", hfp, new_hfp);
/* config vop2 htotal register */
hsync_len = VOP_MODULE_GET(vop2, vp, htotal_pw) & 0xffff;
VOP_MODULE_SET(vop2, vp, htotal_pw, (new_htotal << 16) | hsync_len);
}
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;
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 */
@@ -12032,6 +12142,38 @@ static void vop2_crtc_update_vrr(struct drm_crtc *crtc)
rockchip_connector_update_vfp_for_vrr(crtc, adjust_mode, 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;
if (!vcstate->min_refresh_rate || !vcstate->max_refresh_rate)
return;
if (vcstate->request_refresh_rate < vcstate->min_refresh_rate ||
vcstate->request_refresh_rate > vcstate->max_refresh_rate) {
DRM_ERROR("invalid rate:%d\n", vcstate->request_refresh_rate);
return;
}
switch (vcstate->vrr_type) {
case ROCKCHIP_VRR_DCLK_MODE:
vop2_crtc_dclk_seamless_switch(crtc);
break;
case ROCKCHIP_VRR_HFP_MODE:
vop2_crtc_hfp_seamless_switch(crtc);
break;
case ROCKCHIP_VRR_VFP_MODE:
vop2_crtc_vfp_seamless_switch(crtc);
fallthrough;
default:
break;
}
}
static bool post_sharp_enabled(struct drm_crtc *crtc)
{
struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);

View File

@@ -2256,6 +2256,7 @@ static const struct vop2_video_port_data rk3588_vop_video_ports[] = {
{
.id = 0,
.splice_vp_id = 1,
.dclk_switch_id = BIT(ROCKCHIP_VOP_VP1),
.lut_dma_rid = 0xd,
.soc_id = { 0x3588, 0x3588 },
.feature = VOP_FEATURE_OUTPUT_10BIT | VOP_FEATURE_ALPHA_SCALE |
@@ -2287,6 +2288,7 @@ static const struct vop2_video_port_data rk3588_vop_video_ports[] = {
},
{
.id = 2,
.dclk_switch_id = BIT(ROCKCHIP_VOP_VP1),
.lut_dma_rid = 0xe,
.soc_id = { 0x3588, 0x3588 },
.feature = VOP_FEATURE_OUTPUT_10BIT | VOP_FEATURE_ALPHA_SCALE,