]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/gpu/drm/drm_crtc.c
Merge tag 'drm-intel-next-2014-09-01' of git://anongit.freedesktop.org/drm-intel...
[mirror_ubuntu-artful-kernel.git] / drivers / gpu / drm / drm_crtc.c
index d7e4c0e2e7964f5321675cff0000fb3c8f8fcc65..7d7c1fd15443f233cc8c75f4dd729abe32c4ff8b 100644 (file)
@@ -45,101 +45,6 @@ static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev,
                                                        struct drm_mode_fb_cmd2 *r,
                                                        struct drm_file *file_priv);
 
-/**
- * drm_modeset_lock_all - take all modeset locks
- * @dev: drm device
- *
- * This function takes all modeset locks, suitable where a more fine-grained
- * scheme isn't (yet) implemented. Locks must be dropped with
- * drm_modeset_unlock_all.
- */
-void drm_modeset_lock_all(struct drm_device *dev)
-{
-       struct drm_mode_config *config = &dev->mode_config;
-       struct drm_modeset_acquire_ctx *ctx;
-       int ret;
-
-       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
-       if (WARN_ON(!ctx))
-               return;
-
-       mutex_lock(&config->mutex);
-
-       drm_modeset_acquire_init(ctx, 0);
-
-retry:
-       ret = drm_modeset_lock(&config->connection_mutex, ctx);
-       if (ret)
-               goto fail;
-       ret = drm_modeset_lock_all_crtcs(dev, ctx);
-       if (ret)
-               goto fail;
-
-       WARN_ON(config->acquire_ctx);
-
-       /* now we hold the locks, so now that it is safe, stash the
-        * ctx for drm_modeset_unlock_all():
-        */
-       config->acquire_ctx = ctx;
-
-       drm_warn_on_modeset_not_all_locked(dev);
-
-       return;
-
-fail:
-       if (ret == -EDEADLK) {
-               drm_modeset_backoff(ctx);
-               goto retry;
-       }
-}
-EXPORT_SYMBOL(drm_modeset_lock_all);
-
-/**
- * drm_modeset_unlock_all - drop all modeset locks
- * @dev: device
- *
- * This function drop all modeset locks taken by drm_modeset_lock_all.
- */
-void drm_modeset_unlock_all(struct drm_device *dev)
-{
-       struct drm_mode_config *config = &dev->mode_config;
-       struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
-
-       if (WARN_ON(!ctx))
-               return;
-
-       config->acquire_ctx = NULL;
-       drm_modeset_drop_locks(ctx);
-       drm_modeset_acquire_fini(ctx);
-
-       kfree(ctx);
-
-       mutex_unlock(&dev->mode_config.mutex);
-}
-EXPORT_SYMBOL(drm_modeset_unlock_all);
-
-/**
- * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked
- * @dev: device
- *
- * Useful as a debug assert.
- */
-void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
-{
-       struct drm_crtc *crtc;
-
-       /* Locking is currently fubar in the panic handler. */
-       if (oops_in_progress)
-               return;
-
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-               WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
-
-       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
-       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
-}
-EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
-
 /* Avoid boilerplate.  I'm tired of typing. */
 #define DRM_ENUM_NAME_FN(fnname, list)                         \
        const char *fnname(int val)                             \
@@ -446,8 +351,12 @@ static struct drm_mode_object *_object_find(struct drm_device *dev,
 
        mutex_lock(&dev->mode_config.idr_mutex);
        obj = idr_find(&dev->mode_config.crtc_idr, id);
-       if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
-           (obj->id != id))
+       if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type)
+               obj = NULL;
+       if (obj && obj->id != id)
+               obj = NULL;
+       /* don't leak out unref'd fb's */
+       if (obj && (obj->type == DRM_MODE_OBJECT_FB))
                obj = NULL;
        mutex_unlock(&dev->mode_config.idr_mutex);
 
@@ -474,9 +383,6 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
         * function.*/
        WARN_ON(type == DRM_MODE_OBJECT_FB);
        obj = _object_find(dev, id, type);
-       /* don't leak out unref'd fb's */
-       if (obj && (obj->type == DRM_MODE_OBJECT_FB))
-               obj = NULL;
        return obj;
 }
 EXPORT_SYMBOL(drm_mode_object_find);
@@ -514,9 +420,6 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
        if (ret)
                goto out;
 
-       /* Grab the idr reference. */
-       drm_framebuffer_reference(fb);
-
        dev->mode_config.num_fb++;
        list_add(&fb->head, &dev->mode_config.fb_list);
 out:
@@ -526,10 +429,34 @@ out:
 }
 EXPORT_SYMBOL(drm_framebuffer_init);
 
+/* dev->mode_config.fb_lock must be held! */
+static void __drm_framebuffer_unregister(struct drm_device *dev,
+                                        struct drm_framebuffer *fb)
+{
+       mutex_lock(&dev->mode_config.idr_mutex);
+       idr_remove(&dev->mode_config.crtc_idr, fb->base.id);
+       mutex_unlock(&dev->mode_config.idr_mutex);
+
+       fb->base.id = 0;
+}
+
 static void drm_framebuffer_free(struct kref *kref)
 {
        struct drm_framebuffer *fb =
                        container_of(kref, struct drm_framebuffer, refcount);
+       struct drm_device *dev = fb->dev;
+
+       /*
+        * The lookup idr holds a weak reference, which has not necessarily been
+        * removed at this point. Check for that.
+        */
+       mutex_lock(&dev->mode_config.fb_lock);
+       if (fb->base.id) {
+               /* Mark fb as reaped and drop idr ref. */
+               __drm_framebuffer_unregister(dev, fb);
+       }
+       mutex_unlock(&dev->mode_config.fb_lock);
+
        fb->funcs->destroy(fb);
 }
 
@@ -566,8 +493,10 @@ struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
 
        mutex_lock(&dev->mode_config.fb_lock);
        fb = __drm_framebuffer_lookup(dev, id);
-       if (fb)
-               drm_framebuffer_reference(fb);
+       if (fb) {
+               if (!kref_get_unless_zero(&fb->refcount))
+                       fb = NULL;
+       }
        mutex_unlock(&dev->mode_config.fb_lock);
 
        return fb;
@@ -611,19 +540,6 @@ static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
        kref_put(&fb->refcount, drm_framebuffer_free_bug);
 }
 
-/* dev->mode_config.fb_lock must be held! */
-static void __drm_framebuffer_unregister(struct drm_device *dev,
-                                        struct drm_framebuffer *fb)
-{
-       mutex_lock(&dev->mode_config.idr_mutex);
-       idr_remove(&dev->mode_config.crtc_idr, fb->base.id);
-       mutex_unlock(&dev->mode_config.idr_mutex);
-
-       fb->base.id = 0;
-
-       __drm_framebuffer_unreference(fb);
-}
-
 /**
  * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
  * @fb: fb to unregister
@@ -851,6 +767,59 @@ static void drm_mode_remove(struct drm_connector *connector,
        drm_mode_destroy(connector->dev, mode);
 }
 
+/**
+ * drm_connector_get_cmdline_mode - reads the user's cmdline mode
+ * @connector: connector to quwery
+ * @mode: returned mode
+ *
+ * The kernel supports per-connector configration of its consoles through
+ * use of the video= parameter. This function parses that option and
+ * extracts the user's specified mode (or enable/disable status) for a
+ * particular connector. This is typically only used during the early fbdev
+ * setup.
+ */
+static void drm_connector_get_cmdline_mode(struct drm_connector *connector)
+{
+       struct drm_cmdline_mode *mode = &connector->cmdline_mode;
+       char *option = NULL;
+
+       if (fb_get_options(connector->name, &option))
+               return;
+
+       if (!drm_mode_parse_command_line_for_connector(option,
+                                                      connector,
+                                                      mode))
+               return;
+
+       if (mode->force) {
+               const char *s;
+
+               switch (mode->force) {
+               case DRM_FORCE_OFF:
+                       s = "OFF";
+                       break;
+               case DRM_FORCE_ON_DIGITAL:
+                       s = "ON - dig";
+                       break;
+               default:
+               case DRM_FORCE_ON:
+                       s = "ON";
+                       break;
+               }
+
+               DRM_INFO("forcing %s connector %s\n", connector->name, s);
+               connector->force = mode->force;
+       }
+
+       DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
+                     connector->name,
+                     mode->xres, mode->yres,
+                     mode->refresh_specified ? mode->refresh : 60,
+                     mode->rb ? " reduced blanking" : "",
+                     mode->margins ? " with margins" : "",
+                     mode->interlace ?  " interlaced" : "");
+}
+
 /**
  * drm_connector_init - Init a preallocated connector
  * @dev: DRM device
@@ -903,6 +872,8 @@ int drm_connector_init(struct drm_device *dev,
        connector->edid_blob_ptr = NULL;
        connector->status = connector_status_unknown;
 
+       drm_connector_get_cmdline_mode(connector);
+
        list_add_tail(&connector->head, &dev->mode_config.connector_list);
        dev->mode_config.num_connector++;
 
@@ -955,6 +926,29 @@ void drm_connector_cleanup(struct drm_connector *connector)
 }
 EXPORT_SYMBOL(drm_connector_cleanup);
 
+/**
+ * drm_connector_index - find the index of a registered connector
+ * @connector: connector to find index for
+ *
+ * Given a registered connector, return the index of that connector within a DRM
+ * device's list of connectors.
+ */
+unsigned int drm_connector_index(struct drm_connector *connector)
+{
+       unsigned int index = 0;
+       struct drm_connector *tmp;
+
+       list_for_each_entry(tmp, &connector->dev->mode_config.connector_list, head) {
+               if (tmp == connector)
+                       return index;
+
+               index++;
+       }
+
+       BUG();
+}
+EXPORT_SYMBOL(drm_connector_index);
+
 /**
  * drm_connector_register - register a connector
  * @connector: the connector to register
@@ -1259,6 +1253,29 @@ void drm_plane_cleanup(struct drm_plane *plane)
 }
 EXPORT_SYMBOL(drm_plane_cleanup);
 
+/**
+ * drm_plane_index - find the index of a registered plane
+ * @plane: plane to find index for
+ *
+ * Given a registered plane, return the index of that CRTC within a DRM
+ * device's list of planes.
+ */
+unsigned int drm_plane_index(struct drm_plane *plane)
+{
+       unsigned int index = 0;
+       struct drm_plane *tmp;
+
+       list_for_each_entry(tmp, &plane->dev->mode_config.plane_list, head) {
+               if (tmp == plane)
+                       return index;
+
+               index++;
+       }
+
+       BUG();
+}
+EXPORT_SYMBOL(drm_plane_index);
+
 /**
  * drm_plane_force_disable - Forcibly disable a plane
  * @plane: plane to disable
@@ -1270,19 +1287,21 @@ EXPORT_SYMBOL(drm_plane_cleanup);
  */
 void drm_plane_force_disable(struct drm_plane *plane)
 {
-       struct drm_framebuffer *old_fb = plane->fb;
        int ret;
 
-       if (!old_fb)
+       if (!plane->fb)
                return;
 
+       plane->old_fb = plane->fb;
        ret = plane->funcs->disable_plane(plane);
        if (ret) {
                DRM_ERROR("failed to disable plane with busy fb\n");
+               plane->old_fb = NULL;
                return;
        }
        /* disconnect the plane from the fb and crtc: */
-       __drm_framebuffer_unreference(old_fb);
+       __drm_framebuffer_unreference(plane->old_fb);
+       plane->old_fb = NULL;
        plane->fb = NULL;
        plane->crtc = NULL;
 }
@@ -2258,23 +2277,21 @@ static int setplane_internal(struct drm_plane *plane,
                             uint32_t src_w, uint32_t src_h)
 {
        struct drm_device *dev = plane->dev;
-       struct drm_framebuffer *old_fb = NULL;
        int ret = 0;
        unsigned int fb_width, fb_height;
        int i;
 
+       drm_modeset_lock_all(dev);
        /* No fb means shut it down */
        if (!fb) {
-               drm_modeset_lock_all(dev);
-               old_fb = plane->fb;
+               plane->old_fb = plane->fb;
                ret = plane->funcs->disable_plane(plane);
                if (!ret) {
                        plane->crtc = NULL;
                        plane->fb = NULL;
                } else {
-                       old_fb = NULL;
+                       plane->old_fb = NULL;
                }
-               drm_modeset_unlock_all(dev);
                goto out;
        }
 
@@ -2314,8 +2331,7 @@ static int setplane_internal(struct drm_plane *plane,
                goto out;
        }
 
-       drm_modeset_lock_all(dev);
-       old_fb = plane->fb;
+       plane->old_fb = plane->fb;
        ret = plane->funcs->update_plane(plane, crtc, fb,
                                         crtc_x, crtc_y, crtc_w, crtc_h,
                                         src_x, src_y, src_w, src_h);
@@ -2324,15 +2340,16 @@ static int setplane_internal(struct drm_plane *plane,
                plane->fb = fb;
                fb = NULL;
        } else {
-               old_fb = NULL;
+               plane->old_fb = NULL;
        }
-       drm_modeset_unlock_all(dev);
 
 out:
        if (fb)
                drm_framebuffer_unreference(fb);
-       if (old_fb)
-               drm_framebuffer_unreference(old_fb);
+       if (plane->old_fb)
+               drm_framebuffer_unreference(plane->old_fb);
+       plane->old_fb = NULL;
+       drm_modeset_unlock_all(dev);
 
        return ret;
 
@@ -2439,7 +2456,7 @@ int drm_mode_set_config_internal(struct drm_mode_set *set)
         * crtcs. Atomic modeset will have saner semantics ...
         */
        list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head)
-               tmp->old_fb = tmp->primary->fb;
+               tmp->primary->old_fb = tmp->primary->fb;
 
        fb = set->fb;
 
@@ -2452,8 +2469,9 @@ int drm_mode_set_config_internal(struct drm_mode_set *set)
        list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
                if (tmp->primary->fb)
                        drm_framebuffer_reference(tmp->primary->fb);
-               if (tmp->old_fb)
-                       drm_framebuffer_unreference(tmp->old_fb);
+               if (tmp->primary->old_fb)
+                       drm_framebuffer_unreference(tmp->primary->old_fb);
+               tmp->primary->old_fb = NULL;
        }
 
        return ret;
@@ -2784,7 +2802,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
        if (crtc->cursor)
                return drm_mode_cursor_universal(crtc, req, file_priv);
 
-       drm_modeset_lock(&crtc->mutex, NULL);
+       drm_modeset_lock_crtc(crtc);
        if (req->flags & DRM_MODE_CURSOR_BO) {
                if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
                        ret = -ENXIO;
@@ -2808,7 +2826,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
                }
        }
 out:
-       drm_modeset_unlock(&crtc->mutex);
+       drm_modeset_unlock_crtc(crtc);
 
        return ret;
 
@@ -3494,9 +3512,10 @@ EXPORT_SYMBOL(drm_property_create_enum);
  * @flags: flags specifying the property type
  * @name: name of the property
  * @props: enumeration lists with property bitflags
- * @num_values: number of pre-defined values
+ * @num_props: size of the @props array
+ * @supported_bits: bitmask of all supported enumeration values
  *
- * This creates a new generic drm property which can then be attached to a drm
+ * This creates a new bitmask drm property which can then be attached to a drm
  * object with drm_object_attach_property. The returned property object must be
  * freed with drm_property_destroy.
  *
@@ -4543,7 +4562,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 {
        struct drm_mode_crtc_page_flip *page_flip = data;
        struct drm_crtc *crtc;
-       struct drm_framebuffer *fb = NULL, *old_fb = NULL;
+       struct drm_framebuffer *fb = NULL;
        struct drm_pending_vblank_event *e = NULL;
        unsigned long flags;
        int ret = -EINVAL;
@@ -4559,7 +4578,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
        if (!crtc)
                return -ENOENT;
 
-       drm_modeset_lock(&crtc->mutex, NULL);
+       drm_modeset_lock_crtc(crtc);
        if (crtc->primary->fb == NULL) {
                /* The framebuffer is currently unbound, presumably
                 * due to a hotplug event, that userspace has not
@@ -4615,7 +4634,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                        (void (*) (struct drm_pending_event *)) kfree;
        }
 
-       old_fb = crtc->primary->fb;
+       crtc->primary->old_fb = crtc->primary->fb;
        ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
        if (ret) {
                if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
@@ -4625,7 +4644,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                        kfree(e);
                }
                /* Keep the old fb, don't unref it. */
-               old_fb = NULL;
+               crtc->primary->old_fb = NULL;
        } else {
                /*
                 * Warn if the driver hasn't properly updated the crtc->fb
@@ -4641,9 +4660,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 out:
        if (fb)
                drm_framebuffer_unreference(fb);
-       if (old_fb)
-               drm_framebuffer_unreference(old_fb);
-       drm_modeset_unlock(&crtc->mutex);
+       if (crtc->primary->old_fb)
+               drm_framebuffer_unreference(crtc->primary->old_fb);
+       crtc->primary->old_fb = NULL;
+       drm_modeset_unlock_crtc(crtc);
 
        return ret;
 }
@@ -4659,9 +4679,14 @@ out:
 void drm_mode_config_reset(struct drm_device *dev)
 {
        struct drm_crtc *crtc;
+       struct drm_plane *plane;
        struct drm_encoder *encoder;
        struct drm_connector *connector;
 
+       list_for_each_entry(plane, &dev->mode_config.plane_list, head)
+               if (plane->funcs->reset)
+                       plane->funcs->reset(plane);
+
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
                if (crtc->funcs->reset)
                        crtc->funcs->reset(crtc);