video: adf: replace fbdev helper's open flag with refcount
authorGreg Hackmann <ghackmann@google.com>
Mon, 24 Mar 2014 23:45:43 +0000 (16:45 -0700)
committerGreg Hackmann <ghackmann@google.com>
Tue, 25 Mar 2014 00:43:12 +0000 (17:43 -0700)
A device's fb_info is shared between clients.  fb_release() is called
when each client is released, not just the last one.  Since the fbdev
helper needs to release its dma-buf when the last client goes away, it
must keep its own reference count.

fbmem and fbcon hold different locks while calling fb_release(), so
explicit locking is needed.

Change-Id: I42cd659f7633adba7c11f407d4b594bd43305d6a
Signed-off-by: Greg Hackmann <ghackmann@google.com>
drivers/video/adf/adf_fbdev.c
include/video/adf_fbdev.h

index cac34d14cbc2f30669c1c501325ab1fe880fda8d..9d3c245850af64400003a2a4674057dab9cb3a76 100644 (file)
@@ -356,18 +356,25 @@ int adf_fbdev_open(struct fb_info *info, int user)
        struct adf_fbdev *fbdev = info->par;
        int ret;
 
-       if (!fbdev->open) {
+       mutex_lock(&fbdev->refcount_lock);
+
+       if (unlikely(fbdev->refcount == UINT_MAX)) {
+               ret = -EMFILE;
+               goto done;
+       }
+
+       if (!fbdev->refcount) {
                struct drm_mode_modeinfo mode;
                struct fb_videomode fbmode;
                struct adf_device *dev = adf_interface_parent(fbdev->intf);
 
                ret = adf_device_attach(dev, fbdev->eng, fbdev->intf);
                if (ret < 0 && ret != -EALREADY)
-                       return ret;
+                       goto done;
 
                ret = adf_fb_alloc(fbdev);
                if (ret < 0)
-                       return ret;
+                       goto done;
 
                adf_interface_current_mode(fbdev->intf, &mode);
                adf_modeinfo_to_fb_videomode(&mode, &fbmode);
@@ -379,13 +386,15 @@ int adf_fbdev_open(struct fb_info *info, int user)
 
        ret = adf_fbdev_post(fbdev);
        if (ret < 0) {
-               if (!fbdev->open)
+               if (!fbdev->refcount)
                        adf_fb_destroy(fbdev);
-               return ret;
+               goto done;
        }
 
-       fbdev->open = true;
-       return 0;
+       fbdev->refcount++;
+done:
+       mutex_unlock(&fbdev->refcount_lock);
+       return ret;
 }
 EXPORT_SYMBOL(adf_fbdev_open);
 
@@ -395,8 +404,12 @@ EXPORT_SYMBOL(adf_fbdev_open);
 int adf_fbdev_release(struct fb_info *info, int user)
 {
        struct adf_fbdev *fbdev = info->par;
-       adf_fb_destroy(fbdev);
-       fbdev->open = false;
+       mutex_lock(&fbdev->refcount_lock);
+       BUG_ON(!fbdev->refcount);
+       fbdev->refcount--;
+       if (!fbdev->refcount)
+               adf_fb_destroy(fbdev);
+       mutex_unlock(&fbdev->refcount_lock);
        return 0;
 }
 EXPORT_SYMBOL(adf_fbdev_release);
@@ -601,6 +614,7 @@ int adf_fbdev_init(struct adf_fbdev *fbdev, struct adf_interface *interface,
                dev_err(dev, "allocating framebuffer device failed\n");
                return -ENOMEM;
        }
+       mutex_init(&fbdev->refcount_lock);
        fbdev->default_xres_virtual = xres_virtual;
        fbdev->default_yres_virtual = yres_virtual;
        fbdev->default_format = format;
@@ -644,8 +658,8 @@ EXPORT_SYMBOL(adf_fbdev_init);
 void adf_fbdev_destroy(struct adf_fbdev *fbdev)
 {
        unregister_framebuffer(fbdev->info);
-       if (WARN_ON(fbdev->open))
-               adf_fb_destroy(fbdev);
+       BUG_ON(fbdev->refcount);
+       mutex_destroy(&fbdev->refcount_lock);
        framebuffer_release(fbdev->info);
 }
 EXPORT_SYMBOL(adf_fbdev_destroy);
index 9c349144b5cdccc61839573db2aea3bb40c28a11..e4d6fb7b25df46a14978a6d0edd53b2a953e7735 100644 (file)
@@ -16,6 +16,7 @@
 #define _VIDEO_ADF_FBDEV_H_
 
 #include <linux/fb.h>
+#include <linux/mutex.h>
 #include <video/adf.h>
 
 struct adf_fbdev {
@@ -24,7 +25,8 @@ struct adf_fbdev {
        struct fb_info *info;
        u32 pseudo_palette[16];
 
-       bool open;
+       unsigned int refcount;
+       struct mutex refcount_lock;
 
        struct dma_buf *dma_buf;
        u32 offset;