static void vigs_crtc_dpms(struct drm_crtc *crtc, int mode)
{
- struct vigs_crtc *vigs_crtc = crtc_to_vigs_crtc(crtc);
struct vigs_device *vigs_dev = crtc->dev->dev_private;
int blank, i;
struct fb_event event;
- DRM_DEBUG_KMS("enter: fb_blank = %d, mode = %d\n",
- vigs_crtc->in_fb_blank,
+ DRM_DEBUG_KMS("enter: in_dpms = %d, mode = %d\n",
+ vigs_dev->in_dpms,
mode);
- if (vigs_crtc->in_fb_blank) {
+ if (vigs_dev->in_dpms) {
return;
}
*/
for (i = 0; i < 5; ++i) {
if (console_trylock()) {
+ /*
+ * We must set in_dpms to true while walking
+ * fb call chain because a callback inside the
+ * call chain might do FB_BLANK on its own, i.e.
+ * 'vigs_fbdev_dpms' might get called from here. To avoid
+ * this we set in_dpms to true and 'vigs_fbdev_dpms'
+ * checks this and returns.
+ */
+ vigs_dev->in_dpms = true;
+
fb_notifier_call_chain(FB_EVENT_BLANK, &event);
+
+ vigs_dev->in_dpms = false;
+
console_unlock();
return;
}
}
/*
- * This is 'drm_fb_helper_dpms' modified to set 'fbdev'
- * flag inside 'mode_config.mutex'.
+ * This is 'drm_fb_helper_dpms' modified to set 'in_dpms'
+ * flag inside drm_modeset_lock_all.
*/
static void vigs_fbdev_dpms(struct fb_info *fbi, int dpms_mode)
{
struct drm_fb_helper *fb_helper = fbi->par;
struct drm_device *dev = fb_helper->dev;
+ struct vigs_device *vigs_dev = dev->dev_private;
struct drm_crtc *crtc;
struct vigs_crtc *vigs_crtc;
struct drm_connector *connector;
return;
}
+ if (vigs_dev->in_dpms) {
+ /*
+ * If this is called from 'vigs_crtc_dpms' then we just
+ * return in order to not deadlock. Note that it's
+ * correct to check this flag here without any locks
+ * being held since 'fb_blank' callback is already called with
+ * console lock being held and 'vigs_crtc_dpms' only sets in_dpms
+ * inside the console lock.
+ */
+ return;
+ }
+
/*
* For each CRTC in this fb, turn the connectors on/off.
*/
continue;
}
- vigs_crtc->in_fb_blank = true;
+ vigs_dev->in_dpms = true;
/* Walk the connectors & encoders on this fb turning them on/off */
for (j = 0; j < fb_helper->connector_count; j++) {
dev->mode_config.dpms_property, dpms_mode);
}
- vigs_crtc->in_fb_blank = false;
+ vigs_dev->in_dpms = false;
}
drm_modeset_unlock_all(dev);