diff options
| -rw-r--r-- | drivers/gpu/drm/drm_crtc.c | 79 | ||||
| -rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 4 | ||||
| -rw-r--r-- | drivers/staging/omapdrm/omap_fbdev.c | 4 | ||||
| -rw-r--r-- | include/drm/drm_crtc.h | 14 |
4 files changed, 85 insertions, 16 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index c418c772a7e5..39afe13a5fa2 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c | |||
| @@ -294,6 +294,8 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, | |||
| 294 | { | 294 | { |
| 295 | int ret; | 295 | int ret; |
| 296 | 296 | ||
| 297 | kref_init(&fb->refcount); | ||
| 298 | |||
| 297 | ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); | 299 | ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); |
| 298 | if (ret) | 300 | if (ret) |
| 299 | return ret; | 301 | return ret; |
| @@ -307,6 +309,38 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, | |||
| 307 | } | 309 | } |
| 308 | EXPORT_SYMBOL(drm_framebuffer_init); | 310 | EXPORT_SYMBOL(drm_framebuffer_init); |
| 309 | 311 | ||
| 312 | static void drm_framebuffer_free(struct kref *kref) | ||
| 313 | { | ||
| 314 | struct drm_framebuffer *fb = | ||
| 315 | container_of(kref, struct drm_framebuffer, refcount); | ||
| 316 | fb->funcs->destroy(fb); | ||
| 317 | } | ||
| 318 | |||
| 319 | /** | ||
| 320 | * drm_framebuffer_unreference - unref a framebuffer | ||
| 321 | * | ||
| 322 | * LOCKING: | ||
| 323 | * Caller must hold mode config lock. | ||
| 324 | */ | ||
| 325 | void drm_framebuffer_unreference(struct drm_framebuffer *fb) | ||
| 326 | { | ||
| 327 | struct drm_device *dev = fb->dev; | ||
| 328 | DRM_DEBUG("FB ID: %d\n", fb->base.id); | ||
| 329 | WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); | ||
| 330 | kref_put(&fb->refcount, drm_framebuffer_free); | ||
| 331 | } | ||
| 332 | EXPORT_SYMBOL(drm_framebuffer_unreference); | ||
| 333 | |||
| 334 | /** | ||
| 335 | * drm_framebuffer_reference - incr the fb refcnt | ||
| 336 | */ | ||
| 337 | void drm_framebuffer_reference(struct drm_framebuffer *fb) | ||
| 338 | { | ||
| 339 | DRM_DEBUG("FB ID: %d\n", fb->base.id); | ||
| 340 | kref_get(&fb->refcount); | ||
| 341 | } | ||
| 342 | EXPORT_SYMBOL(drm_framebuffer_reference); | ||
| 343 | |||
| 310 | /** | 344 | /** |
| 311 | * drm_framebuffer_cleanup - remove a framebuffer object | 345 | * drm_framebuffer_cleanup - remove a framebuffer object |
| 312 | * @fb: framebuffer to remove | 346 | * @fb: framebuffer to remove |
| @@ -320,6 +354,32 @@ EXPORT_SYMBOL(drm_framebuffer_init); | |||
| 320 | void drm_framebuffer_cleanup(struct drm_framebuffer *fb) | 354 | void drm_framebuffer_cleanup(struct drm_framebuffer *fb) |
| 321 | { | 355 | { |
| 322 | struct drm_device *dev = fb->dev; | 356 | struct drm_device *dev = fb->dev; |
| 357 | /* | ||
| 358 | * This could be moved to drm_framebuffer_remove(), but for | ||
| 359 | * debugging is nice to keep around the list of fb's that are | ||
| 360 | * no longer associated w/ a drm_file but are not unreferenced | ||
| 361 | * yet. (i915 and omapdrm have debugfs files which will show | ||
| 362 | * this.) | ||
| 363 | */ | ||
| 364 | drm_mode_object_put(dev, &fb->base); | ||
| 365 | list_del(&fb->head); | ||
| 366 | dev->mode_config.num_fb--; | ||
| 367 | } | ||
| 368 | EXPORT_SYMBOL(drm_framebuffer_cleanup); | ||
| 369 | |||
| 370 | /** | ||
| 371 | * drm_framebuffer_remove - remove and unreference a framebuffer object | ||
| 372 | * @fb: framebuffer to remove | ||
| 373 | * | ||
| 374 | * LOCKING: | ||
| 375 | * Caller must hold mode config lock. | ||
| 376 | * | ||
| 377 | * Scans all the CRTCs and planes in @dev's mode_config. If they're | ||
| 378 | * using @fb, removes it, setting it to NULL. | ||
| 379 | */ | ||
| 380 | void drm_framebuffer_remove(struct drm_framebuffer *fb) | ||
| 381 | { | ||
| 382 | struct drm_device *dev = fb->dev; | ||
| 323 | struct drm_crtc *crtc; | 383 | struct drm_crtc *crtc; |
| 324 | struct drm_plane *plane; | 384 | struct drm_plane *plane; |
| 325 | struct drm_mode_set set; | 385 | struct drm_mode_set set; |
| @@ -350,11 +410,11 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) | |||
| 350 | } | 410 | } |
| 351 | } | 411 | } |
| 352 | 412 | ||
| 353 | drm_mode_object_put(dev, &fb->base); | 413 | list_del(&fb->filp_head); |
| 354 | list_del(&fb->head); | 414 | |
| 355 | dev->mode_config.num_fb--; | 415 | drm_framebuffer_unreference(fb); |
| 356 | } | 416 | } |
| 357 | EXPORT_SYMBOL(drm_framebuffer_cleanup); | 417 | EXPORT_SYMBOL(drm_framebuffer_remove); |
| 358 | 418 | ||
| 359 | /** | 419 | /** |
| 360 | * drm_crtc_init - Initialise a new CRTC object | 420 | * drm_crtc_init - Initialise a new CRTC object |
| @@ -1031,7 +1091,7 @@ void drm_mode_config_cleanup(struct drm_device *dev) | |||
| 1031 | } | 1091 | } |
| 1032 | 1092 | ||
| 1033 | list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { | 1093 | list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { |
| 1034 | fb->funcs->destroy(fb); | 1094 | drm_framebuffer_remove(fb); |
| 1035 | } | 1095 | } |
| 1036 | 1096 | ||
| 1037 | list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, | 1097 | list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, |
| @@ -2337,11 +2397,7 @@ int drm_mode_rmfb(struct drm_device *dev, | |||
| 2337 | goto out; | 2397 | goto out; |
| 2338 | } | 2398 | } |
| 2339 | 2399 | ||
| 2340 | /* TODO release all crtc connected to the framebuffer */ | 2400 | drm_framebuffer_remove(fb); |
| 2341 | /* TODO unhock the destructor from the buffer object */ | ||
| 2342 | |||
| 2343 | list_del(&fb->filp_head); | ||
| 2344 | fb->funcs->destroy(fb); | ||
| 2345 | 2401 | ||
| 2346 | out: | 2402 | out: |
| 2347 | mutex_unlock(&dev->mode_config.mutex); | 2403 | mutex_unlock(&dev->mode_config.mutex); |
| @@ -2491,8 +2547,7 @@ void drm_fb_release(struct drm_file *priv) | |||
| 2491 | 2547 | ||
| 2492 | mutex_lock(&dev->mode_config.mutex); | 2548 | mutex_lock(&dev->mode_config.mutex); |
| 2493 | list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { | 2549 | list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { |
| 2494 | list_del(&fb->filp_head); | 2550 | drm_framebuffer_remove(fb); |
| 2495 | fb->funcs->destroy(fb); | ||
| 2496 | } | 2551 | } |
| 2497 | mutex_unlock(&dev->mode_config.mutex); | 2552 | mutex_unlock(&dev->mode_config.mutex); |
| 2498 | } | 2553 | } |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index d5586cc75163..f4ac43356583 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c | |||
| @@ -266,8 +266,8 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev, | |||
| 266 | /* release drm framebuffer and real buffer */ | 266 | /* release drm framebuffer and real buffer */ |
| 267 | if (fb_helper->fb && fb_helper->fb->funcs) { | 267 | if (fb_helper->fb && fb_helper->fb->funcs) { |
| 268 | fb = fb_helper->fb; | 268 | fb = fb_helper->fb; |
| 269 | if (fb && fb->funcs->destroy) | 269 | if (fb) |
| 270 | fb->funcs->destroy(fb); | 270 | drm_framebuffer_remove(fb); |
| 271 | } | 271 | } |
| 272 | 272 | ||
| 273 | /* release linux framebuffer */ | 273 | /* release linux framebuffer */ |
diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c index 8c6ed3b0c6f6..8a027bb77d97 100644 --- a/drivers/staging/omapdrm/omap_fbdev.c +++ b/drivers/staging/omapdrm/omap_fbdev.c | |||
| @@ -276,7 +276,7 @@ fail: | |||
| 276 | if (fbi) | 276 | if (fbi) |
| 277 | framebuffer_release(fbi); | 277 | framebuffer_release(fbi); |
| 278 | if (fb) | 278 | if (fb) |
| 279 | fb->funcs->destroy(fb); | 279 | drm_framebuffer_remove(fb); |
| 280 | } | 280 | } |
| 281 | 281 | ||
| 282 | return ret; | 282 | return ret; |
| @@ -401,7 +401,7 @@ void omap_fbdev_free(struct drm_device *dev) | |||
| 401 | 401 | ||
| 402 | /* this will free the backing object */ | 402 | /* this will free the backing object */ |
| 403 | if (fbdev->fb) | 403 | if (fbdev->fb) |
| 404 | fbdev->fb->funcs->destroy(fbdev->fb); | 404 | drm_framebuffer_remove(fbdev->fb); |
| 405 | 405 | ||
| 406 | kfree(fbdev); | 406 | kfree(fbdev); |
| 407 | 407 | ||
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index eb91d520ce0b..68fdb299e39f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h | |||
| @@ -218,6 +218,7 @@ struct drm_display_info { | |||
| 218 | }; | 218 | }; |
| 219 | 219 | ||
| 220 | struct drm_framebuffer_funcs { | 220 | struct drm_framebuffer_funcs { |
| 221 | /* note: use drm_framebuffer_remove() */ | ||
| 221 | void (*destroy)(struct drm_framebuffer *framebuffer); | 222 | void (*destroy)(struct drm_framebuffer *framebuffer); |
| 222 | int (*create_handle)(struct drm_framebuffer *fb, | 223 | int (*create_handle)(struct drm_framebuffer *fb, |
| 223 | struct drm_file *file_priv, | 224 | struct drm_file *file_priv, |
| @@ -242,6 +243,16 @@ struct drm_framebuffer_funcs { | |||
| 242 | 243 | ||
| 243 | struct drm_framebuffer { | 244 | struct drm_framebuffer { |
| 244 | struct drm_device *dev; | 245 | struct drm_device *dev; |
| 246 | /* | ||
| 247 | * Note that the fb is refcounted for the benefit of driver internals, | ||
| 248 | * for example some hw, disabling a CRTC/plane is asynchronous, and | ||
| 249 | * scanout does not actually complete until the next vblank. So some | ||
| 250 | * cleanup (like releasing the reference(s) on the backing GEM bo(s)) | ||
| 251 | * should be deferred. In cases like this, the driver would like to | ||
| 252 | * hold a ref to the fb even though it has already been removed from | ||
| 253 | * userspace perspective. | ||
| 254 | */ | ||
| 255 | struct kref refcount; | ||
| 245 | struct list_head head; | 256 | struct list_head head; |
| 246 | struct drm_mode_object base; | 257 | struct drm_mode_object base; |
| 247 | const struct drm_framebuffer_funcs *funcs; | 258 | const struct drm_framebuffer_funcs *funcs; |
| @@ -919,6 +930,9 @@ extern void drm_framebuffer_set_object(struct drm_device *dev, | |||
| 919 | extern int drm_framebuffer_init(struct drm_device *dev, | 930 | extern int drm_framebuffer_init(struct drm_device *dev, |
| 920 | struct drm_framebuffer *fb, | 931 | struct drm_framebuffer *fb, |
| 921 | const struct drm_framebuffer_funcs *funcs); | 932 | const struct drm_framebuffer_funcs *funcs); |
| 933 | extern void drm_framebuffer_unreference(struct drm_framebuffer *fb); | ||
| 934 | extern void drm_framebuffer_reference(struct drm_framebuffer *fb); | ||
| 935 | extern void drm_framebuffer_remove(struct drm_framebuffer *fb); | ||
| 922 | extern void drm_framebuffer_cleanup(struct drm_framebuffer *fb); | 936 | extern void drm_framebuffer_cleanup(struct drm_framebuffer *fb); |
| 923 | extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc); | 937 | extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc); |
| 924 | extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); | 938 | extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); |
