int drm_mode_set_config_internal(struct drm_mode_set *set)
{
struct drm_crtc *crtc = set->crtc;
- struct drm_framebuffer *fb, *old_fb;
+ struct drm_framebuffer *fb;
+ struct drm_crtc *tmp;
int ret;
- old_fb = crtc->fb;
+ /*
+ * NOTE: ->set_config can also disable other crtcs (if we steal all
+ * connectors from it), hence we need to refcount the fbs across all
+ * crtcs. Atomic modeset will have saner semantics ...
+ */
+ list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head)
+ tmp->old_fb = tmp->fb;
+
fb = set->fb;
ret = crtc->funcs->set_config(set);
if (ret == 0) {
/* crtc->fb must be updated by ->set_config, enforces this. */
WARN_ON(fb != crtc->fb);
+ }
- if (old_fb)
- drm_framebuffer_unreference(old_fb);
- if (fb)
- drm_framebuffer_reference(fb);
+ list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
+ if (tmp->fb)
+ drm_framebuffer_reference(tmp->fb);
+ if (tmp->old_fb)
+ drm_framebuffer_unreference(tmp->old_fb);
}
return ret;
/* framebuffer the connector is currently bound to */
struct drm_framebuffer *fb;
+ /* Temporary tracking of the old fb while a modeset is ongoing. Used
+ * by drm_mode_set_config_internal to implement correct refcounting. */
+ struct drm_framebuffer *old_fb;
+
bool enabled;
/* Requested mode from modesetting. */