Display controllers usually provide a lot features like overlay planes, hardware scalers, hardware rotations, ... Most of the time those features work just fine when enabled independently, but activating all of them at the same time tend to show other limitations like the limited memory bus or scaler bandwidth. This sort of bandwidth estimation is hard to get right, and we sometimes fail to predict that a specific workload will not pass, which will most likely result in underrun errors somewhere in the display pipeline (most of the time at the CRTC level). This path aims at making underrun error tracking generic and exposing underrun errors to userspace through a debugfs file. This way, CI tools like IGT, can try to enable a bunch of features and if such underrun errors are reported after the settings have been accepted and applied by the KMS infrastructure. When that happens it means the load tracking algorithm needs some tweaking/fixing. Signed-off-by: Boris Brezillon <boris.brezillon@xxxxxxxxxxx> --- Changes in v2: - New patch --- drivers/gpu/drm/drm_atomic.c | 12 +++++++ drivers/gpu/drm/drm_atomic_helper.c | 4 +++ include/drm/drm_atomic_helper.h | 53 +++++++++++++++++++++++++++++ include/drm/drm_device.h | 15 ++++++++ include/drm/drm_mode_config.h | 26 ++++++++++++++ 5 files changed, 110 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 0e27d45feba9..350348961ab3 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1179,9 +1179,21 @@ static int drm_state_info(struct seq_file *m, void *data) return 0; } +static int drm_underrun_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = m->private; + struct drm_device *dev = node->minor->dev; + struct drm_printer p = drm_seq_file_printer(m); + + drm_printf(&p, "%c\n", atomic_read(&dev->underrun) ? 'Y' : 'N'); + + return 0; +} + /* any use in debugfs files to dump individual planes/crtc/etc? */ static const struct drm_info_list drm_atomic_debugfs_list[] = { {"state", drm_state_info, 0}, + {"underrun", drm_underrun_info, 0 }, }; int drm_atomic_debugfs_init(struct drm_minor *minor) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 6f66777dca4b..9956c2928c5b 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1456,6 +1456,8 @@ void drm_atomic_helper_commit_tail(struct drm_atomic_state *old_state) { struct drm_device *dev = old_state->dev; + drm_atomic_helper_underrun_stop(dev); + drm_atomic_helper_commit_modeset_disables(dev, old_state); drm_atomic_helper_commit_planes(dev, old_state, 0); @@ -1466,6 +1468,8 @@ void drm_atomic_helper_commit_tail(struct drm_atomic_state *old_state) drm_atomic_helper_commit_hw_done(old_state); + drm_atomic_helper_underrun_start(dev); + drm_atomic_helper_wait_for_vblanks(dev, old_state); drm_atomic_helper_cleanup_planes(dev, old_state); diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 25ca0097563e..f712144dd26d 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -221,4 +221,57 @@ drm_atomic_plane_disabling(struct drm_plane_state *old_plane_state, return old_plane_state->crtc && !new_plane_state->crtc; } +/** + * drm_atomic_helper_underrun_set() - Set the underrun bit + * @dev: device the underrun happened on + * + * This function should be called when an underrun happens after an update + * has been committed to the device. + * Underrun interrupts are masked to avoid flooding the CPU with useless + * interrupts. + */ +static inline void drm_atomic_helper_underrun_set(struct drm_device *dev) +{ + atomic_set(&dev->underrun, 1); + + if (dev->mode_config.funcs->atomic_mask_underrun) + dev->mode_config.funcs->atomic_mask_underrun(dev); +} + +/** + * drm_atomic_helper_underrun_stop() - Stop generating underrun events + * @dev: device we want to stop underrun on + * + * This function should be called in the atomic commit path, just before + * committing changes to the hardware. + * The underrun interrupt is simply masked prior to getting underrun events + * while we are applying the new config. + */ +static inline void drm_atomic_helper_underrun_stop(struct drm_device *dev) +{ + if (dev->mode_config.funcs->atomic_mask_underrun) + dev->mode_config.funcs->atomic_mask_underrun(dev); +} + +/** + * drm_atomic_helper_underrun_start() - Start listening to underrun events + * @dev: device we want to start underrun on + * + * This function should be called in the atomic commit path, just after + * the changes have been committed to the hardware. + * Any pending underrun events are cleared before unmasking the interrupt to + * guarantee that further underrun events are really caused by the new + * configuration. + */ +static inline void drm_atomic_helper_underrun_start(struct drm_device *dev) +{ + atomic_set(&dev->underrun, 0); + + if (dev->mode_config.funcs->atomic_clear_underrun) + dev->mode_config.funcs->atomic_clear_underrun(dev); + + if (dev->mode_config.funcs->atomic_unmask_underrun) + dev->mode_config.funcs->atomic_unmask_underrun(dev); +} + #endif /* DRM_ATOMIC_HELPER_H_ */ diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index 42411b3ea0c8..59c63e03a776 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -231,6 +231,21 @@ struct drm_device { * Set by drm_fb_helper_init() and cleared by drm_fb_helper_fini(). */ struct drm_fb_helper *fb_helper; + + /** + * @underrun: + * + * Set to 1 when an underrun error happened since the last atomic + * commit, 0 otherwise. + * This is particularly useful to detect when a specific modeset is too + * demanding in term of memory bandwidth or other kind limitations that + * are hard to guess at atomic check time. + * Drivers should only set underrun to 1 (using + * drm_atomic_helper_underrun_set()) when an underrun interrupt occurs + * and clear if from their atomic commit hook (using + * drm_atomic_helper_underrun_{stop,start}()). + */ + atomic_t underrun; }; #endif diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 8d540cdae57d..28b83e295fd2 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -325,6 +325,32 @@ struct drm_mode_config_funcs { * &drm_private_state and &drm_private_obj. */ void (*atomic_state_free)(struct drm_atomic_state *state); + + /** + * @atomic_mask_underrun: + * + * This is an optional hook called when the core needs to disable/mask + * the underrun interrupt. It's used by drm_atomic_helper_underrun_set() + * and drm_atomic_helper_underrun_stop(). + */ + void (*atomic_mask_underrun)(struct drm_device *dev); + + /** + * @atomic_clear_underrun: + * + * This is an optional hook called when the core needs to clear pending + * underrun events. It's used by drm_atomic_helper_underrun_start() to + * before unmask the underrun interrupt. + */ + void (*atomic_clear_underrun)(struct drm_device *dev); + + /** + * @atomic_unmask_underrun: + * + * This is an optional hook called when the core needs to unmask + * underrun interrupts. It's used by drm_atomic_helper_underrun_start(). + */ + void (*atomic_unmask_underrun)(struct drm_device *dev); }; /** -- 2.17.1 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel