diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 5a201074519c..0e79f221f2c6 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -953,6 +953,54 @@ static void rockchip_drm_debugfs_cleanup(struct drm_minor *minor) } #endif +static int rockchip_drm_create_properties(struct drm_device *dev) +{ + struct drm_property *prop; + struct rockchip_drm_private *private = dev->dev_private; + const struct drm_prop_enum_list cabc_mode_enum_list[] = { + { ROCKCHIP_DRM_CABC_MODE_DISABLE, "Disable" }, + { ROCKCHIP_DRM_CABC_MODE_NORMAL, "Normal" }, + { ROCKCHIP_DRM_CABC_MODE_LOWPOWER, "LowPower" }, + { ROCKCHIP_DRM_CABC_MODE_USERSPACE, "Userspace" }, + }; + + prop = drm_property_create_enum(dev, 0, "CABC_MODE", cabc_mode_enum_list, + ARRAY_SIZE(cabc_mode_enum_list)); + + private->cabc_mode_property = prop; + + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CABC_LUT", 0); + if (!prop) + return -ENOMEM; + private->cabc_lut_property = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "CABC_STAGE_UP", 0, 512); + if (!prop) + return -ENOMEM; + private->cabc_stage_up_property = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "CABC_STAGE_DOWN", 0, 255); + if (!prop) + return -ENOMEM; + private->cabc_stage_down_property = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "CABC_GLOBAL_DN", 0, 255); + if (!prop) + return -ENOMEM; + private->cabc_global_dn_property = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "CABC_CALC_PIXEL_NUM", 0, 1000); + if (!prop) + return -ENOMEM; + private->cabc_calc_pixel_num_property = prop; + + return 0; +} + static int rockchip_drm_bind(struct device *dev) { struct drm_device *drm_dev; @@ -1012,6 +1060,7 @@ static int rockchip_drm_bind(struct device *dev) drm_mode_config_init(drm_dev); rockchip_drm_mode_config_init(drm_dev); + rockchip_drm_create_properties(drm_dev); ret = rockchip_drm_init_iommu(drm_dev); if (ret) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 600f4a1a7f20..50eed5037760 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -75,6 +75,7 @@ struct rockchip_dclk_pll { struct rockchip_crtc_state { struct drm_crtc_state base; + struct drm_property_blob *cabc_lut; struct rockchip_dclk_pll *pll; int left_margin; int right_margin; @@ -86,6 +87,11 @@ struct rockchip_crtc_state { int afbdc_win_ptr; int afbdc_win_id; int afbdc_en; + int cabc_mode; + int cabc_stage_up; + int cabc_stage_down; + int cabc_global_dn; + int cabc_calc_pixel_num; int dsp_layer_sel; int output_type; int output_mode; @@ -126,6 +132,13 @@ struct rockchip_logo { struct rockchip_drm_private { struct rockchip_logo *logo; struct drm_property *logo_ymirror_prop; + struct drm_property *cabc_mode_property; + struct drm_property *cabc_lut_property; + struct drm_property *cabc_stage_up_property; + struct drm_property *cabc_stage_down_property; + struct drm_property *cabc_global_dn_property; + struct drm_property *cabc_calc_pixel_num_property; + void *backlight; struct drm_fb_helper *fbdev_helper; struct drm_gem_object *fbdev_bo; const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC]; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 7f287f745768..67ece59d3cf0 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -206,6 +206,8 @@ struct vop { u32 *lut; u32 lut_len; bool lut_active; + void __iomem *cabc_lut_regs; + u32 cabc_lut_len; /* one time only one process allowed to config the register */ spinlock_t reg_lock; @@ -353,6 +355,11 @@ static inline uint32_t vop_read_lut(struct vop *vop, uint32_t offset) return readl(vop->lut_regs + offset); } +static inline void vop_write_cabc_lut(struct vop *vop, uint32_t offset, uint32_t v) +{ + writel(v, vop->cabc_lut_regs + offset); +} + static bool has_rb_swapped(uint32_t format) { switch (format) { @@ -1795,6 +1802,13 @@ static void vop_crtc_enable(struct drm_crtc *crtc) VOP_CTRL_SET(vop, core_dclk_div, !!(adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK)); + VOP_CTRL_SET(vop, cabc_total_num, hdisplay * vdisplay); + VOP_CTRL_SET(vop, cabc_config_mode, STAGE_BY_STAGE); + VOP_CTRL_SET(vop, cabc_stage_up_mode, MUL_MODE); + VOP_CTRL_SET(vop, cabc_scale_cfg_value, 1); + VOP_CTRL_SET(vop, cabc_scale_cfg_enable, 0); + VOP_CTRL_SET(vop, cabc_global_dn_limit_en, 1); + clk_set_rate(vop->dclk, adjusted_mode->crtc_clock * 1000); vop_cfg_done(vop); @@ -2041,6 +2055,86 @@ static void vop_post_config(struct drm_crtc *crtc) } } +static void vop_update_cabc_lut(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct rockchip_crtc_state *s = + to_rockchip_crtc_state(crtc->state); + struct rockchip_crtc_state *old_s = + to_rockchip_crtc_state(old_crtc_state); + struct drm_property_blob *cabc_lut = s->cabc_lut; + struct drm_property_blob *old_cabc_lut = old_s->cabc_lut; + struct vop *vop = to_vop(crtc); + int lut_size; + u32 *lut; + u32 lut_len = vop->cabc_lut_len; + int i, dle; + + if (!cabc_lut && old_cabc_lut) { + VOP_CTRL_SET(vop, cabc_lut_en, 0); + return; + } + if (!cabc_lut) + return; + + if (old_cabc_lut && old_cabc_lut->base.id == cabc_lut->base.id) + return; + + lut = (u32 *)cabc_lut->data; + lut_size = cabc_lut->length / sizeof(u32); + if (WARN(lut_size != lut_len, "Unexpect cabc lut size not match\n")) + return; + +#define CTRL_GET(name) VOP_CTRL_GET(vop, name) + if (CTRL_GET(cabc_lut_en)) { + VOP_CTRL_SET(vop, cabc_lut_en, 0); + vop_cfg_done(vop); + readx_poll_timeout(CTRL_GET, cabc_lut_en, dle, !dle, 5, 33333); + } + + for (i = 0; i < lut_len; i++) + vop_write_cabc_lut(vop, (i << 2), lut[i]); +#undef CTRL_GET + VOP_CTRL_SET(vop, cabc_lut_en, 1); +} + +static void vop_update_cabc(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct rockchip_crtc_state *s = + to_rockchip_crtc_state(crtc->state); + struct vop *vop = to_vop(crtc); + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + int pixel_total = mode->hdisplay * mode->vdisplay; + + if (!vop->cabc_lut_regs) + return; + + vop_update_cabc_lut(crtc, old_crtc_state); + + if (s->cabc_mode != ROCKCHIP_DRM_CABC_MODE_DISABLE) { + VOP_CTRL_SET(vop, cabc_en, 1); + VOP_CTRL_SET(vop, cabc_handle_en, 1); + VOP_CTRL_SET(vop, cabc_stage_up, s->cabc_stage_up); + VOP_CTRL_SET(vop, cabc_stage_down, s->cabc_stage_down); + VOP_CTRL_SET(vop, cabc_global_dn, s->cabc_global_dn); + VOP_CTRL_SET(vop, cabc_calc_pixel_num, + s->cabc_calc_pixel_num * pixel_total / 1000); + } else { + /* + * There are some hardware issues on cabc disabling: + * 1: if cabc auto gating enable, cabc disabling will cause + * vop die + * 2: cabc disabling always would make timing several + * pixel cycle abnormal, cause some panel abnormal. + * + * So just keep cabc enable, and make it no work with max + * cabc_calc_pixel_num, it only has little power consume. + */ + VOP_CTRL_SET(vop, cabc_calc_pixel_num, pixel_total); + } +} + static void vop_cfg_update(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { @@ -2147,6 +2241,8 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc, vop->is_iommu_enabled = true; } + vop_update_cabc(crtc, old_crtc_state); + vop_cfg_done(vop); /* @@ -2262,6 +2358,7 @@ static int vop_crtc_atomic_get_property(struct drm_crtc *crtc, uint64_t *val) { struct drm_device *drm_dev = crtc->dev; + struct rockchip_drm_private *private = drm_dev->dev_private; struct drm_mode_config *mode_config = &drm_dev->mode_config; struct rockchip_crtc_state *s = to_rockchip_crtc_state(state); @@ -2285,6 +2382,36 @@ static int vop_crtc_atomic_get_property(struct drm_crtc *crtc, return 0; } + if (property == private->cabc_mode_property) { + *val = s->cabc_mode; + return 0; + } + + if (property == private->cabc_stage_up_property) { + *val = s->cabc_stage_up; + return 0; + } + + if (property == private->cabc_stage_down_property) { + *val = s->cabc_stage_down; + return 0; + } + + if (property == private->cabc_global_dn_property) { + *val = s->cabc_global_dn; + return 0; + } + + if (property == private->cabc_calc_pixel_num_property) { + *val = s->cabc_calc_pixel_num; + return 0; + } + + if (property == private->cabc_lut_property) { + *val = s->cabc_lut ? s->cabc_lut->base.id : 0; + return 0; + } + DRM_ERROR("failed to get vop crtc property\n"); return -EINVAL; } @@ -2295,8 +2422,10 @@ static int vop_crtc_atomic_set_property(struct drm_crtc *crtc, uint64_t val) { struct drm_device *drm_dev = crtc->dev; + struct rockchip_drm_private *private = drm_dev->dev_private; struct drm_mode_config *mode_config = &drm_dev->mode_config; struct rockchip_crtc_state *s = to_rockchip_crtc_state(state); + struct vop *vop = to_vop(crtc); if (property == mode_config->tv_left_margin_property) { s->left_margin = val; @@ -2318,6 +2447,57 @@ static int vop_crtc_atomic_set_property(struct drm_crtc *crtc, return 0; } + if (property == private->cabc_mode_property) { + s->cabc_mode = val; + /* + * Pre-define lowpower and normal mode to make cabc + * easier to use. + */ + if (s->cabc_mode == ROCKCHIP_DRM_CABC_MODE_NORMAL) { + s->cabc_stage_up = 257; + s->cabc_stage_down = 255; + s->cabc_global_dn = 192; + s->cabc_calc_pixel_num = 995; + } else if (s->cabc_mode == ROCKCHIP_DRM_CABC_MODE_LOWPOWER) { + s->cabc_stage_up = 260; + s->cabc_stage_down = 252; + s->cabc_global_dn = 180; + s->cabc_calc_pixel_num = 992; + } + return 0; + } + + if (property == private->cabc_stage_up_property) { + s->cabc_stage_up = val; + return 0; + } + + if (property == private->cabc_stage_down_property) { + s->cabc_stage_down = val; + return 0; + } + + if (property == private->cabc_calc_pixel_num_property) { + s->cabc_calc_pixel_num = val; + return 0; + } + + if (property == private->cabc_global_dn_property) { + s->cabc_global_dn = val; + return 0; + } + + if (property == private->cabc_lut_property) { + bool replaced; + ssize_t size = vop->cabc_lut_len * 4; + + return drm_atomic_replace_property_blob_from_id(crtc, + &s->cabc_lut, + val, + size, + &replaced); + } + DRM_ERROR("failed to set vop crtc property\n"); return -EINVAL; } @@ -2502,6 +2682,7 @@ static int vop_create_crtc(struct vop *vop) struct device *dev = vop->dev; const struct vop_data *vop_data = vop->data; struct drm_device *drm_dev = vop->drm_dev; + struct rockchip_drm_private *private = drm_dev->dev_private; struct drm_plane *primary = NULL, *cursor = NULL, *plane, *tmp; struct drm_crtc *crtc = &vop->crtc; struct device_node *port; @@ -2582,8 +2763,16 @@ static int vop_create_crtc(struct vop *vop) VOP_ATTACH_MODE_CONFIG_PROP(tv_right_margin_property, 100); VOP_ATTACH_MODE_CONFIG_PROP(tv_top_margin_property, 100); VOP_ATTACH_MODE_CONFIG_PROP(tv_bottom_margin_property, 100); + #undef VOP_ATTACH_MODE_CONFIG_PROP + drm_object_attach_property(&crtc->base, private->cabc_lut_property, 0); + drm_object_attach_property(&crtc->base, private->cabc_mode_property, 0); + drm_object_attach_property(&crtc->base, private->cabc_stage_up_property, 0); + drm_object_attach_property(&crtc->base, private->cabc_stage_down_property, 0); + drm_object_attach_property(&crtc->base, private->cabc_global_dn_property, 0); + drm_object_attach_property(&crtc->base, private->cabc_calc_pixel_num_property, 0); + if (vop_data->feature & VOP_FEATURE_AFBDC) feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_AFBDC); drm_object_attach_property(&crtc->base, vop->feature_prop, @@ -2880,6 +3069,22 @@ static int vop_bind(struct device *dev, struct device *master, void *data) } } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cabc_lut"); + vop->cabc_lut_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(vop->cabc_lut_regs)) { + dev_warn(vop->dev, "failed to get vop cabc lut registers\n"); + vop->cabc_lut_regs = NULL; + } + + if (vop->cabc_lut_regs) { + vop->cabc_lut_len = resource_size(res) >> 2; + if (vop->cabc_lut_len != 128) { + dev_err(vop->dev, "unsupport cabc lut sizes %d\n", + vop->cabc_lut_len); + return -EINVAL; + } + } + vop->hclk = devm_clk_get(vop->dev, "hclk_vop"); if (IS_ERR(vop->hclk)) { dev_err(vop->dev, "failed to get hclk source\n"); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index dd2db3f75c42..4083bd340a86 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -27,6 +27,17 @@ #define AFBDC_FMT_U8U8U8U8 0x5 #define AFBDC_FMT_U8U8U8 0x4 +enum cabc_stage_mode { + LAST_FRAME_PWM_VAL = 0x0, + CUR_FRAME_PWM_VAL = 0x1, + STAGE_BY_STAGE = 0x2 +}; + +enum cabc_stage_up_mode { + MUL_MODE, + ADD_MODE, +}; + enum vop_csc_format { CSC_BT601, CSC_BT709, @@ -139,6 +150,21 @@ struct vop_ctrl { struct vop_reg afbdc_hdr_ptr; struct vop_reg afbdc_rstn; + /* CABC */ + struct vop_reg cabc_total_num; + struct vop_reg cabc_config_mode; + struct vop_reg cabc_stage_up_mode; + struct vop_reg cabc_scale_cfg_value; + struct vop_reg cabc_scale_cfg_enable; + struct vop_reg cabc_global_dn_limit_en; + struct vop_reg cabc_lut_en; + struct vop_reg cabc_en; + struct vop_reg cabc_handle_en; + struct vop_reg cabc_stage_up; + struct vop_reg cabc_stage_down; + struct vop_reg cabc_global_dn; + struct vop_reg cabc_calc_pixel_num; + struct vop_reg cfg_done; }; diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h index 47a2c0328383..ff6d53e325c9 100644 --- a/include/uapi/drm/rockchip_drm.h +++ b/include/uapi/drm/rockchip_drm.h @@ -125,6 +125,13 @@ enum rockchip_crtc_feture { ROCKCHIP_DRM_CRTC_FEATURE_AFBDC, }; +enum rockchip_cabc_mode { + ROCKCHIP_DRM_CABC_MODE_DISABLE, + ROCKCHIP_DRM_CABC_MODE_NORMAL, + ROCKCHIP_DRM_CABC_MODE_LOWPOWER, + ROCKCHIP_DRM_CABC_MODE_USERSPACE, +}; + #define DRM_ROCKCHIP_GEM_CREATE 0x00 #define DRM_ROCKCHIP_GEM_MAP_OFFSET 0x01 #define DRM_ROCKCHIP_GEM_CPU_ACQUIRE 0x02