drm/i915: Update to Linux 3.16
authorFrançois Tigeot <ftigeot@wolfpond.org>
Fri, 24 Jul 2015 08:44:53 +0000 (10:44 +0200)
committerFrançois Tigeot <ftigeot@wolfpond.org>
Sun, 2 Aug 2015 07:08:13 +0000 (09:08 +0200)
* Much improved support for Broadwell GPUs. Acceleration should now be fully
  operational and the giant L4 eDRAM cache is now enabled when present.

* Baytrail/Valleyview support improvements

* prelimary support for Cherryview (14nm Atom SOCs)

* Various fixes and performance improvements on most other GPU generations

* Improved runtime power management

* 5.4GHz DisplayPort support

* Large cursor support (up to 256x256 pixels), useful for high-dpi displays.

* Mapping of user pages into video memory (userptr)
  This allows zero-copy downloads and efficient readback to/from the GPU,
  allowing faster rendering of client-side software rasterisers, mitigation
  of stalls due to read back and faster pipelining of texture data (such as
  pixel buffer objects in GL or data blobs in CL). Mixed CPU/GPU operations
  become more efficient in general.

114 files changed:
sys/dev/drm/drm/Makefile
sys/dev/drm/drm_auth.c
sys/dev/drm/drm_bufs.c
sys/dev/drm/drm_cache.c
sys/dev/drm/drm_crtc.c
sys/dev/drm/drm_crtc_helper.c
sys/dev/drm/drm_crtc_internal.h [new file with mode: 0644]
sys/dev/drm/drm_dragonfly.c [new file with mode: 0644]
sys/dev/drm/drm_drv.c
sys/dev/drm/drm_edid.c
sys/dev/drm/drm_fb_helper.c
sys/dev/drm/drm_gem.c
sys/dev/drm/drm_ioctl.c
sys/dev/drm/drm_irq.c
sys/dev/drm/drm_mm.c
sys/dev/drm/drm_modes.c
sys/dev/drm/drm_modeset_lock.c [new file with mode: 0644]
sys/dev/drm/drm_pci.c
sys/dev/drm/drm_plane_helper.c [new file with mode: 0644]
sys/dev/drm/drm_probe_helper.c [new file with mode: 0644]
sys/dev/drm/drm_scatter.c
sys/dev/drm/drm_stub.c
sys/dev/drm/drm_sysfs.c
sys/dev/drm/i915/Makefile
sys/dev/drm/i915/dvo.h [new file with mode: 0644]
sys/dev/drm/i915/dvo_ch7017.c [new file with mode: 0644]
sys/dev/drm/i915/dvo_ch7xxx.c [new file with mode: 0644]
sys/dev/drm/i915/dvo_ivch.c [new file with mode: 0644]
sys/dev/drm/i915/dvo_ns2501.c [new file with mode: 0644]
sys/dev/drm/i915/dvo_sil164.c [new file with mode: 0644]
sys/dev/drm/i915/dvo_tfp410.c [new file with mode: 0644]
sys/dev/drm/i915/i915_cmd_parser.c [new file with mode: 0644]
sys/dev/drm/i915/i915_dma.c
sys/dev/drm/i915/i915_drv.c
sys/dev/drm/i915/i915_drv.h
sys/dev/drm/i915/i915_gem.c
sys/dev/drm/i915/i915_gem_context.c
sys/dev/drm/i915/i915_gem_evict.c
sys/dev/drm/i915/i915_gem_execbuffer.c
sys/dev/drm/i915/i915_gem_gtt.c
sys/dev/drm/i915/i915_gem_gtt.h [new file with mode: 0644]
sys/dev/drm/i915/i915_gem_render_state.c [new file with mode: 0644]
sys/dev/drm/i915/i915_gem_stolen.c
sys/dev/drm/i915/i915_gem_tiling.c
sys/dev/drm/i915/i915_gem_userptr.c [new file with mode: 0644]
sys/dev/drm/i915/i915_irq.c
sys/dev/drm/i915/i915_params.c [new file with mode: 0644]
sys/dev/drm/i915/i915_reg.h
sys/dev/drm/i915/i915_suspend.c
sys/dev/drm/i915/i915_trace.h
sys/dev/drm/i915/i915_ums.c
sys/dev/drm/i915/intel_bios.c
sys/dev/drm/i915/intel_bios.h
sys/dev/drm/i915/intel_crt.c
sys/dev/drm/i915/intel_ddi.c
sys/dev/drm/i915/intel_display.c
sys/dev/drm/i915/intel_dp.c
sys/dev/drm/i915/intel_drv.h
sys/dev/drm/i915/intel_dsi.c
sys/dev/drm/i915/intel_dsi.h
sys/dev/drm/i915/intel_dsi_cmd.c
sys/dev/drm/i915/intel_dsi_cmd.h
sys/dev/drm/i915/intel_dsi_panel_vbt.c [new file with mode: 0644]
sys/dev/drm/i915/intel_dvo.c [new file with mode: 0644]
sys/dev/drm/i915/intel_fbdev.c
sys/dev/drm/i915/intel_hdmi.c
sys/dev/drm/i915/intel_lvds.c
sys/dev/drm/i915/intel_opregion.c
sys/dev/drm/i915/intel_overlay.c
sys/dev/drm/i915/intel_panel.c
sys/dev/drm/i915/intel_pm.c
sys/dev/drm/i915/intel_renderstate.h [copied from sys/dev/drm/drm_sysfs.c with 56% similarity]
sys/dev/drm/i915/intel_renderstate_gen6.c [new file with mode: 0644]
sys/dev/drm/i915/intel_renderstate_gen7.c [new file with mode: 0644]
sys/dev/drm/i915/intel_renderstate_gen8.c [new file with mode: 0644]
sys/dev/drm/i915/intel_ringbuffer.c
sys/dev/drm/i915/intel_ringbuffer.h
sys/dev/drm/i915/intel_sdvo.c
sys/dev/drm/i915/intel_sideband.c
sys/dev/drm/i915/intel_sprite.c
sys/dev/drm/i915/intel_tv.c
sys/dev/drm/i915/intel_uncore.c
sys/dev/drm/include/drm/drmP.h
sys/dev/drm/include/drm/drm_crtc.h
sys/dev/drm/include/drm/drm_crtc_helper.h
sys/dev/drm/include/drm/drm_dp_helper.h
sys/dev/drm/include/drm/drm_edid.h
sys/dev/drm/include/drm/drm_fb_helper.h
sys/dev/drm/include/drm/drm_mm.h
sys/dev/drm/include/drm/drm_modes.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_modeset_lock.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_os_linux.h
sys/dev/drm/include/drm/drm_plane_helper.h [copied from sys/dev/drm/drm_sysfs.c with 51% similarity]
sys/dev/drm/include/drm/drm_vma_manager.h
sys/dev/drm/include/drm/i915_drm.h
sys/dev/drm/include/drm/i915_pciids.h
sys/dev/drm/include/drm/i915_powerwell.h
sys/dev/drm/include/linux/atomic.h
sys/dev/drm/include/linux/fb.h
sys/dev/drm/include/linux/i2c.h
sys/dev/drm/include/linux/kref.h
sys/dev/drm/include/linux/list.h
sys/dev/drm/include/linux/workqueue.h
sys/dev/drm/include/linux/ww_mutex.h
sys/dev/drm/include/uapi_drm/drm.h
sys/dev/drm/include/uapi_drm/drm_mode.h
sys/dev/drm/include/uapi_drm/i915_drm.h
sys/dev/drm/radeon/atombios_crtc.c
sys/dev/drm/radeon/r100.c
sys/dev/drm/radeon/radeon_connectors.c
sys/dev/drm/radeon/radeon_device.c
sys/dev/drm/radeon/radeon_display.c
sys/dev/drm/radeon/radeon_irq_kms.c
sys/dev/drm/radeon/radeon_legacy_crtc.c

index 7fe2376..3b95150 100644 (file)
@@ -13,6 +13,7 @@ SRCS  = \
        drm_dma.c \
        drm_dp_helper.c \
        drm_dp_iic_helper.c \
+       drm_dragonfly.c \
        drm_drv.c \
        drm_edid.c \
        drm_fb_helper.c \
@@ -27,7 +28,10 @@ SRCS = \
        drm_memory.c \
        drm_mm.c \
        drm_modes.c \
+       drm_modeset_lock.c \
        drm_pci.c \
+       drm_plane_helper.c \
+       drm_probe_helper.c \
        drm_rect.c \
        drm_scatter.c \
        drm_stub.c \
index 9e0cb4d..78a62ac 100644 (file)
@@ -112,34 +112,39 @@ static int drm_remove_magic(struct drm_device *dev, drm_magic_t magic)
 }
 
 /**
- * Called by the client, this returns a unique magic number to be authorized
- * by the master.
+ * Get a unique magic number (ioctl).
  *
- * The master may use its own knowledge of the client (such as the X
- * connection that the magic is passed over) to determine if the magic number
- * should be authenticated.
+ * \param inode device inode.
+ * \param file_priv DRM file private.
+ * \param cmd command.
+ * \param arg pointer to a resulting drm_auth structure.
+ * \return zero on success, or a negative number on failure.
+ *
+ * If there is a magic number in drm_file::magic then use it, otherwise
+ * searches an unique non-zero magic number and add it associating it with \p
+ * file_priv.
+ * This ioctl needs protection by the drm_global_mutex, which protects
+ * struct drm_file::magic and struct drm_magic_entry::priv.
  */
 int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
        static drm_magic_t sequence = 0;
+       static struct spinlock lock = SPINLOCK_INITIALIZER(&lock, "drm_gm");
        struct drm_auth *auth = data;
 
        /* Find unique magic */
        if (file_priv->magic) {
                auth->magic = file_priv->magic;
        } else {
-               DRM_LOCK(dev);
                do {
-                       int old = sequence;
-
-                       auth->magic = old+1;
-
-                       if (!atomic_cmpset_int(&sequence, old, auth->magic))
-                               continue;
+                       spin_lock(&lock);
+                       if (!sequence)
+                               ++sequence;     /* reserve 0 */
+                       auth->magic = sequence++;
+                       spin_unlock(&lock);
                } while (drm_find_file(dev, auth->magic));
                file_priv->magic = auth->magic;
                drm_add_magic(dev, file_priv, auth->magic);
-               DRM_UNLOCK(dev);
        }
 
        DRM_DEBUG("%u\n", auth->magic);
@@ -148,25 +153,29 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
 }
 
 /**
- * Marks the client associated with the given magic number as authenticated.
+ * Authenticate with a magic.
+ *
+ * \param inode device inode.
+ * \param file_priv DRM file private.
+ * \param cmd command.
+ * \param arg pointer to a drm_auth structure.
+ * \return zero if authentication successed, or a negative number otherwise.
+ *
+ * Checks if \p file_priv is associated with the magic number passed in \arg.
+ * This ioctl needs protection by the drm_global_mutex, which protects
+ * struct drm_file::magic and struct drm_magic_entry::priv.
  */
 int drm_authmagic(struct drm_device *dev, void *data,
                  struct drm_file *file_priv)
 {
        struct drm_auth *auth = data;
-       struct drm_file *priv;
+       struct drm_file *file;
 
        DRM_DEBUG("%u\n", auth->magic);
-
-       DRM_LOCK(dev);
-       priv = drm_find_file(dev, auth->magic);
-       if (priv != NULL) {
-               priv->authenticated = 1;
+       if ((file = drm_find_file(dev, auth->magic))) {
+               file->authenticated = 1;
                drm_remove_magic(dev, auth->magic);
-               DRM_UNLOCK(dev);
                return 0;
-       } else {
-               DRM_UNLOCK(dev);
-               return EINVAL;
        }
+       return -EINVAL;
 }
index 09e1fab..d3ef94b 100644 (file)
@@ -526,7 +526,7 @@ static int drm_do_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *reque
                buf->pending = 0;
                buf->file_priv = NULL;
 
-               buf->dev_priv_size = dev->driver->buf_priv_size;
+               buf->dev_priv_size = dev->driver->dev_priv_size;
                buf->dev_private = kmalloc(buf->dev_priv_size, M_DRM,
                                           M_WAITOK | M_NULLOK | M_ZERO);
                if (buf->dev_private == NULL) {
@@ -672,7 +672,7 @@ static int drm_do_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *reque
                        buf->pending = 0;
                        buf->file_priv = NULL;
 
-                       buf->dev_priv_size = dev->driver->buf_priv_size;
+                       buf->dev_priv_size = dev->driver->dev_priv_size;
                        buf->dev_private = kmalloc(buf->dev_priv_size,
                                                   M_DRM,
                                                   M_WAITOK | M_NULLOK |
@@ -788,7 +788,7 @@ static int drm_do_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *reques
                buf->pending = 0;
                buf->file_priv = NULL;
 
-               buf->dev_priv_size = dev->driver->buf_priv_size;
+               buf->dev_priv_size = dev->driver->dev_priv_size;
                buf->dev_private = kmalloc(buf->dev_priv_size, M_DRM,
                                           M_WAITOK | M_NULLOK | M_ZERO);
                if (buf->dev_private == NULL) {
index 2d754d4..c05f8f1 100644 (file)
@@ -37,8 +37,9 @@
 #define cpu_has_clflush        1
 
 void
-drm_clflush_virt_range(char *addr, unsigned long length)
+drm_clflush_virt_range(void *in_addr, unsigned long length)
 {
+       char *addr = in_addr;
        if (cpu_has_clflush) {
                char *end = addr + length;
                cpu_mfence();
index 8b23b05..61821b9 100644 (file)
  */
 #include <linux/ctype.h>
 #include <linux/list.h>
-#include <linux/slab.h>
 #include <linux/export.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
 #include <uapi_drm/drm_fourcc.h>
+#include <linux/slab.h>
+#include <drm/drm_modeset_lock.h>
+
+#include "drm_crtc_internal.h"
 
 /**
  * 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.
+ * 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_crtc *crtc;
+       struct drm_mode_config *config = &dev->mode_config;
+       struct drm_modeset_acquire_ctx *ctx;
+       int ret;
 
-       mutex_lock(&dev->mode_config.mutex);
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (WARN_ON(!ctx))
+               return;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-               lockmgr(&crtc->mutex, LK_EXCLUSIVE);
+       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_crtc *crtc;
+       struct drm_mode_config *config = &dev->mode_config;
+       struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-               mutex_unlock(&crtc->mutex);
+       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);
 }
@@ -74,6 +117,8 @@ 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)
 {
@@ -86,8 +131,9 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
 #endif
 
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-               WARN_ON(!mutex_is_locked(&crtc->mutex));
+               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);
@@ -116,6 +162,13 @@ static const struct drm_prop_enum_list drm_dpms_enum_list[] =
 
 DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
 
+static const struct drm_prop_enum_list drm_plane_type_enum_list[] =
+{
+       { DRM_PLANE_TYPE_OVERLAY, "Overlay" },
+       { DRM_PLANE_TYPE_PRIMARY, "Primary" },
+       { DRM_PLANE_TYPE_CURSOR, "Cursor" },
+};
+
 /*
  * Optional properties
  */
@@ -215,30 +268,46 @@ static const struct drm_prop_enum_list drm_encoder_enum_list[] =
        { DRM_MODE_ENCODER_TVDAC, "TV" },
        { DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
        { DRM_MODE_ENCODER_DSI, "DSI" },
+       { DRM_MODE_ENCODER_DPMST, "DP MST" },
 };
 
-const char *drm_get_encoder_name(const struct drm_encoder *encoder)
+static const struct drm_prop_enum_list drm_subpixel_enum_list[] =
 {
-       static char buf[32];
+       { SubPixelUnknown, "Unknown" },
+       { SubPixelHorizontalRGB, "Horizontal RGB" },
+       { SubPixelHorizontalBGR, "Horizontal BGR" },
+       { SubPixelVerticalRGB, "Vertical RGB" },
+       { SubPixelVerticalBGR, "Vertical BGR" },
+       { SubPixelNone, "None" },
+};
 
-       ksnprintf(buf, 32, "%s-%d",
-                drm_encoder_enum_list[encoder->encoder_type].name,
-                encoder->base.id);
-       return buf;
+void drm_connector_ida_init(void)
+{
+#if 0
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
+               ida_init(&drm_connector_enum_list[i].ida);
+#endif
 }
-EXPORT_SYMBOL(drm_get_encoder_name);
 
-const char *drm_get_connector_name(const struct drm_connector *connector)
+void drm_connector_ida_destroy(void)
 {
-       static char buf[32];
+#if 0
+       int i;
 
-       ksnprintf(buf, 32, "%s-%d",
-                drm_connector_enum_list[connector->connector_type].name,
-                connector->connector_type_id);
-       return buf;
+       for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
+               ida_destroy(&drm_connector_enum_list[i].ida);
+#endif
 }
-EXPORT_SYMBOL(drm_get_connector_name);
 
+/**
+ * drm_get_connector_status_name - return a string for connector status
+ * @status: connector status to compute name of
+ *
+ * In contrast to the other drm_get_*_name functions this one here returns a
+ * const pointer and hence is threadsafe.
+ */
 const char *drm_get_connector_status_name(enum drm_connector_status status)
 {
        if (status == connector_status_connected)
@@ -250,11 +319,33 @@ const char *drm_get_connector_status_name(enum drm_connector_status status)
 }
 EXPORT_SYMBOL(drm_get_connector_status_name);
 
+/**
+ * drm_get_subpixel_order_name - return a string for a given subpixel enum
+ * @order: enum of subpixel_order
+ *
+ * Note you could abuse this and return something out of bounds, but that
+ * would be a caller error.  No unscrubbed user data should make it here.
+ */
+const char *drm_get_subpixel_order_name(enum subpixel_order order)
+{
+       return drm_subpixel_enum_list[order].name;
+}
+EXPORT_SYMBOL(drm_get_subpixel_order_name);
+
 static char printable_char(int c)
 {
        return isascii(c) && isprint(c) ? c : '?';
 }
 
+/**
+ * drm_get_format_name - return a string for drm fourcc format
+ * @format: format to compute name of
+ *
+ * Note that the buffer used by this function is globally shared and owned by
+ * the function itself.
+ *
+ * FIXME: This isn't really multithreading safe.
+ */
 const char *drm_get_format_name(uint32_t format)
 {
        static char buf[32];
@@ -279,14 +370,16 @@ EXPORT_SYMBOL(drm_get_format_name);
  * @obj_type: object type
  *
  * Create a unique identifier based on @ptr in @dev's identifier space.  Used
- * for tracking modes, CRTCs and connectors.
+ * for tracking modes, CRTCs and connectors. Note that despite the _get postfix
+ * modeset identifiers are _not_ reference counted. Hence don't use this for
+ * reference counted modeset objects like framebuffers.
  *
- * RETURNS:
+ * Returns:
  * New unique (relative to other objects in @dev) integer identifier for the
  * object.
  */
-static int drm_mode_object_get(struct drm_device *dev,
-                              struct drm_mode_object *obj, uint32_t obj_type)
+int drm_mode_object_get(struct drm_device *dev,
+                       struct drm_mode_object *obj, uint32_t obj_type)
 {
        int ret;
 
@@ -310,16 +403,33 @@ static int drm_mode_object_get(struct drm_device *dev,
  * @dev: DRM device
  * @object: object to free
  *
- * Free @id from @dev's unique identifier pool.
+ * Free @id from @dev's unique identifier pool. Note that despite the _get
+ * postfix modeset identifiers are _not_ reference counted. Hence don't use this
+ * for reference counted modeset objects like framebuffers.
  */
-static void drm_mode_object_put(struct drm_device *dev,
-                               struct drm_mode_object *object)
+void drm_mode_object_put(struct drm_device *dev,
+                        struct drm_mode_object *object)
 {
        mutex_lock(&dev->mode_config.idr_mutex);
        idr_remove(&dev->mode_config.crtc_idr, object->id);
        mutex_unlock(&dev->mode_config.idr_mutex);
 }
 
+static struct drm_mode_object *_object_find(struct drm_device *dev,
+               uint32_t id, uint32_t type)
+{
+       struct drm_mode_object *obj = NULL;
+
+       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))
+               obj = NULL;
+       mutex_unlock(&dev->mode_config.idr_mutex);
+
+       return obj;
+}
+
 /**
  * drm_mode_object_find - look up a drm object with static lifetime
  * @dev: drm device
@@ -327,7 +437,9 @@ static void drm_mode_object_put(struct drm_device *dev,
  * @type: type of the mode object
  *
  * Note that framebuffers cannot be looked up with this functions - since those
- * are reference counted, they need special treatment.
+ * are reference counted, they need special treatment.  Even with
+ * DRM_MODE_OBJECT_ANY (although that will simply return NULL
+ * rather than WARN_ON()).
  */
 struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
                uint32_t id, uint32_t type)
@@ -337,13 +449,10 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
        /* Framebuffers are reference counted and need their own lookup
         * function.*/
        WARN_ON(type == DRM_MODE_OBJECT_FB);
-
-       mutex_lock(&dev->mode_config.idr_mutex);
-       obj = idr_find(&dev->mode_config.crtc_idr, id);
-       if (!obj || (obj->type != type) || (obj->id != id))
+       obj = _object_find(dev, id, type);
+       /* 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);
-
        return obj;
 }
 EXPORT_SYMBOL(drm_mode_object_find);
@@ -363,7 +472,7 @@ EXPORT_SYMBOL(drm_mode_object_find);
  * since all the fb attributes are invariant over its lifetime, no further
  * locking but only correct reference counting is required.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, error code on failure.
  */
 int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
@@ -424,7 +533,7 @@ static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev,
  *
  * If successful, this grabs an additional reference to the framebuffer -
  * callers need to make sure to eventually unreference the returned framebuffer
- * again.
+ * again, using @drm_framebuffer_unreference.
  */
 struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
                                               uint32_t id)
@@ -449,7 +558,7 @@ EXPORT_SYMBOL(drm_framebuffer_lookup);
  */
 void drm_framebuffer_unreference(struct drm_framebuffer *fb)
 {
-       DRM_DEBUG("FB ID: %d\n", fb->base.id);
+       DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
        kref_put(&fb->refcount, drm_framebuffer_free);
 }
 EXPORT_SYMBOL(drm_framebuffer_unreference);
@@ -457,10 +566,12 @@ EXPORT_SYMBOL(drm_framebuffer_unreference);
 /**
  * drm_framebuffer_reference - incr the fb refcnt
  * @fb: framebuffer
+ *
+ * This functions increments the fb's refcount.
  */
 void drm_framebuffer_reference(struct drm_framebuffer *fb)
 {
-       DRM_DEBUG("FB ID: %d\n", fb->base.id);
+       DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
        kref_get(&fb->refcount);
 }
 EXPORT_SYMBOL(drm_framebuffer_reference);
@@ -472,7 +583,7 @@ static void drm_framebuffer_free_bug(struct kref *kref)
 
 static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
 {
-       DRM_DEBUG("FB ID: %d\n", fb->base.id);
+       DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
        kref_put(&fb->refcount, drm_framebuffer_free_bug);
 }
 
@@ -513,8 +624,9 @@ EXPORT_SYMBOL(drm_framebuffer_unregister_private);
  * drm_framebuffer_cleanup - remove a framebuffer object
  * @fb: framebuffer to remove
  *
- * Cleanup references to a user-created framebuffer. This function is intended
- * to be used from the drivers ->destroy callback.
+ * Cleanup framebuffer. This function is intended to be used from the drivers
+ * ->destroy callback. It can also be used to clean up driver private
+ *  framebuffers embedded into a larger structure.
  *
  * Note that this function does not remove the fb from active usuage - if it is
  * still used anywhere, hilarity can ensue since userspace could call getfb on
@@ -577,7 +689,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
                drm_modeset_lock_all(dev);
                /* remove from any CRTC */
                list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-                       if (crtc->fb == fb) {
+                       if (crtc->primary->fb == fb) {
                                /* should turn off the crtc */
                                memset(&set, 0, sizeof(struct drm_mode_set));
                                set.crtc = crtc;
@@ -599,20 +711,28 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 }
 EXPORT_SYMBOL(drm_framebuffer_remove);
 
+DEFINE_WW_CLASS(crtc_ww_class);
+
 /**
- * drm_crtc_init - Initialise a new CRTC object
+ * drm_crtc_init_with_planes - Initialise a new CRTC object with
+ *    specified primary and cursor planes.
  * @dev: DRM device
  * @crtc: CRTC object to init
+ * @primary: Primary plane for CRTC
+ * @cursor: Cursor plane for CRTC
  * @funcs: callbacks for the new CRTC
  *
  * Inits a new object created as base part of a driver crtc object.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, error code on failure.
  */
-int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
-                  const struct drm_crtc_funcs *funcs)
+int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
+                             struct drm_plane *primary,
+                             void *cursor,
+                             const struct drm_crtc_funcs *funcs)
 {
+       struct drm_mode_config *config = &dev->mode_config;
        int ret;
 
        crtc->dev = dev;
@@ -620,8 +740,9 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
        crtc->invert_dimensions = false;
 
        drm_modeset_lock_all(dev);
-       lockinit(&crtc->mutex, "drmcm", 0, LK_CANRECURSE);
-       lockmgr(&crtc->mutex, LK_EXCLUSIVE);
+       drm_modeset_lock_init(&crtc->mutex);
+       /* dropped by _unlock_all(): */
+       drm_modeset_lock(&crtc->mutex, config->acquire_ctx);
 
        ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
        if (ret)
@@ -629,15 +750,19 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
 
        crtc->base.properties = &crtc->properties;
 
-       list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
-       dev->mode_config.num_crtc++;
+       list_add_tail(&crtc->head, &config->crtc_list);
+       config->num_crtc++;
+
+       crtc->primary = primary;
+       if (primary)
+               primary->possible_crtcs = 1 << drm_crtc_index(crtc);
 
  out:
        drm_modeset_unlock_all(dev);
 
        return ret;
 }
-EXPORT_SYMBOL(drm_crtc_init);
+EXPORT_SYMBOL(drm_crtc_init_with_planes);
 
 /**
  * drm_crtc_cleanup - Clean up the core crtc usage
@@ -654,6 +779,8 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
        kfree(crtc->gamma_store);
        crtc->gamma_store = NULL;
 
+       drm_modeset_lock_fini(&crtc->mutex);
+
        drm_mode_object_put(dev, &crtc->base);
        list_del(&crtc->head);
        dev->mode_config.num_crtc--;
@@ -683,20 +810,6 @@ unsigned int drm_crtc_index(struct drm_crtc *crtc)
 }
 EXPORT_SYMBOL(drm_crtc_index);
 
-/**
- * drm_mode_probed_add - add a mode to a connector's probed mode list
- * @connector: connector the new mode
- * @mode: mode data
- *
- * Add @mode to @connector's mode list for later use.
- */
-void drm_mode_probed_add(struct drm_connector *connector,
-                        struct drm_display_mode *mode)
-{
-       list_add_tail(&mode->head, &connector->probed_modes);
-}
-EXPORT_SYMBOL(drm_mode_probed_add);
-
 /*
  * drm_mode_remove - remove and free a mode
  * @connector: connector list to modify
@@ -721,7 +834,7 @@ static void drm_mode_remove(struct drm_connector *connector,
  * Initialises a preallocated connector. Connectors should be
  * subclassed as part of driver connector objects.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, error code on failure.
  */
 int drm_connector_init(struct drm_device *dev,
@@ -735,7 +848,7 @@ int drm_connector_init(struct drm_device *dev,
 
        ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
        if (ret)
-               goto out;
+               goto out_unlock;
 
        connector->base.properties = &connector->properties;
        connector->dev = dev;
@@ -745,9 +858,9 @@ int drm_connector_init(struct drm_device *dev,
                ++drm_connector_enum_list[connector_type].count; /* TODO */
        if (connector->connector_type_id < 0) {
                ret = connector->connector_type_id;
-               drm_mode_object_put(dev, &connector->base);
-               goto out;
+               goto out_put;
        }
+
        INIT_LIST_HEAD(&connector->probed_modes);
        INIT_LIST_HEAD(&connector->modes);
        connector->edid_blob_ptr = NULL;
@@ -764,7 +877,11 @@ int drm_connector_init(struct drm_device *dev,
        drm_object_attach_property(&connector->base,
                                      dev->mode_config.dpms_property, 0);
 
- out:
+out_put:
+       if (ret)
+               drm_mode_object_put(dev, &connector->base);
+
+out_unlock:
        drm_modeset_unlock_all(dev);
 
        return ret;
@@ -794,19 +911,91 @@ void drm_connector_cleanup(struct drm_connector *connector)
 }
 EXPORT_SYMBOL(drm_connector_cleanup);
 
+/**
+ * drm_connector_unplug_all - unregister connector userspace interfaces
+ * @dev: drm device
+ *
+ * This function unregisters all connector userspace interfaces in sysfs. Should
+ * be call when the device is disconnected, e.g. from an usb driver's
+ * ->disconnect callback.
+ */
 void drm_connector_unplug_all(struct drm_device *dev)
 {
-#if 0
        struct drm_connector *connector;
 
        /* taking the mode config mutex ends up in a clash with sysfs */
        list_for_each_entry(connector, &dev->mode_config.connector_list, head)
                drm_sysfs_connector_remove(connector);
 
-#endif
 }
 EXPORT_SYMBOL(drm_connector_unplug_all);
 
+/**
+ * drm_bridge_init - initialize a drm transcoder/bridge
+ * @dev: drm device
+ * @bridge: transcoder/bridge to set up
+ * @funcs: bridge function table
+ *
+ * Initialises a preallocated bridge. Bridges should be
+ * subclassed as part of driver connector objects.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge,
+               const struct drm_bridge_funcs *funcs)
+{
+       int ret;
+
+       drm_modeset_lock_all(dev);
+
+       ret = drm_mode_object_get(dev, &bridge->base, DRM_MODE_OBJECT_BRIDGE);
+       if (ret)
+               goto out;
+
+       bridge->dev = dev;
+       bridge->funcs = funcs;
+
+       list_add_tail(&bridge->head, &dev->mode_config.bridge_list);
+       dev->mode_config.num_bridge++;
+
+ out:
+       drm_modeset_unlock_all(dev);
+       return ret;
+}
+EXPORT_SYMBOL(drm_bridge_init);
+
+/**
+ * drm_bridge_cleanup - cleans up an initialised bridge
+ * @bridge: bridge to cleanup
+ *
+ * Cleans up the bridge but doesn't free the object.
+ */
+void drm_bridge_cleanup(struct drm_bridge *bridge)
+{
+       struct drm_device *dev = bridge->dev;
+
+       drm_modeset_lock_all(dev);
+       drm_mode_object_put(dev, &bridge->base);
+       list_del(&bridge->head);
+       dev->mode_config.num_bridge--;
+       drm_modeset_unlock_all(dev);
+}
+EXPORT_SYMBOL(drm_bridge_cleanup);
+
+/**
+ * drm_encoder_init - Init a preallocated encoder
+ * @dev: drm device
+ * @encoder: the encoder to init
+ * @funcs: callbacks for this encoder
+ * @encoder_type: user visible type of the encoder
+ *
+ * Initialises a preallocated encoder. Encoder should be
+ * subclassed as part of driver encoder objects.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
 int drm_encoder_init(struct drm_device *dev,
                      struct drm_encoder *encoder,
                      const struct drm_encoder_funcs *funcs,
@@ -818,7 +1007,7 @@ int drm_encoder_init(struct drm_device *dev,
 
        ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
        if (ret)
-               goto out;
+               goto out_unlock;
 
        encoder->dev = dev;
        encoder->encoder_type = encoder_type;
@@ -827,18 +1016,26 @@ int drm_encoder_init(struct drm_device *dev,
        list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
        dev->mode_config.num_encoder++;
 
- out:
+out_unlock:
        drm_modeset_unlock_all(dev);
 
        return ret;
 }
 EXPORT_SYMBOL(drm_encoder_init);
 
+/**
+ * drm_encoder_cleanup - cleans up an initialised encoder
+ * @encoder: encoder to cleanup
+ *
+ * Cleans up the encoder but doesn't free the object.
+ */
 void drm_encoder_cleanup(struct drm_encoder *encoder)
 {
        struct drm_device *dev = encoder->dev;
        drm_modeset_lock_all(dev);
        drm_mode_object_put(dev, &encoder->base);
+       kfree(encoder->name);
+       encoder->name = NULL;
        list_del(&encoder->head);
        dev->mode_config.num_encoder--;
        drm_modeset_unlock_all(dev);
@@ -846,25 +1043,25 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
 EXPORT_SYMBOL(drm_encoder_cleanup);
 
 /**
- * drm_plane_init - Initialise a new plane object
+ * drm_universal_plane_init - Initialize a new universal plane object
  * @dev: DRM device
  * @plane: plane object to init
  * @possible_crtcs: bitmask of possible CRTCs
  * @funcs: callbacks for the new plane
  * @formats: array of supported formats (%DRM_FORMAT_*)
  * @format_count: number of elements in @formats
- * @priv: plane is private (hidden from userspace)?
+ * @type: type of plane (overlay, primary, cursor)
  *
- * Inits a new object created as base part of a driver plane object.
+ * Initializes a plane object of type @type.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, error code on failure.
  */
-int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
-                  unsigned long possible_crtcs,
-                  const struct drm_plane_funcs *funcs,
-                  const uint32_t *formats, uint32_t format_count,
-                  bool priv)
+int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
+                            unsigned long possible_crtcs,
+                            const struct drm_plane_funcs *funcs,
+                            const uint32_t *formats, uint32_t format_count,
+                            enum drm_plane_type type)
 {
        int ret;
 
@@ -889,23 +1086,53 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
        memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
        plane->format_count = format_count;
        plane->possible_crtcs = possible_crtcs;
+       plane->type = type;
 
-       /* private planes are not exposed to userspace, but depending on
-        * display hardware, might be convenient to allow sharing programming
-        * for the scanout engine with the crtc implementation.
-        */
-       if (!priv) {
-               list_add_tail(&plane->head, &dev->mode_config.plane_list);
-               dev->mode_config.num_plane++;
-       } else {
-               INIT_LIST_HEAD(&plane->head);
-       }
+       list_add_tail(&plane->head, &dev->mode_config.plane_list);
+       dev->mode_config.num_total_plane++;
+       if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+               dev->mode_config.num_overlay_plane++;
+
+       drm_object_attach_property(&plane->base,
+                                  dev->mode_config.plane_type_property,
+                                  plane->type);
 
  out:
        drm_modeset_unlock_all(dev);
 
        return ret;
 }
+EXPORT_SYMBOL(drm_universal_plane_init);
+
+/**
+ * drm_plane_init - Initialize a legacy plane
+ * @dev: DRM device
+ * @plane: plane object to init
+ * @possible_crtcs: bitmask of possible CRTCs
+ * @funcs: callbacks for the new plane
+ * @formats: array of supported formats (%DRM_FORMAT_*)
+ * @format_count: number of elements in @formats
+ * @is_primary: plane type (primary vs overlay)
+ *
+ * Legacy API to initialize a DRM plane.
+ *
+ * New drivers should call drm_universal_plane_init() instead.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
+                  unsigned long possible_crtcs,
+                  const struct drm_plane_funcs *funcs,
+                  const uint32_t *formats, uint32_t format_count,
+                  bool is_primary)
+{
+       enum drm_plane_type type;
+
+       type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
+       return drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
+                                       formats, format_count, type);
+}
 EXPORT_SYMBOL(drm_plane_init);
 
 /**
@@ -923,11 +1150,13 @@ void drm_plane_cleanup(struct drm_plane *plane)
        drm_modeset_lock_all(dev);
        kfree(plane->format_types);
        drm_mode_object_put(dev, &plane->base);
-       /* if not added to a list, it must be a private plane */
-       if (!list_empty(&plane->head)) {
-               list_del(&plane->head);
-               dev->mode_config.num_plane--;
-       }
+
+       BUG_ON(list_empty(&plane->head));
+
+       list_del(&plane->head);
+       dev->mode_config.num_total_plane--;
+       if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+               dev->mode_config.num_overlay_plane--;
        drm_modeset_unlock_all(dev);
 }
 EXPORT_SYMBOL(drm_plane_cleanup);
@@ -943,65 +1172,24 @@ EXPORT_SYMBOL(drm_plane_cleanup);
  */
 void drm_plane_force_disable(struct drm_plane *plane)
 {
+       struct drm_framebuffer *old_fb = plane->fb;
        int ret;
 
-       if (!plane->fb)
+       if (!old_fb)
                return;
 
        ret = plane->funcs->disable_plane(plane);
-       if (ret)
+       if (ret) {
                DRM_ERROR("failed to disable plane with busy fb\n");
+               return;
+       }
        /* disconnect the plane from the fb and crtc: */
-       __drm_framebuffer_unreference(plane->fb);
+       __drm_framebuffer_unreference(old_fb);
        plane->fb = NULL;
        plane->crtc = NULL;
 }
 EXPORT_SYMBOL(drm_plane_force_disable);
 
-/**
- * drm_mode_create - create a new display mode
- * @dev: DRM device
- *
- * Create a new drm_display_mode, give it an ID, and return it.
- *
- * RETURNS:
- * Pointer to new mode on success, NULL on error.
- */
-struct drm_display_mode *drm_mode_create(struct drm_device *dev)
-{
-       struct drm_display_mode *nmode;
-
-       nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL);
-       if (!nmode)
-               return NULL;
-
-       if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) {
-               kfree(nmode);
-               return NULL;
-       }
-
-       return nmode;
-}
-EXPORT_SYMBOL(drm_mode_create);
-
-/**
- * drm_mode_destroy - remove a mode
- * @dev: DRM device
- * @mode: mode to remove
- *
- * Free @mode's unique identifier, then free it.
- */
-void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
-{
-       if (!mode)
-               return;
-
-       drm_mode_object_put(dev, &mode->base);
-
-       kfree(mode);
-}
-EXPORT_SYMBOL(drm_mode_destroy);
-
 static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 {
        struct drm_property *edid;
@@ -1023,6 +1211,21 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
        return 0;
 }
 
+static int drm_mode_create_standard_plane_properties(struct drm_device *dev)
+{
+       struct drm_property *type;
+
+       /*
+        * Standard properties (apply to all planes)
+        */
+       type = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
+                                       "type", drm_plane_type_enum_list,
+                                       ARRAY_SIZE(drm_plane_type_enum_list));
+       dev->mode_config.plane_type_property = type;
+
+       return 0;
+}
+
 /**
  * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
  * @dev: DRM device
@@ -1192,6 +1395,7 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr
        total_objects += dev->mode_config.num_crtc;
        total_objects += dev->mode_config.num_connector;
        total_objects += dev->mode_config.num_encoder;
+       total_objects += dev->mode_config.num_bridge;
 
        group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL);
        if (!group->id_list)
@@ -1200,15 +1404,27 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr
        group->num_crtcs = 0;
        group->num_connectors = 0;
        group->num_encoders = 0;
+       group->num_bridges = 0;
        return 0;
 }
 
+void drm_mode_group_destroy(struct drm_mode_group *group)
+{
+       kfree(group->id_list);
+       group->id_list = NULL;
+}
+
+/*
+ * NOTE: Driver's shouldn't ever call drm_mode_group_init_legacy_group - it is
+ * the drm core's responsibility to set up mode control groups.
+ */
 int drm_mode_group_init_legacy_group(struct drm_device *dev,
                                     struct drm_mode_group *group)
 {
        struct drm_crtc *crtc;
        struct drm_encoder *encoder;
        struct drm_connector *connector;
+       struct drm_bridge *bridge;
        int ret;
 
        if ((ret = drm_mode_group_init(dev, group)))
@@ -1225,6 +1441,11 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
                group->id_list[group->num_crtcs + group->num_encoders +
                               group->num_connectors++] = connector->base.id;
 
+       list_for_each_entry(bridge, &dev->mode_config.bridge_list, head)
+               group->id_list[group->num_crtcs + group->num_encoders +
+                              group->num_connectors + group->num_bridges++] =
+                                       bridge->base.id;
+
        return 0;
 }
 EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
@@ -1273,7 +1494,7 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
  * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to
  * the caller.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, errno on failure.
  */
 static int drm_crtc_convert_umode(struct drm_display_mode *out,
@@ -1316,7 +1537,7 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out,
  *
  * Called by the user via ioctl.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, errno on failure.
  */
 int drm_mode_getresources(struct drm_device *dev, void *data,
@@ -1369,13 +1590,9 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
        mutex_unlock(&file_priv->fbs_lock);
 
        drm_modeset_lock_all(dev);
-#if 0
-       mode_group = &file_priv->master->minor->mode_group;
-       if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
-#else
-       mode_group = NULL; /* XXXKIB */
-       if (1 || file_priv->master) {
-#endif
+       if (!drm_is_primary_client(file_priv)) {
+
+               mode_group = NULL;
                list_for_each(lh, &dev->mode_config.crtc_list)
                        crtc_count++;
 
@@ -1400,7 +1617,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
        if (card_res->count_crtcs >= crtc_count) {
                copied = 0;
                crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
-               if (1 || file_priv->master) {
+               if (!mode_group) {
                        list_for_each_entry(crtc, &dev->mode_config.crtc_list,
                                            head) {
                                DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
@@ -1427,12 +1644,12 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
        if (card_res->count_encoders >= encoder_count) {
                copied = 0;
                encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
-               if (file_priv->master) {
+               if (!mode_group) {
                        list_for_each_entry(encoder,
                                            &dev->mode_config.encoder_list,
                                            head) {
                                DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id,
-                                               drm_get_encoder_name(encoder));
+                                               encoder->name);
                                if (put_user(encoder->base.id, encoder_id +
                                             copied)) {
                                        ret = -EFAULT;
@@ -1458,13 +1675,13 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
        if (card_res->count_connectors >= connector_count) {
                copied = 0;
                connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
-               if (file_priv->master) {
+               if (!mode_group) {
                        list_for_each_entry(connector,
                                            &dev->mode_config.connector_list,
                                            head) {
                                DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
                                        connector->base.id,
-                                       drm_get_connector_name(connector));
+                                       connector->name);
                                if (put_user(connector->base.id,
                                             connector_id + copied)) {
                                        ret = -EFAULT;
@@ -1505,7 +1722,7 @@ out:
  *
  * Called by the user via ioctl.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, errno on failure.
  */
 int drm_mode_getcrtc(struct drm_device *dev,
@@ -1513,7 +1730,6 @@ int drm_mode_getcrtc(struct drm_device *dev,
 {
        struct drm_mode_crtc *crtc_resp = data;
        struct drm_crtc *crtc;
-       struct drm_mode_object *obj;
        int ret = 0;
 
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
@@ -1521,19 +1737,17 @@ int drm_mode_getcrtc(struct drm_device *dev,
 
        drm_modeset_lock_all(dev);
 
-       obj = drm_mode_object_find(dev, crtc_resp->crtc_id,
-                                  DRM_MODE_OBJECT_CRTC);
-       if (!obj) {
+       crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
+       if (!crtc) {
                ret = -ENOENT;
                goto out;
        }
-       crtc = obj_to_crtc(obj);
 
        crtc_resp->x = crtc->x;
        crtc_resp->y = crtc->y;
        crtc_resp->gamma_size = crtc->gamma_size;
-       if (crtc->fb)
-               crtc_resp->fb_id = crtc->fb->base.id;
+       if (crtc->primary->fb)
+               crtc_resp->fb_id = crtc->primary->fb->base.id;
        else
                crtc_resp->fb_id = 0;
 
@@ -1574,14 +1788,13 @@ static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
  *
  * Called by the user via ioctl.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, errno on failure.
  */
 int drm_mode_getconnector(struct drm_device *dev, void *data,
                          struct drm_file *file_priv)
 {
        struct drm_mode_get_connector *out_resp = data;
-       struct drm_mode_object *obj;
        struct drm_connector *connector;
        struct drm_display_mode *mode;
        int mode_count = 0;
@@ -1604,14 +1817,13 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
        DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
 
        mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
 
-       obj = drm_mode_object_find(dev, out_resp->connector_id,
-                                  DRM_MODE_OBJECT_CONNECTOR);
-       if (!obj) {
+       connector = drm_connector_find(dev, out_resp->connector_id);
+       if (!connector) {
                ret = -ENOENT;
                goto out;
        }
-       connector = obj_to_connector(obj);
 
        props_count = connector->properties.count;
 
@@ -1704,16 +1916,29 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
        out_resp->count_encoders = encoders_count;
 
 out:
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
        mutex_unlock(&dev->mode_config.mutex);
 
        return ret;
 }
 
+/**
+ * drm_mode_getencoder - get encoder configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Construct a encoder configuration structure to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_getencoder(struct drm_device *dev, void *data,
                        struct drm_file *file_priv)
 {
        struct drm_mode_get_encoder *enc_resp = data;
-       struct drm_mode_object *obj;
        struct drm_encoder *encoder;
        int ret = 0;
 
@@ -1721,13 +1946,11 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
                return -EINVAL;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, enc_resp->encoder_id,
-                                  DRM_MODE_OBJECT_ENCODER);
-       if (!obj) {
+       encoder = drm_encoder_find(dev, enc_resp->encoder_id);
+       if (!encoder) {
                ret = -ENOENT;
                goto out;
        }
-       encoder = obj_to_encoder(obj);
 
        if (encoder->crtc)
                enc_resp->crtc_id = encoder->crtc->base.id;
@@ -1744,21 +1967,27 @@ out:
 }
 
 /**
- * drm_mode_getplane_res - get plane info
+ * drm_mode_getplane_res - enumerate all plane resources
  * @dev: DRM device
  * @data: ioctl data
  * @file_priv: DRM file info
  *
- * Return an plane count and set of IDs.
+ * Construct a list of plane ids to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
  */
 int drm_mode_getplane_res(struct drm_device *dev, void *data,
-                           struct drm_file *file_priv)
+                         struct drm_file *file_priv)
 {
        struct drm_mode_get_plane_res *plane_resp = data;
        struct drm_mode_config *config;
        struct drm_plane *plane;
        uint32_t __user *plane_ptr;
        int copied = 0, ret = 0;
+       unsigned num_planes;
 
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
@@ -1766,15 +1995,28 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
        drm_modeset_lock_all(dev);
        config = &dev->mode_config;
 
+       if (file_priv->universal_planes)
+               num_planes = config->num_total_plane;
+       else
+               num_planes = config->num_overlay_plane;
+
        /*
         * This ioctl is called twice, once to determine how much space is
         * needed, and the 2nd time to fill it.
         */
-       if (config->num_plane &&
-           (plane_resp->count_planes >= config->num_plane)) {
+       if (num_planes &&
+           (plane_resp->count_planes >= num_planes)) {
                plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr;
 
                list_for_each_entry(plane, &config->plane_list, head) {
+                       /*
+                        * Unless userspace set the 'universal planes'
+                        * capability bit, only advertise overlays.
+                        */
+                       if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
+                           !file_priv->universal_planes)
+                               continue;
+
                        if (put_user(plane->base.id, plane_ptr + copied)) {
                                ret = -EFAULT;
                                goto out;
@@ -1782,7 +2024,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
                        copied++;
                }
        }
-       plane_resp->count_planes = config->num_plane;
+       plane_resp->count_planes = num_planes;
 
 out:
        drm_modeset_unlock_all(dev);
@@ -1790,19 +2032,22 @@ out:
 }
 
 /**
- * drm_mode_getplane - get plane info
+ * drm_mode_getplane - get plane configuration
  * @dev: DRM device
  * @data: ioctl data
  * @file_priv: DRM file info
  *
- * Return plane info, including formats supported, gamma size, any
- * current fb, etc.
+ * Construct a plane configuration structure to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
  */
 int drm_mode_getplane(struct drm_device *dev, void *data,
-                       struct drm_file *file_priv)
+                     struct drm_file *file_priv)
 {
        struct drm_mode_get_plane *plane_resp = data;
-       struct drm_mode_object *obj;
        struct drm_plane *plane;
        uint32_t __user *format_ptr;
        int ret = 0;
@@ -1811,13 +2056,11 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
                return -EINVAL;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, plane_resp->plane_id,
-                                  DRM_MODE_OBJECT_PLANE);
-       if (!obj) {
+       plane = drm_plane_find(dev, plane_resp->plane_id);
+       if (!plane) {
                ret = -ENOENT;
                goto out;
        }
-       plane = obj_to_plane(obj);
 
        if (plane->crtc)
                plane_resp->crtc_id = plane->crtc->base.id;
@@ -1855,19 +2098,21 @@ out:
 }
 
 /**
- * drm_mode_setplane - set up or tear down an plane
+ * drm_mode_setplane - configure a plane's configuration
  * @dev: DRM device
  * @data: ioctl data*
  * @file_priv: DRM file info
  *
- * Set plane info, including placement, fb, scaling, and other factors.
+ * Set plane configuration, including placement, fb, scaling, and other factors.
  * Or pass a NULL fb to disable.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
  */
 int drm_mode_setplane(struct drm_device *dev, void *data,
-                       struct drm_file *file_priv)
+                     struct drm_file *file_priv)
 {
        struct drm_mode_set_plane *plane_req = data;
-       struct drm_mode_object *obj;
        struct drm_plane *plane;
        struct drm_crtc *crtc;
        struct drm_framebuffer *fb = NULL, *old_fb = NULL;
@@ -1882,35 +2127,35 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
         * First, find the plane, crtc, and fb objects.  If not available,
         * we don't bother to call the driver.
         */
-       obj = drm_mode_object_find(dev, plane_req->plane_id,
-                                  DRM_MODE_OBJECT_PLANE);
-       if (!obj) {
+       plane = drm_plane_find(dev, plane_req->plane_id);
+       if (!plane) {
                DRM_DEBUG_KMS("Unknown plane ID %d\n",
                              plane_req->plane_id);
                return -ENOENT;
        }
-       plane = obj_to_plane(obj);
 
        /* No fb means shut it down */
        if (!plane_req->fb_id) {
                drm_modeset_lock_all(dev);
                old_fb = plane->fb;
-               plane->funcs->disable_plane(plane);
-               plane->crtc = NULL;
-               plane->fb = NULL;
+               ret = plane->funcs->disable_plane(plane);
+               if (!ret) {
+                       plane->crtc = NULL;
+                       plane->fb = NULL;
+               } else {
+                       old_fb = NULL;
+               }
                drm_modeset_unlock_all(dev);
                goto out;
        }
 
-       obj = drm_mode_object_find(dev, plane_req->crtc_id,
-                                  DRM_MODE_OBJECT_CRTC);
-       if (!obj) {
+       crtc = drm_crtc_find(dev, plane_req->crtc_id);
+       if (!crtc) {
                DRM_DEBUG_KMS("Unknown crtc ID %d\n",
                              plane_req->crtc_id);
                ret = -ENOENT;
                goto out;
        }
-       crtc = obj_to_crtc(obj);
 
        fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
        if (!fb) {
@@ -1966,16 +2211,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
        }
 
        drm_modeset_lock_all(dev);
+       old_fb = plane->fb;
        ret = plane->funcs->update_plane(plane, crtc, fb,
                                         plane_req->crtc_x, plane_req->crtc_y,
                                         plane_req->crtc_w, plane_req->crtc_h,
                                         plane_req->src_x, plane_req->src_y,
                                         plane_req->src_w, plane_req->src_h);
        if (!ret) {
-               old_fb = plane->fb;
                plane->crtc = crtc;
                plane->fb = fb;
                fb = NULL;
+       } else {
+               old_fb = NULL;
        }
        drm_modeset_unlock_all(dev);
 
@@ -1994,6 +2241,9 @@ out:
  *
  * This is a little helper to wrap internal calls to the ->set_config driver
  * interface. The only thing it adds is correct refcounting dance.
+ * 
+ * Returns:
+ * Zero on success, errno on failure.
  */
 int drm_mode_set_config_internal(struct drm_mode_set *set)
 {
@@ -2008,19 +2258,19 @@ 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->fb;
+               tmp->old_fb = tmp->primary->fb;
 
        fb = set->fb;
 
        ret = crtc->funcs->set_config(set);
        if (ret == 0) {
-               /* crtc->fb must be updated by ->set_config, enforces this. */
-               WARN_ON(fb != crtc->fb);
+               crtc->primary->crtc = crtc;
+               crtc->primary->fb = fb;
        }
 
        list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
-               if (tmp->fb)
-                       drm_framebuffer_reference(tmp->fb);
+               if (tmp->primary->fb)
+                       drm_framebuffer_reference(tmp->primary->fb);
                if (tmp->old_fb)
                        drm_framebuffer_unreference(tmp->old_fb);
        }
@@ -2029,14 +2279,19 @@ int drm_mode_set_config_internal(struct drm_mode_set *set)
 }
 EXPORT_SYMBOL(drm_mode_set_config_internal);
 
-/*
- * Checks that the framebuffer is big enough for the CRTC viewport
- * (x, y, hdisplay, vdisplay)
+/**
+ * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the
+ *     CRTC viewport
+ * @crtc: CRTC that framebuffer will be displayed on
+ * @x: x panning
+ * @y: y panning
+ * @mode: mode that framebuffer will be displayed under
+ * @fb: framebuffer to check size of
  */
-static int drm_crtc_check_viewport(const struct drm_crtc *crtc,
-                                  int x, int y,
-                                  const struct drm_display_mode *mode,
-                                  const struct drm_framebuffer *fb)
+int drm_crtc_check_viewport(const struct drm_crtc *crtc,
+                           int x, int y,
+                           const struct drm_display_mode *mode,
+                           const struct drm_framebuffer *fb)
 
 {
        int hdisplay, vdisplay;
@@ -2067,6 +2322,7 @@ static int drm_crtc_check_viewport(const struct drm_crtc *crtc,
 
        return 0;
 }
+EXPORT_SYMBOL(drm_crtc_check_viewport);
 
 /**
  * drm_mode_setcrtc - set CRTC configuration
@@ -2078,7 +2334,7 @@ static int drm_crtc_check_viewport(const struct drm_crtc *crtc,
  *
  * Called by the user via ioctl.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, errno on failure.
  */
 int drm_mode_setcrtc(struct drm_device *dev, void *data,
@@ -2086,7 +2342,6 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 {
        struct drm_mode_config *config = &dev->mode_config;
        struct drm_mode_crtc *crtc_req = data;
-       struct drm_mode_object *obj;
        struct drm_crtc *crtc;
        struct drm_connector **connector_set = NULL, *connector;
        struct drm_framebuffer *fb = NULL;
@@ -2104,26 +2359,24 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                return -ERANGE;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, crtc_req->crtc_id,
-                                  DRM_MODE_OBJECT_CRTC);
-       if (!obj) {
+       crtc = drm_crtc_find(dev, crtc_req->crtc_id);
+       if (!crtc) {
                DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
                ret = -ENOENT;
                goto out;
        }
-       crtc = obj_to_crtc(obj);
        DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
 
        if (crtc_req->mode_valid) {
                /* If we have a mode we need a framebuffer. */
                /* If we pass -1, set the mode with the currently bound fb */
                if (crtc_req->fb_id == -1) {
-                       if (!crtc->fb) {
+                       if (!crtc->primary->fb) {
                                DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
                                ret = -EINVAL;
                                goto out;
                        }
-                       fb = crtc->fb;
+                       fb = crtc->primary->fb;
                        /* Make refcounting symmetric with the lookup path. */
                        drm_framebuffer_reference(fb);
                } else {
@@ -2194,18 +2447,16 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                                goto out;
                        }
 
-                       obj = drm_mode_object_find(dev, out_id,
-                                                  DRM_MODE_OBJECT_CONNECTOR);
-                       if (!obj) {
+                       connector = drm_connector_find(dev, out_id);
+                       if (!connector) {
                                DRM_DEBUG_KMS("Connector id %d unknown\n",
                                                out_id);
                                ret = -ENOENT;
                                goto out;
                        }
-                       connector = obj_to_connector(obj);
                        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
                                        connector->base.id,
-                                       drm_get_connector_name(connector));
+                                       connector->name);
 
                        connector_set[i] = connector;
                }
@@ -2234,7 +2485,6 @@ static int drm_mode_cursor_common(struct drm_device *dev,
                                  struct drm_mode_cursor2 *req,
                                  struct drm_file *file_priv)
 {
-       struct drm_mode_object *obj;
        struct drm_crtc *crtc;
        int ret = 0;
 
@@ -2244,14 +2494,13 @@ static int drm_mode_cursor_common(struct drm_device *dev,
        if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
                return -EINVAL;
 
-       obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
-       if (!obj) {
+       crtc = drm_crtc_find(dev, req->crtc_id);
+       if (!crtc) {
                DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
                return -ENOENT;
        }
-       crtc = obj_to_crtc(obj);
 
-       mutex_lock(&crtc->mutex);
+       drm_modeset_lock(&crtc->mutex, NULL);
        if (req->flags & DRM_MODE_CURSOR_BO) {
                if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
                        ret = -ENXIO;
@@ -2275,13 +2524,28 @@ static int drm_mode_cursor_common(struct drm_device *dev,
                }
        }
 out:
-       mutex_unlock(&crtc->mutex);
+       drm_modeset_unlock(&crtc->mutex);
 
        return ret;
 
 }
+
+
+/**
+ * drm_mode_cursor_ioctl - set CRTC's cursor configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Set the cursor configuration based on user request.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_cursor_ioctl(struct drm_device *dev,
-                       void *data, struct drm_file *file_priv)
+                         void *data, struct drm_file *file_priv)
 {
        struct drm_mode_cursor *req = data;
        struct drm_mode_cursor2 new_req;
@@ -2292,6 +2556,21 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,
        return drm_mode_cursor_common(dev, &new_req, file_priv);
 }
 
+/**
+ * drm_mode_cursor2_ioctl - set CRTC's cursor configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Set the cursor configuration based on user request. This implements the 2nd
+ * version of the cursor ioctl, which allows userspace to additionally specify
+ * the hotspot of the pointer.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_cursor2_ioctl(struct drm_device *dev,
                           void *data, struct drm_file *file_priv)
 {
@@ -2299,7 +2578,14 @@ int drm_mode_cursor2_ioctl(struct drm_device *dev,
        return drm_mode_cursor_common(dev, req, file_priv);
 }
 
-/* Original addfb only supported RGB formats, so figure out which one */
+/**
+ * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description
+ * @bpp: bits per pixels
+ * @depth: bit depth per pixel
+ *
+ * Computes a drm fourcc pixel format code for the given @bpp/@depth values.
+ * Useful in fbdev emulation code, since that deals in those values.
+ */
 uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
 {
        uint32_t fmt;
@@ -2341,11 +2627,12 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format);
  * @data: data pointer for the ioctl
  * @file_priv: drm file for the ioctl call
  *
- * Add a new FB to the specified CRTC, given a user request.
+ * Add a new FB to the specified CRTC, given a user request. This is the
+ * original addfb ioclt which only supported RGB formats.
  *
  * Called by the user via ioctl.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, errno on failure.
  */
 int drm_mode_addfb(struct drm_device *dev,
@@ -2518,11 +2805,13 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
  * @data: data pointer for the ioctl
  * @file_priv: drm file for the ioctl call
  *
- * Add a new FB to the specified CRTC, given a user request with format.
+ * Add a new FB to the specified CRTC, given a user request with format. This is
+ * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
+ * and uses fourcc codes as pixel format specifiers.
  *
  * Called by the user via ioctl.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, errno on failure.
  */
 int drm_mode_addfb2(struct drm_device *dev,
@@ -2582,7 +2871,7 @@ int drm_mode_addfb2(struct drm_device *dev,
  *
  * Called by the user via ioctl.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, errno on failure.
  */
 int drm_mode_rmfb(struct drm_device *dev,
@@ -2636,7 +2925,7 @@ fail_lookup:
  *
  * Called by the user via ioctl.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, errno on failure.
  */
 int drm_mode_getfb(struct drm_device *dev,
@@ -2658,16 +2947,36 @@ int drm_mode_getfb(struct drm_device *dev,
        r->depth = fb->depth;
        r->bpp = fb->bits_per_pixel;
        r->pitch = fb->pitches[0];
-       if (fb->funcs->create_handle)
+       if (fb->funcs->create_handle) {
                ret = fb->funcs->create_handle(fb, file_priv, &r->handle);
-       else
+       } else {
                ret = -ENODEV;
+       }
 
        drm_framebuffer_unreference(fb);
 
        return ret;
 }
 
+/**
+ * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Lookup the FB and flush out the damaged area supplied by userspace as a clip
+ * rectangle list. Generic userspace which does frontbuffer rendering must call
+ * this ioctl to flush out the changes on manual-update display outputs, e.g.
+ * usb display-link, mipi manual update panels or edp panel self refresh modes.
+ *
+ * Modesetting drivers which always update the frontbuffer do not need to
+ * implement the corresponding ->dirty framebuffer callback.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
                           void *data, struct drm_file *file_priv)
 {
@@ -2745,16 +3054,12 @@ out_err1:
  *
  * Called by the user via ioctl.
  *
- * RETURNS:
+ * Returns:
  * Zero on success, errno on failure.
  */
 void drm_fb_release(struct drm_file *priv)
 {
-#if 0
-       struct drm_device *dev = priv->minor->dev;
-#else
        struct drm_device *dev = priv->dev;
-#endif
        struct drm_framebuffer *fb, *tfb;
 
        mutex_lock(&priv->fbs_lock);
@@ -2773,6 +3078,20 @@ void drm_fb_release(struct drm_file *priv)
        mutex_unlock(&priv->fbs_lock);
 }
 
+/**
+ * drm_property_create - create a new property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @num_values: number of pre-defined values
+ *
+ * This creates a new generic 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.
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
 struct drm_property *drm_property_create(struct drm_device *dev, int flags,
                                         const char *name, int num_values)
 {
@@ -2783,6 +3102,8 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
        if (!property)
                return NULL;
 
+       property->dev = dev;
+
        if (num_values) {
                property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL);
                if (!property->values)
@@ -2803,6 +3124,9 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
        }
 
        list_add_tail(&property->head, &dev->mode_config.property_list);
+
+       WARN_ON(!drm_property_type_valid(property));
+
        return property;
 fail:
        kfree(property->values);
@@ -2811,6 +3135,24 @@ fail:
 }
 EXPORT_SYMBOL(drm_property_create);
 
+/**
+ * drm_property_create - create a new enumeration property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @props: enumeration lists with property values
+ * @num_values: number of pre-defined values
+ *
+ * This creates a new generic 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.
+ *
+ * Userspace is only allowed to set one of the predefined values for enumeration
+ * properties.
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
 struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
                                         const char *name,
                                         const struct drm_prop_enum_list *props,
@@ -2839,6 +3181,24 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
 }
 EXPORT_SYMBOL(drm_property_create_enum);
 
+/**
+ * drm_property_create - create a new bitmask property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @props: enumeration lists with property bitflags
+ * @num_values: number of pre-defined values
+ *
+ * This creates a new generic 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.
+ *
+ * Compared to plain enumeration properties userspace is allowed to set any
+ * or'ed together combination of the predefined property bitflag values
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
 struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
                                         int flags, const char *name,
                                         const struct drm_prop_enum_list *props,
@@ -2867,14 +3227,12 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_property_create_bitmask);
 
-struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
-                                        const char *name,
+static struct drm_property *property_create_range(struct drm_device *dev,
+                                        int flags, const char *name,
                                         uint64_t min, uint64_t max)
 {
        struct drm_property *property;
 
-       flags |= DRM_MODE_PROP_RANGE;
-
        property = drm_property_create(dev, flags, name, 2);
        if (!property)
                return NULL;
@@ -2884,21 +3242,90 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags
 
        return property;
 }
+
+/**
+ * drm_property_create - create a new ranged property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @min: minimum value of the property
+ * @max: maximum value of the property
+ *
+ * This creates a new generic 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.
+ *
+ * Userspace is allowed to set any interger value in the (min, max) range
+ * inclusive.
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
+struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
+                                        const char *name,
+                                        uint64_t min, uint64_t max)
+{
+       return property_create_range(dev, DRM_MODE_PROP_RANGE | flags,
+                       name, min, max);
+}
 EXPORT_SYMBOL(drm_property_create_range);
 
+struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
+                                        int flags, const char *name,
+                                        int64_t min, int64_t max)
+{
+       return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags,
+                       name, I642U64(min), I642U64(max));
+}
+EXPORT_SYMBOL(drm_property_create_signed_range);
+
+struct drm_property *drm_property_create_object(struct drm_device *dev,
+                                        int flags, const char *name, uint32_t type)
+{
+       struct drm_property *property;
+
+       flags |= DRM_MODE_PROP_OBJECT;
+
+       property = drm_property_create(dev, flags, name, 1);
+       if (!property)
+               return NULL;
+
+       property->values[0] = type;
+
+       return property;
+}
+EXPORT_SYMBOL(drm_property_create_object);
+
+/**
+ * drm_property_add_enum - add a possible value to an enumeration property
+ * @property: enumeration property to change
+ * @index: index of the new enumeration
+ * @value: value of the new enumeration
+ * @name: symbolic name of the new enumeration
+ *
+ * This functions adds enumerations to a property.
+ *
+ * It's use is deprecated, drivers should use one of the more specific helpers
+ * to directly create the property with all enumerations already attached.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
 int drm_property_add_enum(struct drm_property *property, int index,
                          uint64_t value, const char *name)
 {
        struct drm_property_enum *prop_enum;
 
-       if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
+       if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
                return -EINVAL;
 
        /*
         * Bitmask enum properties have the additional constraint of values
         * from 0 to 63
         */
-       if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63))
+       if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
+                       (value > 63))
                return -EINVAL;
 
        if (!list_empty(&property->enum_blob_list)) {
@@ -2925,6 +3352,14 @@ int drm_property_add_enum(struct drm_property *property, int index,
 }
 EXPORT_SYMBOL(drm_property_add_enum);
 
+/**
+ * drm_property_destroy - destroy a drm property
+ * @dev: drm device
+ * @property: property to destry
+ *
+ * This function frees a property including any attached resources like
+ * enumeration values.
+ */
 void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
 {
        struct drm_property_enum *prop_enum, *pt;
@@ -2942,6 +3377,16 @@ void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
 }
 EXPORT_SYMBOL(drm_property_destroy);
 
+/**
+ * drm_object_attach_property - attach a property to a modeset object
+ * @obj: drm modeset object
+ * @property: property to attach
+ * @init_val: initial value of the property
+ *
+ * This attaches the given property to the modeset object with the given initial
+ * value. Currently this function cannot fail since the properties are stored in
+ * a statically sized array.
+ */
 void drm_object_attach_property(struct drm_mode_object *obj,
                                struct drm_property *property,
                                uint64_t init_val)
@@ -2962,6 +3407,19 @@ void drm_object_attach_property(struct drm_mode_object *obj,
 }
 EXPORT_SYMBOL(drm_object_attach_property);
 
+/**
+ * drm_object_property_set_value - set the value of a property
+ * @obj: drm mode object to set property value for
+ * @property: property to set
+ * @val: value the property should be set to
+ *
+ * This functions sets a given property on a given object. This function only
+ * changes the software state of the property, it does not call into the
+ * driver's ->set_property callback.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
 int drm_object_property_set_value(struct drm_mode_object *obj,
                                  struct drm_property *property, uint64_t val)
 {
@@ -2978,6 +3436,20 @@ int drm_object_property_set_value(struct drm_mode_object *obj,
 }
 EXPORT_SYMBOL(drm_object_property_set_value);
 
+/**
+ * drm_object_property_get_value - retrieve the value of a property
+ * @obj: drm mode object to get property value from
+ * @property: property to retrieve
+ * @val: storage for the property value
+ *
+ * This function retrieves the softare state of the given property for the given
+ * property. Since there is no driver callback to retrieve the current property
+ * value this might be out of sync with the hardware, depending upon the driver
+ * and property.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
 int drm_object_property_get_value(struct drm_mode_object *obj,
                                  struct drm_property *property, uint64_t *val)
 {
@@ -2994,10 +3466,22 @@ int drm_object_property_get_value(struct drm_mode_object *obj,
 }
 EXPORT_SYMBOL(drm_object_property_get_value);
 
+/**
+ * drm_mode_getproperty_ioctl - get the current value of a connector's property
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This function retrieves the current value for an connectors's property.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_getproperty_ioctl(struct drm_device *dev,
                               void *data, struct drm_file *file_priv)
 {
-       struct drm_mode_object *obj;
        struct drm_mode_get_property *out_resp = data;
        struct drm_property *property;
        int enum_count = 0;
@@ -3016,17 +3500,17 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
                return -EINVAL;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
-       if (!obj) {
+       property = drm_property_find(dev, out_resp->prop_id);
+       if (!property) {
                ret = -ENOENT;
                goto done;
        }
-       property = obj_to_property(obj);
 
-       if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
+       if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
                list_for_each_entry(prop_enum, &property->enum_blob_list, head)
                        enum_count++;
-       } else if (property->flags & DRM_MODE_PROP_BLOB) {
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
                list_for_each_entry(prop_blob, &property->enum_blob_list, head)
                        blob_count++;
        }
@@ -3048,7 +3532,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
        }
        out_resp->count_values = value_count;
 
-       if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
+       if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
                if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
                        copied = 0;
                        enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
@@ -3070,7 +3555,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
                out_resp->count_enum_blobs = enum_count;
        }
 
-       if (property->flags & DRM_MODE_PROP_BLOB) {
+       if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
                if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
                        copied = 0;
                        blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr;
@@ -3132,10 +3617,23 @@ static void drm_property_destroy_blob(struct drm_device *dev,
        kfree(blob);
 }
 
+/**
+ * drm_mode_getblob_ioctl - get the contents of a blob property value
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This function retrieves the contents of a blob property. The value stored in
+ * an object's blob property is just a normal modeset object id.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_getblob_ioctl(struct drm_device *dev,
                           void *data, struct drm_file *file_priv)
 {
-       struct drm_mode_object *obj;
        struct drm_mode_get_blob *out_resp = data;
        struct drm_property_blob *blob;
        int ret = 0;
@@ -3145,12 +3643,11 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
                return -EINVAL;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
-       if (!obj) {
+       blob = drm_property_blob_find(dev, out_resp->blob_id);
+       if (!blob) {
                ret = -ENOENT;
                goto done;
        }
-       blob = obj_to_blob(obj);
 
        if (out_resp->length == blob->length) {
                blob_ptr = (void __user *)(unsigned long)out_resp->data;
@@ -3166,6 +3663,17 @@ done:
        return ret;
 }
 
+/**
+ * drm_mode_connector_update_edid_property - update the edid property of a connector
+ * @connector: drm connector
+ * @edid: new value of the edid property
+ *
+ * This function creates a new blob modeset object and assigns its id to the
+ * connector's edid property.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_connector_update_edid_property(struct drm_connector *connector,
                                            struct edid *edid)
 {
@@ -3201,19 +3709,40 @@ static bool drm_property_change_is_valid(struct drm_property *property,
 {
        if (property->flags & DRM_MODE_PROP_IMMUTABLE)
                return false;
-       if (property->flags & DRM_MODE_PROP_RANGE) {
+
+       if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
                if (value < property->values[0] || value > property->values[1])
                        return false;
                return true;
-       } else if (property->flags & DRM_MODE_PROP_BITMASK) {
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) {
+               int64_t svalue = U642I64(value);
+               if (svalue < U642I64(property->values[0]) ||
+                               svalue > U642I64(property->values[1]))
+                       return false;
+               return true;
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
                int i;
                uint64_t valid_mask = 0;
                for (i = 0; i < property->num_values; i++)
                        valid_mask |= (1ULL << property->values[i]);
                return !(value & ~valid_mask);
-       } else if (property->flags & DRM_MODE_PROP_BLOB) {
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
                /* Only the driver knows */
                return true;
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
+               struct drm_mode_object *obj;
+               /* a zero value for an object property translates to null: */
+               if (value == 0)
+                       return true;
+               /*
+                * NOTE: use _object_find() directly to bypass restriction on
+                * looking up refcnt'd objects (ie. fb's).  For a refcnt'd
+                * object this could race against object finalization, so it
+                * simply tells us that the object *was* valid.  Which is good
+                * enough.
+                */
+               obj = _object_find(property->dev, value, property->values[0]);
+               return obj != NULL;
        } else {
                int i;
                for (i = 0; i < property->num_values; i++)
@@ -3223,6 +3752,20 @@ static bool drm_property_change_is_valid(struct drm_property *property,
        }
 }
 
+/**
+ * drm_mode_connector_property_set_ioctl - set the current value of a connector property
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This function sets the current value for a connectors's property. It also
+ * calls into a driver's ->set_property callback to update the hardware state
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
                                       void *data, struct drm_file *file_priv)
 {
@@ -3289,6 +3832,21 @@ static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj,
        return ret;
 }
 
+/**
+ * drm_mode_getproperty_ioctl - get the current value of a object's property
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This function retrieves the current value for an object's property. Compared
+ * to the connector specific ioctl this one is extended to also work on crtc and
+ * plane objects.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
                                      struct drm_file *file_priv)
 {
@@ -3345,6 +3903,22 @@ out:
        return ret;
 }
 
+/**
+ * drm_mode_obj_set_property_ioctl - set the current value of an object's property
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This function sets the current value for an object's property. It also calls
+ * into a driver's ->set_property callback to update the hardware state.
+ * Compared to the connector specific ioctl this one is extended to also work on
+ * crtc and plane objects.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
                                    struct drm_file *file_priv)
 {
@@ -3404,6 +3978,18 @@ out:
        return ret;
 }
 
+/**
+ * drm_mode_connector_attach_encoder - attach a connector to an encoder
+ * @connector: connector to attach
+ * @encoder: encoder to attach @connector to
+ *
+ * This function links up a connector to an encoder. Note that the routing
+ * restrictions between encoders and crtcs are exposed to userspace through the
+ * possible_clones and possible_crtcs bitmasks.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_connector_attach_encoder(struct drm_connector *connector,
                                      struct drm_encoder *encoder)
 {
@@ -3419,23 +4005,20 @@ int drm_mode_connector_attach_encoder(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_mode_connector_attach_encoder);
 
-void drm_mode_connector_detach_encoder(struct drm_connector *connector,
-                                   struct drm_encoder *encoder)
-{
-       int i;
-       for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
-               if (connector->encoder_ids[i] == encoder->base.id) {
-                       connector->encoder_ids[i] = 0;
-                       if (connector->encoder == encoder)
-                               connector->encoder = NULL;
-                       break;
-               }
-       }
-}
-EXPORT_SYMBOL(drm_mode_connector_detach_encoder);
-
+/**
+ * drm_mode_crtc_set_gamma_size - set the gamma table size
+ * @crtc: CRTC to set the gamma table size for
+ * @gamma_size: size of the gamma table
+ *
+ * Drivers which support gamma tables should set this to the supported gamma
+ * table size when initializing the CRTC. Currently the drm core only supports a
+ * fixed gamma table size.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
-                                 int gamma_size)
+                                int gamma_size)
 {
        crtc->gamma_size = gamma_size;
 
@@ -3449,11 +4032,24 @@ int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
 }
 EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
 
+/**
+ * drm_mode_gamma_set_ioctl - set the gamma table
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Set the gamma table of a CRTC to the one passed in by the user. Userspace can
+ * inquire the required gamma table size through drm_mode_gamma_get_ioctl.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_gamma_set_ioctl(struct drm_device *dev,
                             void *data, struct drm_file *file_priv)
 {
        struct drm_mode_crtc_lut *crtc_lut = data;
-       struct drm_mode_object *obj;
        struct drm_crtc *crtc;
        void *r_base, *g_base, *b_base;
        int size;
@@ -3463,12 +4059,11 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
                return -EINVAL;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
-       if (!obj) {
+       crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+       if (!crtc) {
                ret = -ENOENT;
                goto out;
        }
-       crtc = obj_to_crtc(obj);
 
        if (crtc->funcs->gamma_set == NULL) {
                ret = -ENOSYS;
@@ -3508,11 +4103,25 @@ out:
 
 }
 
+/**
+ * drm_mode_gamma_get_ioctl - get the gamma table
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Copy the current gamma table into the storage provided. This also provides
+ * the gamma table size the driver expects, which can be used to size the
+ * allocated storage.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_gamma_get_ioctl(struct drm_device *dev,
                             void *data, struct drm_file *file_priv)
 {
        struct drm_mode_crtc_lut *crtc_lut = data;
-       struct drm_mode_object *obj;
        struct drm_crtc *crtc;
        void *r_base, *g_base, *b_base;
        int size;
@@ -3522,12 +4131,11 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
                return -EINVAL;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
-       if (!obj) {
+       crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+       if (!crtc) {
                ret = -ENOENT;
                goto out;
        }
-       crtc = obj_to_crtc(obj);
 
        /* memcpy into gamma store */
        if (crtc_lut->gamma_size != crtc->gamma_size) {
@@ -3558,20 +4166,28 @@ out:
        return ret;
 }
 
-/*
- * The Linux version of kfree() is a macro and can't be called
- * directly via a function pointer
+/**
+ * drm_mode_page_flip_ioctl - schedule an asynchronous fb update
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This schedules an asynchronous update on a given CRTC, called page flip.
+ * Optionally a drm event is generated to signal the completion of the event.
+ * Generic drivers cannot assume that a pageflip with changed framebuffer
+ * properties (including driver specific metadata like tiling layout) will work,
+ * but some drivers support e.g. pixel format changes through the pageflip
+ * ioctl.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
  */
-static void drm_kms_free(void *arg)
-{
-       kfree(arg);
-}
-
 int drm_mode_page_flip_ioctl(struct drm_device *dev,
                             void *data, struct drm_file *file_priv)
 {
        struct drm_mode_crtc_page_flip *page_flip = data;
-       struct drm_mode_object *obj;
        struct drm_crtc *crtc;
        struct drm_framebuffer *fb = NULL, *old_fb = NULL;
        struct drm_pending_vblank_event *e = NULL;
@@ -3584,13 +4200,12 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
        if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
                return -EINVAL;
 
-       obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
-       if (!obj)
+       crtc = drm_crtc_find(dev, page_flip->crtc_id);
+       if (!crtc)
                return -ENOENT;
-       crtc = obj_to_crtc(obj);
 
-       mutex_lock(&crtc->mutex);
-       if (crtc->fb == NULL) {
+       drm_modeset_lock(&crtc->mutex, NULL);
+       if (crtc->primary->fb == NULL) {
                /* The framebuffer is currently unbound, presumably
                 * due to a hotplug event, that userspace has not
                 * yet discovered.
@@ -3612,7 +4227,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
        if (ret)
                goto out;
 
-       if (crtc->fb->pixel_format != fb->pixel_format) {
+       if (crtc->primary->fb->pixel_format != fb->pixel_format) {
                DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
                ret = -EINVAL;
                goto out;
@@ -3642,10 +4257,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                e->base.event = &e->event.base;
                e->base.file_priv = file_priv;
                e->base.destroy =
-                       (void (*) (struct drm_pending_event *)) drm_kms_free;
+                       (void (*) (struct drm_pending_event *)) kfree;
        }
 
-       old_fb = crtc->fb;
+       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) {
@@ -3663,7 +4278,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                 * Failing to do so will screw with the reference counting
                 * on framebuffers.
                 */
-               WARN_ON(crtc->fb != fb);
+               WARN_ON(crtc->primary->fb != fb);
                /* Unref only the old framebuffer. */
                fb = NULL;
        }
@@ -3673,11 +4288,19 @@ out:
                drm_framebuffer_unreference(fb);
        if (old_fb)
                drm_framebuffer_unreference(old_fb);
-       mutex_unlock(&crtc->mutex);
+       drm_modeset_unlock(&crtc->mutex);
 
        return ret;
 }
 
+/**
+ * drm_mode_config_reset - call ->reset callbacks
+ * @dev: drm device
+ *
+ * This functions calls all the crtc's, encoder's and connector's ->reset
+ * callback. Drivers can use this in e.g. their driver load or resume code to
+ * reset hardware and software state.
+ */
 void drm_mode_config_reset(struct drm_device *dev)
 {
        struct drm_crtc *crtc;
@@ -3701,16 +4324,66 @@ void drm_mode_config_reset(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_mode_config_reset);
 
+/**
+ * drm_mode_create_dumb_ioctl - create a dumb backing storage buffer
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This creates a new dumb buffer in the driver's backing storage manager (GEM,
+ * TTM or something else entirely) and returns the resulting buffer handle. This
+ * handle can then be wrapped up into a framebuffer modeset object.
+ *
+ * Note that userspace is not allowed to use such objects for render
+ * acceleration - drivers must create their own private ioctls for such a use
+ * case.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_create_dumb_ioctl(struct drm_device *dev,
                               void *data, struct drm_file *file_priv)
 {
        struct drm_mode_create_dumb *args = data;
+       u32 cpp, stride, size;
 
        if (!dev->driver->dumb_create)
                return -ENOSYS;
+       if (!args->width || !args->height || !args->bpp)
+               return -EINVAL;
+
+       /* overflow checks for 32bit size calculations */
+       cpp = DIV_ROUND_UP(args->bpp, 8);
+       if (cpp > 0xffffffffU / args->width)
+               return -EINVAL;
+       stride = cpp * args->width;
+       if (args->height > 0xffffffffU / stride)
+               return -EINVAL;
+
+       /* test for wrap-around */
+       size = args->height * stride;
+       if (PAGE_ALIGN(size) == 0)
+               return -EINVAL;
+
        return dev->driver->dumb_create(file_priv, dev, args);
 }
 
+/**
+ * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Allocate an offset in the drm device node's address space to be able to
+ * memory map a dumb buffer.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
                             void *data, struct drm_file *file_priv)
 {
@@ -3723,6 +4396,21 @@ int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
        return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset);
 }
 
+/**
+ * drm_mode_destroy_dumb_ioctl - destroy a dumb backing strage buffer
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This destroys the userspace handle for the given dumb backing storage buffer.
+ * Since buffer objects must be reference counted in the kernel a buffer object
+ * won't be immediately freed if a framebuffer modeset object still uses it.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
 int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
                                void *data, struct drm_file *file_priv)
 {
@@ -3734,9 +4422,14 @@ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
        return dev->driver->dumb_destroy(file_priv, dev, args->handle);
 }
 
-/*
- * Just need to support RGB formats here for compat with code that doesn't
- * use pixel formats directly yet.
+/**
+ * drm_fb_get_bpp_depth - get the bpp/depth values for format
+ * @format: pixel format (DRM_FORMAT_*)
+ * @depth: storage for the depth value
+ * @bpp: storage for the bpp value
+ *
+ * This only supports RGB formats here for compat with code that doesn't use
+ * pixel formats directly yet.
  */
 void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
                          int *bpp)
@@ -3808,7 +4501,7 @@ EXPORT_SYMBOL(drm_fb_get_bpp_depth);
  * drm_format_num_planes - get the number of planes for format
  * @format: pixel format (DRM_FORMAT_*)
  *
- * RETURNS:
+ * Returns:
  * The number of planes used by the specified pixel format.
  */
 int drm_format_num_planes(uint32_t format)
@@ -3843,7 +4536,7 @@ EXPORT_SYMBOL(drm_format_num_planes);
  * @format: pixel format (DRM_FORMAT_*)
  * @plane: plane index
  *
- * RETURNS:
+ * Returns:
  * The bytes per pixel value for the specified plane.
  */
 int drm_format_plane_cpp(uint32_t format, int plane)
@@ -3889,7 +4582,7 @@ EXPORT_SYMBOL(drm_format_plane_cpp);
  * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor
  * @format: pixel format (DRM_FORMAT_*)
  *
- * RETURNS:
+ * Returns:
  * The horizontal chroma subsampling factor for the
  * specified pixel format.
  */
@@ -3924,7 +4617,7 @@ EXPORT_SYMBOL(drm_format_horz_chroma_subsampling);
  * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor
  * @format: pixel format (DRM_FORMAT_*)
  *
- * RETURNS:
+ * Returns:
  * The vertical chroma subsampling factor for the
  * specified pixel format.
  */
@@ -3960,11 +4653,13 @@ EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);
 void drm_mode_config_init(struct drm_device *dev)
 {
        lockinit(&dev->mode_config.mutex, "drmmcm", 0, LK_CANRECURSE);
+       drm_modeset_lock_init(&dev->mode_config.connection_mutex);
        lockinit(&dev->mode_config.idr_mutex, "mcfgidr", 0, LK_CANRECURSE);
        lockinit(&dev->mode_config.fb_lock, "drmfbl", 0, LK_CANRECURSE);
        INIT_LIST_HEAD(&dev->mode_config.fb_list);
        INIT_LIST_HEAD(&dev->mode_config.crtc_list);
        INIT_LIST_HEAD(&dev->mode_config.connector_list);
+       INIT_LIST_HEAD(&dev->mode_config.bridge_list);
        INIT_LIST_HEAD(&dev->mode_config.encoder_list);
        INIT_LIST_HEAD(&dev->mode_config.property_list);
        INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
@@ -3973,6 +4668,7 @@ void drm_mode_config_init(struct drm_device *dev)
 
        drm_modeset_lock_all(dev);
        drm_mode_create_standard_connector_properties(dev);
+       drm_mode_create_standard_plane_properties(dev);
        drm_modeset_unlock_all(dev);
 
        /* Just to be sure */
@@ -3980,6 +4676,8 @@ void drm_mode_config_init(struct drm_device *dev)
        dev->mode_config.num_connector = 0;
        dev->mode_config.num_crtc = 0;
        dev->mode_config.num_encoder = 0;
+       dev->mode_config.num_overlay_plane = 0;
+       dev->mode_config.num_total_plane = 0;
 }
 EXPORT_SYMBOL(drm_mode_config_init);
 
@@ -4001,6 +4699,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
        struct drm_connector *connector, *ot;
        struct drm_crtc *crtc, *ct;
        struct drm_encoder *encoder, *enct;
+       struct drm_bridge *bridge, *brt;
        struct drm_framebuffer *fb, *fbt;
        struct drm_property *property, *pt;
        struct drm_property_blob *blob, *bt;
@@ -4011,6 +4710,11 @@ void drm_mode_config_cleanup(struct drm_device *dev)
                encoder->funcs->destroy(encoder);
        }
 
+       list_for_each_entry_safe(bridge, brt,
+                                &dev->mode_config.bridge_list, head) {
+               bridge->funcs->destroy(bridge);
+       }
+
        list_for_each_entry_safe(connector, ot,
                                 &dev->mode_config.connector_list, head) {
                connector->funcs->destroy(connector);
@@ -4049,5 +4753,6 @@ void drm_mode_config_cleanup(struct drm_device *dev)
        }
 
        idr_destroy(&dev->mode_config.crtc_idr);
+       drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
 }
 EXPORT_SYMBOL(drm_mode_config_cleanup);
index 629689f..3b05572 100644 (file)
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
-#include <uapi_drm/drm_fourcc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_edid.h>
 
-bool
-drm_fetch_cmdline_mode_from_kenv(struct drm_connector *connector,
-    struct drm_cmdline_mode *cmdline_mode)
-{
-       char *tun_var_name, *tun_mode;
-       static const char tun_prefix[] = "drm_mode.";
-       bool res;
-
-       res = false;
-       tun_var_name = kmalloc(sizeof(tun_prefix) +
-           strlen(drm_get_connector_name(connector)), M_TEMP, M_WAITOK);
-       strcpy(tun_var_name, tun_prefix);
-       strcat(tun_var_name, drm_get_connector_name(connector));
-       tun_mode = kgetenv(tun_var_name);
-       if (tun_mode != NULL) {
-               res = drm_mode_parse_command_line_for_connector(tun_mode,
-                   connector, cmdline_mode);
-               kfreeenv(tun_mode);
-       }
-       drm_free(tun_var_name, M_TEMP);
-       return (res);
-}
-
 MODULE_AUTHOR("David Airlie, Jesse Barnes");
 MODULE_DESCRIPTION("DRM KMS helper");
+MODULE_LICENSE("GPL and additional rights");
 
 /**
  * drm_helper_move_panel_connectors_to_head() - move panels to the front in the
@@ -94,165 +71,24 @@ void drm_helper_move_panel_connectors_to_head(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head);
 
-static bool drm_kms_helper_poll = true;
-module_param_named(poll, drm_kms_helper_poll, bool, 0600);
-
-static void drm_mode_validate_flag(struct drm_connector *connector,
-                                  int flags)
-{
-       struct drm_display_mode *mode;
-
-       if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE |
-                     DRM_MODE_FLAG_3D_MASK))
-               return;
-
-       list_for_each_entry(mode, &connector->modes, head) {
-               if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
-                               !(flags & DRM_MODE_FLAG_INTERLACE))
-                       mode->status = MODE_NO_INTERLACE;
-               if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) &&
-                               !(flags & DRM_MODE_FLAG_DBLSCAN))
-                       mode->status = MODE_NO_DBLESCAN;
-               if ((mode->flags & DRM_MODE_FLAG_3D_MASK) &&
-                               !(flags & DRM_MODE_FLAG_3D_MASK))
-                       mode->status = MODE_NO_STEREO;
-       }
-
-       return;
-}
-
-/**
- * drm_helper_probe_single_connector_modes - get complete set of display modes
- * @connector: connector to probe
- * @maxX: max width for modes
- * @maxY: max height for modes
- *
- * LOCKING:
- * Caller must hold mode config lock.
- *
- * Based on the helper callbacks implemented by @connector try to detect all
- * valid modes.  Modes will first be added to the connector's probed_modes list,
- * then culled (based on validity and the @maxX, @maxY parameters) and put into
- * the normal modes list.
- *
- * Intended to be use as a generic implementation of the ->probe() @connector
- * callback for drivers that use the crtc helpers for output mode filtering and
- * detection.
- *
- * RETURNS:
- * Number of modes found on @connector.
- */
-int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
-                                           uint32_t maxX, uint32_t maxY)
-{
-       struct drm_device *dev = connector->dev;
-       struct drm_display_mode *mode;
-       struct drm_connector_helper_funcs *connector_funcs =
-               connector->helper_private;
-       int count = 0;
-       int mode_flags = 0;
-       bool verbose_prune = true;
-
-       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
-                       drm_get_connector_name(connector));
-       /* set all modes to the unverified state */
-       list_for_each_entry(mode, &connector->modes, head)
-               mode->status = MODE_UNVERIFIED;
-
-       if (connector->force) {
-               if (connector->force == DRM_FORCE_ON)
-                       connector->status = connector_status_connected;
-               else
-                       connector->status = connector_status_disconnected;
-               if (connector->funcs->force)
-                       connector->funcs->force(connector);
-       } else {
-               connector->status = connector->funcs->detect(connector, true);
-       }
-
-       /* Re-enable polling in case the global poll config changed. */
-       if (drm_kms_helper_poll != dev->mode_config.poll_running)
-               drm_kms_helper_poll_enable(dev);
-
-       dev->mode_config.poll_running = drm_kms_helper_poll;
-
-       if (connector->status == connector_status_disconnected) {
-               DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n",
-                       connector->base.id, drm_get_connector_name(connector));
-               drm_mode_connector_update_edid_property(connector, NULL);
-               verbose_prune = false;
-               goto prune;
-       }
-
-#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
-       count = drm_load_edid_firmware(connector);
-       if (count == 0)
-#endif
-               count = (*connector_funcs->get_modes)(connector);
-
-       if (count == 0 && connector->status == connector_status_connected)
-               count = drm_add_modes_noedid(connector, 1024, 768);
-       if (count == 0)
-               goto prune;
-
-       drm_mode_connector_list_update(connector);
-
-       if (maxX && maxY)
-               drm_mode_validate_size(dev, &connector->modes, maxX,
-                                      maxY, 0);
-
-       if (connector->interlace_allowed)
-               mode_flags |= DRM_MODE_FLAG_INTERLACE;
-       if (connector->doublescan_allowed)
-               mode_flags |= DRM_MODE_FLAG_DBLSCAN;
-       if (connector->stereo_allowed)
-               mode_flags |= DRM_MODE_FLAG_3D_MASK;
-       drm_mode_validate_flag(connector, mode_flags);
-
-       list_for_each_entry(mode, &connector->modes, head) {
-               if (mode->status == MODE_OK)
-                       mode->status = connector_funcs->mode_valid(connector,
-                                                                  mode);
-       }
-
-prune:
-       drm_mode_prune_invalid(dev, &connector->modes, verbose_prune);
-
-       if (list_empty(&connector->modes))
-               return 0;
-
-       list_for_each_entry(mode, &connector->modes, head)
-               mode->vrefresh = drm_mode_vrefresh(mode);
-
-       drm_mode_sort(&connector->modes);
-
-       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id,
-                       drm_get_connector_name(connector));
-       list_for_each_entry(mode, &connector->modes, head) {
-               drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
-               drm_mode_debug_printmodeline(mode);
-       }
-
-       return count;
-}
-EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
-
 /**
  * drm_helper_encoder_in_use - check if a given encoder is in use
  * @encoder: encoder to check
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
- * Walk @encoders's DRM device's mode_config and see if it's in use.
+ * Checks whether @encoder is with the current mode setting output configuration
+ * in use by any connector. This doesn't mean that it is actually enabled since
+ * the DPMS state is tracked separately.
  *
- * RETURNS:
- * True if @encoder is part of the mode_config, false otherwise.
+ * Returns:
+ * True if @encoder is used, false otherwise.
  */
 bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
 {
        struct drm_connector *connector;
        struct drm_device *dev = encoder->dev;
+
+       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
        list_for_each_entry(connector, &dev->mode_config.connector_list, head)
                if (connector->encoder == encoder)
                        return true;
@@ -264,19 +100,19 @@ EXPORT_SYMBOL(drm_helper_encoder_in_use);
  * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config
  * @crtc: CRTC to check
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
- * Walk @crtc's DRM device's mode_config and see if it's in use.
+ * Checks whether @crtc is with the current mode setting output configuration
+ * in use by any connector. This doesn't mean that it is actually enabled since
+ * the DPMS state is tracked separately.
  *
- * RETURNS:
- * True if @crtc is part of the mode_config, false otherwise.
+ * Returns:
+ * True if @crtc is used, false otherwise.
  */
 bool drm_helper_crtc_in_use(struct drm_crtc *crtc)
 {
        struct drm_encoder *encoder;
        struct drm_device *dev = crtc->dev;
-       /* FIXME: Locking around list access? */
+
+       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
                if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder))
                        return true;
@@ -289,40 +125,29 @@ drm_encoder_disable(struct drm_encoder *encoder)
 {
        struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
 
+       if (encoder->bridge)
+               encoder->bridge->funcs->disable(encoder->bridge);
+
        if (encoder_funcs->disable)
                (*encoder_funcs->disable)(encoder);
        else
-               if (encoder_funcs->dpms)
-                       (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
+               (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
+
+       if (encoder->bridge)
+               encoder->bridge->funcs->post_disable(encoder->bridge);
 }
 
-/**
- * drm_helper_disable_unused_functions - disable unused objects
- * @dev: DRM device
- *
- * LOCKING:
- * Caller must hold mode config lock.
- *
- * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled
- * by calling its dpms function, which should power it off.
- */
-void drm_helper_disable_unused_functions(struct drm_device *dev)
+static void __drm_helper_disable_unused_functions(struct drm_device *dev)
 {
        struct drm_encoder *encoder;
-       struct drm_connector *connector;
        struct drm_crtc *crtc;
 
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-               if (!connector->encoder)
-                       continue;
-               if (connector->status == connector_status_disconnected)
-                       connector->encoder = NULL;
-       }
+       drm_warn_on_modeset_not_all_locked(dev);
 
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
                if (!drm_helper_encoder_in_use(encoder)) {
                        drm_encoder_disable(encoder);
-                       /* disconnector encoder from any connector */
+                       /* disconnect encoder from any connector */
                        encoder->crtc = NULL;
                }
        }
@@ -334,12 +159,28 @@ void drm_helper_disable_unused_functions(struct drm_device *dev)
                        if (crtc_funcs->disable)
                                (*crtc_funcs->disable)(crtc);
                        else
-                               if (crtc_funcs->dpms)
-                                       (*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF);
-                       crtc->fb = NULL;
+                               (*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF);
+                       crtc->primary->fb = NULL;
                }
        }
 }
+
+/**
+ * drm_helper_disable_unused_functions - disable unused objects
+ * @dev: DRM device
+ *
+ * This function walks through the entire mode setting configuration of @dev. It
+ * will remove any crtc links of unused encoders and encoder links of
+ * disconnected connectors. Then it will disable all unused encoders and crtcs
+ * either by calling their disable callback if available or by calling their
+ * dpms callback with DRM_MODE_DPMS_OFF.
+ */
+void drm_helper_disable_unused_functions(struct drm_device *dev)
+{
+       drm_modeset_lock_all(dev);
+       __drm_helper_disable_unused_functions(dev);
+       drm_modeset_unlock_all(dev);
+}
 EXPORT_SYMBOL(drm_helper_disable_unused_functions);
 
 /*
@@ -373,9 +214,6 @@ drm_crtc_prepare_encoders(struct drm_device *dev)
  * @y: vertical offset into the surface
  * @old_fb: old framebuffer, for cleanup
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
  * Try to set @mode on @crtc.  Give @crtc and its associated connectors a chance
  * to fixup or reject the mode prior to trying to set it. This is an internal
  * helper that drivers could e.g. use to update properties that require the
@@ -385,8 +223,8 @@ drm_crtc_prepare_encoders(struct drm_device *dev)
  * drm_crtc_helper_set_config() helper function to drive the mode setting
  * sequence.
  *
- * RETURNS:
- * True if the mode was set successfully, or false otherwise.
+ * Returns:
+ * True if the mode was set successfully, false otherwise.
  */
 bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
                              struct drm_display_mode *mode,
@@ -402,6 +240,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
        struct drm_encoder *encoder;
        bool ret = true;
 
+       drm_warn_on_modeset_not_all_locked(dev);
+
        saved_enabled = crtc->enabled;
        crtc->enabled = drm_helper_crtc_in_use(crtc);
        if (!crtc->enabled)
@@ -432,6 +272,16 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
 
                if (encoder->crtc != crtc)
                        continue;
+
+               if (encoder->bridge && encoder->bridge->funcs->mode_fixup) {
+                       ret = encoder->bridge->funcs->mode_fixup(
+                                       encoder->bridge, mode, adjusted_mode);
+                       if (!ret) {
+                               DRM_DEBUG_KMS("Bridge fixup failed\n");
+                               goto done;
+                       }
+               }
+
                encoder_funcs = encoder->helper_private;
                if (!(ret = encoder_funcs->mode_fixup(encoder, mode,
                                                      adjusted_mode))) {
@@ -451,9 +301,16 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
 
                if (encoder->crtc != crtc)
                        continue;
+
+               if (encoder->bridge)
+                       encoder->bridge->funcs->disable(encoder->bridge);
+
                encoder_funcs = encoder->helper_private;
                /* Disable the encoders as the first thing we do. */
                encoder_funcs->prepare(encoder);
+
+               if (encoder->bridge)
+                       encoder->bridge->funcs->post_disable(encoder->bridge);
        }
 
        drm_crtc_prepare_encoders(dev);
@@ -473,10 +330,14 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
                        continue;
 
                DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n",
-                       encoder->base.id, drm_get_encoder_name(encoder),
+                       encoder->base.id, encoder->name,
                        mode->base.id, mode->name);
                encoder_funcs = encoder->helper_private;
                encoder_funcs->mode_set(encoder, mode, adjusted_mode);
+
+               if (encoder->bridge && encoder->bridge->funcs->mode_set)
+                       encoder->bridge->funcs->mode_set(encoder->bridge, mode,
+                                       adjusted_mode);
        }
 
        /* Now enable the clocks, plane, pipe, and connectors that we set up. */
@@ -487,9 +348,14 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
                if (encoder->crtc != crtc)
                        continue;
 
+               if (encoder->bridge)
+                       encoder->bridge->funcs->pre_enable(encoder->bridge);
+
                encoder_funcs = encoder->helper_private;
                encoder_funcs->commit(encoder);
 
+               if (encoder->bridge)
+                       encoder->bridge->funcs->enable(encoder->bridge);
        }
 
        /* Store real post-adjustment hardware mode. */
@@ -515,8 +381,7 @@ done:
 }
 EXPORT_SYMBOL(drm_crtc_helper_set_mode);
 
-
-static int
+static void
 drm_crtc_helper_disable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -544,25 +409,21 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
                }
        }
 
-       drm_helper_disable_unused_functions(dev);
-       return 0;
+       __drm_helper_disable_unused_functions(dev);
 }
 
 /**
  * drm_crtc_helper_set_config - set a new config from userspace
  * @set: mode set configuration
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
  * Setup a new configuration, provided by the upper layers (either an ioctl call
- * from userspace or internally e.g. from the fbdev suppport code) in @set, and
+ * from userspace or internally e.g. from the fbdev support code) in @set, and
  * enable it. This is the main helper functions for drivers that implement
  * kernel mode setting with the crtc helper functions and the assorted
  * ->prepare(), ->modeset() and ->commit() helper callbacks.
  *
- * RETURNS:
- * Returns 0 on success, -ERRNO on failure.
+ * Returns:
+ * Returns 0 on success, negative errno numbers on failure.
  */
 int drm_crtc_helper_set_config(struct drm_mode_set *set)
 {
@@ -599,11 +460,14 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                                (int)set->num_connectors, set->x, set->y);
        } else {
                DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id);
-               return drm_crtc_helper_disable(set->crtc);
+               drm_crtc_helper_disable(set->crtc);
+               return 0;
        }
 
        dev = set->crtc->dev;
 
+       drm_warn_on_modeset_not_all_locked(dev);
+
        /*
         * Allocate space for the backup of all (non-pointer) encoder and
         * connector data.
@@ -639,19 +503,19 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
        save_set.mode = &set->crtc->mode;
        save_set.x = set->crtc->x;
        save_set.y = set->crtc->y;
-       save_set.fb = set->crtc->fb;
+       save_set.fb = set->crtc->primary->fb;
 
        /* We should be able to check here if the fb has the same properties
         * and then just flip_or_move it */
-       if (set->crtc->fb != set->fb) {
+       if (set->crtc->primary->fb != set->fb) {
                /* If we have no fb then treat it as a full mode set */
-               if (set->crtc->fb == NULL) {
+               if (set->crtc->primary->fb == NULL) {
                        DRM_DEBUG_KMS("crtc has no fb, full mode set\n");
                        mode_changed = true;
                } else if (set->fb == NULL) {
                        mode_changed = true;
                } else if (set->fb->pixel_format !=
-                          set->crtc->fb->pixel_format) {
+                          set->crtc->primary->fb->pixel_format) {
                        mode_changed = true;
                } else
                        fb_changed = true;
@@ -681,12 +545,13 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                                if (new_encoder == NULL)
                                        /* don't break so fail path works correct */
                                        fail = 1;
-                               break;
 
                                if (connector->dpms != DRM_MODE_DPMS_ON) {
                                        DRM_DEBUG_KMS("connector dpms not on, full mode switch\n");
                                        mode_changed = true;
                                }
+
+                               break;
                        }
                }
 
@@ -735,11 +600,11 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                }
                if (new_crtc) {
                        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
-                               connector->base.id, drm_get_connector_name(connector),
+                               connector->base.id, connector->name,
                                new_crtc->base.id);
                } else {
                        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
-                               connector->base.id, drm_get_connector_name(connector));
+                               connector->base.id, connector->name);
                }
        }
 
@@ -752,34 +617,34 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                        DRM_DEBUG_KMS("attempting to set mode from"
                                        " userspace\n");
                        drm_mode_debug_printmodeline(set->mode);
-                       set->crtc->fb = set->fb;
+                       set->crtc->primary->fb = set->fb;
                        if (!drm_crtc_helper_set_mode(set->crtc, set->mode,
                                                      set->x, set->y,
                                                      save_set.fb)) {
                                DRM_ERROR("failed to set mode on [CRTC:%d]\n",
                                          set->crtc->base.id);
-                               set->crtc->fb = save_set.fb;
+                               set->crtc->primary->fb = save_set.fb;
                                ret = -EINVAL;
                                goto fail;
                        }
                        DRM_DEBUG_KMS("Setting connector DPMS state to on\n");
                        for (i = 0; i < set->num_connectors; i++) {
                                DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
-                                             drm_get_connector_name(set->connectors[i]));
+                                             set->connectors[i]->name);
                                set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
                        }
                }
-               drm_helper_disable_unused_functions(dev);
+               __drm_helper_disable_unused_functions(dev);
        } else if (fb_changed) {
                set->crtc->x = set->x;
                set->crtc->y = set->y;
-               set->crtc->fb = set->fb;
+               set->crtc->primary->fb = set->fb;
                ret = crtc_funcs->mode_set_base(set->crtc,
                                                set->x, set->y, save_set.fb);
                if (ret != 0) {
                        set->crtc->x = save_set.x;
                        set->crtc->y = save_set.y;
-                       set->crtc->fb = save_set.fb;
+                       set->crtc->primary->fb = save_set.fb;
                        goto fail;
                }
        }
@@ -825,6 +690,31 @@ static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder)
        return dpms;
 }
 
+/* Helper which handles bridge ordering around encoder dpms */
+static void drm_helper_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+       struct drm_bridge *bridge = encoder->bridge;
+       struct drm_encoder_helper_funcs *encoder_funcs;
+
+       if (bridge) {
+               if (mode == DRM_MODE_DPMS_ON)
+                       bridge->funcs->pre_enable(bridge);
+               else
+                       bridge->funcs->disable(bridge);
+       }
+
+       encoder_funcs = encoder->helper_private;
+       if (encoder_funcs->dpms)
+               encoder_funcs->dpms(encoder, mode);
+
+       if (bridge) {
+               if (mode == DRM_MODE_DPMS_ON)
+                       bridge->funcs->enable(bridge);
+               else
+                       bridge->funcs->post_disable(bridge);
+       }
+}
+
 static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc)
 {
        int dpms = DRM_MODE_DPMS_OFF;
@@ -852,7 +742,7 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
 {
        struct drm_encoder *encoder = connector->encoder;
        struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
-       int old_dpms;
+       int old_dpms, encoder_dpms = DRM_MODE_DPMS_OFF;
 
        if (mode == connector->dpms)
                return;
@@ -860,6 +750,9 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
        old_dpms = connector->dpms;
        connector->dpms = mode;
 
+       if (encoder)
+               encoder_dpms = drm_helper_choose_encoder_dpms(encoder);
+
        /* from off to on, do crtc then encoder */
        if (mode < old_dpms) {
                if (crtc) {
@@ -868,22 +761,14 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
                                (*crtc_funcs->dpms) (crtc,
                                                     drm_helper_choose_crtc_dpms(crtc));
                }
-               if (encoder) {
-                       struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
-                       if (encoder_funcs->dpms)
-                               (*encoder_funcs->dpms) (encoder,
-                                                       drm_helper_choose_encoder_dpms(encoder));
-               }
+               if (encoder)
+                       drm_helper_encoder_dpms(encoder, encoder_dpms);
        }
 
        /* from on to off, do encoder then crtc */
        if (mode > old_dpms) {
-               if (encoder) {
-                       struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
-                       if (encoder_funcs->dpms)
-                               (*encoder_funcs->dpms) (encoder,
-                                                       drm_helper_choose_encoder_dpms(encoder));
-               }
+               if (encoder)
+                       drm_helper_encoder_dpms(encoder, encoder_dpms);
                if (crtc) {
                        struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
                        if (crtc_funcs->dpms)
@@ -896,8 +781,16 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
 }
 EXPORT_SYMBOL(drm_helper_connector_dpms);
 
-int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
-                                  struct drm_mode_fb_cmd2 *mode_cmd)
+/**
+ * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata
+ * @fb: drm_framebuffer object to fill out
+ * @mode_cmd: metadata from the userspace fb creation request
+ *
+ * This helper can be used in a drivers fb_create callback to pre-fill the fb's
+ * metadata fields.
+ */
+void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
+                                   struct drm_mode_fb_cmd2 *mode_cmd)
 {
        int i;
 
@@ -910,27 +803,47 @@ int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
        drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
                                    &fb->bits_per_pixel);
        fb->pixel_format = mode_cmd->pixel_format;
-
-       return 0;
 }
 EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
 
-int drm_helper_resume_force_mode(struct drm_device *dev)
+/**
+ * drm_helper_resume_force_mode - force-restore mode setting configuration
+ * @dev: drm_device which should be restored
+ *
+ * Drivers which use the mode setting helpers can use this function to
+ * force-restore the mode setting configuration e.g. on resume or when something
+ * else might have trampled over the hw state (like some overzealous old BIOSen
+ * tended to do).
+ *
+ * This helper doesn't provide a error return value since restoring the old
+ * config should never fail due to resource allocation issues since the driver
+ * has successfully set the restored configuration already. Hence this should
+ * boil down to the equivalent of a few dpms on calls, which also don't provide
+ * an error code.
+ *
+ * Drivers where simply restoring an old configuration again might fail (e.g.
+ * due to slight differences in allocating shared resources when the
+ * configuration is restored in a different order than when userspace set it up)
+ * need to use their own restore logic.
+ */
+void drm_helper_resume_force_mode(struct drm_device *dev)
 {
        struct drm_crtc *crtc;
        struct drm_encoder *encoder;
-       struct drm_encoder_helper_funcs *encoder_funcs;
        struct drm_crtc_helper_funcs *crtc_funcs;
-       int ret;
+       int encoder_dpms;
+       bool ret;
 
+       drm_modeset_lock_all(dev);
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 
                if (!crtc->enabled)
                        continue;
 
                ret = drm_crtc_helper_set_mode(crtc, &crtc->mode,
-                                              crtc->x, crtc->y, crtc->fb);
+                                              crtc->x, crtc->y, crtc->primary->fb);
 
+               /* Restoring the old config should never fail! */
                if (ret == false)
                        DRM_ERROR("failed to set mode on crtc %p\n", crtc);
 
@@ -941,10 +854,10 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
                                if(encoder->crtc != crtc)
                                        continue;
 
-                               encoder_funcs = encoder->helper_private;
-                               if (encoder_funcs->dpms)
-                                       (*encoder_funcs->dpms) (encoder,
-                                                               drm_helper_choose_encoder_dpms(encoder));
+                               encoder_dpms = drm_helper_choose_encoder_dpms(
+                                                       encoder);
+
+                               drm_helper_encoder_dpms(encoder, encoder_dpms);
                        }
 
                        crtc_funcs = crtc->helper_private;
@@ -953,157 +866,9 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
                                                     drm_helper_choose_crtc_dpms(crtc));
                }
        }
+
        /* disable the unused connectors while restoring the modesetting */
-       drm_helper_disable_unused_functions(dev);
-       return 0;
+       __drm_helper_disable_unused_functions(dev);
+       drm_modeset_unlock_all(dev);
 }
 EXPORT_SYMBOL(drm_helper_resume_force_mode);
-
-void drm_kms_helper_hotplug_event(struct drm_device *dev)
-{
-       /* send a uevent + call fbdev */
-#if 0
-       drm_sysfs_hotplug_event(dev);
-#endif
-       if (dev->mode_config.funcs->output_poll_changed)
-               dev->mode_config.funcs->output_poll_changed(dev);
-}
-EXPORT_SYMBOL(drm_kms_helper_hotplug_event);
-
-#define DRM_OUTPUT_POLL_PERIOD (10*HZ)
-static void output_poll_execute(struct work_struct *work)
-{
-       struct delayed_work *delayed_work = to_delayed_work(work);
-       struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work);
-       struct drm_connector *connector;
-       enum drm_connector_status old_status;
-       bool repoll = false, changed = false;
-
-       if (!drm_kms_helper_poll)
-               return;
-
-       mutex_lock(&dev->mode_config.mutex);
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-
-               /* Ignore forced connectors. */
-               if (connector->force)
-                       continue;
-
-               /* Ignore HPD capable connectors and connectors where we don't
-                * want any hotplug detection at all for polling. */
-               if (!connector->polled || connector->polled == DRM_CONNECTOR_POLL_HPD)
-                       continue;
-
-               repoll = true;
-
-               old_status = connector->status;
-               /* if we are connected and don't want to poll for disconnect
-                  skip it */
-               if (old_status == connector_status_connected &&
-                   !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT))
-                       continue;
-
-               connector->status = connector->funcs->detect(connector, false);
-               if (old_status != connector->status) {
-                       const char *old, *new;
-
-                       old = drm_get_connector_status_name(old_status);
-                       new = drm_get_connector_status_name(connector->status);
-
-                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] "
-                                     "status updated from %s to %s\n",
-                                     connector->base.id,
-                                     drm_get_connector_name(connector),
-                                     old, new);
-
-                       changed = true;
-               }
-       }
-
-       mutex_unlock(&dev->mode_config.mutex);
-
-       if (changed)
-               drm_kms_helper_hotplug_event(dev);
-
-       if (repoll)
-               schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD);
-}
-
-void drm_kms_helper_poll_disable(struct drm_device *dev)
-{
-       if (!dev->mode_config.poll_enabled)
-               return;
-       cancel_delayed_work_sync(&dev->mode_config.output_poll_work);
-}
-EXPORT_SYMBOL(drm_kms_helper_poll_disable);
-
-void drm_kms_helper_poll_enable(struct drm_device *dev)
-{
-       bool poll = false;
-       struct drm_connector *connector;
-
-       if (!dev->mode_config.poll_enabled || !drm_kms_helper_poll)
-               return;
-
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-               if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT |
-                                        DRM_CONNECTOR_POLL_DISCONNECT))
-                       poll = true;
-       }
-
-       if (poll)
-               schedule_delayed_work(&dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD);
-}
-EXPORT_SYMBOL(drm_kms_helper_poll_enable);
-
-void drm_kms_helper_poll_init(struct drm_device *dev)
-{
-       INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute);
-       dev->mode_config.poll_enabled = true;
-
-       drm_kms_helper_poll_enable(dev);
-}
-EXPORT_SYMBOL(drm_kms_helper_poll_init);
-
-void drm_kms_helper_poll_fini(struct drm_device *dev)
-{
-       drm_kms_helper_poll_disable(dev);
-}
-EXPORT_SYMBOL(drm_kms_helper_poll_fini);
-
-bool drm_helper_hpd_irq_event(struct drm_device *dev)
-{
-       struct drm_connector *connector;
-       enum drm_connector_status old_status;
-       bool changed = false;
-
-       if (!dev->mode_config.poll_enabled)
-               return false;
-
-       mutex_lock(&dev->mode_config.mutex);
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-
-               /* Only handle HPD capable connectors. */
-               if (!(connector->polled & DRM_CONNECTOR_POLL_HPD))
-                       continue;
-
-               old_status = connector->status;
-
-               connector->status = connector->funcs->detect(connector, false);
-               DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n",
-                             connector->base.id,
-                             drm_get_connector_name(connector),
-                             drm_get_connector_status_name(old_status),
-                             drm_get_connector_status_name(connector->status));
-               if (old_status != connector->status)
-                       changed = true;
-       }
-
-       mutex_unlock(&dev->mode_config.mutex);
-
-       if (changed)
-               drm_kms_helper_hotplug_event(dev);
-
-       return changed;
-}
-EXPORT_SYMBOL(drm_helper_hpd_irq_event);
diff --git a/sys/dev/drm/drm_crtc_internal.h b/sys/dev/drm/drm_crtc_internal.h
new file mode 100644 (file)
index 0000000..a2945ee
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2006 Keith Packard
+ * Copyright © 2007-2008 Dave Airlie
+ * Copyright © 2007-2008 Intel Corporation
+ *   Jesse Barnes <jesse.barnes@intel.com>
+ * Copyright © 2014 Intel Corporation
+ *   Daniel Vetter <daniel.vetter@ffwll.ch>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * This header file contains mode setting related functions and definitions
+ * which are only used within the drm module as internal implementation details
+ * and are not exported to drivers.
+ */
+
+int drm_mode_object_get(struct drm_device *dev,
+                       struct drm_mode_object *obj, uint32_t obj_type);
+void drm_mode_object_put(struct drm_device *dev,
+                        struct drm_mode_object *object);
+
diff --git a/sys/dev/drm/drm_dragonfly.c b/sys/dev/drm/drm_dragonfly.c
new file mode 100644 (file)
index 0000000..c7362c2
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015 Imre Vadász <imre@vdsz.com>
+ *
+ * DRM Dragonfly-specific helper functions
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <sys/libkern.h>
+#include <sys/ctype.h>
+#include <drm/drmP.h>
+
+#if 0
+commit 26a028bf8c7694e64d44f9e2bb8bd0fba47d7519
+Author: Imre Vadász <imre@vdsz.com>
+Date:   Tue Jun 2 23:14:52 2015 +0200
+
+    drm: hack together an implementation of fb_get_options
+    
+    This can be used to set the video mode used for the syscons fb console,
+    a la "video=..." in linux.
+#endif
+
+/*
+ * An implementation of fb_get_options()
+ * This can be used to set the video mode used for the syscons fb console,
+ * a la "video=..." in linux.
+ */
+int
+fb_get_options(const char *connector_name, char **option)
+{
+       char buf[128], str[1024];
+       int i;
+
+       /*
+        * This hack allows us to use drm.video.lvds1="<video-mode>"
+        * in loader.conf, where linux would use video=LVDS-1:<video-mode>.
+        * e.g. drm.video.lvds1=1024x768 sets the LVDS-1 connector to
+        * a 1024x768 video mode in the syscons framebuffer console.
+        * See https://wiki.archlinux.org/index.php/Kernel_mode_setting
+        * for an explanation of the video mode command line option.
+        * (This corresponds to the video= Linux kernel command-line
+        * option)
+        */
+       memset(str, 0, sizeof(str));
+       ksnprintf(buf, sizeof(buf), "drm.video.%s", connector_name);
+       i = 0;
+       while (i < strlen(buf)) {
+               buf[i] = tolower(buf[i]);
+               if (buf[i] == '-') {
+                       memmove(&buf[i], &buf[i+1], strlen(buf)-i);
+               } else {
+                       i++;
+               }
+       }
+       kprintf("looking up kenv for \"%s\"\n", buf);
+       if (kgetenv_string(buf, str, sizeof(str)-1)) {
+               kprintf("found kenv %s=%s\n", buf, str);
+               *option = kstrdup(str, M_DRM);
+               return (0);
+       } else {
+               kprintf("didn't find value for kenv %s\n", buf);
+               return (1);
+       }
+}
+
index 322035b..03b6788 100644 (file)
 #define DRM_DEBUGBITS_ON (0x0)
 #endif
 
-int drm_debug = DRM_DEBUGBITS_ON;
 int drm_notyet_flag = 0;
 
-unsigned int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
-unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
-
 static int drm_load(struct drm_device *dev);
 drm_pci_id_list_t *drm_find_description(int vendor, int device,
     drm_pci_id_list_t *idlist);
@@ -99,8 +95,8 @@ MODULE_DEPEND(drm, agp, 1, 1, 1);
 MODULE_DEPEND(drm, pci, 1, 1, 1);
 MODULE_DEPEND(drm, iicbus, 1, 1, 1);
 
-#define DRM_IOCTL_DEF(ioctl, func, flags) \
-       [DRM_IOCTL_NR(ioctl)] = {ioctl, func, flags}
+#define DRM_IOCTL_DEF(ioctl, _func, _flags) \
+       [DRM_IOCTL_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, .cmd_drv = 0, .name = #ioctl}
 
 /** Ioctl table */
 static struct drm_ioctl_desc drm_ioctls[] = {
@@ -162,7 +158,7 @@ static struct drm_ioctl_desc drm_ioctls[] = {
        DRM_IOCTL_DEF(DRM_IOCTL_AGP_BIND, drm_agp_bind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
        DRM_IOCTL_DEF(DRM_IOCTL_AGP_UNBIND, drm_agp_unbind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 
-       DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_sg_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_sg_alloc, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
        DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 
        DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, DRM_UNLOCKED),
index 1b4aa3e..de0cf7b 100644 (file)
@@ -74,6 +74,8 @@
 #define EDID_QUIRK_FORCE_REDUCED_BLANKING      (1 << 7)
 /* Force 8bpc */
 #define EDID_QUIRK_FORCE_8BPC                  (1 << 8)
+/* Force 12bpc */
+#define EDID_QUIRK_FORCE_12BPC                 (1 << 9)
 
 struct detailed_mode_closure {
        struct drm_connector *connector;
@@ -129,6 +131,9 @@ static struct edid_quirk {
        { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 },
        { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 },
 
+       /* Sony PVM-2541A does up to 12 bpc, but only reports max 8 bpc */
+       { "SNY", 0x2541, EDID_QUIRK_FORCE_12BPC },
+
        /* ViewSonic VA2026w */
        { "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING },
 
@@ -988,9 +993,13 @@ static const u8 edid_header[] = {
        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
 };
 
- /*
- * Sanity check the header of the base EDID block.  Return 8 if the header
- * is perfect, down to 0 if it's totally wrong.
+/**
+ * drm_edid_header_is_valid - sanity check the header of the base EDID block
+ * @raw_edid: pointer to raw base EDID block
+ *
+ * Sanity check the header of the base EDID block.
+ *
+ * Return: 8 if the header is perfect, down to 0 if it's totally wrong.
  */
 int drm_edid_header_is_valid(const u8 *raw_edid)
 {
@@ -1009,9 +1018,16 @@ module_param_named(edid_fixup, edid_fixup, int, 0400);
 MODULE_PARM_DESC(edid_fixup,
                 "Minimum number of valid EDID header bytes (0-8, default 6)");
 
-/*
- * Sanity check the EDID block (base or extension).  Return 0 if the block
- * doesn't check out, or 1 if it's valid.
+/**
+ * drm_edid_block_valid - Sanity check the EDID block (base or extension)
+ * @raw_edid: pointer to raw EDID block
+ * @block: type of block to validate (0 for base, extension otherwise)
+ * @print_bad_edid: if true, dump bad EDID blocks to the console
+ *
+ * Validate a base or extension EDID block and optionally dump bad blocks to
+ * the console.
+ *
+ * Return: True if the block is valid, false otherwise.
  */
 bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid)
 {
@@ -1089,6 +1105,8 @@ EXPORT_SYMBOL(drm_edid_block_valid);
  * @edid: EDID data
  *
  * Sanity-check an entire EDID record (including extensions)
+ *
+ * Return: True if the EDID data is valid, false otherwise.
  */
 bool drm_edid_is_valid(struct edid *edid)
 {
@@ -1108,14 +1126,15 @@ EXPORT_SYMBOL(drm_edid_is_valid);
 
 #define DDC_SEGMENT_ADDR 0x30
 /**
- * Get EDID information via I2C.
+ * drm_do_probe_ddc_edid() - get EDID information via I2C
+ * @adapter: I2C device adaptor
+ * @buf: EDID data buffer to be filled
+ * @block: 128 byte EDID block to start fetching from
+ * @len: EDID data buffer length to fetch
  *
- * \param adapter : i2c device adaptor
- * \param buf     : EDID data buffer to be filled
- * \param len     : EDID data buffer length
- * \return 0 on success or -1 on failure.
+ * Try to fetch EDID information by calling I2C driver functions.
  *
- * Try to fetch EDID information by calling i2c driver function.
+ * Return: 0 on success or -1 on failure.
  */
 static int
 drm_do_probe_ddc_edid(struct device *adapter, unsigned char *buf,
@@ -1126,7 +1145,8 @@ drm_do_probe_ddc_edid(struct device *adapter, unsigned char *buf,
        unsigned char xfers = segment ? 3 : 2;
        int ret, retries = 5;
 
-       /* The core i2c driver will automatically retry the transfer if the
+       /*
+        * The core I2C driver will automatically retry the transfer if the
         * adapter reports EAGAIN. However, we find that bit-banging transfers
         * are susceptible to errors under a heavily loaded machine and
         * generate spurious NAKs and timeouts. Retrying the transfer
@@ -1152,10 +1172,10 @@ drm_do_probe_ddc_edid(struct device *adapter, unsigned char *buf,
                        }
                };
 
-       /*
-        * Avoid sending the segment addr to not upset non-compliant ddc
-        * monitors.
-        */
+               /*
+                * Avoid sending the segment addr to not upset non-compliant
+                * DDC monitors.
+                */
                ret = iicbus_transfer(adapter, &msgs[3 - xfers], xfers);
 
                if (ret != 0)
@@ -1226,7 +1246,7 @@ drm_do_get_edid(struct drm_connector *connector, struct device *adapter)
                if (i == 4 && print_bad_edid) {
                        dev_warn(connector->dev->dev,
                         "%s: Ignoring invalid EDID block %d.\n",
-                        drm_get_connector_name(connector), j);
+                        connector->name, j);
 
                        connector->bad_edid_counter++;
                }
@@ -1235,8 +1255,7 @@ drm_do_get_edid(struct drm_connector *connector, struct device *adapter)
        if (valid_extensions != block[0x7e]) {
                block[EDID_LENGTH-1] += block[0x7e] - valid_extensions;
                block[0x7e] = valid_extensions;
-               new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH,
-                   M_DRM, M_WAITOK);
+               new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, M_DRM, M_WAITOK);
                if (!new)
                        goto out;
                block = new;
@@ -1247,7 +1266,7 @@ drm_do_get_edid(struct drm_connector *connector, struct device *adapter)
 carp:
        if (print_bad_edid) {
                dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n",
-                        drm_get_connector_name(connector), j);
+                        connector->name, j);
        }
        connector->bad_edid_counter++;
 
@@ -1257,10 +1276,10 @@ out:
 }
 
 /**
- * Probe DDC presence.
+ * drm_probe_ddc() - probe DDC presence
+ * @adapter: I2C adapter to probe
  *
- * \param adapter : i2c device adaptor
- * \return 1 on success
+ * Return: True on success, false on failure.
  */
 bool
 drm_probe_ddc(struct device *adapter)
@@ -1274,12 +1293,12 @@ EXPORT_SYMBOL(drm_probe_ddc);
 /**
  * drm_get_edid - get EDID data, if available
  * @connector: connector we're probing
- * @adapter: i2c adapter to use for DDC
+ * @adapter: I2C adapter to use for DDC
  *
- * Poke the given i2c channel to grab EDID data if possible.  If found,
+ * Poke the given I2C channel to grab EDID data if possible.  If found,
  * attach it to the connector.
  *
- * Return edid data or NULL if we couldn't find any.
+ * Return: Pointer to valid EDID or NULL if we couldn't find any.
  */
 struct edid *drm_get_edid(struct drm_connector *connector,
                          struct device *adapter)
@@ -1297,7 +1316,7 @@ EXPORT_SYMBOL(drm_get_edid);
  * drm_edid_duplicate - duplicate an EDID and the extensions
  * @edid: EDID to duplicate
  *
- * Return duplicate edid or NULL on allocation failure.
+ * Return: Pointer to duplicated EDID or NULL on allocation failure.
  */
 struct edid *drm_edid_duplicate(const struct edid *edid)
 {
@@ -1420,7 +1439,8 @@ mode_is_rb(const struct drm_display_mode *mode)
  * @rb: Mode reduced-blanking-ness
  *
  * Walk the DMT mode list looking for a match for the given parameters.
- * Return a newly allocated copy of the mode, or NULL if not found.
+ *
+ * Return: A newly allocated copy of the mode, or NULL if not found.
  */
 struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
                                           int hsize, int vsize, int fresh,
@@ -1601,15 +1621,16 @@ bad_std_timing(u8 a, u8 b)
 
 /**
  * drm_mode_std - convert standard mode info (width, height, refresh) into mode
+ * @connector: connector of for the EDID block
+ * @edid: EDID block to scan
  * @t: standard timing params
- * @timing_level: standard timing level
  *
  * Take the standard timing params (in this case width, aspect, and refresh)
  * and convert them into a real mode using CVT/GTF/DMT.
  */
 static struct drm_display_mode *
 drm_mode_std(struct drm_connector *connector, struct edid *edid,
-            struct std_timing *t, int revision)
+            struct std_timing *t)
 {
        struct drm_device *dev = connector->dev;
        struct drm_display_mode *m, *mode = NULL;
@@ -1630,7 +1651,7 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid,
        vrefresh_rate = vfreq + 60;
        /* the vdisplay is calculated based on the aspect ratio */
        if (aspect_ratio == 0) {
-               if (revision < 3)
+               if (edid->revision < 3)
                        vsize = hsize;
                else
                        vsize = (hsize * 10) / 16;
@@ -2147,6 +2168,7 @@ do_established_modes(struct detailed_timing *timing, void *c)
 
 /**
  * add_established_modes - get est. modes from EDID and add them
+ * @connector: connector to add mode(s) to
  * @edid: EDID block to scan
  *
  * Each EDID block contains a bitmap of the supported "established modes" list
@@ -2197,8 +2219,7 @@ do_standard_modes(struct detailed_timing *timing, void *c)
                        struct drm_display_mode *newmode;
 
                        std = &data->data.timings[i];
-                       newmode = drm_mode_std(connector, edid, std,
-                                              edid->revision);
+                       newmode = drm_mode_std(connector, edid, std);
                        if (newmode) {
                                drm_mode_probed_add(connector, newmode);
                                closure->modes++;
@@ -2209,6 +2230,7 @@ do_standard_modes(struct detailed_timing *timing, void *c)
 
 /**
  * add_standard_modes - get std. modes from EDID and add them
+ * @connector: connector to add mode(s) to
  * @edid: EDID block to scan
  *
  * Standard modes can be calculated using the appropriate standard (DMT,
@@ -2226,8 +2248,7 @@ add_standard_modes(struct drm_connector *connector, struct edid *edid)
                struct drm_display_mode *newmode;
 
                newmode = drm_mode_std(connector, edid,
-                                      &edid->standard_timings[i],
-                                      edid->revision);
+                                      &edid->standard_timings[i]);
                if (newmode) {
                        drm_mode_probed_add(connector, newmode);
                        modes++;
@@ -2430,7 +2451,7 @@ cea_mode_alternate_clock(const struct drm_display_mode *cea_mode)
  * drm_match_cea_mode - look for a CEA mode matching given mode
  * @to_match: display mode
  *
- * Returns the CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861
+ * Return: The CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861
  * mode.
  */
 u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
@@ -2457,6 +2478,22 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
 }
 EXPORT_SYMBOL(drm_match_cea_mode);
 
+/**
+ * drm_get_cea_aspect_ratio - get the picture aspect ratio corresponding to
+ * the input VIC from the CEA mode list
+ * @video_code: ID given to each of the CEA modes
+ *
+ * Returns picture aspect ratio
+ */
+enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code)
+{
+       /* return picture aspect ratio for video_code - 1 to access the
+        * right array element
+       */
+       return edid_cea_modes[video_code-1].picture_aspect_ratio;
+}
+EXPORT_SYMBOL(drm_get_cea_aspect_ratio);
+
 /*
  * Calculate the alternate clock for HDMI modes (those from the HDMI vendor
  * specific block).
@@ -2595,6 +2632,9 @@ drm_display_mode_from_vic_index(struct drm_connector *connector,
                return NULL;
 
        newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]);
+       if (!newmode)
+               return NULL;
+
        newmode->vrefresh = 0;
 
        return newmode;
@@ -3025,11 +3065,9 @@ monitor_name(struct detailed_timing *t, void *data)
  * @connector: connector corresponding to the HDMI/DP sink
  * @edid: EDID to parse
  *
- * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver.
- * Some ELD fields are left to the graphics driver caller:
- * - Conn_Type
- * - HDCP
- * - Port_ID
+ * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The
+ * Conn_Type, HDCP and Port_ID ELD fields are left for the graphics driver to
+ * fill in.
  */
 void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
 {
@@ -3113,9 +3151,10 @@ EXPORT_SYMBOL(drm_edid_to_eld);
  * @sads: pointer that will be set to the extracted SADs
  *
  * Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from it.
- * Note: returned pointer needs to be kfreed
  *
- * Return number of found SADs or negative number on error.
+ * Note: The returned pointer needs to be freed using kfree().
+ *
+ * Return: The number of found SADs or negative number on error.
  */
 int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads)
 {
@@ -3172,9 +3211,11 @@ EXPORT_SYMBOL(drm_edid_to_sad);
  * @sadb: pointer to the speaker block
  *
  * Looks for CEA EDID block and extracts the Speaker Allocation Data Block from it.
- * Note: returned pointer needs to be kfreed
  *
- * Return number of found Speaker Allocation Blocks or negative number on error.
+ * Note: The returned pointer needs to be freed using kfree().
+ *
+ * Return: The number of found Speaker Allocation Blocks or negative number on
+ * error.
  */
 int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
 {
@@ -3206,8 +3247,9 @@ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
 
                        /* Speaker Allocation Data Block */
                        if (dbl == 3) {
-                               *sadb = kmalloc(dbl, M_DRM, M_WAITOK);
-                               memcpy(*sadb, &db[1], dbl);
+                               *sadb = kmemdup(&db[1], dbl, GFP_KERNEL);
+                               if (!*sadb)
+                                       return -ENOMEM;
                                count = dbl;
                                break;
                        }
@@ -3219,9 +3261,12 @@ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
 EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
 
 /**
- * drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond
+ * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay
  * @connector: connector associated with the HDMI/DP sink
  * @mode: the display mode
+ *
+ * Return: The HDMI/DP sink's audio-video sync delay in milliseconds or 0 if
+ * the sink doesn't support audio or video.
  */
 int drm_av_sync_delay(struct drm_connector *connector,
                      struct drm_display_mode *mode)
@@ -3263,6 +3308,9 @@ EXPORT_SYMBOL(drm_av_sync_delay);
  *
  * It's possible for one encoder to be associated with multiple HDMI/DP sinks.
  * The policy is now hard coded to simply use the first HDMI/DP sink's ELD.
+ *
+ * Return: The connector associated with the first HDMI/DP sink that has ELD
+ * attached to it.
  */
 struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
                                     struct drm_display_mode *mode)
@@ -3270,6 +3318,8 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
        struct drm_connector *connector;
        struct drm_device *dev = encoder->dev;
 
+       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+
        list_for_each_entry(connector, &dev->mode_config.connector_list, head)
                if (connector->encoder == encoder && connector->eld[0])
                        return connector;
@@ -3279,11 +3329,12 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
 EXPORT_SYMBOL(drm_select_eld);
 
 /**
- * drm_detect_hdmi_monitor - detect whether monitor is hdmi.
+ * drm_detect_hdmi_monitor - detect whether monitor is HDMI
  * @edid: monitor EDID information
  *
  * Parse the CEA extension according to CEA-861-B.
- * Return true if HDMI, false if not or unknown.
+ *
+ * Return: True if the monitor is HDMI, false if not or unknown.
  */
 bool drm_detect_hdmi_monitor(struct edid *edid)
 {
@@ -3313,6 +3364,7 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor);
 
 /**
  * drm_detect_monitor_audio - check monitor audio capability
+ * @edid: EDID block to scan
  *
  * Monitor should have CEA extension block.
  * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic
@@ -3320,6 +3372,7 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor);
  * audio format, assume at least 'basic audio' support, even if 'basic
  * audio' is not defined in EDID.
  *
+ * Return: True if the monitor supports audio, false otherwise.
  */
 bool drm_detect_monitor_audio(struct edid *edid)
 {
@@ -3358,10 +3411,13 @@ EXPORT_SYMBOL(drm_detect_monitor_audio);
 
 /**
  * drm_rgb_quant_range_selectable - is RGB quantization range selectable?
+ * @edid: EDID block to scan
  *
  * Check whether the monitor reports the RGB quantization range selection
  * as supported. The AVI infoframe can then be used to inform the monitor
  * which quantization range (full or limited) is used.
+ *
+ * Return: True if the RGB quantization range is selectable, false otherwise.
  */
 bool drm_rgb_quant_range_selectable(struct edid *edid)
 {
@@ -3387,17 +3443,117 @@ bool drm_rgb_quant_range_selectable(struct edid *edid)
 }
 EXPORT_SYMBOL(drm_rgb_quant_range_selectable);
 
+/**
+ * drm_assign_hdmi_deep_color_info - detect whether monitor supports
+ * hdmi deep color modes and update drm_display_info if so.
+ *
+ * @edid: monitor EDID information
+ * @info: Updated with maximum supported deep color bpc and color format
+ *        if deep color supported.
+ *
+ * Parse the CEA extension according to CEA-861-B.
+ * Return true if HDMI deep color supported, false if not or unknown.
+ */
+static bool drm_assign_hdmi_deep_color_info(struct edid *edid,
+                                            struct drm_display_info *info,
+                                            struct drm_connector *connector)
+{
+       u8 *edid_ext, *hdmi;
+       int i;
+       int start_offset, end_offset;
+       unsigned int dc_bpc = 0;
+
+       edid_ext = drm_find_cea_extension(edid);
+       if (!edid_ext)
+               return false;
+
+       if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
+               return false;
+
+       /*
+        * Because HDMI identifier is in Vendor Specific Block,
+        * search it from all data blocks of CEA extension.
+        */
+       for_each_cea_db(edid_ext, i, start_offset, end_offset) {
+               if (cea_db_is_hdmi_vsdb(&edid_ext[i])) {
+                       /* HDMI supports at least 8 bpc */
+                       info->bpc = 8;
+
+                       hdmi = &edid_ext[i];
+                       if (cea_db_payload_len(hdmi) < 6)
+                               return false;
+
+                       if (hdmi[6] & DRM_EDID_HDMI_DC_30) {
+                               dc_bpc = 10;
+                               DRM_DEBUG("%s: HDMI sink does deep color 30.\n",
+                                                 connector->name);
+                       }
+
+                       if (hdmi[6] & DRM_EDID_HDMI_DC_36) {
+                               dc_bpc = 12;
+                               DRM_DEBUG("%s: HDMI sink does deep color 36.\n",
+                                                 connector->name);
+                       }
+
+                       if (hdmi[6] & DRM_EDID_HDMI_DC_48) {
+                               dc_bpc = 16;
+                               DRM_DEBUG("%s: HDMI sink does deep color 48.\n",
+                                                 connector->name);
+                       }
+
+                       if (dc_bpc > 0) {
+                               DRM_DEBUG("%s: Assigning HDMI sink color depth as %d bpc.\n",
+                                                 connector->name, dc_bpc);
+                               info->bpc = dc_bpc;
+
+                               /*
+                                * Deep color support mandates RGB444 support for all video
+                                * modes and forbids YCRCB422 support for all video modes per
+                                * HDMI 1.3 spec.
+                                */
+                               info->color_formats = DRM_COLOR_FORMAT_RGB444;
+
+                               /* YCRCB444 is optional according to spec. */
+                               if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
+                                       info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
+                                       DRM_DEBUG("%s: HDMI sink does YCRCB444 in deep color.\n",
+                                                         connector->name);
+                               }
+
+                               /*
+                                * Spec says that if any deep color mode is supported at all,
+                                * then deep color 36 bit must be supported.
+                                */
+                               if (!(hdmi[6] & DRM_EDID_HDMI_DC_36)) {
+                                       DRM_DEBUG("%s: HDMI sink should do DC_36, but does not!\n",
+                                                         connector->name);
+                               }
+
+                               return true;
+                       }
+                       else {
+                               DRM_DEBUG("%s: No deep color support on this HDMI sink.\n",
+                                                 connector->name);
+                       }
+               }
+       }
+
+       return false;
+}
+
 /**
  * drm_add_display_info - pull display info out if present
  * @edid: EDID data
  * @info: display info (attached to connector)
+ * @connector: connector whose edid is used to build display info
  *
  * Grab any available display info and stuff it into the drm_display_info
  * structure that's part of the connector.  Useful for tracking bpp and
  * color spaces.
  */
 static void drm_add_display_info(struct edid *edid,
-                                struct drm_display_info *info)
+                                 struct drm_display_info *info,
+                                 struct drm_connector *connector)
 {
        u8 *edid_ext;
 
@@ -3427,6 +3583,9 @@ static void drm_add_display_info(struct edid *edid,
                        info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
        }
 
+       /* HDMI deep color modes supported? Assign to info, if so */
+       drm_assign_hdmi_deep_color_info(edid, info, connector);
+
        /* Only defined for 1.4 with digital displays */
        if (edid->revision < 4)
                return;
@@ -3456,6 +3615,9 @@ static void drm_add_display_info(struct edid *edid,
                break;
        }
 
+       DRM_DEBUG("%s: Assigning EDID-1.4 digital sink color depth as %d bpc.\n",
+                         connector->name, info->bpc);
+
        info->color_formats |= DRM_COLOR_FORMAT_RGB444;
        if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444)
                info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
@@ -3466,11 +3628,11 @@ static void drm_add_display_info(struct edid *edid,
 /**
  * drm_add_edid_modes - add modes from EDID data, if available
  * @connector: connector we're probing
- * @edid: edid data
+ * @edid: EDID data
  *
  * Add the specified modes to the connector's mode list.
  *
- * Return number of modes added or 0 if we couldn't find any.
+ * Return: The number of modes added or 0 if we couldn't find any.
  */
 int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
 {
@@ -3482,7 +3644,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
        }
        if (!drm_edid_is_valid(edid)) {
                dev_warn(connector->dev->dev, "%s: EDID invalid.\n",
-                        drm_get_connector_name(connector));
+                        connector->name);
                return 0;
        }
 
@@ -3514,11 +3676,14 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
        if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
                edid_fixup_preferred(connector, quirks);
 
-       drm_add_display_info(edid, &connector->display_info);
+       drm_add_display_info(edid, &connector->display_info, connector);
 
        if (quirks & EDID_QUIRK_FORCE_8BPC)
                connector->display_info.bpc = 8;
 
+       if (quirks & EDID_QUIRK_FORCE_12BPC)
+               connector->display_info.bpc = 12;
+
        return num_modes;
 }
 EXPORT_SYMBOL(drm_add_edid_modes);
@@ -3532,7 +3697,7 @@ EXPORT_SYMBOL(drm_add_edid_modes);
  * Add the specified modes to the connector's mode list. Only when the
  * hdisplay/vdisplay is not beyond the given limit, it will be added.
  *
- * Return number of modes added or 0 if we couldn't find any.
+ * Return: The number of modes added or 0 if we couldn't find any.
  */
 int drm_add_modes_noedid(struct drm_connector *connector,
                        int hdisplay, int vdisplay)
@@ -3571,14 +3736,23 @@ int drm_add_modes_noedid(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_add_modes_noedid);
 
+/**
+ * drm_set_preferred_mode - Sets the preferred mode of a connector
+ * @connector: connector whose mode list should be processed
+ * @hpref: horizontal resolution of preferred mode
+ * @vpref: vertical resolution of preferred mode
+ *
+ * Marks a mode as preferred if it matches the resolution specified by @hpref
+ * and @vpref.
+ */
 void drm_set_preferred_mode(struct drm_connector *connector,
                           int hpref, int vpref)
 {
        struct drm_display_mode *mode;
 
        list_for_each_entry(mode, &connector->probed_modes, head) {
-               if (drm_mode_width(mode)  == hpref &&
-                   drm_mode_height(mode) == vpref)
+               if (mode->hdisplay == hpref &&
+                   mode->vdisplay == vpref)
                        mode->type |= DRM_MODE_TYPE_PREFERRED;
        }
 }
@@ -3590,7 +3764,7 @@ EXPORT_SYMBOL(drm_set_preferred_mode);
  * @frame: HDMI AVI infoframe
  * @mode: DRM display mode
  *
- * Returns 0 on success or a negative error code on failure.
+ * Return: 0 on success or a negative error code on failure.
  */
 int
 drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
@@ -3611,7 +3785,14 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
        frame->video_code = drm_match_cea_mode(mode);
 
        frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
+
+       /* Populate picture aspect ratio from CEA mode list */
+       if (frame->video_code > 0)
+               frame->picture_aspect = drm_get_cea_aspect_ratio(
+                                               frame->video_code);
+
        frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
+       frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
 
        return 0;
 }
@@ -3654,7 +3835,7 @@ s3d_structure_from_display_mode(const struct drm_display_mode *mode)
  * 4k or stereoscopic 3D mode. So when giving any other mode as input this
  * function will return -EINVAL, error that can be safely ignored.
  *
- * Returns 0 on success or a negative error code on failure.
+ * Return: 0 on success or a negative error code on failure.
  */
 int
 drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
index 55d2996..9f6d4b5 100644 (file)
  *      Jesse Barnes <jesse.barnes@intel.com>
  */
 
+#include <linux/kernel.h>
+#include <linux/fb.h>
+#include <linux/module.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
 
-#include <sys/ctype.h>
-
 static LINUX_LIST_HEAD(kernel_fb_helper_list);
 
 /**
  * DOC: fbdev helpers
  *
  * The fb helper functions are useful to provide an fbdev on top of a drm kernel
- * mode setting driver. They can be used mostly independantely from the crtc
+ * mode setting driver. They can be used mostly independently from the crtc
  * helper functions used by many drivers to implement the kernel mode setting
  * interfaces.
  *
  * Initialization is done as a three-step process with drm_fb_helper_init(),
  * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
- * Drivers with fancier requirements than the default beheviour can override the
+ * Drivers with fancier requirements than the default behaviour can override the
  * second step with their own code.  Teardown is done with drm_fb_helper_fini().
  *
  * At runtime drivers should restore the fbdev console by calling
@@ -55,7 +56,7 @@ static LINUX_LIST_HEAD(kernel_fb_helper_list);
  * should also notify the fb helper code from updates to the output
  * configuration by calling drm_fb_helper_hotplug_event(). For easier
  * integration with the output polling code in drm_crtc_helper.c the modeset
- * code proves a ->output_poll_changed callback.
+ * code provides a ->output_poll_changed callback.
  *
  * All other functions exported by the fb helper library can be used to
  * implement the fbdev driver interface by the driver.
@@ -101,44 +102,6 @@ fail:
 }
 EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
 
-static int
-fb_get_options(const char *connector_name, char **option)
-{
-       char buf[128], str[1024];
-       int i;
-
-       /*
-        * This hack allows us to use drm.video.lvds1="<video-mode>"
-        * in loader.conf, where linux would use video=LVDS-1:<video-mode>.
-        * e.g. drm.video.lvds1=1024x768 sets the LVDS-1 connector to
-        * a 1024x768 video mode in the syscons framebuffer console.
-        * See https://wiki.archlinux.org/index.php/Kernel_mode_setting
-        * for an explanation of the video mode command line option.
-        * (This corresponds to the video= Linux kernel command-line
-        * option)
-        */
-       memset(str, 0, sizeof(str));
-       ksnprintf(buf, sizeof(buf), "drm.video.%s", connector_name);
-       i = 0;
-       while (i < strlen(buf)) {
-               buf[i] = tolower(buf[i]);
-               if (buf[i] == '-') {
-                       memmove(&buf[i], &buf[i+1], strlen(buf)-i);
-               } else {
-                       i++;
-               }
-       }
-       kprintf("looking up kenv for \"%s\"\n", buf);
-       if (kgetenv_string(buf, str, sizeof(str)-1)) {
-               kprintf("found kenv %s=%s\n", buf, str);
-               *option = kstrdup(str, M_DRM);
-               return (0);
-       } else {
-               kprintf("didn't find value for kenv %s\n", buf);
-               return (1);
-       }
-}
-
 static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
 {
        struct drm_fb_helper_connector *fb_helper_conn;
@@ -154,8 +117,7 @@ static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
                mode = &fb_helper_conn->cmdline_mode;
 
                /* do something on return - turn off connector maybe */
-               /* XXX use driver name and device index for lookup */
-               if (fb_get_options(drm_get_connector_name(connector), &option))
+               if (fb_get_options(connector->name, &option))
                        continue;
 
                if (drm_mode_parse_command_line_for_connector(option,
@@ -177,22 +139,18 @@ static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
                                }
 
                                DRM_INFO("forcing %s connector %s\n",
-                                        drm_get_connector_name(connector), s);
+                                        connector->name, s);
                                connector->force = mode->force;
                        }
 
-                       DRM_LOG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
-                                     drm_get_connector_name(connector),
+                       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" : "");
                }
-               if (option != NULL) {
-                       kfree(option);
-                       option = NULL;
-               }
 
        }
        return 0;
@@ -272,7 +230,7 @@ static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
 
        list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
                if (crtc->base.id == c->base.id)
-                       return c->fb;
+                       return c->primary->fb;
        }
 
        return NULL;
@@ -314,15 +272,7 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
 EXPORT_SYMBOL(drm_fb_helper_debug_leave);
 #endif
 
-/**
- * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration
- * @fb_helper: fbcon to restore
- *
- * This should be called from driver's drm ->lastclose callback
- * when implementing an fbcon on top of kms using this helper. This ensures that
- * the user isn't greeted with a black screen when e.g. X dies.
- */
-bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
+static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 {
        struct drm_device *dev = fb_helper->dev;
        struct drm_plane *plane;
@@ -332,7 +282,8 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
        drm_warn_on_modeset_not_all_locked(dev);
 
        list_for_each_entry(plane, &dev->mode_config.plane_list, head)
-               drm_plane_force_disable(plane);
+               if (plane->type != DRM_PLANE_TYPE_PRIMARY)
+                       drm_plane_force_disable(plane);
 
        for (i = 0; i < fb_helper->crtc_count; i++) {
                struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
@@ -351,7 +302,40 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
        }
        return error;
 }
-EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);
+/**
+ * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration
+ * @fb_helper: fbcon to restore
+ *
+ * This should be called from driver's drm ->lastclose callback
+ * when implementing an fbcon on top of kms using this helper. This ensures that
+ * the user isn't greeted with a black screen when e.g. X dies.
+ *
+ * Use this variant if you need to bypass locking (panic), or already
+ * hold all modeset locks.  Otherwise use drm_fb_helper_restore_fbdev_mode_unlocked()
+ */
+static bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
+{
+       return restore_fbdev_mode(fb_helper);
+}
+
+/**
+ * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
+ * @fb_helper: fbcon to restore
+ *
+ * This should be called from driver's drm ->lastclose callback
+ * when implementing an fbcon on top of kms using this helper. This ensures that
+ * the user isn't greeted with a black screen when e.g. X dies.
+ */
+bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
+{
+       struct drm_device *dev = fb_helper->dev;
+       bool ret;
+       drm_modeset_lock_all(dev);
+       ret = restore_fbdev_mode(fb_helper);
+       drm_modeset_unlock_all(dev);
+       return ret;
+}
+EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
 
 #if 0
 /*
@@ -367,12 +351,25 @@ static bool drm_fb_helper_force_kernel_mode(void)
                return false;
 
        list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
-               if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+               struct drm_device *dev = helper->dev;
+
+               if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
                        continue;
 
+               /* NOTE: we use lockless flag below to avoid grabbing other
+                * modeset locks.  So just trylock the underlying mutex
+                * directly:
+                */
+               if (!mutex_trylock(&dev->mode_config.mutex)) {
+                       error = true;
+                       continue;
+               }
+
                ret = drm_fb_helper_restore_fbdev_mode(helper);
                if (ret)
                        error = true;
+
+               mutex_unlock(&dev->mode_config.mutex);
        }
        return error;
 }
@@ -402,15 +399,23 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
        struct drm_crtc *crtc;
        int bound = 0, crtcs_bound = 0;
 
+#if 0
+       /* Sometimes user space wants everything disabled, so don't steal the
+        * display if there's a master. */
+       if (dev->primary->master)
+               return false;
+#endif
+
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               if (crtc->fb)
+               if (crtc->primary->fb)
                        crtcs_bound++;
-               if (crtc->fb == fb_helper->fb)
+               if (crtc->primary->fb == fb_helper->fb)
                        bound++;
        }
 
        if (bound < crtcs_bound)
                return false;
+
        return true;
 }
 
@@ -555,6 +560,9 @@ int drm_fb_helper_init(struct drm_device *dev,
        struct drm_crtc *crtc;
        int i;
 
+       if (!max_conn_count)
+               return -EINVAL;
+
        fb_helper->dev = dev;
 
        INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
@@ -849,25 +857,14 @@ EXPORT_SYMBOL(drm_fb_helper_check_var);
 int drm_fb_helper_set_par(struct fb_info *info)
 {
        struct drm_fb_helper *fb_helper = info->par;
-       struct drm_device *dev = fb_helper->dev;
        struct fb_var_screeninfo *var = &info->var;
-       int ret;
-       int i;
 
        if (var->pixclock != 0) {
                DRM_ERROR("PIXEL CLOCK SET\n");
                return -EINVAL;
        }
 
-       drm_modeset_lock_all(dev);
-       for (i = 0; i < fb_helper->crtc_count; i++) {
-               ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set);
-               if (ret) {
-                       drm_modeset_unlock_all(dev);
-                       return ret;
-               }
-       }
-       drm_modeset_unlock_all(dev);
+       drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
 
        if (fb_helper->delayed_hotplug) {
                fb_helper->delayed_hotplug = false;
@@ -917,6 +914,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
 EXPORT_SYMBOL(drm_fb_helper_pan_display);
 #endif
 
+/* XXX: DragonFly-specific */
 static void
 do_restore_fbdev_mode(void *context, int pending)
 {
@@ -931,6 +929,7 @@ do_restore_fbdev_mode(void *context, int pending)
        drm_modeset_unlock_all(dev);
 }
 
+/* XXX: DragonFly-specific */
 static void
 sc_restore_fbdev_mode(void *cookie)
 {
@@ -956,7 +955,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
        struct fb_info *info;
        struct drm_fb_helper_surface_size sizes;
        int gamma_size = 0;
-       int kms_console = 1;
+       int kms_console = 1;    /* XXX: DragonFly-specific */
 
        memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
        sizes.surface_depth = 24;
@@ -1046,6 +1045,8 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
                if (fb_helper->crtc_info[i].mode_set.num_connectors)
                        fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
 
+
+       /* XXX: DragonFly-specific */
        TUNABLE_INT_FETCH("kern.kms_console", &kms_console);
        if (kms_console) {
                TASK_INIT(&fb_helper->fb_mode_task, 0, do_restore_fbdev_mode,
@@ -1218,19 +1219,20 @@ static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
        return count;
 }
 
-static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
+struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
 {
        struct drm_display_mode *mode;
 
        list_for_each_entry(mode, &fb_connector->connector->modes, head) {
-               if (drm_mode_width(mode) > width ||
-                   drm_mode_height(mode) > height)
+               if (mode->hdisplay > width ||
+                   mode->vdisplay > height)
                        continue;
                if (mode->type & DRM_MODE_TYPE_PREFERRED)
                        return mode;
        }
        return NULL;
 }
+EXPORT_SYMBOL(drm_has_preferred_mode);
 
 static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
 {
@@ -1239,11 +1241,12 @@ static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
        return cmdline_mode->specified;
 }
 
-static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
+struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
                                                      int width, int height)
 {
        struct drm_cmdline_mode *cmdline_mode;
        struct drm_display_mode *mode = NULL;
+       bool prefer_non_interlace;
 
        cmdline_mode = &fb_helper_conn->cmdline_mode;
        if (cmdline_mode->specified == false)
@@ -1255,6 +1258,8 @@ static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_conne
        if (cmdline_mode->rb || cmdline_mode->margins)
                goto create_mode;
 
+       prefer_non_interlace = !cmdline_mode->interlace;
+ again:
        list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
                /* check width/height */
                if (mode->hdisplay != cmdline_mode->xres ||
@@ -1269,16 +1274,25 @@ static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_conne
                if (cmdline_mode->interlace) {
                        if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
                                continue;
+               } else if (prefer_non_interlace) {
+                       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+                               continue;
                }
                return mode;
        }
 
+       if (prefer_non_interlace) {
+               prefer_non_interlace = false;
+               goto again;
+       }
+
 create_mode:
        mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
                                                 cmdline_mode);
        list_add(&mode->head, &fb_helper_conn->connector->modes);
        return mode;
 }
+EXPORT_SYMBOL(drm_pick_cmdline_mode);
 
 static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
 {
@@ -1621,9 +1635,11 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
 
        drm_fb_helper_parse_command_line(fb_helper);
 
+       mutex_lock(&dev->mode_config.mutex);
        count = drm_fb_helper_probe_connector_modes(fb_helper,
                                                    dev->mode_config.max_width,
                                                    dev->mode_config.max_height);
+       mutex_unlock(&dev->mode_config.mutex);
        /*
         * we shouldn't end up with no modes here.
         */
index 48ccc42..e005703 100644 (file)
 #endif
 
 /**
- * Initialize the GEM device fields
+ * drm_gem_init - Initialize the GEM device fields
+ * @dev: drm_devic structure to initialize
  */
-
 int
 drm_gem_init(struct drm_device *dev)
 {
@@ -147,6 +147,7 @@ drm_gem_init(struct drm_device *dev)
        drm_vma_offset_manager_init(&mm->vma_manager,
                                    DRM_FILE_PAGE_OFFSET_START,
                                    DRM_FILE_PAGE_OFFSET_SIZE);
+
        return 0;
 }
 
@@ -171,21 +172,21 @@ drm_gem_destroy(struct drm_device *dev)
 int drm_gem_object_init(struct drm_device *dev,
                        struct drm_gem_object *obj, size_t size)
 {
-       BUG_ON((size & (PAGE_SIZE - 1)) != 0);
+       drm_gem_private_object_init(dev, obj, size);
 
-       obj->dev = dev;
        obj->vm_obj = default_pager_alloc(NULL, size,
            VM_PROT_READ | VM_PROT_WRITE, 0);
 
-       kref_init(&obj->refcount);
-       atomic_set(&obj->handle_count, 0);
-       obj->size = size;
-
        return 0;
 }
 EXPORT_SYMBOL(drm_gem_object_init);
 
 /**
+ * drm_gem_object_init - initialize an allocated private GEM object
+ * @dev: drm_device the object should be initialized for
+ * @obj: drm_gem_object to initialize
+ * @size: object size
+ *
  * Initialize an already allocated GEM object of the specified size with
  * no GEM provided backing store. Instead the caller is responsible for
  * backing the object and handling it.
@@ -199,7 +200,7 @@ void drm_gem_private_object_init(struct drm_device *dev,
        obj->vm_obj = NULL;
 
        kref_init(&obj->refcount);
-       atomic_set(&obj->handle_count, 0);
+       obj->handle_count = 0;
        obj->size = size;
        drm_vma_node_reset(&obj->vma_node);
 }
@@ -213,19 +214,22 @@ drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp)
                drm_prime_remove_buf_handle(&filp->prime,
                                obj->import_attach->dmabuf);
        }
-
-       /*
-        * Note: obj->dma_buf can't disappear as long as we still hold a
-        * handle reference in obj->handle_count.
-        */
-       if (obj->dma_buf) {
+       if (obj->export_dma_buf) {
                drm_prime_remove_buf_handle(&filp->prime,
-                               obj->dma_buf);
+                               obj->export_dma_buf);
        }
 #endif
 }
 
+static void drm_gem_object_ref_bug(struct kref *list_kref)
+{
+       BUG();
+}
+
 /**
+ * drm_gem_object_free - release resources bound to userspace handles
+ * @obj: GEM object to clean up.
+ *
  * Called after the last handle to the object has been closed
  *
  * Removes any name for the object. Note that this must be
@@ -240,6 +244,13 @@ static void drm_gem_object_handle_free(struct drm_gem_object *obj)
        if (obj->name) {
                idr_remove(&dev->object_name_idr, obj->name);
                obj->name = 0;
+       /*
+        * The object name held a reference to this object, drop
+        * that now.
+       *
+       * This cannot be the last reference, since the handle holds one too.
+        */
+               kref_put(&obj->refcount, drm_gem_object_ref_bug);
        }
 }
 
@@ -257,7 +268,7 @@ static void drm_gem_object_exported_dma_buf_free(struct drm_gem_object *obj)
 static void
 drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj)
 {
-       if (WARN_ON(atomic_read(&obj->handle_count) == 0))
+       if (WARN_ON(obj->handle_count == 0))
                return;
 
        /*
@@ -266,13 +277,21 @@ drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj)
        * checked for a name
        */
 
-       if (atomic_dec_and_test(&obj->handle_count))
+       mutex_lock(&obj->dev->object_name_lock);
+       if (--obj->handle_count == 0)
                drm_gem_object_handle_free(obj);
+       mutex_unlock(&obj->dev->object_name_lock);
+
        drm_gem_object_unreference_unlocked(obj);
 }
 
 /**
- * Removes the mapping from handle to filp for this object.
+ * drm_gem_handle_delete - deletes the given file-private handle
+ * @filp: drm file-private structure to use for the handle look up
+ * @handle: userspace handle to delete
+ *
+ * Removes the GEM handle from the @filp lookup table and if this is the last
+ * handle also cleans up linked resources like GEM names.
  */
 int
 drm_gem_handle_delete(struct drm_file *filp, u32 handle)
@@ -315,6 +334,9 @@ EXPORT_SYMBOL(drm_gem_handle_delete);
 
 /**
  * drm_gem_dumb_destroy - dumb fb callback helper for gem based drivers
+ * @file: drm file-private structure to remove the dumb handle from
+ * @dev: corresponding drm_device
+ * @handle: the dumb handle to remove
  * 
  * This implements the ->dumb_destroy kms driver callback for drivers which use
  * gem to manage their backing storage.
@@ -343,21 +365,22 @@ drm_gem_handle_create(struct drm_file *file_priv,
        /*
         * Get the user-visible handle using idr.
         */
-again:
-       /* ensure there is space available to allocate a handle */
-       if (idr_pre_get(&file_priv->object_idr, GFP_KERNEL) == 0)
-               return -ENOMEM;
-
-       /* do the allocation under our spinlock */
+       idr_preload(GFP_KERNEL);
+       lockmgr(&dev->object_name_lock, LK_EXCLUSIVE);
        lockmgr(&file_priv->table_lock, LK_EXCLUSIVE);
-       ret = idr_get_new_above(&file_priv->object_idr, obj, 1, (int *)handlep);
+
+       ret = idr_alloc(&file_priv->object_idr, obj, 1, 0, GFP_NOWAIT);
+       drm_gem_object_reference(obj);
+       obj->handle_count++;
        lockmgr(&file_priv->table_lock, LK_RELEASE);
-       if (ret == -EAGAIN)
-               goto again;
-       else if (ret)
+       lockmgr(&dev->object_name_lock, LK_RELEASE);
+       idr_preload_end();
+       if (ret < 0) {
+               drm_gem_object_handle_unreference_unlocked(obj);
                return ret;
+       }
+       *handlep = ret;
 
-       drm_gem_object_handle_reference(obj);
 
        if (dev->driver->gem_open_object) {
                ret = dev->driver->gem_open_object(obj, file_priv);
@@ -478,6 +501,11 @@ drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp,
 EXPORT_SYMBOL(drm_gem_object_lookup);
 
 /**
+ * drm_gem_close_ioctl - implementation of the GEM_CLOSE ioctl
+ * @dev: drm_device
+ * @data: ioctl data
+ * @file_priv: drm file-private structure
+ *
  * Releases the handle to an mm object.
  */
 int
@@ -509,43 +537,48 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data,
        struct drm_gem_object *obj;
        int ret;
 
-       if (!drm_core_check_feature(dev, DRIVER_GEM))
+       if (!(dev->driver->driver_features & DRIVER_GEM))
                return -ENODEV;
 
        obj = drm_gem_object_lookup(dev, file_priv, args->handle);
        if (obj == NULL)
                return -ENOENT;
 
-again:
-       if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) {
-               ret = -ENOMEM;
+       idr_preload(GFP_KERNEL);
+       lockmgr(&dev->object_name_lock, LK_EXCLUSIVE);
+       /* prevent races with concurrent gem_close. */
+       if (obj->handle_count == 0) {
+               ret = -ENOENT;
                goto err;
        }
 
-       lockmgr(&dev->object_name_lock, LK_EXCLUSIVE);
        if (!obj->name) {
-               ret = idr_get_new_above(&dev->object_name_idr, obj, 1,
-                                       &obj->name);
-               args->name = (uint64_t) obj->name;
-               lockmgr(&dev->object_name_lock, LK_RELEASE);
-
-               if (ret == -EAGAIN)
-                       goto again;
-               else if (ret)
+               ret = idr_alloc(&dev->object_name_idr, obj, 1, 0, GFP_NOWAIT);
+               if (ret < 0)
                        goto err;
 
-       } else {
-               args->name = (uint64_t) obj->name;
-               lockmgr(&dev->object_name_lock, LK_RELEASE);
-               ret = 0;
+               obj->name = ret;
+
+               /* Allocate a reference for the name table.  */
+               drm_gem_object_reference(obj);
        }
 
+       args->name = (uint64_t) obj->name;
+       ret = 0;
+
 err:
+       lockmgr(&dev->object_name_lock, LK_RELEASE);
+       idr_preload_end();
        drm_gem_object_unreference_unlocked(obj);
        return ret;
 }
 
 /**
+ * drm_gem_open - implementation of the GEM_OPEN ioctl
+ * @dev: drm_device
+ * @data: ioctl data
+ * @file_priv: drm file-private structure
+ *
  * Open an object using the global name, returning a handle and the size.
  *
  * This handle (of course) holds a reference to the object, so the object
@@ -583,6 +616,10 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data,
 }
 
 /**
+ * gem_gem_open - initalizes GEM file-private structures at devnode open time
+ * @dev: drm_device which is being opened by userspace
+ * @file_private: drm file-private structure to set up
+ *
  * Called at device open time, sets up the structure for handling refcounting
  * of mm objects.
  */
@@ -593,7 +630,7 @@ drm_gem_open(struct drm_device *dev, struct drm_file *file_private)
        lockinit(&file_private->table_lock, "fptab", 0, LK_CANRECURSE);
 }
 
-/**
+/*
  * Called at device close to release the file's
  * handle references on objects.
  */
@@ -615,6 +652,10 @@ drm_gem_object_release_handle(int id, void *ptr, void *data)
 }
 
 /**
+ * drm_gem_release - release file-private GEM resources
+ * @dev: drm_device which is being closed by userspace
+ * @file_private: drm file-private structure to clean up
+ *
  * Called at close time when the filp is going away.
  *
  * Releases any remaining references on objects by this filp.
@@ -639,6 +680,9 @@ drm_gem_object_release(struct drm_gem_object *obj)
 EXPORT_SYMBOL(drm_gem_object_release);
 
 /**
+ * drm_gem_object_free - free a GEM object
+ * @kref: kref of the object to free
+ *
  * Called after the last reference to the object has been lost.
  * Must be called holding struct_ mutex
  *
index 05938b0..2bf0a12 100644 (file)
@@ -325,6 +325,13 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
                        return -EINVAL;
                file_priv->stereo_allowed = req->value;
                break;
+       case DRM_CLIENT_CAP_UNIVERSAL_PLANES:
+               if (!drm_universal_planes)
+                       return -EINVAL;
+               if (req->value > 1)
+                       return -EINVAL;
+               file_priv->universal_planes = req->value;
+               break;
        default:
                return -EINVAL;
        }
index 1794df4..78ca58f 100644 (file)
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
-#include <linux/export.h>
-#include <linux/mutex.h>
-#include <linux/time.h>
-#include <linux/timer.h>
 #include <drm/drmP.h>
 
+#include <linux/slab.h>
+
+#include <linux/export.h>
+
 /* Access macro for slots in vblank timestamp ringbuffer. */
 #define vblanktimestamp(dev, crtc, count) \
        ((dev)->vblank[crtc].time[(count) % DRM_VBLANKTIME_RBSIZE])
  */
 #define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000
 
-/**
- * Get interrupt from bus id.
- *
- * \param inode device inode.
- * \param file_priv DRM file private.
- * \param cmd command.
- * \param arg user argument, pointing to a drm_irq_busid structure.
- * \return zero on success or a negative number on failure.
- *
- * Finds the PCI device with the specified bus id and gets its IRQ number.
- * This IOCTL is deprecated, and will now return EINVAL for any busid not equal
- * to that of the device that this DRM instance attached to.
- */
-int drm_irq_by_busid(struct drm_device *dev, void *data,
-                    struct drm_file *file_priv)
-{
-       struct drm_irq_busid *irq = data;
-
-       if ((irq->busnum >> 8) != dev->pci_domain ||
-           (irq->busnum & 0xff) != dev->pci_bus ||
-           irq->devnum != dev->pci_slot ||
-           irq->funcnum != dev->pci_func)
-               return EINVAL;
-
-       irq->irq = dev->irq;
-
-       DRM_DEBUG("%d:%d:%d => IRQ %d\n",
-           irq->busnum, irq->devnum, irq->funcnum, irq->irq);
-
-       return 0;
-}
-
 /*
  * Clear vblank timestamp buffer for a crtc.
  */
@@ -157,7 +125,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
         */
        if ((vblrc > 0) && (abs64(diff_ns) > 1000000)) {
                atomic_inc(&dev->vblank[crtc].count);
-               smp_mb__after_atomic_inc();
+               smp_mb__after_atomic();
        }
 
        /* Invalidate all timestamps while vblank irq's are off. */
@@ -168,32 +136,33 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
 
 static void vblank_disable_fn(unsigned long arg)
 {
-       struct drm_device *dev = (struct drm_device *)arg;
-       int i;
+       struct drm_vblank_crtc *vblank = (void *)arg;
+       struct drm_device *dev = vblank->dev;
+       int crtc = vblank->crtc;
 
        if (!dev->vblank_disable_allowed)
                return;
 
-       for (i = 0; i < dev->num_crtcs; i++) {
-               lockmgr(&dev->vbl_lock, LK_EXCLUSIVE);
-               if (atomic_read(&dev->vblank[i].refcount) == 0 &&
-                   dev->vblank[i].enabled) {
-                       DRM_DEBUG("disabling vblank on crtc %d\n", i);
-                       vblank_disable_and_save(dev, i);
-               }
-               lockmgr(&dev->vbl_lock, LK_RELEASE);
+       lockmgr(&dev->vbl_lock, LK_EXCLUSIVE);
+       if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) {
+               DRM_DEBUG("disabling vblank on crtc %d\n", crtc);
+               vblank_disable_and_save(dev, crtc);
        }
+       lockmgr(&dev->vbl_lock, LK_RELEASE);
 }
 
 void drm_vblank_cleanup(struct drm_device *dev)
 {
+       int crtc;
+
        /* Bail if the driver didn't call drm_vblank_init() */
        if (dev->num_crtcs == 0)
                return;
 
-       del_timer_sync(&dev->vblank_disable_timer);
-
-       vblank_disable_fn((unsigned long)dev);
+       for (crtc = 0; crtc < dev->num_crtcs; crtc++) {
+               del_timer_sync(&dev->vblank[crtc].disable_timer);
+               vblank_disable_fn((unsigned long)&dev->vblank[crtc]);
+       }
 
        kfree(dev->vblank);
 
@@ -205,8 +174,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
 {
        int i, ret = -ENOMEM;
 
-       setup_timer(&dev->vblank_disable_timer, vblank_disable_fn,
-                   (unsigned long)dev);
        lockinit(&dev->vbl_lock, "drmvbl", 0, LK_CANRECURSE);
        lockinit(&dev->vblank_time_lock, "drmvtl", 0, LK_CANRECURSE);
 
@@ -216,8 +183,13 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
        if (!dev->vblank)
                goto err;
 
-       for (i = 0; i < num_crtcs; i++)
+       for (i = 0; i < num_crtcs; i++) {
+               dev->vblank[i].dev = dev;
+               dev->vblank[i].crtc = i;
                init_waitqueue_head(&dev->vblank[i].queue);
+               setup_timer(&dev->vblank[i].disable_timer, vblank_disable_fn,
+                           (unsigned long)&dev->vblank[i]);
+       }
 
        DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
 
@@ -237,6 +209,31 @@ err:
 }
 EXPORT_SYMBOL(drm_vblank_init);
 
+#if 0
+static void drm_irq_vgaarb_nokms(void *cookie, bool state)
+{
+       struct drm_device *dev = cookie;
+
+       if (dev->driver->vgaarb_irq) {
+               dev->driver->vgaarb_irq(dev, state);
+               return;
+       }
+
+       if (!dev->irq_enabled)
+               return;
+
+       if (state) {
+               if (dev->driver->irq_uninstall)
+                       dev->driver->irq_uninstall(dev);
+       } else {
+               if (dev->driver->irq_preinstall)
+                       dev->driver->irq_preinstall(dev);
+               if (dev->driver->irq_postinstall)
+                       dev->driver->irq_postinstall(dev);
+       }
+}
+#endif
+
 /**
  * Install IRQ handler.
  *
@@ -246,32 +243,25 @@ EXPORT_SYMBOL(drm_vblank_init);
  * \c irq_preinstall() and \c irq_postinstall() functions
  * before and after the installation.
  */
-int drm_irq_install(struct drm_device *dev)
+int drm_irq_install(struct drm_device *dev, int irq)
 {
        int ret;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
                return -EINVAL;
 
-       if (dev->irq == 0)
+       if (irq == 0)
                return -EINVAL;
 
-       DRM_LOCK(dev);
-
        /* Driver must have been initialized */
-       if (!dev->dev_private) {
-               DRM_UNLOCK(dev);
+       if (!dev->dev_private)
                return -EINVAL;
-       }
 
-       if (dev->irq_enabled) {
-               DRM_UNLOCK(dev);
+       if (dev->irq_enabled)
                return -EBUSY;
-       }
        dev->irq_enabled = 1;
-       DRM_UNLOCK(dev);
 
-       DRM_DEBUG("irq=%d\n", dev->irq);
+       DRM_DEBUG("irq=%d\n", irq);
 
        /* Before installing handler */
        if (dev->driver->irq_preinstall)
@@ -282,9 +272,7 @@ int drm_irq_install(struct drm_device *dev)
            dev->driver->irq_handler, dev, &dev->irqh, &dev->irq_lock);
 
        if (ret != 0) {
-               DRM_LOCK(dev);
                dev->irq_enabled = 0;
-               DRM_UNLOCK(dev);
                return ret;
        }
 
@@ -293,10 +281,10 @@ int drm_irq_install(struct drm_device *dev)
                ret = dev->driver->irq_postinstall(dev);
 
        if (ret < 0) {
-               DRM_LOCK(dev);
                dev->irq_enabled = 0;
-               DRM_UNLOCK(dev);
                bus_teardown_intr(dev->dev, dev->irqr, dev->irqh);
+       } else {
+               dev->irq = irq;
        }
 
        return ret;
@@ -318,10 +306,8 @@ int drm_irq_uninstall(struct drm_device *dev)
        if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
                return -EINVAL;
 
-       mutex_lock(&dev->struct_mutex);
        irq_enabled = dev->irq_enabled;
        dev->irq_enabled = false;
-       mutex_unlock(&dev->struct_mutex);
 
        /*
         * Wake up any waiters so they don't hang.
@@ -366,28 +352,38 @@ int drm_control(struct drm_device *dev, void *data,
                struct drm_file *file_priv)
 {
        struct drm_control *ctl = data;
+       int ret = 0, irq;
 
        /* if we haven't irq we fallback for compatibility reasons -
         * this used to be a separate function in drm_dma.h
         */
 
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
+               return 0;
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               return 0;
+       /* UMS was only ever support on pci devices. */
+       if (WARN_ON(!dev->pdev))
+               return -EINVAL;
 
        switch (ctl->func) {
        case DRM_INST_HANDLER:
-               if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
-                       return 0;
-               if (drm_core_check_feature(dev, DRIVER_MODESET))
-                       return 0;
+               irq = dev->irq;
+
                if (dev->if_version < DRM_IF_VERSION(1, 2) &&
-                   ctl->irq != dev->irq)
+                   ctl->irq != irq)
                        return -EINVAL;
-               return drm_irq_install(dev);
+               mutex_lock(&dev->struct_mutex);
+               ret = drm_irq_install(dev, irq);
+               mutex_unlock(&dev->struct_mutex);
+
+               return ret;
        case DRM_UNINST_HANDLER:
-               if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
-                       return 0;
-               if (drm_core_check_feature(dev, DRIVER_MODESET))
-                       return 0;
-               return drm_irq_uninstall(dev);
+               mutex_lock(&dev->struct_mutex);
+               ret = drm_irq_uninstall(dev);
+               mutex_unlock(&dev->struct_mutex);
+
+               return ret;
        default:
                return -EINVAL;
        }
@@ -550,7 +546,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
                 */
 #if 0
                if (!drm_timestamp_monotonic)
-                       mono_time_offset = 0;
+                       mono_time_offset = ktime_get_monotonic_offset();
 #endif
 
                /* Return as no-op if scanout query unsupported or failed. */
@@ -622,11 +618,15 @@ EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
 
 static struct timeval get_drm_timestamp(void)
 {
-       struct timeval now;
+       ktime_t now;
 
-       getmicrouptime(&now);
+       now = ktime_get();
+#if 0
+       if (!drm_timestamp_monotonic)
+               now = ktime_sub(now, ktime_get_monotonic_offset());
+#endif
 
-       return now;
+       return ktime_to_timeval(now);
 }
 
 /**
@@ -727,7 +727,6 @@ static void send_vblank_event(struct drm_device *dev,
                struct drm_pending_vblank_event *e,
                unsigned long seq, struct timeval *now)
 {
-       KKASSERT(mutex_is_locked(&dev->event_lock));
        e->event.sequence = seq;
        e->event.tv_sec = now->tv_sec;
        e->event.tv_usec = now->tv_usec;
@@ -736,6 +735,7 @@ static void send_vblank_event(struct drm_device *dev,
                      &e->base.file_priv->event_list);
        drm_event_wakeup(&e->base);
 #if 0
+       wake_up_interruptible(&e->base.file_priv->event_wait);
        trace_drm_vblank_event_delivered(e->base.pid, e->pipe,
                                         e->event.sequence);
 #endif
@@ -826,9 +826,44 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc)
                vblanktimestamp(dev, crtc, tslot) = t_vblank;
        }
 
-       smp_mb__before_atomic_inc();
+       smp_mb__before_atomic();
        atomic_add(diff, &dev->vblank[crtc].count);
-       smp_mb__after_atomic_inc();
+       smp_mb__after_atomic();
+}
+
+/**
+ * drm_vblank_enable - enable the vblank interrupt on a CRTC
+ * @dev: DRM device
+ * @crtc: CRTC in question
+ */
+static int drm_vblank_enable(struct drm_device *dev, int crtc)
+{
+       int ret = 0;
+
+       assert_spin_locked(&dev->vbl_lock);
+
+       lockmgr(&dev->vblank_time_lock, LK_EXCLUSIVE);
+
+       if (!dev->vblank[crtc].enabled) {
+               /* Enable vblank irqs under vblank_time_lock protection.
+                * All vblank count & timestamp updates are held off
+                * until we are done reinitializing master counter and
+                * timestamps. Filtercode in drm_handle_vblank() will
+                * prevent double-accounting of same vblank interval.
+                */
+               ret = dev->driver->enable_vblank(dev, crtc);
+               DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret);
+               if (ret)
+                       atomic_dec(&dev->vblank[crtc].refcount);
+               else {
+                       dev->vblank[crtc].enabled = true;
+                       drm_update_vblank_count(dev, crtc);
+               }
+       }
+
+       lockmgr(&dev->vblank_time_lock, LK_RELEASE);
+
+       return ret;
 }
 
 /**
@@ -849,25 +884,7 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
        lockmgr(&dev->vbl_lock, LK_EXCLUSIVE);
        /* Going from 0->1 means we have to enable interrupts again */
        if (atomic_add_return(1, &dev->vblank[crtc].refcount) == 1) {
-               lockmgr(&dev->vblank_time_lock, LK_EXCLUSIVE);
-               if (!dev->vblank[crtc].enabled) {
-                       /* Enable vblank irqs under vblank_time_lock protection.
-                        * All vblank count & timestamp updates are held off
-                        * until we are done reinitializing master counter and
-                        * timestamps. Filtercode in drm_handle_vblank() will
-                        * prevent double-accounting of same vblank interval.
-                        */
-                       ret = dev->driver->enable_vblank(dev, crtc);
-                       DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n",
-                                 crtc, ret);
-                       if (ret)
-                               atomic_dec(&dev->vblank[crtc].refcount);
-                       else {
-                               dev->vblank[crtc].enabled = true;
-                               drm_update_vblank_count(dev, crtc);
-                       }
-               }
-               lockmgr(&dev->vblank_time_lock, LK_RELEASE);
+               ret = drm_vblank_enable(dev, crtc);
        } else {
                if (!dev->vblank[crtc].enabled) {
                        atomic_dec(&dev->vblank[crtc].refcount);
@@ -880,6 +897,24 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
 }
 EXPORT_SYMBOL(drm_vblank_get);
 
+/**
+ * drm_crtc_vblank_get - get a reference count on vblank events
+ * @crtc: which CRTC to own
+ *
+ * Acquire a reference count on vblank events to avoid having them disabled
+ * while in use.
+ *
+ * This is the native kms version of drm_vblank_off().
+ *
+ * Returns:
+ * Zero on success, nonzero on failure.
+ */
+int drm_crtc_vblank_get(struct drm_crtc *crtc)
+{
+       return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_get);
+
 /**
  * drm_vblank_put - give up ownership of vblank events
  * @dev: DRM device
@@ -895,17 +930,39 @@ void drm_vblank_put(struct drm_device *dev, int crtc)
        /* Last user schedules interrupt disable */
        if (atomic_dec_and_test(&dev->vblank[crtc].refcount) &&
            (drm_vblank_offdelay > 0))
-               mod_timer(&dev->vblank_disable_timer,
+               mod_timer(&dev->vblank[crtc].disable_timer,
                          jiffies + ((drm_vblank_offdelay * HZ)/1000));
 }
 EXPORT_SYMBOL(drm_vblank_put);
 
+/**
+ * drm_crtc_vblank_put - give up ownership of vblank events
+ * @crtc: which counter to give up
+ *
+ * Release ownership of a given vblank counter, turning off interrupts
+ * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
+ *
+ * This is the native kms version of drm_vblank_put().
+ */
+void drm_crtc_vblank_put(struct drm_crtc *crtc)
+{
+       drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_put);
+
 /**
  * drm_vblank_off - disable vblank events on a CRTC
  * @dev: DRM device
  * @crtc: CRTC in question
  *
- * Caller must hold event lock.
+ * Drivers can use this function to shut down the vblank interrupt handling when
+ * disabling a crtc. This function ensures that the latest vblank frame count is
+ * stored so that drm_vblank_on() can restore it again.
+ *
+ * Drivers must use this function when the hardware vblank counter can get
+ * reset, e.g. when suspending.
+ *
+ * This is the legacy version of drm_crtc_vblank_off().
  */
 void drm_vblank_off(struct drm_device *dev, int crtc)
 {
@@ -937,6 +994,65 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
 }
 EXPORT_SYMBOL(drm_vblank_off);
 
+/**
+ * drm_crtc_vblank_off - disable vblank events on a CRTC
+ * @crtc: CRTC in question
+ *
+ * Drivers can use this function to shut down the vblank interrupt handling when
+ * disabling a crtc. This function ensures that the latest vblank frame count is
+ * stored so that drm_vblank_on can restore it again.
+ *
+ * Drivers must use this function when the hardware vblank counter can get
+ * reset, e.g. when suspending.
+ *
+ * This is the native kms version of drm_vblank_off().
+ */
+void drm_crtc_vblank_off(struct drm_crtc *crtc)
+{
+       drm_vblank_off(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_off);
+
+/**
+ * drm_vblank_on - enable vblank events on a CRTC
+ * @dev: DRM device
+ * @crtc: CRTC in question
+ *
+ * This functions restores the vblank interrupt state captured with
+ * drm_vblank_off() again. Note that calls to drm_vblank_on() and
+ * drm_vblank_off() can be unbalanced and so can also be unconditionaly called
+ * in driver load code to reflect the current hardware state of the crtc.
+ *
+ * This is the legacy version of drm_crtc_vblank_on().
+ */
+void drm_vblank_on(struct drm_device *dev, int crtc)
+{
+
+       lockmgr(&dev->vbl_lock, LK_EXCLUSIVE);
+       /* re-enable interrupts if there's are users left */
+       if (atomic_read(&dev->vblank[crtc].refcount) != 0)
+               WARN_ON(drm_vblank_enable(dev, crtc));
+       lockmgr(&dev->vbl_lock, LK_RELEASE);
+}
+EXPORT_SYMBOL(drm_vblank_on);
+
+/**
+ * drm_crtc_vblank_on - enable vblank events on a CRTC
+ * @crtc: CRTC in question
+ *
+ * This functions restores the vblank interrupt state captured with
+ * drm_vblank_off() again. Note that calls to drm_vblank_on() and
+ * drm_vblank_off() can be unbalanced and so can also be unconditionaly called
+ * in driver load code to reflect the current hardware state of the crtc.
+ *
+ * This is the native kms version of drm_vblank_on().
+ */
+void drm_crtc_vblank_on(struct drm_crtc *crtc)
+{
+       drm_vblank_on(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_on);
+
 /**
  * drm_vblank_pre_modeset - account for vblanks across mode sets
  * @dev: DRM device
@@ -1031,8 +1147,7 @@ int drm_modeset_ctl(struct drm_device *dev, void *data,
 static void
 drm_vblank_event_destroy(struct drm_pending_event *e)
 {
-
-       drm_free(e, M_DRM);
+       kfree(e);
 }
 
 static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
@@ -1122,9 +1237,8 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
        int ret;
        unsigned int flags, seq, crtc, high_crtc;
 
-       if (drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
-               if ((!dev->irq_enabled))
-                       return -EINVAL;
+       if (!dev->irq_enabled)
+               return -EINVAL;
 
        if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
                return -EINVAL;
@@ -1184,6 +1298,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
        DRM_WAIT_ON(ret, dev->vblank[crtc].queue, 3 * HZ,
                    (((drm_vblank_count(dev, crtc) -
                       vblwait->request.sequence) <= (1 << 23)) ||
+                    !dev->vblank[crtc].enabled ||
                     !dev->irq_enabled));
 
        if (ret != -EINTR) {
@@ -1288,9 +1403,9 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
                /* Increment cooked vblank count. This also atomically commits
                 * the timestamp computed above.
                 */
-               smp_mb__before_atomic_inc();
+               smp_mb__before_atomic();
                atomic_inc(&dev->vblank[crtc].count);
-               smp_mb__after_atomic_inc();
+               smp_mb__after_atomic();
        } else {
                DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n",
                          crtc, (int) diff_ns);
index df4daca..36e2d96 100644 (file)
@@ -74,11 +74,6 @@ static struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic)
        return child;
 }
 
-/* drm_mm_pre_get() - pre allocate drm_mm_node structure
- * drm_mm:     memory manager struct we are pre-allocating for
- *
- * Returns 0 on success or -ENOMEM if allocation fails.
- */
 int drm_mm_pre_get(struct drm_mm *mm)
 {
        struct drm_mm_node *node;
@@ -100,12 +95,55 @@ int drm_mm_pre_get(struct drm_mm *mm)
        spin_unlock(&mm->unused_lock);
        return 0;
 }
-EXPORT_SYMBOL(drm_mm_pre_get);
+
+/**
+ * DOC: Overview
+ *
+ * drm_mm provides a simple range allocator. The drivers are free to use the
+ * resource allocator from the linux core if it suits them, the upside of drm_mm
+ * is that it's in the DRM core. Which means that it's easier to extend for
+ * some of the crazier special purpose needs of gpus.
+ *
+ * The main data struct is &drm_mm, allocations are tracked in &drm_mm_node.
+ * Drivers are free to embed either of them into their own suitable
+ * datastructures. drm_mm itself will not do any allocations of its own, so if
+ * drivers choose not to embed nodes they need to still allocate them
+ * themselves.
+ *
+ * The range allocator also supports reservation of preallocated blocks. This is
+ * useful for taking over initial mode setting configurations from the firmware,
+ * where an object needs to be created which exactly matches the firmware's
+ * scanout target. As long as the range is still free it can be inserted anytime
+ * after the allocator is initialized, which helps with avoiding looped
+ * depencies in the driver load sequence.
+ *
+ * drm_mm maintains a stack of most recently freed holes, which of all
+ * simplistic datastructures seems to be a fairly decent approach to clustering
+ * allocations and avoiding too much fragmentation. This means free space
+ * searches are O(num_holes). Given that all the fancy features drm_mm supports
+ * something better would be fairly complex and since gfx thrashing is a fairly
+ * steep cliff not a real concern. Removing a node again is O(1).
+ *
+ * drm_mm supports a few features: Alignment and range restrictions can be
+ * supplied. Further more every &drm_mm_node has a color value (which is just an
+ * opaqua unsigned long) which in conjunction with a driver callback can be used
+ * to implement sophisticated placement restrictions. The i915 DRM driver uses
+ * this to implement guard pages between incompatible caching domains in the
+ * graphics TT.
+ *
+ * Two behaviors are supported for searching and allocating: bottom-up and top-down.
+ * The default is bottom-up. Top-down allocation can be used if the memory area
+ * has different restrictions, or just to reduce fragmentation.
+ *
+ * Finally iteration helpers to walk all nodes and all holes are provided as are
+ * some basic allocator dumpers for debugging.
+ */
 
 static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
                                 struct drm_mm_node *node,
                                 unsigned long size, unsigned alignment,
-                                unsigned long color)
+                                unsigned long color,
+                                enum drm_mm_allocator_flags flags)
 {
        struct drm_mm *mm = hole_node->mm;
        unsigned long hole_start = drm_mm_hole_node_start(hole_node);
@@ -118,12 +156,22 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
        if (mm->color_adjust)
                mm->color_adjust(hole_node, color, &adj_start, &adj_end);
 
+       if (flags & DRM_MM_CREATE_TOP)
+               adj_start = adj_end - size;
+
        if (alignment) {
                unsigned tmp = adj_start % alignment;
-               if (tmp)
-                       adj_start += alignment - tmp;
+               if (tmp) {
+                       if (flags & DRM_MM_CREATE_TOP)
+                               adj_start -= tmp;
+                       else
+                               adj_start += alignment - tmp;
+               }
        }
 
+       BUG_ON(adj_start < hole_start);
+       BUG_ON(adj_end > hole_end);
+
        if (adj_start == hole_start) {
                hole_node->hole_follows = 0;
                list_del(&hole_node->hole_stack);
@@ -147,6 +195,20 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
        }
 }
 
+/**
+ * drm_mm_reserve_node - insert an pre-initialized node
+ * @mm: drm_mm allocator to insert @node into
+ * @node: drm_mm_node to insert
+ *
+ * This functions inserts an already set-up drm_mm_node into the allocator,
+ * meaning that start, size and color must be set by the caller. This is useful
+ * to initialize the allocator with preallocated objects which must be set-up
+ * before the range allocator can be set-up, e.g. when taking over a firmware
+ * framebuffer.
+ *
+ * Returns:
+ * 0 on success, -ENOSPC if there's no hole where @node is.
+ */
 int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
 {
        struct drm_mm_node *hole;
@@ -181,8 +243,6 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
                return 0;
        }
 
-       WARN(1, "no hole found for node 0x%lx + 0x%lx\n",
-            node->start, node->size);
        return -ENOSPC;
 }
 EXPORT_SYMBOL(drm_mm_reserve_node);
@@ -199,30 +259,40 @@ struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node,
        if (unlikely(node == NULL))
                return NULL;
 
-       drm_mm_insert_helper(hole_node, node, size, alignment, color);
+       drm_mm_insert_helper(hole_node, node, size, alignment, color, DRM_MM_CREATE_DEFAULT);
 
        return node;
 }
-EXPORT_SYMBOL(drm_mm_get_block_generic);
 
 /**
- * Search for free space and insert a preallocated memory node. Returns
- * -ENOSPC if no suitable free area is available. The preallocated memory node
- * must be cleared.
+ * drm_mm_insert_node_generic - search for space and insert @node
+ * @mm: drm_mm to allocate from
+ * @node: preallocate node to insert
+ * @size: size of the allocation
+ * @alignment: alignment of the allocation
+ * @color: opaque tag value to use for this node
+ * @sflags: flags to fine-tune the allocation search
+ * @aflags: flags to fine-tune the allocation behavior
+ *
+ * The preallocated node must be cleared to 0.
+ *
+ * Returns:
+ * 0 on success, -ENOSPC if there's no suitable hole.
  */
 int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node,
                               unsigned long size, unsigned alignment,
                               unsigned long color,
-                              enum drm_mm_search_flags flags)
+                              enum drm_mm_search_flags sflags,
+                              enum drm_mm_allocator_flags aflags)
 {
        struct drm_mm_node *hole_node;
 
        hole_node = drm_mm_search_free_generic(mm, size, alignment,
-                                              color, flags);
+                                              color, sflags);
        if (!hole_node)
                return -ENOSPC;
 
-       drm_mm_insert_helper(hole_node, node, size, alignment, color);
+       drm_mm_insert_helper(hole_node, node, size, alignment, color, aflags);
        return 0;
 }
 EXPORT_SYMBOL(drm_mm_insert_node_generic);
@@ -231,7 +301,8 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
                                       struct drm_mm_node *node,
                                       unsigned long size, unsigned alignment,
                                       unsigned long color,
-                                      unsigned long start, unsigned long end)
+                                      unsigned long start, unsigned long end,
+                                      enum drm_mm_allocator_flags flags)
 {
        struct drm_mm *mm = hole_node->mm;
        unsigned long hole_start = drm_mm_hole_node_start(hole_node);
@@ -246,13 +317,20 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
        if (adj_end > end)
                adj_end = end;
 
+       if (flags & DRM_MM_CREATE_TOP)
+               adj_start = adj_end - size;
+
        if (mm->color_adjust)
                mm->color_adjust(hole_node, color, &adj_start, &adj_end);
 
        if (alignment) {
                unsigned tmp = adj_start % alignment;
-               if (tmp)
-                       adj_start += alignment - tmp;
+               if (tmp) {
+                       if (flags & DRM_MM_CREATE_TOP)
+                               adj_start -= tmp;
+                       else
+                               adj_start += alignment - tmp;
+               }
        }
 
        if (adj_start == hole_start) {
@@ -269,6 +347,8 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
        INIT_LIST_HEAD(&node->hole_stack);
        list_add(&node->node_list, &hole_node->node_list);
 
+       BUG_ON(node->start < start);
+       BUG_ON(node->start < adj_start);
        BUG_ON(node->start + node->size > adj_end);
        BUG_ON(node->start + node->size > end);
 
@@ -294,39 +374,57 @@ struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *hole_node
                return NULL;
 
        drm_mm_insert_helper_range(hole_node, node, size, alignment, color,
-                                  start, end);
+                                  start, end, DRM_MM_CREATE_DEFAULT);
 
        return node;
 }
-EXPORT_SYMBOL(drm_mm_get_block_range_generic);
 
 /**
- * Search for free space and insert a preallocated memory node. Returns
- * -ENOSPC if no suitable free area is available. This is for range
- * restricted allocations. The preallocated memory node must be cleared.
+ * drm_mm_insert_node_in_range_generic - ranged search for space and insert @node
+ * @mm: drm_mm to allocate from
+ * @node: preallocate node to insert
+ * @size: size of the allocation
+ * @alignment: alignment of the allocation
+ * @color: opaque tag value to use for this node
+ * @start: start of the allowed range for this node
+ * @end: end of the allowed range for this node
+ * @sflags: flags to fine-tune the allocation search
+ * @aflags: flags to fine-tune the allocation behavior
+ *
+ * The preallocated node must be cleared to 0.
+ *
+ * Returns:
+ * 0 on success, -ENOSPC if there's no suitable hole.
  */
 int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node,
-                                       unsigned long size, unsigned alignment, unsigned long color,
+                                       unsigned long size, unsigned alignment,
+                                       unsigned long color,
                                        unsigned long start, unsigned long end,
-                                       enum drm_mm_search_flags flags)
+                                       enum drm_mm_search_flags sflags,
+                                       enum drm_mm_allocator_flags aflags)
 {
        struct drm_mm_node *hole_node;
 
        hole_node = drm_mm_search_free_in_range_generic(mm,
                                                        size, alignment, color,
-                                                       start, end, flags);
+                                                       start, end, sflags);
        if (!hole_node)
                return -ENOSPC;
 
        drm_mm_insert_helper_range(hole_node, node,
                                   size, alignment, color,
-                                  start, end);
+                                  start, end, aflags);
        return 0;
 }
 EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic);
 
 /**
- * Remove a memory node from the allocator.
+ * drm_mm_remove_node - Remove a memory node from the allocator.
+ * @node: drm_mm_node to remove
+ *
+ * This just removes a node from its drm_mm allocator. The node does not need to
+ * be cleared again before it can be re-inserted into this or any other drm_mm
+ * allocator. It is a bug to call this function on a un-allocated node.
  */
 void drm_mm_remove_node(struct drm_mm_node *node)
 {
@@ -382,7 +480,6 @@ void drm_mm_put_block(struct drm_mm_node *node)
                kfree(node);
        spin_unlock(&mm->unused_lock);
 }
-EXPORT_SYMBOL(drm_mm_put_block);
 
 static int check_free_hole(unsigned long start, unsigned long end,
                           unsigned long size, unsigned alignment)
@@ -400,10 +497,10 @@ static int check_free_hole(unsigned long start, unsigned long end,
 }
 
 struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
-                                              unsigned long size,
-                                              unsigned alignment,
-                                              unsigned long color,
-                                              enum drm_mm_search_flags flags)
+                                                     unsigned long size,
+                                                     unsigned alignment,
+                                                     unsigned long color,
+                                                     enum drm_mm_search_flags flags)
 {
        struct drm_mm_node *entry;
        struct drm_mm_node *best;
@@ -416,7 +513,10 @@ struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
        best = NULL;
        best_size = ~0UL;
 
-       drm_mm_for_each_hole(entry, mm, adj_start, adj_end) {
+       __drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
+                              flags & DRM_MM_SEARCH_BELOW) {
+               unsigned long hole_size = adj_end - adj_start;
+
                if (mm->color_adjust) {
                        mm->color_adjust(entry, color, &adj_start, &adj_end);
                        if (adj_end <= adj_start)
@@ -429,15 +529,14 @@ struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
                if (!(flags & DRM_MM_SEARCH_BEST))
                        return entry;
 
-               if (entry->size < best_size) {
+               if (hole_size < best_size) {
                        best = entry;
-                       best_size = entry->size;
+                       best_size = hole_size;
                }
        }
 
        return best;
 }
-EXPORT_SYMBOL(drm_mm_search_free_generic);
 
 struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
                                                        unsigned long size,
@@ -458,7 +557,10 @@ struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
        best = NULL;
        best_size = ~0UL;
 
-       drm_mm_for_each_hole(entry, mm, adj_start, adj_end) {
+       __drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
+                              flags & DRM_MM_SEARCH_BELOW) {
+               unsigned long hole_size = adj_end - adj_start;
+
                if (adj_start < start)
                        adj_start = start;
                if (adj_end > end)
@@ -476,18 +578,23 @@ struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
                if (!(flags & DRM_MM_SEARCH_BEST))
                        return entry;
 
-               if (entry->size < best_size) {
+               if (hole_size < best_size) {
                        best = entry;
-                       best_size = entry->size;
+                       best_size = hole_size;
                }
        }
 
        return best;
 }
-EXPORT_SYMBOL(drm_mm_search_free_in_range_generic);
 
 /**
- * Moves an allocation. To be used with embedded struct drm_mm_node.
+ * drm_mm_replace_node - move an allocation from @old to @new
+ * @old: drm_mm_node to remove from the allocator
+ * @new: drm_mm_node which should inherit @old's allocation
+ *
+ * This is useful for when drivers embed the drm_mm_node structure and hence
+ * can't move allocations by reassigning pointers. It's a combination of remove
+ * and insert with the guarantee that the allocation start will match.
  */
 void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
 {
@@ -505,12 +612,46 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
 EXPORT_SYMBOL(drm_mm_replace_node);
 
 /**
- * Initializa lru scanning.
+ * DOC: lru scan roaster
+ *
+ * Very often GPUs need to have continuous allocations for a given object. When
+ * evicting objects to make space for a new one it is therefore not most
+ * efficient when we simply start to select all objects from the tail of an LRU
+ * until there's a suitable hole: Especially for big objects or nodes that
+ * otherwise have special allocation constraints there's a good chance we evict
+ * lots of (smaller) objects unecessarily.
+ *
+ * The DRM range allocator supports this use-case through the scanning
+ * interfaces. First a scan operation needs to be initialized with
+ * drm_mm_init_scan() or drm_mm_init_scan_with_range(). The the driver adds
+ * objects to the roaster (probably by walking an LRU list, but this can be
+ * freely implemented) until a suitable hole is found or there's no further
+ * evitable object.
+ *
+ * The the driver must walk through all objects again in exactly the reverse
+ * order to restore the allocator state. Note that while the allocator is used
+ * in the scan mode no other operation is allowed.
+ *
+ * Finally the driver evicts all objects selected in the scan. Adding and
+ * removing an object is O(1), and since freeing a node is also O(1) the overall
+ * complexity is O(scanned_objects). So like the free stack which needs to be
+ * walked before a scan operation even begins this is linear in the number of
+ * objects. It doesn't seem to hurt badly.
+ */
+
+/**
+ * drm_mm_init_scan - initialize lru scanning
+ * @mm: drm_mm to scan
+ * @size: size of the allocation
+ * @alignment: alignment of the allocation
+ * @color: opaque tag value to use for the allocation
  *
  * This simply sets up the scanning routines with the parameters for the desired
- * hole.
+ * hole. Note that there's no need to specify allocation flags, since they only
+ * change the place a node is allocated from within a suitable hole.
  *
- * Warning: As long as the scan list is non-empty, no other operations than
+ * Warning:
+ * As long as the scan list is non-empty, no other operations than
  * adding/removing nodes to/from the scan list are allowed.
  */
 void drm_mm_init_scan(struct drm_mm *mm,
@@ -530,12 +671,20 @@ void drm_mm_init_scan(struct drm_mm *mm,
 EXPORT_SYMBOL(drm_mm_init_scan);
 
 /**
- * Initializa lru scanning.
+ * drm_mm_init_scan - initialize range-restricted lru scanning
+ * @mm: drm_mm to scan
+ * @size: size of the allocation
+ * @alignment: alignment of the allocation
+ * @color: opaque tag value to use for the allocation
+ * @start: start of the allowed range for the allocation
+ * @end: end of the allowed range for the allocation
  *
  * This simply sets up the scanning routines with the parameters for the desired
- * hole. This version is for range-restricted scans.
+ * hole. Note that there's no need to specify allocation flags, since they only
+ * change the place a node is allocated from within a suitable hole.
  *
- * Warning: As long as the scan list is non-empty, no other operations than
+ * Warning:
+ * As long as the scan list is non-empty, no other operations than
  * adding/removing nodes to/from the scan list are allowed.
  */
 void drm_mm_init_scan_with_range(struct drm_mm *mm,
@@ -559,12 +708,16 @@ void drm_mm_init_scan_with_range(struct drm_mm *mm,
 EXPORT_SYMBOL(drm_mm_init_scan_with_range);
 
 /**
+ * drm_mm_scan_add_block - add a node to the scan list
+ * @node: drm_mm_node to add
+ *
  * Add a node to the scan list that might be freed to make space for the desired
  * hole.
  *
- * Returns non-zero, if a hole has been found, zero otherwise.
+ * Returns:
+ * True if a hole has been found, false otherwise.
  */
-int drm_mm_scan_add_block(struct drm_mm_node *node)
+bool drm_mm_scan_add_block(struct drm_mm_node *node)
 {
        struct drm_mm *mm = node->mm;
        struct drm_mm_node *prev_node;
@@ -604,15 +757,16 @@ int drm_mm_scan_add_block(struct drm_mm_node *node)
                            mm->scan_size, mm->scan_alignment)) {
                mm->scan_hit_start = hole_start;
                mm->scan_hit_end = hole_end;
-               return 1;
+               return true;
        }
 
-       return 0;
+       return false;
 }
 EXPORT_SYMBOL(drm_mm_scan_add_block);
 
 /**
- * Remove a node from the scan list.
+ * drm_mm_scan_remove_block - remove a node from the scan list
+ * @node: drm_mm_node to remove
  *
  * Nodes _must_ be removed in the exact same order from the scan list as they
  * have been added, otherwise the internal state of the memory manager will be
@@ -622,10 +776,11 @@ EXPORT_SYMBOL(drm_mm_scan_add_block);
  * immediately following drm_mm_search_free with !DRM_MM_SEARCH_BEST will then
  * return the just freed block (because its at the top of the free_stack list).
  *
- * Returns one if this block should be evicted, zero otherwise. Will always
- * return zero when no hole has been found.
+ * Returns:
+ * True if this block should be evicted, false otherwise. Will always
+ * return false when no hole has been found.
  */
-int drm_mm_scan_remove_block(struct drm_mm_node *node)
+bool drm_mm_scan_remove_block(struct drm_mm_node *node)
 {
        struct drm_mm *mm = node->mm;
        struct drm_mm_node *prev_node;
@@ -646,7 +801,15 @@ int drm_mm_scan_remove_block(struct drm_mm_node *node)
 }
 EXPORT_SYMBOL(drm_mm_scan_remove_block);
 
-int drm_mm_clean(struct drm_mm * mm)
+/**
+ * drm_mm_clean - checks whether an allocator is clean
+ * @mm: drm_mm allocator to check
+ *
+ * Returns:
+ * True if the allocator is completely free, false if there's still a node
+ * allocated in it.
+ */
+bool drm_mm_clean(struct drm_mm * mm)
 {
        struct list_head *head = &mm->head_node.node_list;
 
@@ -654,13 +817,18 @@ int drm_mm_clean(struct drm_mm * mm)
 }
 EXPORT_SYMBOL(drm_mm_clean);
 
+/**
+ * drm_mm_init - initialize a drm-mm allocator
+ * @mm: the drm_mm structure to initialize
+ * @start: start of the range managed by @mm
+ * @size: end of the range managed by @mm
+ *
+ * Note that @mm must be cleared to 0 before calling this function.
+ */
 void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
 {
        INIT_LIST_HEAD(&mm->hole_stack);
-       INIT_LIST_HEAD(&mm->unused_nodes);
-       mm->num_unused = 0;
        mm->scanned_blocks = 0;
-       spin_init(&mm->unused_lock, "drmmminit");
 
        /* Clever trick to avoid a special case in the free hole tracking. */
        INIT_LIST_HEAD(&mm->head_node.node_list);
@@ -678,57 +846,56 @@ void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
 }
 EXPORT_SYMBOL(drm_mm_init);
 
+/**
+ * drm_mm_takedown - clean up a drm_mm allocator
+ * @mm: drm_mm allocator to clean up
+ *
+ * Note that it is a bug to call this function on an allocator which is not
+ * clean.
+ */
 void drm_mm_takedown(struct drm_mm * mm)
 {
-       struct drm_mm_node *entry, *next;
+       WARN(!list_empty(&mm->head_node.node_list),
+            "Memory manager not clean during takedown.\n");
+}
+EXPORT_SYMBOL(drm_mm_takedown);
 
-       if (!list_empty(&mm->head_node.node_list)) {
-               DRM_ERROR("Memory manager not clean. Delaying takedown\n");
-               return;
-       }
+static unsigned long drm_mm_debug_hole(struct drm_mm_node *entry,
+                                      const char *prefix)
+{
+       unsigned long hole_start, hole_end, hole_size;
 
-       spin_lock(&mm->unused_lock);
-       list_for_each_entry_safe(entry, next, &mm->unused_nodes, node_list) {
-               list_del(&entry->node_list);
-               kfree(entry);
-               --mm->num_unused;
+       if (entry->hole_follows) {
+               hole_start = drm_mm_hole_node_start(entry);
+               hole_end = drm_mm_hole_node_end(entry);
+               hole_size = hole_end - hole_start;
+               printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
+                       prefix, hole_start, hole_end,
+                       hole_size);
+               return hole_size;
        }
-       spin_unlock(&mm->unused_lock);
 
-       BUG_ON(mm->num_unused != 0);
+       return 0;
 }
-EXPORT_SYMBOL(drm_mm_takedown);
 
+/**
+ * drm_mm_debug_table - dump allocator state to dmesg
+ * @mm: drm_mm allocator to dump
+ * @prefix: prefix to use for dumping to dmesg
+ */
 void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
 {
        struct drm_mm_node *entry;
        unsigned long total_used = 0, total_free = 0, total = 0;
-       unsigned long hole_start, hole_end, hole_size;
 
-       hole_start = drm_mm_hole_node_start(&mm->head_node);
-       hole_end = drm_mm_hole_node_end(&mm->head_node);
-       hole_size = hole_end - hole_start;
-       if (hole_size)
-               printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
-                       prefix, hole_start, hole_end,
-                       hole_size);
-       total_free += hole_size;
+       total_free += drm_mm_debug_hole(&mm->head_node, prefix);
 
        drm_mm_for_each_node(entry, mm) {
                printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: used\n",
                        prefix, entry->start, entry->start + entry->size,
                        entry->size);
                total_used += entry->size;
-
-               if (entry->hole_follows) {
-                       hole_start = drm_mm_hole_node_start(entry);
-                       hole_end = drm_mm_hole_node_end(entry);
-                       hole_size = hole_end - hole_start;
-                       printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
-                               prefix, hole_start, hole_end,
-                               hole_size);
-                       total_free += hole_size;
-               }
+               total_free += drm_mm_debug_hole(entry, prefix);
        }
        total = total_free + total_used;
 
@@ -754,6 +921,11 @@ static unsigned long drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *en
        return 0;
 }
 
+/**
+ * drm_mm_dump_table - dump allocator state to a seq_file
+ * @m: seq_file to dump to
+ * @mm: drm_mm allocator to dump
+ */
 int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
 {
        struct drm_mm_node *entry;
index 40f8c86..4ae7eec 100644 (file)
 #include <linux/export.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_modes.h>
+
+#include "drm_crtc_internal.h"
 
 /**
- * drm_mode_debug_printmodeline - debug print a mode
- * @dev: DRM device
+ * drm_mode_debug_printmodeline - print a mode to dmesg
  * @mode: mode to print
  *
- * LOCKING:
- * None.
- *
  * Describe @mode using DRM_DEBUG.
  */
 void drm_mode_debug_printmodeline(const struct drm_display_mode *mode)
@@ -58,18 +57,77 @@ void drm_mode_debug_printmodeline(const struct drm_display_mode *mode)
 EXPORT_SYMBOL(drm_mode_debug_printmodeline);
 
 /**
- * drm_cvt_mode -create a modeline based on CVT algorithm
+ * drm_mode_create - create a new display mode
  * @dev: DRM device
- * @hdisplay: hdisplay size
- * @vdisplay: vdisplay size
- * @vrefresh  : vrefresh rate
- * @reduced : Whether the GTF calculation is simplified
- * @interlaced:Whether the interlace is supported
  *
- * LOCKING:
- * none.
+ * Create a new, cleared drm_display_mode with kzalloc, allocate an ID for it
+ * and return it.
  *
- * return the modeline based on CVT algorithm
+ * Returns:
+ * Pointer to new mode on success, NULL on error.
+ */
+struct drm_display_mode *drm_mode_create(struct drm_device *dev)
+{
+       struct drm_display_mode *nmode;
+
+       nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL);
+       if (!nmode)
+               return NULL;
+
+       if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) {
+               kfree(nmode);
+               return NULL;
+       }
+
+       return nmode;
+}
+EXPORT_SYMBOL(drm_mode_create);
+
+/**
+ * drm_mode_destroy - remove a mode
+ * @dev: DRM device
+ * @mode: mode to remove
+ *
+ * Release @mode's unique ID, then free it @mode structure itself using kfree.
+ */
+void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
+{
+       if (!mode)
+               return;
+
+       drm_mode_object_put(dev, &mode->base);
+
+       kfree(mode);
+}
+EXPORT_SYMBOL(drm_mode_destroy);
+
+/**
+ * drm_mode_probed_add - add a mode to a connector's probed_mode list
+ * @connector: connector the new mode
+ * @mode: mode data
+ *
+ * Add @mode to @connector's probed_mode list for later use. This list should
+ * then in a second step get filtered and all the modes actually supported by
+ * the hardware moved to the @connector's modes list.
+ */
+void drm_mode_probed_add(struct drm_connector *connector,
+                        struct drm_display_mode *mode)
+{
+       WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex));
+
+       list_add_tail(&mode->head, &connector->probed_modes);
+}
+EXPORT_SYMBOL(drm_mode_probed_add);
+
+/**
+ * drm_cvt_mode -create a modeline based on the CVT algorithm
+ * @dev: drm device
+ * @hdisplay: hdisplay size
+ * @vdisplay: vdisplay size
+ * @vrefresh: vrefresh rate
+ * @reduced: whether to use reduced blanking
+ * @interlaced: whether to compute an interlaced mode
+ * @margins: whether to add margins (borders)
  *
  * This function is called to generate the modeline based on CVT algorithm
  * according to the hdisplay, vdisplay, vrefresh.
@@ -79,12 +137,17 @@ EXPORT_SYMBOL(drm_mode_debug_printmodeline);
  *
  * And it is copied from xf86CVTmode in xserver/hw/xfree86/modes/xf86cvt.c.
  * What I have done is to translate it by using integer calculation.
+ *
+ * Returns:
+ * The modeline based on the CVT algorithm stored in a drm_display_mode object.
+ * The display mode object is allocated with drm_mode_create(). Returns NULL
+ * when no mode could be allocated.
  */
-#define HV_FACTOR                      1000
 struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay,
                                      int vdisplay, int vrefresh,
                                      bool reduced, bool interlaced, bool margins)
 {
+#define HV_FACTOR                      1000
        /* 1) top/bottom margin size (% of height) - default: 1.8, */
 #define        CVT_MARGIN_PERCENTAGE           18
        /* 2) character cell horizontal granularity (pixels) - default 8 */
@@ -278,23 +341,25 @@ struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay,
 EXPORT_SYMBOL(drm_cvt_mode);
 
 /**
- * drm_gtf_mode_complex - create the modeline based on full GTF algorithm
- *
- * @dev                :drm device
- * @hdisplay   :hdisplay size
- * @vdisplay   :vdisplay size
- * @vrefresh   :vrefresh rate.
- * @interlaced :whether the interlace is supported
- * @margins    :desired margin size
- * @GTF_[MCKJ]  :extended GTF formula parameters
- *
- * LOCKING.
- * none.
- *
- * return the modeline based on full GTF algorithm.
+ * drm_gtf_mode_complex - create the modeline based on the full GTF algorithm
+ * @dev: drm device
+ * @hdisplay: hdisplay size
+ * @vdisplay: vdisplay size
+ * @vrefresh: vrefresh rate.
+ * @interlaced: whether to compute an interlaced mode
+ * @margins: desired margin (borders) size
+ * @GTF_M: extended GTF formula parameters
+ * @GTF_2C: extended GTF formula parameters
+ * @GTF_K: extended GTF formula parameters
+ * @GTF_2J: extended GTF formula parameters
  *
  * GTF feature blocks specify C and J in multiples of 0.5, so we pass them
  * in here multiplied by two.  For a C of 40, pass in 80.
+ *
+ * Returns:
+ * The modeline based on the full GTF algorithm stored in a drm_display_mode object.
+ * The display mode object is allocated with drm_mode_create(). Returns NULL
+ * when no mode could be allocated.
  */
 struct drm_display_mode *
 drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay,
@@ -464,17 +529,13 @@ drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay,
 EXPORT_SYMBOL(drm_gtf_mode_complex);
 
 /**
- * drm_gtf_mode - create the modeline based on GTF algorithm
- *
- * @dev                :drm device
- * @hdisplay   :hdisplay size
- * @vdisplay   :vdisplay size
- * @vrefresh   :vrefresh rate.
- * @interlaced :whether the interlace is supported
- * @margins    :whether the margin is supported
- *
- * LOCKING.
- * none.
+ * drm_gtf_mode - create the modeline based on the GTF algorithm
+ * @dev: drm device
+ * @hdisplay: hdisplay size
+ * @vdisplay: vdisplay size
+ * @vrefresh: vrefresh rate.
+ * @interlaced: whether to compute an interlaced mode
+ * @margins: desired margin (borders) size
  *
  * return the modeline based on GTF algorithm
  *
@@ -493,19 +554,32 @@ EXPORT_SYMBOL(drm_gtf_mode_complex);
  * C = 40
  * K = 128
  * J = 20
+ *
+ * Returns:
+ * The modeline based on the GTF algorithm stored in a drm_display_mode object.
+ * The display mode object is allocated with drm_mode_create(). Returns NULL
+ * when no mode could be allocated.
  */
 struct drm_display_mode *
 drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh,
-            bool lace, int margins)
+            bool interlaced, int margins)
 {
-       return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh, lace,
-                                   margins, 600, 40 * 2, 128, 20 * 2);
+       return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh,
+                                   interlaced, margins,
+                                   600, 40 * 2, 128, 20 * 2);
 }
 EXPORT_SYMBOL(drm_gtf_mode);
 
 #ifdef CONFIG_VIDEOMODE_HELPERS
-int drm_display_mode_from_videomode(const struct videomode *vm,
-                                   struct drm_display_mode *dmode)
+/**
+ * drm_display_mode_from_videomode - fill in @dmode using @vm,
+ * @vm: videomode structure to use as source
+ * @dmode: drm_display_mode structure to use as destination
+ *
+ * Fills out @dmode using the display mode specified in @vm.
+ */
+void drm_display_mode_from_videomode(const struct videomode *vm,
+                                    struct drm_display_mode *dmode)
 {
        dmode->hdisplay = vm->hactive;
        dmode->hsync_start = dmode->hdisplay + vm->hfront_porch;
@@ -535,8 +609,6 @@ int drm_display_mode_from_videomode(const struct videomode *vm,
        if (vm->flags & DISPLAY_FLAGS_DOUBLECLK)
                dmode->flags |= DRM_MODE_FLAG_DBLCLK;
        drm_mode_set_name(dmode);
-
-       return 0;
 }
 
 #ifdef CONFIG_OF
@@ -549,6 +621,9 @@ int drm_display_mode_from_videomode(const struct videomode *vm,
  * This function is expensive and should only be used, if only one mode is to be
  * read from DT. To get multiple modes start with of_get_display_timings and
  * work with that instead.
+ *
+ * Returns:
+ * 0 on success, a negative errno code when no of videomode node was found.
  */
 int of_get_drm_display_mode(struct device_node *np,
                            struct drm_display_mode *dmode, int index)
@@ -575,10 +650,8 @@ int of_get_drm_display_mode(struct device_node *np,
  * drm_mode_set_name - set the name on a mode
  * @mode: name will be set in this mode
  *
- * LOCKING:
- * None.
- *
- * Set the name of @mode to a standard format.
+ * Set the name of @mode to a standard format which is <hdisplay>x<vdisplay>
+ * with an optional 'i' suffix for interlaced modes.
  */
 void drm_mode_set_name(struct drm_display_mode *mode)
 {
@@ -590,54 +663,12 @@ void drm_mode_set_name(struct drm_display_mode *mode)
 }
 EXPORT_SYMBOL(drm_mode_set_name);
 
-/**
- * drm_mode_width - get the width of a mode
- * @mode: mode
- *
- * LOCKING:
- * None.
- *
- * Return @mode's width (hdisplay) value.
- *
- * FIXME: is this needed?
- *
- * RETURNS:
- * @mode->hdisplay
- */
-int drm_mode_width(const struct drm_display_mode *mode)
-{
-       return mode->hdisplay;
-
-}
-EXPORT_SYMBOL(drm_mode_width);
-
-/**
- * drm_mode_height - get the height of a mode
- * @mode: mode
- *
- * LOCKING:
- * None.
- *
- * Return @mode's height (vdisplay) value.
- *
- * FIXME: is this needed?
- *
- * RETURNS:
- * @mode->vdisplay
- */
-int drm_mode_height(const struct drm_display_mode *mode)
-{
-       return mode->vdisplay;
-}
-EXPORT_SYMBOL(drm_mode_height);
-
 /** drm_mode_hsync - get the hsync of a mode
  * @mode: mode
  *
- * LOCKING:
- * None.
- *
- * Return @modes's hsync rate in kHz, rounded to the nearest int.
+ * Returns:
+ * @modes's hsync rate in kHz, rounded to the nearest integer. Calculates the
+ * value first if it is not yet set.
  */
 int drm_mode_hsync(const struct drm_display_mode *mode)
 {
@@ -661,17 +692,9 @@ EXPORT_SYMBOL(drm_mode_hsync);
  * drm_mode_vrefresh - get the vrefresh of a mode
  * @mode: mode
  *
- * LOCKING:
- * None.
- *
- * Return @mode's vrefresh rate in Hz or calculate it if necessary.
- *
- * FIXME: why is this needed?  shouldn't vrefresh be set already?
- *
- * RETURNS:
- * Vertical refresh rate. It will be the result of actual value plus 0.5.
- * If it is 70.288, it will return 70Hz.
- * If it is 59.6, it will return 60Hz.
+ * Returns:
+ * @modes's vrefresh rate in Hz, rounded to the nearest integer. Calculates the
+ * value first if it is not yet set.
  */
 int drm_mode_vrefresh(const struct drm_display_mode *mode)
 {
@@ -700,14 +723,11 @@ int drm_mode_vrefresh(const struct drm_display_mode *mode)
 EXPORT_SYMBOL(drm_mode_vrefresh);
 
 /**
- * drm_mode_set_crtcinfo - set CRTC modesetting parameters
+ * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters
  * @p: mode
  * @adjust_flags: a combination of adjustment flags
  *
- * LOCKING:
- * None.
- *
- * Setup the CRTC modesetting parameters for @p, adjusting if necessary.
+ * Setup the CRTC modesetting timing parameters for @p, adjusting if necessary.
  *
  * - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of
  *   interlaced modes.
@@ -775,15 +795,11 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
 }
 EXPORT_SYMBOL(drm_mode_set_crtcinfo);
 
-
 /**
  * drm_mode_copy - copy the mode
  * @dst: mode to overwrite
  * @src: mode to copy
  *
- * LOCKING:
- * None.
- *
  * Copy an existing mode into another mode, preserving the object id and
  * list head of the destination mode.
  */
@@ -800,13 +816,14 @@ EXPORT_SYMBOL(drm_mode_copy);
 
 /**
  * drm_mode_duplicate - allocate and duplicate an existing mode
- * @m: mode to duplicate
- *
- * LOCKING:
- * None.
+ * @dev: drm_device to allocate the duplicated mode for
+ * @mode: mode to duplicate
  *
  * Just allocate a new mode, copy the existing mode into it, and return
  * a pointer to it.  Used to create new instances of established modes.
+ *
+ * Returns:
+ * Pointer to duplicated mode on success, NULL on error.
  */
 struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
                                            const struct drm_display_mode *mode)
@@ -828,12 +845,9 @@ EXPORT_SYMBOL(drm_mode_duplicate);
  * @mode1: first mode
  * @mode2: second mode
  *
- * LOCKING:
- * None.
- *
  * Check to see if @mode1 and @mode2 are equivalent.
  *
- * RETURNS:
+ * Returns:
  * True if the modes are equal, false otherwise.
  */
 bool drm_mode_equal(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2)
@@ -859,13 +873,10 @@ EXPORT_SYMBOL(drm_mode_equal);
  * @mode1: first mode
  * @mode2: second mode
  *
- * LOCKING:
- * None.
- *
  * Check to see if @mode1 and @mode2 are equivalent, but
  * don't check the pixel clocks nor the stereo layout.
  *
- * RETURNS:
+ * Returns:
  * True if the modes are equal, false otherwise.
  */
 bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1,
@@ -895,25 +906,19 @@ EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo);
  * @mode_list: list of modes to check
  * @maxX: maximum width
  * @maxY: maximum height
- * @maxPitch: max pitch
  *
- * LOCKING:
- * Caller must hold a lock protecting @mode_list.
- *
- * The DRM device (@dev) has size and pitch limits.  Here we validate the
- * modes we probed for @dev against those limits and set their status as
- * necessary.
+ * This function is a helper which can be used to validate modes against size
+ * limitations of the DRM device/connector. If a mode is too big its status
+ * memeber is updated with the appropriate validation failure code. The list
+ * itself is not changed.
  */
 void drm_mode_validate_size(struct drm_device *dev,
                            struct list_head *mode_list,
-                           int maxX, int maxY, int maxPitch)
+                           int maxX, int maxY)
 {
        struct drm_display_mode *mode;
 
        list_for_each_entry(mode, mode_list, head) {
-               if (maxPitch > 0 && mode->hdisplay > maxPitch)
-                       mode->status = MODE_BAD_WIDTH;
-
                if (maxX > 0 && mode->hdisplay > maxX)
                        mode->status = MODE_VIRTUAL_X;
 
@@ -929,12 +934,10 @@ EXPORT_SYMBOL(drm_mode_validate_size);
  * @mode_list: list of modes to check
  * @verbose: be verbose about it
  *
- * LOCKING:
- * Caller must hold a lock protecting @mode_list.
- *
- * Once mode list generation is complete, a caller can use this routine to
- * remove invalid modes from a mode list.  If any of the modes have a
- * status other than %MODE_OK, they are removed from @mode_list and freed.
+ * This helper function can be used to prune a display mode list after
+ * validation has been completed. All modes who's status is not MODE_OK will be
+ * removed from the list, and if @verbose the status code and mode name is also
+ * printed to dmesg.
  */
 void drm_mode_prune_invalid(struct drm_device *dev,
                            struct list_head *mode_list, bool verbose)
@@ -961,13 +964,10 @@ EXPORT_SYMBOL(drm_mode_prune_invalid);
  * @lh_a: list_head for first mode
  * @lh_b: list_head for second mode
  *
- * LOCKING:
- * None.
- *
  * Compare two modes, given by @lh_a and @lh_b, returning a value indicating
  * which is better.
  *
- * RETURNS:
+ * Returns:
  * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or
  * positive if @lh_b is better than @lh_a.
  */
@@ -995,12 +995,9 @@ static int drm_mode_compare(void *priv, struct list_head *lh_a, struct list_head
 
 /**
  * drm_mode_sort - sort mode list
- * @mode_list: list to sort
+ * @mode_list: list of drm_display_mode structures to sort
  *
- * LOCKING:
- * Caller must hold a lock protecting @mode_list.
- *
- * Sort @mode_list by favorability, putting good modes first.
+ * Sort @mode_list by favorability, moving good modes to the head of the list.
  */
 void drm_mode_sort(struct list_head *mode_list)
 {
@@ -1011,21 +1008,24 @@ EXPORT_SYMBOL(drm_mode_sort);
 /**
  * drm_mode_connector_list_update - update the mode list for the connector
  * @connector: the connector to update
- *
- * LOCKING:
- * Caller must hold a lock protecting @mode_list.
+ * @merge_type_bits: whether to merge or overright type bits.
  *
  * This moves the modes from the @connector probed_modes list
  * to the actual mode list. It compares the probed mode against the current
- * list and only adds different modes. All modes unverified after this point
- * will be removed by the prune invalid modes.
+ * list and only adds different/new modes.
+ *
+ * This is just a helper functions doesn't validate any modes itself and also
+ * doesn't prune any invalid modes. Callers need to do that themselves.
  */
-void drm_mode_connector_list_update(struct drm_connector *connector)
+void drm_mode_connector_list_update(struct drm_connector *connector,
+                                   bool merge_type_bits)
 {
        struct drm_display_mode *mode;
        struct drm_display_mode *pmode, *pt;
        int found_it;
 
+       WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex));
+
        list_for_each_entry_safe(pmode, pt, &connector->probed_modes,
                                 head) {
                found_it = 0;
@@ -1036,7 +1036,10 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
                                /* if equal delete the probed mode */
                                mode->status = pmode->status;
                                /* Merge type bits together */
-                               mode->type |= pmode->type;
+                               if (merge_type_bits)
+                                       mode->type |= pmode->type;
+                               else
+                                       mode->type = pmode->type;
                                list_del(&pmode->head);
                                drm_mode_destroy(connector->dev, pmode);
                                break;
@@ -1051,17 +1054,25 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
 EXPORT_SYMBOL(drm_mode_connector_list_update);
 
 /**
- * drm_mode_parse_command_line_for_connector - parse command line for connector
- * @mode_option - per connector mode option
- * @connector - connector to parse line for
+ * drm_mode_parse_command_line_for_connector - parse command line modeline for connector
+ * @mode_option: optional per connector mode option
+ * @connector: connector to parse modeline for
+ * @mode: preallocated drm_cmdline_mode structure to fill out
+ *
+ * This parses @mode_option command line modeline for modes and options to
+ * configure the connector. If @mode_option is NULL the default command line
+ * modeline in fb_mode_option will be parsed instead.
  *
- * This parses the connector specific then generic command lines for
- * modes and options to configure the connector.
+ * This uses the same parameters as the fb modedb.c, except for an extra
+ * force-enable, force-enable-digital and force-disable bit at the end:
  *
- * This uses the same parameters as the fb modedb.c, except for extra
  *     <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
  *
- * enable/enable Digital/disable bit at the end
+ * The intermediate drm_cmdline_mode structure is required to store additional
+ * options from the command line modline like the force-enabel/disable flag.
+ *
+ * Returns:
+ * True if a valid modeline has been parsed, false otherwise.
  */
 bool drm_mode_parse_command_line_for_connector(const char *mode_option,
                                               struct drm_connector *connector,
@@ -1214,6 +1225,14 @@ done:
 }
 EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector);
 
+/**
+ * drm_mode_create_from_cmdline_mode - convert a command line modeline into a DRM display mode
+ * @dev: DRM device to create the new mode for
+ * @cmd: input command line modeline
+ *
+ * Returns:
+ * Pointer to converted mode on success, NULL on error.
+ */
 struct drm_display_mode *
 drm_mode_create_from_cmdline_mode(struct drm_device *dev,
                                  struct drm_cmdline_mode *cmd)
diff --git a/sys/dev/drm/drm_modeset_lock.c b/sys/dev/drm/drm_modeset_lock.c
new file mode 100644 (file)
index 0000000..0dc57d5
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_modeset_lock.h>
+
+/**
+ * DOC: kms locking
+ *
+ * As KMS moves toward more fine grained locking, and atomic ioctl where
+ * userspace can indirectly control locking order, it becomes necessary
+ * to use ww_mutex and acquire-contexts to avoid deadlocks.  But because
+ * the locking is more distributed around the driver code, we want a bit
+ * of extra utility/tracking out of our acquire-ctx.  This is provided
+ * by drm_modeset_lock / drm_modeset_acquire_ctx.
+ *
+ * For basic principles of ww_mutex, see: Documentation/ww-mutex-design.txt
+ *
+ * The basic usage pattern is to:
+ *
+ *     drm_modeset_acquire_init(&ctx)
+ *   retry:
+ *     foreach (lock in random_ordered_set_of_locks) {
+ *       ret = drm_modeset_lock(lock, &ctx)
+ *       if (ret == -EDEADLK) {
+ *          drm_modeset_backoff(&ctx);
+ *          goto retry;
+ *       }
+ *     }
+ *
+ *     ... do stuff ...
+ *
+ *     drm_modeset_drop_locks(&ctx);
+ *     drm_modeset_acquire_fini(&ctx);
+ */
+
+
+/**
+ * drm_modeset_acquire_init - initialize acquire context
+ * @ctx: the acquire context
+ * @flags: for future
+ */
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
+               uint32_t flags)
+{
+       memset(ctx, 0, sizeof(*ctx));
+       ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
+       INIT_LIST_HEAD(&ctx->locked);
+}
+EXPORT_SYMBOL(drm_modeset_acquire_init);
+
+/**
+ * drm_modeset_acquire_fini - cleanup acquire context
+ * @ctx: the acquire context
+ */
+void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
+{
+       ww_acquire_fini(&ctx->ww_ctx);
+}
+EXPORT_SYMBOL(drm_modeset_acquire_fini);
+
+/**
+ * drm_modeset_drop_locks - drop all locks
+ * @ctx: the acquire context
+ *
+ * Drop all locks currently held against this acquire context.
+ */
+void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
+{
+       WARN_ON(ctx->contended);
+       while (!list_empty(&ctx->locked)) {
+               struct drm_modeset_lock *lock;
+
+               lock = list_first_entry(&ctx->locked,
+                               struct drm_modeset_lock, head);
+
+               drm_modeset_unlock(lock);
+       }
+}
+EXPORT_SYMBOL(drm_modeset_drop_locks);
+
+static inline int modeset_lock(struct drm_modeset_lock *lock,
+               struct drm_modeset_acquire_ctx *ctx,
+               bool interruptible, bool slow)
+{
+       int ret;
+
+       WARN_ON(ctx->contended);
+
+       if (interruptible && slow) {
+               ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
+       } else if (interruptible) {
+               ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
+       } else if (slow) {
+               ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
+               ret = 0;
+       } else {
+               ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
+       }
+       if (!ret) {
+               WARN_ON(!list_empty(&lock->head));
+               list_add(&lock->head, &ctx->locked);
+       } else if (ret == -EALREADY) {
+               /* we already hold the lock.. this is fine.  For atomic
+                * we will need to be able to drm_modeset_lock() things
+                * without having to keep track of what is already locked
+                * or not.
+                */
+               ret = 0;
+       } else if (ret == -EDEADLK) {
+               ctx->contended = lock;
+       }
+
+       return ret;
+}
+
+static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
+               bool interruptible)
+{
+       struct drm_modeset_lock *contended = ctx->contended;
+
+       ctx->contended = NULL;
+
+       if (WARN_ON(!contended))
+               return 0;
+
+       drm_modeset_drop_locks(ctx);
+
+       return modeset_lock(contended, ctx, interruptible, true);
+}
+
+/**
+ * drm_modeset_backoff - deadlock avoidance backoff
+ * @ctx: the acquire context
+ *
+ * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
+ * you must call this function to drop all currently held locks and
+ * block until the contended lock becomes available.
+ */
+void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
+{
+       modeset_backoff(ctx, false);
+}
+EXPORT_SYMBOL(drm_modeset_backoff);
+
+/**
+ * drm_modeset_backoff_interruptible - deadlock avoidance backoff
+ * @ctx: the acquire context
+ *
+ * Interruptible version of drm_modeset_backoff()
+ */
+int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
+{
+       return modeset_backoff(ctx, true);
+}
+EXPORT_SYMBOL(drm_modeset_backoff_interruptible);
+
+/**
+ * drm_modeset_lock - take modeset lock
+ * @lock: lock to take
+ * @ctx: acquire ctx
+ *
+ * If ctx is not NULL, then its ww acquire context is used and the
+ * lock will be tracked by the context and can be released by calling
+ * drm_modeset_drop_locks().  If -EDEADLK is returned, this means a
+ * deadlock scenario has been detected and it is an error to attempt
+ * to take any more locks without first calling drm_modeset_backoff().
+ */
+int drm_modeset_lock(struct drm_modeset_lock *lock,
+               struct drm_modeset_acquire_ctx *ctx)
+{
+       if (ctx)
+               return modeset_lock(lock, ctx, false, false);
+
+       ww_mutex_lock(&lock->mutex, NULL);
+       return 0;
+}
+EXPORT_SYMBOL(drm_modeset_lock);
+
+/**
+ * drm_modeset_lock_interruptible - take modeset lock
+ * @lock: lock to take
+ * @ctx: acquire ctx
+ *
+ * Interruptible version of drm_modeset_lock()
+ */
+int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
+               struct drm_modeset_acquire_ctx *ctx)
+{
+       if (ctx)
+               return modeset_lock(lock, ctx, true, false);
+
+       return ww_mutex_lock_interruptible(&lock->mutex, NULL);
+}
+EXPORT_SYMBOL(drm_modeset_lock_interruptible);
+
+/**
+ * drm_modeset_unlock - drop modeset lock
+ * @lock: lock to release
+ */
+void drm_modeset_unlock(struct drm_modeset_lock *lock)
+{
+       list_del_init(&lock->head);
+       ww_mutex_unlock(&lock->mutex);
+}
+EXPORT_SYMBOL(drm_modeset_unlock);
+
+/* Temporary.. until we have sufficiently fine grained locking, there
+ * are a couple scenarios where it is convenient to grab all crtc locks.
+ * It is planned to remove this:
+ */
+int drm_modeset_lock_all_crtcs(struct drm_device *dev,
+               struct drm_modeset_acquire_ctx *ctx)
+{
+       struct drm_mode_config *config = &dev->mode_config;
+       struct drm_crtc *crtc;
+       int ret = 0;
+
+       list_for_each_entry(crtc, &config->crtc_list, head) {
+               ret = drm_modeset_lock(&crtc->mutex, ctx);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);
index 643c100..97b5698 100644 (file)
@@ -175,3 +175,35 @@ int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask)
        DRM_INFO("probing gen 2 caps for device %x:%x = %x/%x\n", pci_get_vendor(root), pci_get_device(root), lnkcap, lnkcap2);
        return 0;
 }
+
+/**
+ * Get interrupt from bus id.
+ *
+ * \param inode device inode.
+ * \param file_priv DRM file private.
+ * \param cmd command.
+ * \param arg user argument, pointing to a drm_irq_busid structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Finds the PCI device with the specified bus id and gets its IRQ number.
+ * This IOCTL is deprecated, and will now return EINVAL for any busid not equal
+ * to that of the device that this DRM instance attached to.
+ */
+int drm_irq_by_busid(struct drm_device *dev, void *data,
+                    struct drm_file *file_priv)
+{
+       struct drm_irq_busid *irq = data;
+
+       if ((irq->busnum >> 8) != dev->pci_domain ||
+           (irq->busnum & 0xff) != dev->pci_bus ||
+           irq->devnum != dev->pci_slot ||
+           irq->funcnum != dev->pci_func)
+               return EINVAL;
+
+       irq->irq = dev->irq;
+
+       DRM_DEBUG("%d:%d:%d => IRQ %d\n",
+           irq->busnum, irq->devnum, irq->funcnum, irq->irq);
+
+       return 0;
+}
diff --git a/sys/dev/drm/drm_plane_helper.c b/sys/dev/drm/drm_plane_helper.c
new file mode 100644 (file)
index 0000000..a4c0024
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * DRM universal plane helper functions
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ *&nb