drm/rockchip: driver: add debug and bandwidth calc function

Change-Id: I6273b05f4a1943000494a54a785bdec43b35cfb3
Signed-off-by: Sandy Huang <hjc@rock-chips.com>
This commit is contained in:
Sandy Huang 2019-06-19 19:50:13 +08:00 committed by Tao Huang
parent 35c52e2194
commit 3145e8695c
3 changed files with 743 additions and 6 deletions

View File

@ -846,6 +846,31 @@ void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
iommu_detach_device(domain, dev);
}
int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
const struct rockchip_crtc_funcs *crtc_funcs)
{
int pipe = drm_crtc_index(crtc);
struct rockchip_drm_private *priv = crtc->dev->dev_private;
if (pipe >= ROCKCHIP_MAX_CRTC)
return -EINVAL;
priv->crtc_funcs[pipe] = crtc_funcs;
return 0;
}
void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc)
{
int pipe = drm_crtc_index(crtc);
struct rockchip_drm_private *priv = crtc->dev->dev_private;
if (pipe >= ROCKCHIP_MAX_CRTC)
return;
priv->crtc_funcs[pipe] = NULL;
}
static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
{
struct rockchip_drm_private *private = drm_dev->dev_private;
@ -882,6 +907,80 @@ static void rockchip_iommu_cleanup(struct drm_device *drm_dev)
iommu_domain_free(private->domain);
}
#ifdef CONFIG_DEBUG_FS
static int rockchip_drm_mm_dump(struct seq_file *s, void *data)
{
struct drm_info_node *node = s->private;
struct drm_minor *minor = node->minor;
struct drm_device *drm_dev = minor->dev;
struct rockchip_drm_private *priv = drm_dev->dev_private;
struct drm_printer p = drm_seq_file_printer(s);
if (!priv->domain)
return 0;
mutex_lock(&priv->mm_lock);
drm_mm_print(&priv->mm, &p);
mutex_unlock(&priv->mm_lock);
return 0;
}
static int rockchip_drm_summary_show(struct seq_file *s, void *data)
{
struct drm_info_node *node = s->private;
struct drm_minor *minor = node->minor;
struct drm_device *drm_dev = minor->dev;
struct rockchip_drm_private *priv = drm_dev->dev_private;
struct drm_crtc *crtc;
drm_for_each_crtc(crtc, drm_dev) {
int pipe = drm_crtc_index(crtc);
if (priv->crtc_funcs[pipe] &&
priv->crtc_funcs[pipe]->debugfs_dump)
priv->crtc_funcs[pipe]->debugfs_dump(crtc, s);
}
return 0;
}
static struct drm_info_list rockchip_debugfs_files[] = {
{ "summary", rockchip_drm_summary_show, 0, NULL },
{ "mm_dump", rockchip_drm_mm_dump, 0, NULL },
};
static int rockchip_drm_debugfs_init(struct drm_minor *minor)
{
struct drm_device *dev = minor->dev;
struct rockchip_drm_private *priv = dev->dev_private;
struct drm_crtc *crtc;
int ret;
ret = drm_debugfs_create_files(rockchip_debugfs_files,
ARRAY_SIZE(rockchip_debugfs_files),
minor->debugfs_root,
minor);
if (ret) {
dev_err(dev->dev, "could not install rockchip_debugfs_list\n");
return ret;
}
drm_for_each_crtc(crtc, dev) {
int pipe = drm_crtc_index(crtc);
if (priv->crtc_funcs[pipe] &&
priv->crtc_funcs[pipe]->debugfs_init)
priv->crtc_funcs[pipe]->debugfs_init(minor, crtc);
}
return 0;
}
#endif
static int rockchip_drm_create_properties(struct drm_device *dev)
{
struct drm_property *prop;
@ -1187,6 +1286,9 @@ static struct drm_driver rockchip_drm_driver = {
.gem_prime_mmap = rockchip_gem_mmap_buf,
.gem_prime_begin_cpu_access = rockchip_gem_prime_begin_cpu_access,
.gem_prime_end_cpu_access = rockchip_gem_prime_end_cpu_access,
#ifdef CONFIG_DEBUG_FS
.debugfs_init = rockchip_drm_debugfs_init,
#endif
.ioctls = rockchip_ioctls,
.num_ioctls = ARRAY_SIZE(rockchip_ioctls),
.fops = &rockchip_drm_driver_fops,

View File

@ -17,6 +17,7 @@
#ifndef _ROCKCHIP_DRM_DRV_H
#define _ROCKCHIP_DRM_DRV_H
#include <drm/drm_crtc.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_gem.h>
@ -33,6 +34,32 @@ struct drm_device;
struct drm_connector;
struct iommu_domain;
/*
* Rockchip drm private crtc funcs.
* @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,
unsigned int *plane_num_total);
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);
void (*regs_dump)(struct drm_crtc *crtc, struct seq_file *s);
enum drm_mode_status (*mode_valid)(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
int output_type);
void (*crtc_close)(struct drm_crtc *crtc);
void (*crtc_send_mcu_cmd)(struct drm_crtc *crtc, u32 type, u32 value);
};
struct rockchip_atomic_commit {
struct drm_atomic_state *state;
struct drm_device *dev;
@ -127,6 +154,7 @@ struct rockchip_drm_private {
struct drm_property *alpha_scale_prop;
struct drm_fb_helper *fbdev_helper;
struct drm_gem_object *fbdev_bo;
const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC];
struct drm_atomic_state *state;
struct iommu_domain *domain;
struct gen_pool *secure_buffer_pool;
@ -142,6 +170,9 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
struct device *dev);
void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
struct device *dev);
int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
const struct rockchip_crtc_funcs *crtc_funcs);
void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc);
int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout);
extern struct platform_driver cdn_dp_driver;

View File

@ -22,6 +22,7 @@
#ifdef CONFIG_DRM_ANALOGIX_DP
#include <drm/bridge/analogix_dp.h>
#endif
#include <dt-bindings/clock/rk_system_status.h>
#include <linux/debugfs.h>
#include <linux/fixp-arith.h>
@ -42,6 +43,8 @@
#include <linux/reset.h>
#include <linux/delay.h>
#include <linux/sort.h>
#include <soc/rockchip/rockchip_dmc.h>
#include <soc/rockchip/rockchip-system-status.h>
#include <uapi/linux/videodev2.h>
#include "rockchip_drm_drv.h"
@ -206,6 +209,8 @@ struct vop {
struct drm_crtc crtc;
struct device *dev;
struct drm_device *drm_dev;
struct dentry *debugfs;
struct drm_info_list *debugfs_files;
struct drm_property *plane_zpos_prop;
struct drm_property *plane_feature_prop;
struct drm_property *feature_prop;
@ -218,6 +223,7 @@ struct vop {
struct drm_tv_connector_state active_tv_state;
bool pre_overlay;
bool loader_protect;
struct completion dsp_hold_completion;
/* protected by dev->event_lock */
@ -268,6 +274,18 @@ struct vop {
struct vop_win win[];
};
static __maybe_unused void vop_lock(struct vop *vop)
{
mutex_lock(&vop->vop_lock);
rockchip_dmcfreq_lock();
}
static __maybe_unused void vop_unlock(struct vop *vop)
{
rockchip_dmcfreq_unlock();
mutex_unlock(&vop->vop_lock);
}
static inline void vop_grf_writel(struct vop *vop, struct vop_reg reg, u32 v)
{
u32 val = 0;
@ -1284,10 +1302,12 @@ static void vop_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct vop *vop = to_vop(crtc);
int sys_status = drm_crtc_index(crtc) ?
SYS_STATUS_LCDC1 : SYS_STATUS_LCDC0;
WARN_ON(vop->event);
mutex_lock(&vop->vop_lock);
vop_lock(vop);
drm_crtc_vblank_off(crtc);
vop_disable_all_planes(vop);
@ -1328,7 +1348,9 @@ static void vop_crtc_atomic_disable(struct drm_crtc *crtc,
clk_disable_unprepare(vop->dclk);
clk_disable_unprepare(vop->aclk);
clk_disable_unprepare(vop->hclk);
mutex_unlock(&vop->vop_lock);
vop_unlock(vop);
rockchip_clear_system_status(sys_status);
if (crtc->state->event && !crtc->state->active) {
spin_lock_irq(&crtc->dev->event_lock);
@ -1508,6 +1530,30 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
bool rb_swap, global_alpha_en;
int is_yuv = fb->format->is_yuv;
#if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
bool AFBC_flag = false;
struct vop_dump_list *planlist;
unsigned long num_pages;
struct page **pages;
struct rockchip_drm_fb *rk_fb;
struct drm_gem_object *obj;
struct rockchip_gem_object *rk_obj;
num_pages = 0;
pages = NULL;
rk_fb = to_rockchip_fb(fb);
obj = rk_fb->obj[0];
rk_obj = to_rockchip_obj(obj);
if (rk_obj) {
num_pages = rk_obj->num_pages;
pages = rk_obj->pages;
}
//if (fb->modifier[0] == DRM_FORMAT_MOD_ARM_AFBC)
// AFBC_flag = true;
//else
AFBC_flag = false;
#endif
/*
* can't update plane when vop is disabled.
*/
@ -1611,6 +1657,31 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
* actual_w, actual_h)
*/
vop->is_iommu_needed = true;
#if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
planlist = kmalloc(sizeof(*planlist), GFP_KERNEL);
if (planlist) {
planlist->dump_info.AFBC_flag = AFBC_flag;
planlist->dump_info.area_id = win->area_id;
planlist->dump_info.win_id = win->win_id;
planlist->dump_info.yuv_format =
is_yuv_support(fb->format->format);
planlist->dump_info.num_pages = num_pages;
planlist->dump_info.pages = pages;
planlist->dump_info.offset = vop_plane_state->offset;
planlist->dump_info.pitches = fb->pitches[0];
planlist->dump_info.height = actual_h;
planlist->dump_info.pixel_format = fb->format->format;
list_add_tail(&planlist->entry, &crtc->vop_dump_list_head);
} else {
DRM_ERROR("can't alloc a node of planlist %p\n", planlist);
return;
}
if (crtc->vop_dump_status == DUMP_KEEP ||
crtc->vop_dump_times > 0) {
vop_plane_dump(&planlist->dump_info, crtc->frame_count);
crtc->vop_dump_times--;
}
#endif
}
static const struct drm_plane_helper_funcs plane_helper_funcs = {
@ -1619,6 +1690,70 @@ static const struct drm_plane_helper_funcs plane_helper_funcs = {
.atomic_disable = vop_plane_atomic_disable,
};
static void vop_crtc_disable(struct drm_crtc *crtc)
{
struct vop *vop = to_vop(crtc);
int sys_status = drm_crtc_index(crtc) ?
SYS_STATUS_LCDC1 : SYS_STATUS_LCDC0;
vop_lock(vop);
VOP_CTRL_SET(vop, reg_done_frm, 1);
VOP_CTRL_SET(vop, dsp_interlace, 0);
drm_crtc_vblank_off(crtc);
VOP_CTRL_SET(vop, afbdc_en, 0);
vop_disable_all_planes(vop);
/*
* Vop standby will take effect at end of current frame,
* if dsp hold valid irq happen, it means standby complete.
*
* we must wait standby complete when we want to disable aclk,
* if not, memory bus maybe dead.
*/
reinit_completion(&vop->dsp_hold_completion);
vop_dsp_hold_valid_irq_enable(vop);
spin_lock(&vop->reg_lock);
VOP_CTRL_SET(vop, standby, 1);
spin_unlock(&vop->reg_lock);
WARN_ON(!wait_for_completion_timeout(&vop->dsp_hold_completion,
msecs_to_jiffies(50)));
vop_dsp_hold_valid_irq_disable(vop);
disable_irq(vop->irq);
vop->is_enabled = false;
if (vop->is_iommu_enabled) {
/*
* vop standby complete, so iommu detach is safe.
*/
VOP_CTRL_SET(vop, dma_stop, 1);
rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev);
vop->is_iommu_enabled = false;
}
pm_runtime_put_sync(vop->dev);
clk_disable_unprepare(vop->dclk);
clk_disable_unprepare(vop->aclk);
clk_disable_unprepare(vop->hclk);
vop_unlock(vop);
rockchip_clear_system_status(sys_status);
if (crtc->state->event && !crtc->state->active) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
}
}
static void vop_plane_destroy(struct drm_plane *plane)
{
drm_plane_cleanup(plane);
@ -1807,6 +1942,473 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
spin_unlock_irqrestore(&vop->irq_lock, flags);
}
static void vop_crtc_cancel_pending_vblank(struct drm_crtc *crtc,
struct drm_file *file_priv)
{
struct drm_device *drm = crtc->dev;
struct vop *vop = to_vop(crtc);
struct drm_pending_vblank_event *e;
unsigned long flags;
spin_lock_irqsave(&drm->event_lock, flags);
e = vop->event;
if (e && e->base.file_priv == file_priv) {
vop->event = NULL;
//e->base.destroy(&e->base);//todo
file_priv->event_space += sizeof(e->event);
}
spin_unlock_irqrestore(&drm->event_lock, flags);
}
static int vop_crtc_loader_protect(struct drm_crtc *crtc, bool on)
{
struct rockchip_drm_private *private = crtc->dev->dev_private;
struct vop *vop = to_vop(crtc);
int sys_status = drm_crtc_index(crtc) ?
SYS_STATUS_LCDC1 : SYS_STATUS_LCDC0;
if (on == vop->loader_protect)
return 0;
if (on) {
if (vop->dclk_source) {
struct clk *parent;
parent = clk_get_parent(vop->dclk_source);
if (parent) {
if (clk_is_match(private->default_pll.pll, parent))
vop->pll = &private->default_pll;
else if (clk_is_match(private->hdmi_pll.pll, parent))
vop->pll = &private->hdmi_pll;
if (vop->pll)
vop->pll->use_count++;
}
}
rockchip_set_system_status(sys_status);
vop_initial(crtc);
enable_irq(vop->irq);
drm_crtc_vblank_on(crtc);
vop->loader_protect = true;
} else {
vop_crtc_disable(crtc);
if (vop->dclk_source && vop->pll) {
vop->pll->use_count--;
vop->pll = NULL;
}
vop->loader_protect = false;
}
return 0;
}
#define DEBUG_PRINT(args...) \
do { \
if (s) \
seq_printf(s, args); \
else \
pr_err(args); \
} while (0)
static int vop_plane_info_dump(struct seq_file *s, struct drm_plane *plane)
{
struct vop_win *win = to_vop_win(plane);
struct drm_plane_state *state = plane->state;
struct vop_plane_state *pstate = to_vop_plane_state(state);
struct drm_rect *src, *dest;
struct drm_framebuffer *fb = state->fb;
struct drm_format_name_buf format_name;
int i;
DEBUG_PRINT(" win%d-%d: %s\n", win->win_id, win->area_id,
pstate->enable ? "ACTIVE" : "DISABLED");
if (!fb)
return 0;
src = &pstate->src;
dest = &pstate->dest;
drm_get_format_name(fb->format->format, &format_name);
DEBUG_PRINT("\tformat: %s%s%s[%d] color_space[%d]\n",
format_name.str,
/*fb->modifier[0] == DRM_FORMAT_MOD_ARM_AFBC*/0 ? "[AFBC]" : "",
pstate->eotf ? " HDR" : " SDR", pstate->eotf,
pstate->color_space);
DEBUG_PRINT("\tcsc: y2r[%d] r2r[%d] r2y[%d] csc mode[%d]\n",
pstate->y2r_en, pstate->r2r_en, pstate->r2y_en,
pstate->csc_mode);
DEBUG_PRINT("\tzpos: %d\n", pstate->zpos);
DEBUG_PRINT("\tsrc: pos[%dx%d] rect[%dx%d]\n", src->x1 >> 16,
src->y1 >> 16, drm_rect_width(src) >> 16,
drm_rect_height(src) >> 16);
DEBUG_PRINT("\tdst: pos[%dx%d] rect[%dx%d]\n", dest->x1, dest->y1,
drm_rect_width(dest), drm_rect_height(dest));
for (i = 0; i < drm_format_num_planes(fb->format->format); i++) {
dma_addr_t fb_addr = rockchip_fb_get_dma_addr(fb, i);
DEBUG_PRINT("\tbuf[%d]: addr: %pad pitch: %d offset: %d\n",
i, &fb_addr, fb->pitches[i], fb->offsets[i]);
}
return 0;
}
static int vop_crtc_debugfs_dump(struct drm_crtc *crtc, struct seq_file *s)
{
struct vop *vop = to_vop(crtc);
struct drm_crtc_state *crtc_state = crtc->state;
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
struct rockchip_crtc_state *state = to_rockchip_crtc_state(crtc->state);
bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
struct drm_plane *plane;
int i;
DEBUG_PRINT("VOP [%s]: %s\n", dev_name(vop->dev),
crtc_state->active ? "ACTIVE" : "DISABLED");
if (!crtc_state->active)
return 0;
DEBUG_PRINT(" Connector: todo\n");
DEBUG_PRINT("\toverlay_mode[%d] bus_format[%x] output_mode[%x]",
state->yuv_overlay, state->bus_format, state->output_mode);
DEBUG_PRINT(" color_space[%d]\n",
state->color_space);
DEBUG_PRINT(" Display mode: %dx%d%s%d\n",
mode->hdisplay, mode->vdisplay, interlaced ? "i" : "p",
drm_mode_vrefresh(mode));
DEBUG_PRINT("\tclk[%d] real_clk[%d] type[%x] flag[%x]\n",
mode->clock, mode->crtc_clock, mode->type, mode->flags);
DEBUG_PRINT("\tH: %d %d %d %d\n", mode->hdisplay, mode->hsync_start,
mode->hsync_end, mode->htotal);
DEBUG_PRINT("\tV: %d %d %d %d\n", mode->vdisplay, mode->vsync_start,
mode->vsync_end, mode->vtotal);
for (i = 0; i < vop->num_wins; i++) {
plane = &vop->win[i].base;
vop_plane_info_dump(s, plane);
}
DEBUG_PRINT(" post: sdr2hdr[%d] hdr2sdr[%d]\n",
state->hdr.sdr2hdr_state.bt1886eotf_post_conv_en,
state->hdr.hdr2sdr_en);
DEBUG_PRINT(" pre : sdr2hdr[%d]\n",
state->hdr.sdr2hdr_state.bt1886eotf_pre_conv_en);
DEBUG_PRINT(" post CSC: r2y[%d] y2r[%d] CSC mode[%d]\n",
state->post_r2y_en, state->post_y2r_en,
state->post_csc_mode);
return 0;
}
static void vop_crtc_regs_dump(struct drm_crtc *crtc, struct seq_file *s)
{
struct vop *vop = to_vop(crtc);
struct drm_crtc_state *crtc_state = crtc->state;
int dump_len = vop->len > 0x400 ? 0x400 : vop->len;
int i;
if (!crtc_state->active)
return;
for (i = 0; i < dump_len; i += 4) {
if (i % 16 == 0)
DEBUG_PRINT("\n0x%08x: ", i);
DEBUG_PRINT("%08x ", vop_readl(vop, i));
}
}
static int vop_gamma_show(struct seq_file *s, void *data)
{
struct drm_info_node *node = s->private;
struct vop *vop = node->info_ent->data;
int i;
if (!vop->lut || !vop->lut_active || !vop->lut_regs)
return 0;
for (i = 0; i < vop->lut_len; i++) {
if (i % 8 == 0)
DEBUG_PRINT("\n");
DEBUG_PRINT("0x%08x ", vop->lut[i]);
}
DEBUG_PRINT("\n");
return 0;
}
#undef DEBUG_PRINT
static struct drm_info_list vop_debugfs_files[] = {
{ "gamma_lut", vop_gamma_show, 0, NULL },
};
static int vop_crtc_debugfs_init(struct drm_minor *minor, struct drm_crtc *crtc)
{
struct vop *vop = to_vop(crtc);
int ret, i;
vop->debugfs = debugfs_create_dir(dev_name(vop->dev),
minor->debugfs_root);
if (!vop->debugfs)
return -ENOMEM;
vop->debugfs_files = kmemdup(vop_debugfs_files,
sizeof(vop_debugfs_files),
GFP_KERNEL);
if (!vop->debugfs_files) {
ret = -ENOMEM;
goto remove;
}
#if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
drm_debugfs_vop_add(crtc, vop->debugfs);
#endif
for (i = 0; i < ARRAY_SIZE(vop_debugfs_files); i++)
vop->debugfs_files[i].data = vop;
ret = drm_debugfs_create_files(vop->debugfs_files,
ARRAY_SIZE(vop_debugfs_files),
vop->debugfs,
minor);
if (ret) {
dev_err(vop->dev, "could not install rockchip_debugfs_list\n");
goto free;
}
return 0;
free:
kfree(vop->debugfs_files);
vop->debugfs_files = NULL;
remove:
debugfs_remove(vop->debugfs);
vop->debugfs = NULL;
return ret;
}
static enum drm_mode_status
vop_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode,
int output_type)
{
struct vop *vop = to_vop(crtc);
const struct vop_data *vop_data = vop->data;
int request_clock = mode->clock;
int clock;
if (mode->hdisplay > vop_data->max_output.width)
return MODE_BAD_HVALUE;
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
VOP_MAJOR(vop->version) == 3 &&
VOP_MINOR(vop->version) <= 2)
return MODE_BAD;
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
request_clock *= 2;
clock = clk_round_rate(vop->dclk, request_clock * 1000) / 1000;
/*
* Hdmi or DisplayPort request a Accurate clock.
*/
if (output_type == DRM_MODE_CONNECTOR_HDMIA ||
output_type == DRM_MODE_CONNECTOR_DisplayPort)
if (clock != request_clock)
return MODE_CLOCK_RANGE;
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 = fb->format->cpp[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 <= 0 || src_height <= 0 || dest_width <= 0 ||
dest_height <= 0)
return 0;
bandwidth = src_width * bpp;
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,
unsigned int *plane_num_total)
{
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_plane_state *vop_plane_state;
struct drm_plane_state *pstate;
struct vop_bandwidth *pbandwidth;
struct drm_plane *plane;
u64 bandwidth;
int cnt = 0, plane_num = 0;
if (!htotal || !vdisplay)
return 0;
drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
pstate = plane->state;
if (pstate->crtc != crtc || !pstate->fb)
continue;
plane_num++;
}
if (plane_num_total)
*plane_num_total += plane_num;
pbandwidth = kmalloc_array(plane_num, sizeof(*pbandwidth),
GFP_KERNEL);
if (!pbandwidth)
return -ENOMEM;
drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
pstate = plane->state;
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);
bandwidth = vop_calc_max_bandwidth(pbandwidth, 0, cnt, vdisplay);
kfree(pbandwidth);
/*
* bandwidth(MB/s)
* = line_bandwidth / line_time
* = line_bandwidth(Byte) * clock(KHZ) / 1000 / htotal
*/
bandwidth *= clock;
do_div(bandwidth, htotal * 1000);
return bandwidth;
}
static void vop_crtc_close(struct drm_crtc *crtc)
{
struct vop *vop = NULL;
if (!crtc)
return;
vop = to_vop(crtc);
mutex_lock(&vop->vop_lock);
if (!vop->is_enabled) {
mutex_unlock(&vop->vop_lock);
return;
}
vop_disable_all_planes(vop);
mutex_unlock(&vop->vop_lock);
}
static void vop_crtc_send_mcu_cmd(struct drm_crtc *crtc, u32 type, u32 value)
{
struct vop *vop = NULL;
if (!crtc)
return;
vop = to_vop(crtc);
mutex_lock(&vop->vop_lock);
if (vop && vop->is_enabled) {
switch (type) {
case MCU_WRCMD:
VOP_CTRL_SET(vop, mcu_rs, 0);
VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
VOP_CTRL_SET(vop, mcu_rs, 1);
break;
case MCU_WRDATA:
VOP_CTRL_SET(vop, mcu_rs, 1);
VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
break;
case MCU_SETBYPASS:
VOP_CTRL_SET(vop, mcu_bypass, value ? 1 : 0);
break;
default:
break;
}
}
mutex_unlock(&vop->vop_lock);
}
static const struct rockchip_crtc_funcs private_crtc_funcs = {
.loader_protect = vop_crtc_loader_protect,
.enable_vblank = vop_crtc_enable_vblank,
.disable_vblank = vop_crtc_disable_vblank,
.cancel_pending_vblank = vop_crtc_cancel_pending_vblank,
.debugfs_init = vop_crtc_debugfs_init,
.debugfs_dump = vop_crtc_debugfs_dump,
.regs_dump = vop_crtc_regs_dump,
.mode_valid = vop_crtc_mode_valid,
.bandwidth = vop_crtc_bandwidth,
.crtc_close = vop_crtc_close,
.crtc_send_mcu_cmd = vop_crtc_send_mcu_cmd,
};
static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@ -1947,14 +2549,16 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
u16 vsync_len = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start;
u16 vact_st = adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vsync_start;
u16 vact_end = vact_st + vdisplay;
int sys_status = drm_crtc_index(crtc) ?
SYS_STATUS_LCDC1 : SYS_STATUS_LCDC0;
uint32_t val;
int act_end;
bool interlaced = !!(adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE);
int for_ddr_freq = 0;
bool dclk_inv;
//rockchip_set_system_status(sys_status);
//vop_lock(vop);
rockchip_set_system_status(sys_status);
vop_lock(vop);
DRM_DEV_INFO(vop->dev, "Update mode to %dx%d%s%d, type: %d\n",
hdisplay, vdisplay, interlaced ? "i" : "p",
adjusted_mode->vrefresh, s->output_type);
@ -2075,7 +2679,7 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
enable_irq(vop->irq);
drm_crtc_vblank_on(crtc);
//vop_unlock(vop);
vop_unlock(vop);
}
static int vop_zpos_cmp(const void *a, const void *b)
@ -2666,7 +3270,6 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
}
}
//vop_update_cabc(crtc, old_crtc_state);
vop_update_hdr(crtc, old_crtc_state);
spin_lock_irqsave(&vop->irq_lock, flags);
@ -3188,6 +3791,7 @@ static int vop_create_crtc(struct vop *vop)
init_completion(&vop->dsp_hold_completion);
init_completion(&vop->line_flag_completion);
crtc->port = port;
rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
#define VOP_ATTACH_MODE_CONFIG_PROP(prop, v) \
drm_object_attach_property(&crtc->base, drm_dev->mode_config.prop, v)