drm/rockchip: add cabc support
authorMark Yao <mark.yao@rock-chips.com>
Wed, 28 Jun 2017 07:11:01 +0000 (15:11 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Wed, 19 Jul 2017 06:34:00 +0000 (14:34 +0800)
CABC(Content Adaptive Backlight Control) is used to
increase the contrast of such LCD-screens the backlight
can be (globally) dimmed when the image to be displayed
is dark (i.e. not comprising high intensity image data)
while the image data is numerically corrected and adapted
to the reduced backlight intensity.

Change-Id: I0bd84375264675943f1b601f0cac8b843567087d
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
drivers/gpu/drm/rockchip/rockchip_drm_drv.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.h
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/rockchip/rockchip_drm_vop.h
include/uapi/drm/rockchip_drm.h

index 5a201074519c780473927a20cf912b7d45feedf7..0e79f221f2c63e6ed68ac2e1ccae50094fdc7415 100644 (file)
@@ -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)
index 600f4a1a7f208549562b707023a0db2b98b7f595..50eed5037760552e13f0822109e9a43a1b3fe267 100644 (file)
@@ -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];
index 7f287f7457680f640cfd8cc24ba6e1fd3d239a9f..67ece59d3cf0b0fd0a7dafcd24b1b5930e9f7bc9 100644 (file)
@@ -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");
index dd2db3f75c425e2378f3355cf4e4060d845ac582..4083bd340a863d071ee6e7f1c04875bd682327f5 100644 (file)
 #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;
 };
 
index 47a2c03283830b1d55859b214255cf95f6655289..ff6d53e325c929ad8fbbf2fc30f25cb83fd7713e 100644 (file)
@@ -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