}
#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;
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)
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;
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) {
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);
}
}
+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)
{
vop->is_iommu_enabled = true;
}
+ vop_update_cabc(crtc, old_crtc_state);
+
vop_cfg_done(vop);
/*
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);
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;
}
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;
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;
}
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;
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,
}
}
+ 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");