From: Vincent Abriou Date: Mon, 8 Feb 2016 10:34:31 +0000 (+0100) Subject: drm/sti: add debugfs fps_show/fps_get mechanism for planes X-Git-Tag: v4.6-rc1~61^2~3^2~2 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=bf8f9e4a810206dc91f82a68f50b0eb34f8710ef;p=karo-tx-linux.git drm/sti: add debugfs fps_show/fps_get mechanism for planes Display fps on demand for each used plane: cat /sys/kernel/debug/dri/0/fps_get Display fps in live in the console for each used plane: echo 255 > /sys/kernel/debug/dri/0/fps_show Signed-off-by: Vincent Abriou Reviewed-by: Benjamin Gaignard --- diff --git a/drivers/gpu/drm/sti/sti_cursor.c b/drivers/gpu/drm/sti/sti_cursor.c index 9eac1b969720..82b5711fefef 100644 --- a/drivers/gpu/drm/sti/sti_cursor.c +++ b/drivers/gpu/drm/sti/sti_cursor.c @@ -306,6 +306,8 @@ static void sti_cursor_atomic_update(struct drm_plane *drm_plane, writel(cursor->clut_paddr, cursor->regs + CUR_CML); writel(CUR_CTL_CLUT_UPDATE, cursor->regs + CUR_CTL); + sti_plane_update_fps(plane, true, false); + plane->status = STI_PLANE_UPDATED; } diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index bcb1861ef871..e5ef6b673981 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -20,6 +20,7 @@ #include "sti_crtc.h" #include "sti_drv.h" +#include "sti_plane.h" #define DRIVER_NAME "sti" #define DRIVER_DESC "STMicroelectronics SoC DRM" @@ -30,6 +31,130 @@ #define STI_MAX_FB_HEIGHT 4096 #define STI_MAX_FB_WIDTH 4096 +static int sti_drm_fps_get(void *data, u64 *val) +{ + struct drm_device *drm_dev = data; + struct drm_plane *p; + unsigned int i = 0; + + *val = 0; + list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) { + struct sti_plane *plane = to_sti_plane(p); + + *val |= plane->fps_info.output << i; + i++; + } + + return 0; +} + +static int sti_drm_fps_set(void *data, u64 val) +{ + struct drm_device *drm_dev = data; + struct drm_plane *p; + unsigned int i = 0; + + list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) { + struct sti_plane *plane = to_sti_plane(p); + + plane->fps_info.output = (val >> i) & 1; + i++; + } + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(sti_drm_fps_fops, + sti_drm_fps_get, sti_drm_fps_set, "%llu\n"); + +static int sti_drm_fps_dbg_show(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct drm_device *dev = node->minor->dev; + struct drm_plane *p; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + list_for_each_entry(p, &dev->mode_config.plane_list, head) { + struct sti_plane *plane = to_sti_plane(p); + + seq_printf(s, "%s%s\n", + plane->fps_info.fps_str, + plane->fps_info.fips_str); + } + + mutex_unlock(&dev->struct_mutex); + return 0; +} + +static struct drm_info_list sti_drm_dbg_list[] = { + {"fps_get", sti_drm_fps_dbg_show, 0}, +}; + +static int sti_drm_debugfs_create(struct dentry *root, + struct drm_minor *minor, + const char *name, + const struct file_operations *fops) +{ + struct drm_device *dev = minor->dev; + struct drm_info_node *node; + struct dentry *ent; + + ent = debugfs_create_file(name, S_IRUGO | S_IWUSR, root, dev, fops); + if (IS_ERR(ent)) + return PTR_ERR(ent); + + node = kmalloc(sizeof(*node), GFP_KERNEL); + if (!node) { + debugfs_remove(ent); + return -ENOMEM; + } + + node->minor = minor; + node->dent = ent; + node->info_ent = (void *)fops; + + mutex_lock(&minor->debugfs_lock); + list_add(&node->list, &minor->debugfs_list); + mutex_unlock(&minor->debugfs_lock); + + return 0; +} + +static int sti_drm_dbg_init(struct drm_minor *minor) +{ + int ret; + + ret = drm_debugfs_create_files(sti_drm_dbg_list, + ARRAY_SIZE(sti_drm_dbg_list), + minor->debugfs_root, minor); + if (ret) + goto err; + + ret = sti_drm_debugfs_create(minor->debugfs_root, minor, "fps_show", + &sti_drm_fps_fops); + if (ret) + goto err; + + DRM_INFO("%s: debugfs installed\n", DRIVER_NAME); + return 0; +err: + DRM_ERROR("%s: cannot install debugfs\n", DRIVER_NAME); + return ret; +} + +void sti_drm_dbg_cleanup(struct drm_minor *minor) +{ + drm_debugfs_remove_files(sti_drm_dbg_list, + ARRAY_SIZE(sti_drm_dbg_list), minor); + + drm_debugfs_remove_files((struct drm_info_list *)&sti_drm_fps_fops, + 1, minor); +} + static void sti_atomic_schedule(struct sti_private *private, struct drm_atomic_state *state) { @@ -215,6 +340,9 @@ static struct drm_driver sti_driver = { .gem_prime_vunmap = drm_gem_cma_prime_vunmap, .gem_prime_mmap = drm_gem_cma_prime_mmap, + .debugfs_init = sti_drm_dbg_init, + .debugfs_cleanup = sti_drm_dbg_cleanup, + .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c index 8f0e89fbb3f9..67f606a41c3f 100644 --- a/drivers/gpu/drm/sti/sti_gdp.c +++ b/drivers/gpu/drm/sti/sti_gdp.c @@ -850,6 +850,8 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, } end: + sti_plane_update_fps(plane, true, false); + plane->status = STI_PLANE_UPDATED; } diff --git a/drivers/gpu/drm/sti/sti_hqvdp.c b/drivers/gpu/drm/sti/sti_hqvdp.c index 8d2118a6517b..e9c33fbbfa8d 100644 --- a/drivers/gpu/drm/sti/sti_hqvdp.c +++ b/drivers/gpu/drm/sti/sti_hqvdp.c @@ -326,8 +326,6 @@ struct sti_hqvdp_cmd { * @reset: reset control * @vtg_nb: notifier to handle VTG Vsync * @btm_field_pending: is there any bottom field (interlaced frame) to display - * @curr_field_count: number of field updates - * @last_field_count: number of field updates since last fps measure * @hqvdp_cmd: buffer of commands * @hqvdp_cmd_paddr: physical address of hqvdp_cmd * @vtg: vtg for main data path @@ -343,8 +341,6 @@ struct sti_hqvdp { struct reset_control *reset; struct notifier_block vtg_nb; bool btm_field_pending; - unsigned int curr_field_count; - unsigned int last_field_count; void *hqvdp_cmd; dma_addr_t hqvdp_cmd_paddr; struct sti_vtg *vtg; @@ -836,11 +832,12 @@ int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data) writel(hqvdp->hqvdp_cmd_paddr + btm_cmd_offset, hqvdp->regs + HQVDP_MBX_NEXT_CMD); - hqvdp->curr_field_count++; hqvdp->btm_field_pending = false; dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n", __func__, hqvdp->hqvdp_cmd_paddr); + + sti_plane_update_fps(&hqvdp->plane, false, true); } return 0; @@ -1204,8 +1201,6 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, writel(hqvdp->hqvdp_cmd_paddr + cmd_offset, hqvdp->regs + HQVDP_MBX_NEXT_CMD); - hqvdp->curr_field_count++; - /* Interlaced : get ready to display the bottom field at next Vsync */ if (fb->flags & DRM_MODE_FB_INTERLACED) hqvdp->btm_field_pending = true; @@ -1213,6 +1208,8 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n", __func__, hqvdp->hqvdp_cmd_paddr + cmd_offset); + sti_plane_update_fps(plane, true, true); + plane->status = STI_PLANE_UPDATED; } diff --git a/drivers/gpu/drm/sti/sti_plane.c b/drivers/gpu/drm/sti/sti_plane.c index 2e5c751910c5..f10c98d3f012 100644 --- a/drivers/gpu/drm/sti/sti_plane.c +++ b/drivers/gpu/drm/sti/sti_plane.c @@ -43,6 +43,69 @@ const char *sti_plane_to_str(struct sti_plane *plane) } } +#define STI_FPS_INTERVAL_MS 3000 + +static int sti_plane_timespec_ms_diff(struct timespec lhs, struct timespec rhs) +{ + struct timespec tmp_ts = timespec_sub(lhs, rhs); + u64 tmp_ns = (u64)timespec_to_ns(&tmp_ts); + + do_div(tmp_ns, NSEC_PER_MSEC); + + return (u32)tmp_ns; +} + +void sti_plane_update_fps(struct sti_plane *plane, + bool new_frame, + bool new_field) +{ + struct timespec now; + struct sti_fps_info *fps; + int fpks, fipks, ms_since_last, num_frames, num_fields; + + getrawmonotonic(&now); + + /* Compute number of frame updates */ + fps = &plane->fps_info; + + if (new_field) + fps->curr_field_counter++; + + /* do not perform fps calcul if new_frame is false */ + if (!new_frame) + return; + + fps->curr_frame_counter++; + ms_since_last = sti_plane_timespec_ms_diff(now, fps->last_timestamp); + num_frames = fps->curr_frame_counter - fps->last_frame_counter; + + if (num_frames <= 0 || ms_since_last < STI_FPS_INTERVAL_MS) + return; + + fps->last_timestamp = now; + fps->last_frame_counter = fps->curr_frame_counter; + fpks = (num_frames * 1000000) / ms_since_last; + snprintf(plane->fps_info.fps_str, FPS_LENGTH, "%-6s @ %d.%.3d fps", + sti_plane_to_str(plane), fpks / 1000, fpks % 1000); + + if (fps->curr_field_counter) { + /* Compute number of field updates */ + num_fields = fps->curr_field_counter - fps->last_field_counter; + fps->last_field_counter = fps->curr_field_counter; + fipks = (num_fields * 1000000) / ms_since_last; + snprintf(plane->fps_info.fips_str, + FPS_LENGTH, " - %d.%.3d field/sec", + fipks / 1000, fipks % 1000); + } else { + plane->fps_info.fips_str[0] = '\0'; + } + + if (fps->output) + DRM_INFO("%s%s\n", + plane->fps_info.fps_str, + plane->fps_info.fips_str); +} + static void sti_plane_destroy(struct drm_plane *drm_plane) { DRM_DEBUG_DRIVER("\n"); diff --git a/drivers/gpu/drm/sti/sti_plane.h b/drivers/gpu/drm/sti/sti_plane.h index 86f1e6fc81b9..c50a3b9f5d37 100644 --- a/drivers/gpu/drm/sti/sti_plane.h +++ b/drivers/gpu/drm/sti/sti_plane.h @@ -50,6 +50,18 @@ enum sti_plane_status { STI_PLANE_DISABLED, }; +#define FPS_LENGTH 64 +struct sti_fps_info { + bool output; + unsigned int curr_frame_counter; + unsigned int last_frame_counter; + unsigned int curr_field_counter; + unsigned int last_field_counter; + struct timespec last_timestamp; + char fps_str[FPS_LENGTH]; + char fips_str[FPS_LENGTH]; +}; + /** * STI plane structure * @@ -57,15 +69,20 @@ enum sti_plane_status { * @desc: plane type & id * @status: to know the status of the plane * @zorder: plane z-order + * @fps_info: frame per second info */ struct sti_plane { struct drm_plane drm_plane; enum sti_plane_desc desc; enum sti_plane_status status; int zorder; + struct sti_fps_info fps_info; }; const char *sti_plane_to_str(struct sti_plane *plane); +void sti_plane_update_fps(struct sti_plane *plane, + bool new_frame, + bool new_field); void sti_plane_init_property(struct sti_plane *plane, enum drm_plane_type type); #endif