diff options
author | Daniel Vetter <daniel.vetter@ffwll.ch> | 2012-12-10 19:07:12 -0500 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2013-01-20 16:17:09 -0500 |
commit | b0d1232589df5575c5971224ac4cb30e7e525884 (patch) | |
tree | 21336a0066a019ad4f5bff093a8f824c50540d83 /drivers/gpu/drm/drm_crtc.c | |
parent | 6c2a75325c800de286166c693e0cd33c3a1c5ec8 (diff) |
drm: refcounting for crtc framebuffers
With the prep patch to encapsulate ->set_crtc calls, this is now
rather easy. Hooray for inconsistent semantics between ->set_crtc and
->page_flip, where the driver callback is supposed to update the fb
pointer, and ->update_plane, where the drm core does the same.
Also, since the drm core functions check crtc->fb before calling into
driver callbacks, we can't really reduce the critical sections
protected by the mode_config locks.
Reviewed-by: Rob Clark <rob@ti.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/drm_crtc.c')
-rw-r--r-- | drivers/gpu/drm/drm_crtc.c | 37 |
1 files changed, 30 insertions, 7 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 64ef12079528..6dc75ee10079 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c | |||
@@ -1984,8 +1984,21 @@ out: | |||
1984 | int drm_mode_set_config_internal(struct drm_mode_set *set) | 1984 | int drm_mode_set_config_internal(struct drm_mode_set *set) |
1985 | { | 1985 | { |
1986 | struct drm_crtc *crtc = set->crtc; | 1986 | struct drm_crtc *crtc = set->crtc; |
1987 | struct drm_framebuffer *fb, *old_fb; | ||
1988 | int ret; | ||
1989 | |||
1990 | old_fb = crtc->fb; | ||
1991 | fb = set->fb; | ||
1987 | 1992 | ||
1988 | return crtc->funcs->set_config(set); | 1993 | ret = crtc->funcs->set_config(set); |
1994 | if (ret == 0) { | ||
1995 | if (old_fb) | ||
1996 | drm_framebuffer_unreference(old_fb); | ||
1997 | if (fb) | ||
1998 | drm_framebuffer_reference(fb); | ||
1999 | } | ||
2000 | |||
2001 | return ret; | ||
1989 | } | 2002 | } |
1990 | EXPORT_SYMBOL(drm_mode_set_config_internal); | 2003 | EXPORT_SYMBOL(drm_mode_set_config_internal); |
1991 | 2004 | ||
@@ -2046,6 +2059,8 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, | |||
2046 | goto out; | 2059 | goto out; |
2047 | } | 2060 | } |
2048 | fb = crtc->fb; | 2061 | fb = crtc->fb; |
2062 | /* Make refcounting symmetric with the lookup path. */ | ||
2063 | drm_framebuffer_reference(fb); | ||
2049 | } else { | 2064 | } else { |
2050 | fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); | 2065 | fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); |
2051 | if (!fb) { | 2066 | if (!fb) { |
@@ -2054,9 +2069,6 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, | |||
2054 | ret = -EINVAL; | 2069 | ret = -EINVAL; |
2055 | goto out; | 2070 | goto out; |
2056 | } | 2071 | } |
2057 | /* fb is protect by the mode_config lock, so drop the | ||
2058 | * ref immediately */ | ||
2059 | drm_framebuffer_unreference(fb); | ||
2060 | } | 2072 | } |
2061 | 2073 | ||
2062 | mode = drm_mode_create(dev); | 2074 | mode = drm_mode_create(dev); |
@@ -2156,6 +2168,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, | |||
2156 | ret = drm_mode_set_config_internal(&set); | 2168 | ret = drm_mode_set_config_internal(&set); |
2157 | 2169 | ||
2158 | out: | 2170 | out: |
2171 | if (fb) | ||
2172 | drm_framebuffer_unreference(fb); | ||
2173 | |||
2159 | kfree(connector_set); | 2174 | kfree(connector_set); |
2160 | drm_mode_destroy(dev, mode); | 2175 | drm_mode_destroy(dev, mode); |
2161 | drm_modeset_unlock_all(dev); | 2176 | drm_modeset_unlock_all(dev); |
@@ -3656,7 +3671,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, | |||
3656 | struct drm_mode_crtc_page_flip *page_flip = data; | 3671 | struct drm_mode_crtc_page_flip *page_flip = data; |
3657 | struct drm_mode_object *obj; | 3672 | struct drm_mode_object *obj; |
3658 | struct drm_crtc *crtc; | 3673 | struct drm_crtc *crtc; |
3659 | struct drm_framebuffer *fb; | 3674 | struct drm_framebuffer *fb = NULL, *old_fb = NULL; |
3660 | struct drm_pending_vblank_event *e = NULL; | 3675 | struct drm_pending_vblank_event *e = NULL; |
3661 | unsigned long flags; | 3676 | unsigned long flags; |
3662 | int hdisplay, vdisplay; | 3677 | int hdisplay, vdisplay; |
@@ -3687,8 +3702,6 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, | |||
3687 | fb = drm_framebuffer_lookup(dev, page_flip->fb_id); | 3702 | fb = drm_framebuffer_lookup(dev, page_flip->fb_id); |
3688 | if (!fb) | 3703 | if (!fb) |
3689 | goto out; | 3704 | goto out; |
3690 | /* fb is protect by the mode_config lock, so drop the ref immediately */ | ||
3691 | drm_framebuffer_unreference(fb); | ||
3692 | 3705 | ||
3693 | hdisplay = crtc->mode.hdisplay; | 3706 | hdisplay = crtc->mode.hdisplay; |
3694 | vdisplay = crtc->mode.vdisplay; | 3707 | vdisplay = crtc->mode.vdisplay; |
@@ -3734,6 +3747,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, | |||
3734 | (void (*) (struct drm_pending_event *)) kfree; | 3747 | (void (*) (struct drm_pending_event *)) kfree; |
3735 | } | 3748 | } |
3736 | 3749 | ||
3750 | old_fb = crtc->fb; | ||
3737 | ret = crtc->funcs->page_flip(crtc, fb, e); | 3751 | ret = crtc->funcs->page_flip(crtc, fb, e); |
3738 | if (ret) { | 3752 | if (ret) { |
3739 | if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { | 3753 | if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { |
@@ -3742,9 +3756,18 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, | |||
3742 | spin_unlock_irqrestore(&dev->event_lock, flags); | 3756 | spin_unlock_irqrestore(&dev->event_lock, flags); |
3743 | kfree(e); | 3757 | kfree(e); |
3744 | } | 3758 | } |
3759 | /* Keep the old fb, don't unref it. */ | ||
3760 | old_fb = NULL; | ||
3761 | } else { | ||
3762 | /* Unref only the old framebuffer. */ | ||
3763 | fb = NULL; | ||
3745 | } | 3764 | } |
3746 | 3765 | ||
3747 | out: | 3766 | out: |
3767 | if (fb) | ||
3768 | drm_framebuffer_unreference(fb); | ||
3769 | if (old_fb) | ||
3770 | drm_framebuffer_unreference(old_fb); | ||
3748 | drm_modeset_unlock_all(dev); | 3771 | drm_modeset_unlock_all(dev); |
3749 | return ret; | 3772 | return ret; |
3750 | } | 3773 | } |