UPSTREAM: usb: dwc3: gadget: release spin lock during gadget resume
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / drm_crtc.c
index 030d2db213d65e170e29db3542198e16ef49bed4..4900d2f76e2494a37fc0940290c55493ae4cfb44 100644 (file)
@@ -2802,8 +2802,6 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                        goto out;
                }
 
-               drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
-
                /*
                 * Check whether the primary plane supports the fb pixel format.
                 * Drivers not implementing the universal planes API use a
@@ -3436,6 +3434,24 @@ int drm_mode_addfb2(struct drm_device *dev,
        return 0;
 }
 
+struct drm_mode_rmfb_work {
+       struct work_struct work;
+       struct list_head fbs;
+};
+
+static void drm_mode_rmfb_work_fn(struct work_struct *w)
+{
+       struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work);
+
+       while (!list_empty(&arg->fbs)) {
+               struct drm_framebuffer *fb =
+                       list_first_entry(&arg->fbs, typeof(*fb), filp_head);
+
+               list_del_init(&fb->filp_head);
+               drm_framebuffer_remove(fb);
+       }
+}
+
 /**
  * drm_mode_rmfb - remove an FB from the configuration
  * @dev: drm device for the ioctl
@@ -3476,7 +3492,25 @@ int drm_mode_rmfb(struct drm_device *dev,
        mutex_unlock(&dev->mode_config.fb_lock);
        mutex_unlock(&file_priv->fbs_lock);
 
-       drm_framebuffer_unreference(fb);
+       /*
+        * we now own the reference that was stored in the fbs list
+        *
+        * drm_framebuffer_remove may fail with -EINTR on pending signals,
+        * so run this in a separate stack as there's no way to correctly
+        * handle this after the fb is already removed from the lookup table.
+        */
+       if (atomic_read(&fb->refcount.refcount) > 1) {
+               struct drm_mode_rmfb_work arg;
+
+               INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
+               INIT_LIST_HEAD(&arg.fbs);
+               list_add_tail(&fb->filp_head, &arg.fbs);
+
+               schedule_work(&arg.work);
+               flush_work(&arg.work);
+               destroy_work_on_stack(&arg.work);
+       } else
+               drm_framebuffer_unreference(fb);
 
        return 0;
 
@@ -3629,7 +3663,6 @@ out_err1:
        return ret;
 }
 
-
 /**
  * drm_fb_release - remove and free the FBs on this file
  * @priv: drm file for the ioctl
@@ -3644,6 +3677,9 @@ out_err1:
 void drm_fb_release(struct drm_file *priv)
 {
        struct drm_framebuffer *fb, *tfb;
+       struct drm_mode_rmfb_work arg;
+
+       INIT_LIST_HEAD(&arg.fbs);
 
        /*
         * When the file gets released that means no one else can access the fb
@@ -3656,10 +3692,22 @@ void drm_fb_release(struct drm_file *priv)
         * at it any more.
         */
        list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
-               list_del_init(&fb->filp_head);
+               if (atomic_read(&fb->refcount.refcount) > 1) {
+                       list_move_tail(&fb->filp_head, &arg.fbs);
+               } else {
+                       list_del_init(&fb->filp_head);
 
-               /* This drops the fpriv->fbs reference. */
-               drm_framebuffer_unreference(fb);
+                       /* This drops the fpriv->fbs reference. */
+                       drm_framebuffer_unreference(fb);
+               }
+       }
+
+       if (!list_empty(&arg.fbs)) {
+               INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
+
+               schedule_work(&arg.work);
+               flush_work(&arg.work);
+               destroy_work_on_stack(&arg.work);
        }
 }
 
@@ -5103,6 +5151,20 @@ int drm_mode_connector_attach_encoder(struct drm_connector *connector,
 {
        int i;
 
+       /*
+        * In the past, drivers have attempted to model the static association
+        * of connector to encoder in simple connector/encoder devices using a
+        * direct assignment of connector->encoder = encoder. This connection
+        * is a logical one and the responsibility of the core, so drivers are
+        * expected not to mess with this.
+        *
+        * Note that the error return should've been enough here, but a large
+        * majority of drivers ignores the return value, so add in a big WARN
+        * to get people's attention.
+        */
+       if (WARN_ON(connector->encoder))
+               return -EINVAL;
+
        for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
                if (connector->encoder_ids[i] == 0) {
                        connector->encoder_ids[i] = encoder->base.id;