diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 59b7235e00fd..d7171a764531 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -37,11 +37,14 @@ struct iommu_domain; * @loader_protect: protect loader logo crtc's power * @enable_vblank: enable crtc vblank irq. * @disable_vblank: disable crtc vblank irq. + * @bandwidth: report present crtc bandwidth consume. */ struct rockchip_crtc_funcs { int (*loader_protect)(struct drm_crtc *crtc, bool on); int (*enable_vblank)(struct drm_crtc *crtc); void (*disable_vblank)(struct drm_crtc *crtc); + size_t (*bandwidth)(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state); void (*cancel_pending_vblank)(struct drm_crtc *crtc, struct drm_file *file_priv); int (*debugfs_init)(struct drm_minor *minor, struct drm_crtc *crtc); int (*debugfs_dump)(struct drm_crtc *crtc, struct seq_file *s); @@ -67,6 +70,7 @@ struct rockchip_atomic_commit { struct drm_atomic_state *state; struct drm_device *dev; struct mutex lock; + size_t bandwidth; }; struct rockchip_dclk_pll { diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index cc2a659f2389..20e30443ae87 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "rockchip_drm_drv.h" #include "rockchip_drm_gem.h" @@ -211,11 +212,38 @@ static void rockchip_drm_output_poll_changed(struct drm_device *dev) drm_fb_helper_hotplug_event(fb_helper); } +static int rockchip_drm_bandwidth_atomic_check(struct drm_device *dev, + struct drm_atomic_state *state, + size_t *bandwidth) +{ + struct rockchip_drm_private *priv = dev->dev_private; + struct drm_crtc_state *crtc_state; + const struct rockchip_crtc_funcs *funcs; + struct drm_crtc *crtc; + int i, ret = 0; + + *bandwidth = 0; + for_each_crtc_in_state(state, crtc, crtc_state, i) { + funcs = priv->crtc_funcs[drm_crtc_index(crtc)]; + + if (funcs && funcs->bandwidth) + *bandwidth += funcs->bandwidth(crtc, crtc_state); + } + + /* + * Check ddr frequency support here here. + */ + ret = rockchip_dmcfreq_vop_bandwidth_request(*bandwidth); + + return ret; +} + static void rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit) { struct drm_atomic_state *state = commit->state; struct drm_device *dev = commit->dev; + size_t bandwidth = commit->bandwidth; /* * TODO: do fence wait here. @@ -242,6 +270,8 @@ rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit) rockchip_drm_backlight_update(dev); + rockchip_dmcfreq_vop_bandwidth_update(bandwidth); + drm_atomic_helper_commit_planes(dev, state, true); drm_atomic_helper_wait_for_vblanks(dev, state); @@ -265,12 +295,22 @@ int rockchip_drm_atomic_commit(struct drm_device *dev, { struct rockchip_drm_private *private = dev->dev_private; struct rockchip_atomic_commit *commit = &private->commit; + size_t bandwidth; int ret; ret = drm_atomic_helper_prepare_planes(dev, state); if (ret) return ret; + ret = rockchip_drm_bandwidth_atomic_check(dev, state, &bandwidth); + if (ret) { + /* + * TODO: + * Just report bandwidth can't support now. + */ + DRM_ERROR("vop bandwidth too large %zd\n", bandwidth); + } + /* serialize outstanding asynchronous commits */ mutex_lock(&commit->lock); flush_work(&commit->work); @@ -279,6 +319,7 @@ int rockchip_drm_atomic_commit(struct drm_device *dev, commit->dev = dev; commit->state = state; + commit->bandwidth = bandwidth; if (async) schedule_work(&commit->work); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index b82275b242ee..5ec530599474 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -58,6 +58,9 @@ #define VOP_WIN_SUPPORT(vop, win, name) \ VOP_REG_SUPPORT(vop, win->phy->name) +#define VOP_WIN_SCL_EXT_SUPPORT(vop, win, name) \ + VOP_REG_SUPPORT(vop, win->phy->scl->ext->name) + #define VOP_CTRL_SUPPORT(vop, name) \ VOP_REG_SUPPORT(vop, vop->data->ctrl->name) @@ -1724,6 +1727,124 @@ vop_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode, return MODE_OK; } +struct vop_bandwidth { + size_t bandwidth; + int y1; + int y2; +}; + +static int vop_bandwidth_cmp(const void *a, const void *b) +{ + struct vop_bandwidth *pa = (struct vop_bandwidth *)a; + struct vop_bandwidth *pb = (struct vop_bandwidth *)b; + + return pa->y1 - pb->y2; +} + +static size_t vop_plane_line_bandwidth(struct drm_plane_state *pstate) +{ + struct vop_plane_state *vop_plane_state = to_vop_plane_state(pstate); + struct vop_win *win = to_vop_win(pstate->plane); + struct drm_crtc *crtc = pstate->crtc; + struct vop *vop = to_vop(crtc); + struct drm_framebuffer *fb = pstate->fb; + struct drm_rect *dest = &vop_plane_state->dest; + struct drm_rect *src = &vop_plane_state->src; + int bpp = drm_format_plane_bpp(fb->pixel_format, 0); + int src_width = drm_rect_width(src) >> 16; + int src_height = drm_rect_height(src) >> 16; + int dest_width = drm_rect_width(dest); + int dest_height = drm_rect_height(dest); + int vskiplines = scl_get_vskiplines(src_height, dest_height); + size_t bandwidth; + + if (!src_width || !src_height || !dest_width || !dest_height) + return 0; + + bandwidth = src_width * bpp / 8; + + bandwidth = bandwidth * src_width / dest_width; + bandwidth = bandwidth * src_height / dest_height; + if (vskiplines == 2 && VOP_WIN_SCL_EXT_SUPPORT(vop, win, vsd_yrgb_gt2)) + bandwidth /= 2; + else if (vskiplines == 4 && + VOP_WIN_SCL_EXT_SUPPORT(vop, win, vsd_yrgb_gt4)) + bandwidth /= 4; + + return bandwidth; +} + +static u64 vop_calc_max_bandwidth(struct vop_bandwidth *bw, int start, + int count, int y2) +{ + u64 max_bandwidth = 0; + int i; + + for (i = start; i < count; i++) { + u64 bandwidth = 0; + + if (bw[i].y1 > y2) + continue; + bandwidth = bw[i].bandwidth; + bandwidth += vop_calc_max_bandwidth(bw, i + 1, count, + min(bw[i].y2, y2)); + + if (bandwidth > max_bandwidth) + max_bandwidth = bandwidth; + } + + return max_bandwidth; +} + +static size_t vop_crtc_bandwidth(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + struct drm_atomic_state *state = crtc_state->state; + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + u16 htotal = adjusted_mode->crtc_htotal; + u16 vdisplay = adjusted_mode->crtc_vdisplay; + int clock = adjusted_mode->crtc_clock; + struct vop *vop = to_vop(crtc); + const struct vop_data *vop_data = vop->data; + struct vop_plane_state *vop_plane_state; + struct drm_plane_state *pstate; + struct vop_bandwidth *pbandwidth; + struct drm_plane *plane; + size_t bandwidth; + size_t max_bandwidth; + int i, cnt = 0; + + if (!htotal || !vdisplay) + return 0; + + pbandwidth = kmalloc_array(vop_data->win_size, sizeof(*pbandwidth), + GFP_KERNEL); + if (!pbandwidth) + return -ENOMEM; + + for_each_plane_in_state(state, plane, pstate, i) { + if (pstate->crtc != crtc || !pstate->fb) + continue; + + vop_plane_state = to_vop_plane_state(pstate); + pbandwidth[cnt].y1 = vop_plane_state->dest.y1; + pbandwidth[cnt].y2 = vop_plane_state->dest.y2; + pbandwidth[cnt++].bandwidth = vop_plane_line_bandwidth(pstate); + } + + sort(pbandwidth, cnt, sizeof(pbandwidth[0]), vop_bandwidth_cmp, NULL); + + max_bandwidth = vop_calc_max_bandwidth(pbandwidth, 0, cnt, vdisplay); + /* + * bandwidth(MB/s) + * = line_bandwidth / line_time + * = line_bandwidth(Byte) * clock(KHZ) / 1000 / htotal + */ + bandwidth = max_bandwidth * clock / 1000 / htotal; + + return bandwidth; +} + static const struct rockchip_crtc_funcs private_crtc_funcs = { .loader_protect = vop_crtc_loader_protect, .enable_vblank = vop_crtc_enable_vblank, @@ -1733,6 +1854,7 @@ static const struct rockchip_crtc_funcs private_crtc_funcs = { .debugfs_dump = vop_crtc_debugfs_dump, .regs_dump = vop_crtc_regs_dump, .mode_valid = vop_crtc_mode_valid, + .bandwidth = vop_crtc_bandwidth, }; static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,