drm/i915: Update to Linux 3.17
authorFrançois Tigeot <ftigeot@wolfpond.org>
Thu, 27 Aug 2015 18:53:22 +0000 (20:53 +0200)
committerFrançois Tigeot <ftigeot@wolfpond.org>
Thu, 27 Aug 2015 18:54:08 +0000 (20:54 +0200)
* With an Haswell stability fix from Linux 3.19

* Display-Port code not completely synced with Linux 3.17, this was
  breaking things as noticed by Rimvydas Jasinskas

* Broadwell and Cherryview support improvements

* Runtime power-management improvements

* Panel Self-Refresh (PSR) now enabled by default on Haswell and Broadwell,
  leading to further power savings

* Interrupt handling improvements

* backlight brightness locking fixes by Yellow Rabbit

* As usual, all sort of little fixes everywhere

80 files changed:
sys/dev/drm/drm/Makefile
sys/dev/drm/drm_buffer.c
sys/dev/drm/drm_bufs.c
sys/dev/drm/drm_context.c
sys/dev/drm/drm_crtc.c
sys/dev/drm/drm_crtc_helper.c
sys/dev/drm/drm_dp_helper.c
sys/dev/drm/drm_dp_mst_topology.c [new file with mode: 0644]
sys/dev/drm/drm_drv.c
sys/dev/drm/drm_edid.c
sys/dev/drm/drm_fb_cma_helper.c [new file with mode: 0644]
sys/dev/drm/drm_fb_helper.c
sys/dev/drm/drm_fops.c
sys/dev/drm/drm_ioctl.c
sys/dev/drm/drm_legacy.h [new file with mode: 0644]
sys/dev/drm/drm_lock.c
sys/dev/drm/drm_plane_helper.c
sys/dev/drm/drm_probe_helper.c
sys/dev/drm/drm_stub.c [deleted file]
sys/dev/drm/drm_sysctl.c
sys/dev/drm/i915/i915_cmd_parser.c
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_execbuffer.c
sys/dev/drm/i915/i915_gem_gtt.c
sys/dev/drm/i915/i915_gem_gtt.h
sys/dev/drm/i915/i915_gem_render_state.c
sys/dev/drm/i915/i915_gem_stolen.c
sys/dev/drm/i915/i915_gem_userptr.c
sys/dev/drm/i915/i915_irq.c
sys/dev/drm/i915/i915_params.c
sys/dev/drm/i915/i915_reg.h
sys/dev/drm/i915/intel_bios.c
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_dp_mst.c [new file with mode: 0644]
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
sys/dev/drm/i915/intel_dsi_pll.c
sys/dev/drm/i915/intel_dvo.c
sys/dev/drm/i915/intel_fbdev.c
sys/dev/drm/i915/intel_hdmi.c
sys/dev/drm/i915/intel_i2c.c
sys/dev/drm/i915/intel_lvds.c
sys/dev/drm/i915/intel_modes.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
sys/dev/drm/i915/intel_renderstate_gen6.c
sys/dev/drm/i915/intel_renderstate_gen7.c
sys/dev/drm/i915/intel_renderstate_gen8.c
sys/dev/drm/i915/intel_ringbuffer.c
sys/dev/drm/i915/intel_ringbuffer.h
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_dp_mst_helper.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_fb_cma_helper.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_fb_helper.h
sys/dev/drm/include/drm/drm_gem_cma_helper.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_hashtab.h
sys/dev/drm/include/drm/drm_plane_helper.h
sys/dev/drm/include/drm/drm_rect.h
sys/dev/drm/include/linux/backlight.h
sys/dev/drm/include/linux/fb.h
sys/dev/drm/include/linux/kernel.h
sys/dev/drm/include/uapi_drm/drm_mode.h

index 5b1aa87..be67d3f 100644 (file)
@@ -17,6 +17,7 @@ SRCS  = \
        drm_drv.c \
        drm_edid.c \
        drm_fb_helper.c \
+       drm_fb_cma_helper.c \
        drm_fops.c \
        drm_gem.c \
        drm_global.c \
@@ -33,7 +34,6 @@ SRCS  = \
        drm_probe_helper.c \
        drm_rect.c \
        drm_scatter.c \
-       drm_stub.c \
        drm_sysctl.c \
        drm_sysfs.c \
        drm_vm.c \
index 4036c71..6b7d89c 100644 (file)
@@ -80,11 +80,7 @@ int drm_buffer_alloc(struct drm_buffer **buf, int size)
 
 error_out:
 
-       /* Only last element can be null pointer so check for it first. */
-       if ((*buf)->data[idx])
-               kfree((*buf)->data[idx]);
-
-       for (--idx; idx >= 0; --idx)
+       for (; idx >= 0; --idx)
                kfree((*buf)->data[idx]);
 
        kfree(*buf);
index a2ab8c3..02b2b35 100644 (file)
@@ -989,22 +989,34 @@ int drm_addbufs(struct drm_device *dev, void *data,
  * \param arg pointer to a drm_buf_info structure.
  * \return zero on success or a negative number on failure.
  *
- * Increments drm_device::buf_use while holding the drm_device::count_lock
+ * Increments drm_device::buf_use while holding the drm_device::buf_lock
  * lock, preventing of allocating more buffers after this call. Information
  * about each requested buffer is then copied into user space.
  */
 int drm_infobufs(struct drm_device *dev, void *data,
                 struct drm_file *file_priv)
 {
-       drm_device_dma_t *dma = dev->dma;
+       struct drm_device_dma *dma = dev->dma;
        struct drm_buf_info *request = data;
        int i;
        int count;
-       int retcode = 0;
 
-       spin_lock(&dev->dma_lock);
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
+               return -EINVAL;
+
+       if (!dma)
+               return -EINVAL;
+
+       spin_lock(&dev->buf_lock);
+       if (atomic_read(&dev->buf_alloc)) {
+               spin_unlock(&dev->buf_lock);
+               return -EBUSY;
+       }
        ++dev->buf_use;         /* Can't allocate more after this call */
-       spin_unlock(&dev->dma_lock);
+       spin_unlock(&dev->buf_lock);
 
        for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
                if (dma->bufs[i].buf_count)
@@ -1016,31 +1028,36 @@ int drm_infobufs(struct drm_device *dev, void *data,
        if (request->count >= count) {
                for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
                        if (dma->bufs[i].buf_count) {
-                               struct drm_buf_desc from;
-
-                               from.count = dma->bufs[i].buf_count;
-                               from.size = dma->bufs[i].buf_size;
-                               from.low_mark = dma->bufs[i].freelist.low_mark;
-                               from.high_mark = dma->bufs[i].freelist.high_mark;
-
-                               if (copy_to_user(&request->list[count], &from,
-                                   sizeof(struct drm_buf_desc)) != 0) {
-                                       retcode = EFAULT;
-                                       break;
-                               }
+                               struct drm_buf_desc __user *to =
+                                   &request->list[count];
+                               struct drm_buf_entry *from = &dma->bufs[i];
+                               if (copy_to_user(&to->count,
+                                                &from->buf_count,
+                                                sizeof(from->buf_count)) ||
+                                   copy_to_user(&to->size,
+                                                &from->buf_size,
+                                                sizeof(from->buf_size)) ||
+                                   copy_to_user(&to->low_mark,
+                                                &from->low_mark,
+                                                sizeof(from->low_mark)) ||
+                                   copy_to_user(&to->high_mark,
+                                                &from->high_mark,
+                                                sizeof(from->high_mark)))
+                                       return -EFAULT;
 
                                DRM_DEBUG("%d %d %d %d %d\n",
-                                   i, dma->bufs[i].buf_count,
-                                   dma->bufs[i].buf_size,
-                                   dma->bufs[i].freelist.low_mark,
-                                   dma->bufs[i].freelist.high_mark);
+                                         i,
+                                         dma->bufs[i].buf_count,
+                                         dma->bufs[i].buf_size,
+                                         dma->bufs[i].low_mark,
+                                         dma->bufs[i].high_mark);
                                ++count;
                        }
                }
        }
        request->count = count;
 
-       return retcode;
+       return 0;
 }
 
 /**
@@ -1060,28 +1077,34 @@ int drm_infobufs(struct drm_device *dev, void *data,
 int drm_markbufs(struct drm_device *dev, void *data,
                 struct drm_file *file_priv)
 {
-       drm_device_dma_t *dma = dev->dma;
+       struct drm_device_dma *dma = dev->dma;
        struct drm_buf_desc *request = data;
        int order;
+       struct drm_buf_entry *entry;
+
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
+               return -EINVAL;
+
+       if (!dma)
+               return -EINVAL;
 
        DRM_DEBUG("%d, %d, %d\n",
                  request->size, request->low_mark, request->high_mark);
        order = order_base_2(request->size);
-       if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ||
-           request->low_mark < 0 || request->high_mark < 0) {
-               return EINVAL;
-       }
+       if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
+               return -EINVAL;
+       entry = &dma->bufs[order];
 
-       spin_lock(&dev->dma_lock);
-       if (request->low_mark > dma->bufs[order].buf_count ||
-           request->high_mark > dma->bufs[order].buf_count) {
-               spin_unlock(&dev->dma_lock);
-               return EINVAL;
-       }
+       if (request->low_mark < 0 || request->low_mark > entry->buf_count)
+               return -EINVAL;
+       if (request->high_mark < 0 || request->high_mark > entry->buf_count)
+               return -EINVAL;
 
-       dma->bufs[order].freelist.low_mark  = request->low_mark;
-       dma->bufs[order].freelist.high_mark = request->high_mark;
-       spin_unlock(&dev->dma_lock);
+       entry->low_mark = request->low_mark;
+       entry->high_mark = request->high_mark;
 
        return 0;
 }
index 3519b1f..6b1262c 100644 (file)
@@ -1,18 +1,13 @@
-/**
- * \file drm_context.c
- * IOCTLs for generic contexts
- *
- * \author Rickard E. (Rik) Faith <faith@valinux.com>
- * \author Gareth Hughes <gareth@valinux.com>
- */
-
 /*
- * Created: Fri Nov 24 18:31:37 2000 by gareth@valinux.com
+ * Legacy: Generic DRM Contexts
  *
  * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
  * All Rights Reserved.
  *
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Author: Gareth Hughes <gareth@valinux.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
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
-/*
- * ChangeLog:
- *  2001-11-16 Torsten Duwe <duwe@caldera.de>
- *             added context constructor/destructor hooks,
- *             needed by SiS driver's memory management.
- */
-
 #include <drm/drmP.h>
+#include "drm_legacy.h"
+
+struct drm_ctx_list {
+       struct list_head head;
+       drm_context_t handle;
+       struct drm_file *tag;
+};
 
 /******************************************************************/
 /** \name Context bitmap support */
@@ -56,7 +51,7 @@
  * in drm_device::ctx_idr, while holding the drm_device::struct_mutex
  * lock.
  */
-void drm_ctxbitmap_free(struct drm_device * dev, int ctx_handle)
+void drm_legacy_ctxbitmap_free(struct drm_device * dev, int ctx_handle)
 {
        mutex_lock(&dev->struct_mutex);
        idr_remove(&dev->ctx_idr, ctx_handle);
@@ -72,7 +67,7 @@ void drm_ctxbitmap_free(struct drm_device * dev, int ctx_handle)
  * Allocate a new idr from drm_device::ctx_idr while holding the
  * drm_device::struct_mutex lock.
  */
-static int drm_ctxbitmap_next(struct drm_device * dev)
+static int drm_legacy_ctxbitmap_next(struct drm_device * dev)
 {
        int ret;
 
@@ -90,7 +85,7 @@ static int drm_ctxbitmap_next(struct drm_device * dev)
  *
  * Initialise the drm_device::ctx_idr
  */
-int drm_ctxbitmap_init(struct drm_device * dev)
+int drm_legacy_ctxbitmap_init(struct drm_device * dev)
 {
        idr_init(&dev->ctx_idr);
        return 0;
@@ -104,13 +99,43 @@ int drm_ctxbitmap_init(struct drm_device * dev)
  * Free all idr members using drm_ctx_sarea_free helper function
  * while holding the drm_device::struct_mutex lock.
  */
-void drm_ctxbitmap_cleanup(struct drm_device * dev)
+void drm_legacy_ctxbitmap_cleanup(struct drm_device * dev)
 {
        mutex_lock(&dev->struct_mutex);
        idr_destroy(&dev->ctx_idr);
        mutex_unlock(&dev->struct_mutex);
 }
 
+/**
+ * drm_ctxbitmap_flush() - Flush all contexts owned by a file
+ * @dev: DRM device to operate on
+ * @file: Open file to flush contexts for
+ *
+ * This iterates over all contexts on @dev and drops them if they're owned by
+ * @file. Note that after this call returns, new contexts might be added if
+ * the file is still alive.
+ */
+void drm_legacy_ctxbitmap_flush(struct drm_device *dev, struct drm_file *file)
+{
+       struct drm_ctx_list *pos, *tmp;
+
+       mutex_lock(&dev->ctxlist_mutex);
+
+       list_for_each_entry_safe(pos, tmp, &dev->ctxlist, head) {
+               if (pos->tag == file &&
+                   pos->handle != DRM_KERNEL_CONTEXT) {
+                       if (dev->driver->context_dtor)
+                               dev->driver->context_dtor(dev, pos->handle);
+
+                       drm_legacy_ctxbitmap_free(dev, pos->handle);
+                       list_del(&pos->head);
+                       kfree(pos);
+               }
+       }
+
+       mutex_unlock(&dev->ctxlist_mutex);
+}
+
 /*@}*/
 
 /******************************************************************/
@@ -129,8 +154,8 @@ void drm_ctxbitmap_cleanup(struct drm_device * dev)
  * Gets the map from drm_device::ctx_idr with the handle specified and
  * returns its handle.
  */
-int drm_getsareactx(struct drm_device *dev, void *data,
-                   struct drm_file *file_priv)
+int drm_legacy_getsareactx(struct drm_device *dev, void *data,
+                          struct drm_file *file_priv)
 {
        struct drm_ctx_priv_map *request = data;
        struct drm_local_map *map;
@@ -173,8 +198,8 @@ int drm_getsareactx(struct drm_device *dev, void *data,
  * Searches the mapping specified in \p arg and update the entry in
  * drm_device::ctx_idr with it.
  */
-int drm_setsareactx(struct drm_device *dev, void *data,
-                   struct drm_file *file_priv)
+int drm_legacy_setsareactx(struct drm_device *dev, void *data,
+                          struct drm_file *file_priv)
 {
        struct drm_ctx_priv_map *request = data;
        struct drm_local_map *map = NULL;
@@ -273,8 +298,8 @@ static int drm_context_switch_complete(struct drm_device *dev,
  * \param arg user argument pointing to a drm_ctx_res structure.
  * \return zero on success or a negative number on failure.
  */
-int drm_resctx(struct drm_device *dev, void *data,
-              struct drm_file *file_priv)
+int drm_legacy_resctx(struct drm_device *dev, void *data,
+                     struct drm_file *file_priv)
 {
        struct drm_ctx_res *res = data;
        struct drm_ctx ctx;
@@ -304,16 +329,16 @@ int drm_resctx(struct drm_device *dev, void *data,
  *
  * Get a new handle for the context and copy to userspace.
  */
-int drm_addctx(struct drm_device *dev, void *data,
-              struct drm_file *file_priv)
+int drm_legacy_addctx(struct drm_device *dev, void *data,
+                     struct drm_file *file_priv)
 {
        struct drm_ctx_list *ctx_entry;
        struct drm_ctx *ctx = data;
 
-       ctx->handle = drm_ctxbitmap_next(dev);
+       ctx->handle = drm_legacy_ctxbitmap_next(dev);
        if (ctx->handle == DRM_KERNEL_CONTEXT) {
                /* Skip kernel's context and get a new one. */
-               ctx->handle = drm_ctxbitmap_next(dev);
+               ctx->handle = drm_legacy_ctxbitmap_next(dev);
        }
        DRM_DEBUG("%d\n", ctx->handle);
        if (ctx->handle == -1) {
@@ -349,7 +374,8 @@ int drm_addctx(struct drm_device *dev, void *data,
  * \param arg user argument pointing to a drm_ctx structure.
  * \return zero on success or a negative number on failure.
  */
-int drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv)
+int drm_legacy_getctx(struct drm_device *dev, void *data,
+                     struct drm_file *file_priv)
 {
        struct drm_ctx *ctx = data;
 
@@ -370,8 +396,8 @@ int drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv)
  *
  * Calls context_switch().
  */
-int drm_switchctx(struct drm_device *dev, void *data,
-                 struct drm_file *file_priv)
+int drm_legacy_switchctx(struct drm_device *dev, void *data,
+                        struct drm_file *file_priv)
 {
        struct drm_ctx *ctx = data;
 
@@ -390,8 +416,8 @@ int drm_switchctx(struct drm_device *dev, void *data,
  *
  * Calls context_switch_complete().
  */
-int drm_newctx(struct drm_device *dev, void *data,
-              struct drm_file *file_priv)
+int drm_legacy_newctx(struct drm_device *dev, void *data,
+                     struct drm_file *file_priv)
 {
        struct drm_ctx *ctx = data;
 
@@ -412,8 +438,8 @@ int drm_newctx(struct drm_device *dev, void *data,
  *
  * If not the special kernel context, calls ctxbitmap_free() to free the specified context.
  */
-int drm_rmctx(struct drm_device *dev, void *data,
-             struct drm_file *file_priv)
+int drm_legacy_rmctx(struct drm_device *dev, void *data,
+                    struct drm_file *file_priv)
 {
        struct drm_ctx *ctx = data;
 
@@ -421,7 +447,7 @@ int drm_rmctx(struct drm_device *dev, void *data,
        if (ctx->handle != DRM_KERNEL_CONTEXT) {
                if (dev->driver->context_dtor)
                        dev->driver->context_dtor(dev, ctx->handle);
-               drm_ctxbitmap_free(dev, ctx->handle);
+               drm_legacy_ctxbitmap_free(dev, ctx->handle);
        }
 
        mutex_lock(&dev->ctxlist_mutex);
index fb73434..983b620 100644 (file)
 
 #include "drm_crtc_internal.h"
 
+static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev,
+                                                       struct drm_mode_fb_cmd2 *r,
+                                                       struct drm_file *file_priv);
+
 /**
  * drm_modeset_lock_all - take all modeset locks
  * @dev: drm device
@@ -180,6 +184,12 @@ static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] =
        { DRM_MODE_SCALE_ASPECT, "Full aspect" },
 };
 
+static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = {
+       { DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" },
+       { DRM_MODE_PICTURE_ASPECT_4_3, "4:3" },
+       { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" },
+};
+
 /*
  * Non-global properties, but "required" for certain connectors.
  */
@@ -422,8 +432,12 @@ static struct drm_mode_object *_object_find(struct drm_device *dev,
 
        mutex_lock(&dev->mode_config.idr_mutex);
        obj = idr_find(&dev->mode_config.crtc_idr, id);
-       if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
-           (obj->id != id))
+       if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type)
+               obj = NULL;
+       if (obj && obj->id != id)
+               obj = NULL;
+       /* don't leak out unref'd fb's */
+       if (obj && (obj->type == DRM_MODE_OBJECT_FB))
                obj = NULL;
        mutex_unlock(&dev->mode_config.idr_mutex);
 
@@ -450,9 +464,6 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
         * function.*/
        WARN_ON(type == DRM_MODE_OBJECT_FB);
        obj = _object_find(dev, id, type);
-       /* don't leak out unref'd fb's */
-       if (obj && (obj->type == DRM_MODE_OBJECT_FB))
-               obj = NULL;
        return obj;
 }
 EXPORT_SYMBOL(drm_mode_object_find);
@@ -729,7 +740,7 @@ DEFINE_WW_CLASS(crtc_ww_class);
  */
 int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
                              struct drm_plane *primary,
-                             void *cursor,
+                             struct drm_plane *cursor,
                              const struct drm_crtc_funcs *funcs)
 {
        struct drm_mode_config *config = &dev->mode_config;
@@ -754,8 +765,11 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
        config->num_crtc++;
 
        crtc->primary = primary;
+       crtc->cursor = cursor;
        if (primary)
                primary->possible_crtcs = 1 << drm_crtc_index(crtc);
+       if (cursor)
+               cursor->possible_crtcs = 1 << drm_crtc_index(crtc);
 
  out:
        drm_modeset_unlock_all(dev);
@@ -885,6 +899,8 @@ int drm_connector_init(struct drm_device *dev,
        drm_object_attach_property(&connector->base,
                                      dev->mode_config.dpms_property, 0);
 
+       connector->debugfs_entry = NULL;
+
 out_put:
        if (ret)
                drm_mode_object_put(dev, &connector->base);
@@ -932,7 +948,21 @@ EXPORT_SYMBOL(drm_connector_cleanup);
  */
 int drm_connector_register(struct drm_connector *connector)
 {
-       return drm_sysfs_connector_add(connector);
+       int ret;
+
+       ret = drm_sysfs_connector_add(connector);
+       if (ret)
+               return ret;
+
+#if 0
+       ret = drm_debugfs_connector_add(connector);
+#endif
+       if (ret) {
+               drm_sysfs_connector_remove(connector);
+               return ret;
+       }
+
+       return 0;
 }
 EXPORT_SYMBOL(drm_connector_register);
 
@@ -945,6 +975,9 @@ EXPORT_SYMBOL(drm_connector_register);
 void drm_connector_unregister(struct drm_connector *connector)
 {
        drm_sysfs_connector_remove(connector);
+#if 0
+       drm_debugfs_connector_remove(connector);
+#endif
 }
 EXPORT_SYMBOL(drm_connector_unregister);
 
@@ -1243,6 +1276,7 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 {
        struct drm_property *edid;
        struct drm_property *dpms;
+       struct drm_property *dev_path;
 
        /*
         * Standard properties (apply to all connectors)
@@ -1257,6 +1291,12 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
                                   ARRAY_SIZE(drm_dpms_enum_list));
        dev->mode_config.dpms_property = dpms;
 
+       dev_path = drm_property_create(dev,
+                                      DRM_MODE_PROP_BLOB |
+                                      DRM_MODE_PROP_IMMUTABLE,
+                                      "PATH", 0);
+       dev->mode_config.path_property = dev_path;
+
        return 0;
 }
 
@@ -1412,6 +1452,33 @@ int drm_mode_create_scaling_mode_property(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
 
+/**
+ * drm_mode_create_aspect_ratio_property - create aspect ratio property
+ * @dev: DRM device
+ *
+ * Called by a driver the first time it's needed, must be attached to desired
+ * connectors.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_create_aspect_ratio_property(struct drm_device *dev)
+{
+       if (dev->mode_config.aspect_ratio_property)
+               return 0;
+
+       dev->mode_config.aspect_ratio_property =
+               drm_property_create_enum(dev, 0, "aspect ratio",
+                               drm_aspect_ratio_enum_list,
+                               ARRAY_SIZE(drm_aspect_ratio_enum_list));
+
+       if (dev->mode_config.aspect_ratio_property == NULL)
+               return -ENOMEM;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property);
+
 /**
  * drm_mode_create_dirty_property - create dirty property
  * @dev: DRM device
@@ -1499,6 +1566,15 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
 
+void drm_reinit_primary_mode_group(struct drm_device *dev)
+{
+       drm_modeset_lock_all(dev);
+       drm_mode_group_destroy(&dev->primary->mode_group);
+       drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group);
+       drm_modeset_unlock_all(dev);
+}
+EXPORT_SYMBOL(drm_reinit_primary_mode_group);
+
 /**
  * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
  * @out: drm_mode_modeinfo struct to return to the user
@@ -2146,45 +2222,32 @@ out:
        return ret;
 }
 
-/**
- * drm_mode_setplane - configure a plane's configuration
- * @dev: DRM device
- * @data: ioctl data*
- * @file_priv: DRM file info
+/*
+ * setplane_internal - setplane handler for internal callers
  *
- * Set plane configuration, including placement, fb, scaling, and other factors.
- * Or pass a NULL fb to disable.
+ * Note that we assume an extra reference has already been taken on fb.  If the
+ * update fails, this reference will be dropped before return; if it succeeds,
+ * the previous framebuffer (if any) will be unreferenced instead.
  *
- * Returns:
- * Zero on success, errno on failure.
+ * src_{x,y,w,h} are provided in 16.16 fixed point format
  */
-int drm_mode_setplane(struct drm_device *dev, void *data,
-                     struct drm_file *file_priv)
+static int setplane_internal(struct drm_plane *plane,
+                            struct drm_crtc *crtc,
+                            struct drm_framebuffer *fb,
+                            int32_t crtc_x, int32_t crtc_y,
+                            uint32_t crtc_w, uint32_t crtc_h,
+                            /* src_{x,y,w,h} values are 16.16 fixed point */
+                            uint32_t src_x, uint32_t src_y,
+                            uint32_t src_w, uint32_t src_h)
 {
-       struct drm_mode_set_plane *plane_req = data;
-       struct drm_plane *plane;
-       struct drm_crtc *crtc;
-       struct drm_framebuffer *fb = NULL, *old_fb = NULL;
+       struct drm_device *dev = plane->dev;
+       struct drm_framebuffer *old_fb = NULL;
        int ret = 0;
        unsigned int fb_width, fb_height;
        int i;
 
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       /*
-        * First, find the plane, crtc, and fb objects.  If not available,
-        * we don't bother to call the driver.
-        */
-       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;
-       }
-
        /* No fb means shut it down */
-       if (!plane_req->fb_id) {
+       if (!fb) {
                drm_modeset_lock_all(dev);
                old_fb = plane->fb;
                ret = plane->funcs->disable_plane(plane);
@@ -2198,19 +2261,10 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
                goto out;
        }
 
-       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;
-       }
-
-       fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
-       if (!fb) {
-               DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
-                             plane_req->fb_id);
-               ret = -ENOENT;
+       /* Check whether this plane is usable on this CRTC */
+       if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) {
+               DRM_DEBUG_KMS("Invalid crtc for plane\n");
+               ret = -EINVAL;
                goto out;
        }
 
@@ -2229,43 +2283,25 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
        fb_height = fb->height << 16;
 
        /* Make sure source coordinates are inside the fb. */
-       if (plane_req->src_w > fb_width ||
-           plane_req->src_x > fb_width - plane_req->src_w ||
-           plane_req->src_h > fb_height ||
-           plane_req->src_y > fb_height - plane_req->src_h) {
+       if (src_w > fb_width ||
+           src_x > fb_width - src_w ||
+           src_h > fb_height ||
+           src_y > fb_height - src_h) {
                DRM_DEBUG_KMS("Invalid source coordinates "
                              "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
-                             plane_req->src_w >> 16,
-                             ((plane_req->src_w & 0xffff) * 15625) >> 10,
-                             plane_req->src_h >> 16,
-                             ((plane_req->src_h & 0xffff) * 15625) >> 10,
-                             plane_req->src_x >> 16,
-                             ((plane_req->src_x & 0xffff) * 15625) >> 10,
-                             plane_req->src_y >> 16,
-                             ((plane_req->src_y & 0xffff) * 15625) >> 10);
+                             src_w >> 16, ((src_w & 0xffff) * 15625) >> 10,
+                             src_h >> 16, ((src_h & 0xffff) * 15625) >> 10,
+                             src_x >> 16, ((src_x & 0xffff) * 15625) >> 10,
+                             src_y >> 16, ((src_y & 0xffff) * 15625) >> 10);
                ret = -ENOSPC;
                goto out;
        }
 
-       /* Give drivers some help against integer overflows */
-       if (plane_req->crtc_w > INT_MAX ||
-           plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
-           plane_req->crtc_h > INT_MAX ||
-           plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
-               DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
-                             plane_req->crtc_w, plane_req->crtc_h,
-                             plane_req->crtc_x, plane_req->crtc_y);
-               ret = -ERANGE;
-               goto out;
-       }
-
        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);
+                                        crtc_x, crtc_y, crtc_w, crtc_h,
+                                        src_x, src_y, src_w, src_h);
        if (!ret) {
                plane->crtc = crtc;
                plane->fb = fb;
@@ -2282,6 +2318,85 @@ out:
                drm_framebuffer_unreference(old_fb);
 
        return ret;
+
+}
+
+/**
+ * drm_mode_setplane - configure a plane's configuration
+ * @dev: DRM device
+ * @data: ioctl data*
+ * @file_priv: DRM file info
+ *
+ * Set plane configuration, including placement, fb, scaling, and other factors.
+ * Or pass a NULL fb to disable (planes may be disabled without providing a
+ * valid crtc).
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_setplane(struct drm_device *dev, void *data,
+                     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 = NULL;
+       struct drm_framebuffer *fb = NULL;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       /* Give drivers some help against integer overflows */
+       if (plane_req->crtc_w > INT_MAX ||
+           plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
+           plane_req->crtc_h > INT_MAX ||
+           plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
+               DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
+                             plane_req->crtc_w, plane_req->crtc_h,
+                             plane_req->crtc_x, plane_req->crtc_y);
+               return -ERANGE;
+       }
+
+       /*
+        * 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) {
+               DRM_DEBUG_KMS("Unknown plane ID %d\n",
+                             plane_req->plane_id);
+               return -ENOENT;
+       }
+       plane = obj_to_plane(obj);
+
+       if (plane_req->fb_id) {
+               fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
+               if (!fb) {
+                       DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
+                                     plane_req->fb_id);
+                       return -ENOENT;
+               }
+
+               obj = drm_mode_object_find(dev, plane_req->crtc_id,
+                                          DRM_MODE_OBJECT_CRTC);
+               if (!obj) {
+                       DRM_DEBUG_KMS("Unknown crtc ID %d\n",
+                                     plane_req->crtc_id);
+                       return -ENOENT;
+               }
+               crtc = obj_to_crtc(obj);
+       }
+
+       /*
+        * setplane_internal will take care of deref'ing either the old or new
+        * framebuffer depending on success.
+        */
+       return setplane_internal(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);
 }
 
 /**
@@ -2530,6 +2645,102 @@ out:
        return ret;
 }
 
+/**
+ * drm_mode_cursor_universal - translate legacy cursor ioctl call into a
+ *     universal plane handler call
+ * @crtc: crtc to update cursor for
+ * @req: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Legacy cursor ioctl's work directly with driver buffer handles.  To
+ * translate legacy ioctl calls into universal plane handler calls, we need to
+ * wrap the native buffer handle in a drm_framebuffer.
+ *
+ * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB
+ * buffer with a pitch of 4*width; the universal plane interface should be used
+ * directly in cases where the hardware can support other buffer settings and
+ * userspace wants to make use of these capabilities.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
+static int drm_mode_cursor_universal(struct drm_crtc *crtc,
+                                    struct drm_mode_cursor2 *req,
+                                    struct drm_file *file_priv)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_framebuffer *fb = NULL;
+       struct drm_mode_fb_cmd2 fbreq = {
+               .width = req->width,
+               .height = req->height,
+               .pixel_format = DRM_FORMAT_ARGB8888,
+               .pitches = { req->width * 4 },
+               .handles = { req->handle },
+       };
+       int32_t crtc_x, crtc_y;
+       uint32_t crtc_w = 0, crtc_h = 0;
+       uint32_t src_w = 0, src_h = 0;
+       int ret = 0;
+
+       BUG_ON(!crtc->cursor);
+
+       /*
+        * Obtain fb we'll be using (either new or existing) and take an extra
+        * reference to it if fb != null.  setplane will take care of dropping
+        * the reference if the plane update fails.
+        */
+       if (req->flags & DRM_MODE_CURSOR_BO) {
+               if (req->handle) {
+                       fb = add_framebuffer_internal(dev, &fbreq, file_priv);
+                       if (IS_ERR(fb)) {
+                               DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n");
+                               return PTR_ERR(fb);
+                       }
+
+                       drm_framebuffer_reference(fb);
+               } else {
+                       fb = NULL;
+               }
+       } else {
+               mutex_lock(&dev->mode_config.mutex);
+               fb = crtc->cursor->fb;
+               if (fb)
+                       drm_framebuffer_reference(fb);
+               mutex_unlock(&dev->mode_config.mutex);
+       }
+
+       if (req->flags & DRM_MODE_CURSOR_MOVE) {
+               crtc_x = req->x;
+               crtc_y = req->y;
+       } else {
+               crtc_x = crtc->cursor_x;
+               crtc_y = crtc->cursor_y;
+       }
+
+       if (fb) {
+               crtc_w = fb->width;
+               crtc_h = fb->height;
+               src_w = fb->width << 16;
+               src_h = fb->height << 16;
+       }
+
+       /*
+        * setplane_internal will take care of deref'ing either the old or new
+        * framebuffer depending on success.
+        */
+       ret = setplane_internal(crtc->cursor, crtc, fb,
+                               crtc_x, crtc_y, crtc_w, crtc_h,
+                               0, 0, src_w, src_h);
+
+       /* Update successful; save new cursor position, if necessary */
+       if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) {
+               crtc->cursor_x = req->x;
+               crtc->cursor_y = req->y;
+       }
+
+       return ret;
+}
+
 static int drm_mode_cursor_common(struct drm_device *dev,
                                  struct drm_mode_cursor2 *req,
                                  struct drm_file *file_priv)
@@ -2549,6 +2760,13 @@ static int drm_mode_cursor_common(struct drm_device *dev,
                return -ENOENT;
        }
 
+       /*
+        * If this crtc has a universal cursor plane, call that plane's update
+        * handler rather than using legacy cursor handlers.
+        */
+       if (crtc->cursor)
+               return drm_mode_cursor_universal(crtc, req, file_priv);
+
        drm_modeset_lock(&crtc->mutex, NULL);
        if (req->flags & DRM_MODE_CURSOR_BO) {
                if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
@@ -2848,56 +3066,38 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
        return 0;
 }
 
-/**
- * drm_mode_addfb2 - add an FB to the graphics configuration
- * @dev: drm device for the ioctl
- * @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. 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:
- * Zero on success, errno on failure.
- */
-int drm_mode_addfb2(struct drm_device *dev,
-                   void *data, struct drm_file *file_priv)
+static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev,
+                                                       struct drm_mode_fb_cmd2 *r,
+                                                       struct drm_file *file_priv)
 {
-       struct drm_mode_fb_cmd2 *r = data;
        struct drm_mode_config *config = &dev->mode_config;
        struct drm_framebuffer *fb;
        int ret;
 
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
        if (r->flags & ~DRM_MODE_FB_INTERLACED) {
                DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags);
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
        }
 
        if ((config->min_width > r->width) || (r->width > config->max_width)) {
                DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",
                          r->width, config->min_width, config->max_width);
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
        }
        if ((config->min_height > r->height) || (r->height > config->max_height)) {
                DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",
                          r->height, config->min_height, config->max_height);
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
        }
 
        ret = framebuffer_check(r);
        if (ret)
-               return ret;
+               return ERR_PTR(ret);
 
        fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
        if (IS_ERR(fb)) {
                DRM_DEBUG_KMS("could not create framebuffer\n");
-               return PTR_ERR(fb);
+               return fb;
        }
 
        mutex_lock(&file_priv->fbs_lock);
@@ -2906,8 +3106,37 @@ int drm_mode_addfb2(struct drm_device *dev,
        DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
        mutex_unlock(&file_priv->fbs_lock);
 
+       return fb;
+}
 
-       return ret;
+/**
+ * drm_mode_addfb2 - add an FB to the graphics configuration
+ * @dev: drm device for the ioctl
+ * @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. 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:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_addfb2(struct drm_device *dev,
+                   void *data, struct drm_file *file_priv)
+{
+       struct drm_framebuffer *fb;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       fb = add_framebuffer_internal(dev, data, file_priv);
+       if (IS_ERR(fb))
+               return PTR_ERR(fb);
+
+       return 0;
 }
 
 /**
@@ -3185,7 +3414,7 @@ fail:
 EXPORT_SYMBOL(drm_property_create);
 
 /**
- * drm_property_create - create a new enumeration property type
+ * drm_property_create_enum - create a new enumeration property type
  * @dev: drm device
  * @flags: flags specifying the property type
  * @name: name of the property
@@ -3231,7 +3460,7 @@ 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
+ * drm_property_create_bitmask - create a new bitmask property type
  * @dev: drm device
  * @flags: flags specifying the property type
  * @name: name of the property
@@ -3293,7 +3522,7 @@ static struct drm_property *property_create_range(struct drm_device *dev,
 }
 
 /**
- * drm_property_create - create a new ranged property type
+ * drm_property_create_range - create a new ranged property type
  * @dev: drm device
  * @flags: flags specifying the property type
  * @name: name of the property
@@ -3712,6 +3941,25 @@ done:
        return ret;
 }
 
+int drm_mode_connector_set_path_property(struct drm_connector *connector,
+                                        char *path)
+{
+       struct drm_device *dev = connector->dev;
+       int ret, size;
+       size = strlen(path) + 1;
+
+       connector->path_blob_ptr = drm_property_create_blob(connector->dev,
+                                                           size, path);
+       if (!connector->path_blob_ptr)
+               return -EINVAL;
+
+       ret = drm_object_property_set_value(&connector->base,
+                                           dev->mode_config.path_property,
+                                           connector->path_blob_ptr->base.id);
+       return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_set_path_property);
+
 /**
  * drm_mode_connector_update_edid_property - update the edid property of a connector
  * @connector: drm connector
@@ -3729,6 +3977,10 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
        struct drm_device *dev = connector->dev;
        int ret, size;
 
+       /* ignore requests to set edid when overridden */
+       if (connector->override_edid)
+               return 0;
+
        if (connector->edid_blob_ptr)
                drm_property_destroy_blob(dev, connector->edid_blob_ptr);
 
@@ -4414,8 +4666,9 @@ int drm_mode_create_dumb_ioctl(struct drm_device *dev,
                return -EINVAL;
 
        /* overflow checks for 32bit size calculations */
+       /* NOTE: DIV_ROUND_UP() can overflow */
        cpp = DIV_ROUND_UP(args->bpp, 8);
-       if (cpp > 0xffffffffU / args->width)
+       if (!cpp || cpp > 0xffffffffU / args->width)
                return -EINVAL;
        stride = cpp * args->width;
        if (args->height > 0xffffffffU / stride)
index 3b05572..7b63e15 100644 (file)
@@ -803,6 +803,7 @@ void 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;
+       fb->flags = mode_cmd->flags;
 }
 EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
 
index d9678db..bf44b47 100644 (file)
@@ -144,3 +144,110 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw)
        }
 }
 EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate);
+
+/**
+ * DOC: dp helpers
+ *
+ * The DisplayPort AUX channel is an abstraction to allow generic, driver-
+ * independent access to AUX functionality. Drivers can take advantage of
+ * this by filling in the fields of the drm_dp_aux structure.
+ *
+ * Transactions are described using a hardware-independent drm_dp_aux_msg
+ * structure, which is passed into a driver's .transfer() implementation.
+ * Both native and I2C-over-AUX transactions are supported.
+ */
+
+static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
+                             unsigned int offset, void *buffer, size_t size)
+{
+       struct drm_dp_aux_msg msg;
+       unsigned int retry;
+       int err;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.address = offset;
+       msg.request = request;
+       msg.buffer = buffer;
+       msg.size = size;
+
+       /*
+        * The specification doesn't give any recommendation on how often to
+        * retry native transactions, so retry 7 times like for I2C-over-AUX
+        * transactions.
+        */
+       for (retry = 0; retry < 7; retry++) {
+
+               mutex_lock(&aux->hw_mutex);
+               err = aux->transfer(aux, &msg);
+               mutex_unlock(&aux->hw_mutex);
+               if (err < 0) {
+                       if (err == -EBUSY)
+                               continue;
+
+                       return err;
+               }
+
+
+               switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) {
+               case DP_AUX_NATIVE_REPLY_ACK:
+                       if (err < size)
+                               return -EPROTO;
+                       return err;
+
+               case DP_AUX_NATIVE_REPLY_NACK:
+                       return -EIO;
+
+               case DP_AUX_NATIVE_REPLY_DEFER:
+                       usleep_range(400, 500);
+                       break;
+               }
+       }
+
+       DRM_DEBUG_KMS("too many retries, giving up\n");
+       return -EIO;
+}
+
+/**
+ * drm_dp_dpcd_read() - read a series of bytes from the DPCD
+ * @aux: DisplayPort AUX channel
+ * @offset: address of the (first) register to read
+ * @buffer: buffer to store the register values
+ * @size: number of bytes in @buffer
+ *
+ * Returns the number of bytes transferred on success, or a negative error
+ * code on failure. -EIO is returned if the request was NAKed by the sink or
+ * if the retry count was exceeded. If not all bytes were transferred, this
+ * function returns -EPROTO. Errors from the underlying AUX channel transfer
+ * function, with the exception of -EBUSY (which causes the transaction to
+ * be retried), are propagated to the caller.
+ */
+ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
+                        void *buffer, size_t size)
+{
+       return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, buffer,
+                                 size);
+}
+EXPORT_SYMBOL(drm_dp_dpcd_read);
+
+/**
+ * drm_dp_dpcd_write() - write a series of bytes to the DPCD
+ * @aux: DisplayPort AUX channel
+ * @offset: address of the (first) register to write
+ * @buffer: buffer containing the values to write
+ * @size: number of bytes in @buffer
+ *
+ * Returns the number of bytes transferred on success, or a negative error
+ * code on failure. -EIO is returned if the request was NAKed by the sink or
+ * if the retry count was exceeded. If not all bytes were transferred, this
+ * function returns -EPROTO. Errors from the underlying AUX channel transfer
+ * function, with the exception of -EBUSY (which causes the transaction to
+ * be retried), are propagated to the caller.
+ */
+ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
+                         void *buffer, size_t size)
+{
+       return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer,
+                                 size);
+}
+EXPORT_SYMBOL(drm_dp_dpcd_write);
+
diff --git a/sys/dev/drm/drm_dp_mst_topology.c b/sys/dev/drm/drm_dp_mst_topology.c
new file mode 100644 (file)
index 0000000..90c4913
--- /dev/null
@@ -0,0 +1,2715 @@
+/*
+ * Copyright © 2014 Red Hat
+ *
+ * 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 <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/i2c.h>
+#include <drm/drm_dp_mst_helper.h>
+#include <drm/drmP.h>
+
+#include <drm/drm_fixed.h>
+
+/**
+ * DOC: dp mst helper
+ *
+ * These functions contain parts of the DisplayPort 1.2a MultiStream Transport
+ * protocol. The helpers contain a topology manager and bandwidth manager.
+ * The helpers encapsulate the sending and received of sideband msgs.
+ */
+static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
+                                 char *buf);
+static int test_calc_pbn_mode(void);
+
+static void drm_dp_put_port(struct drm_dp_mst_port *port);
+
+static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
+                                    int id,
+                                    struct drm_dp_payload *payload);
+
+static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
+                                 struct drm_dp_mst_port *port,
+                                 int offset, int size, u8 *bytes);
+
+static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+                                   struct drm_dp_mst_branch *mstb);
+static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
+                                          struct drm_dp_mst_branch *mstb,
+                                          struct drm_dp_mst_port *port);
+static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
+                                u8 *guid);
+
+static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux);
+static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux);
+static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr);
+/* sideband msg handling */
+static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles)
+{
+       u8 bitmask = 0x80;
+       u8 bitshift = 7;
+       u8 array_index = 0;
+       int number_of_bits = num_nibbles * 4;
+       u8 remainder = 0;
+
+       while (number_of_bits != 0) {
+               number_of_bits--;
+               remainder <<= 1;
+               remainder |= (data[array_index] & bitmask) >> bitshift;
+               bitmask >>= 1;
+               bitshift--;
+               if (bitmask == 0) {
+                       bitmask = 0x80;
+                       bitshift = 7;
+                       array_index++;
+               }
+               if ((remainder & 0x10) == 0x10)
+                       remainder ^= 0x13;
+       }
+
+       number_of_bits = 4;
+       while (number_of_bits != 0) {
+               number_of_bits--;
+               remainder <<= 1;
+               if ((remainder & 0x10) != 0)
+                       remainder ^= 0x13;
+       }
+
+       return remainder;
+}
+
+static u8 drm_dp_msg_data_crc4(const uint8_t *data, u8 number_of_bytes)
+{
+       u8 bitmask = 0x80;
+       u8 bitshift = 7;
+       u8 array_index = 0;
+       int number_of_bits = number_of_bytes * 8;
+       u16 remainder = 0;
+
+       while (number_of_bits != 0) {
+               number_of_bits--;
+               remainder <<= 1;
+               remainder |= (data[array_index] & bitmask) >> bitshift;
+               bitmask >>= 1;
+               bitshift--;
+               if (bitmask == 0) {
+                       bitmask = 0x80;
+                       bitshift = 7;
+                       array_index++;
+               }
+               if ((remainder & 0x100) == 0x100)
+                       remainder ^= 0xd5;
+       }
+
+       number_of_bits = 8;
+       while (number_of_bits != 0) {
+               number_of_bits--;
+               remainder <<= 1;
+               if ((remainder & 0x100) != 0)
+                       remainder ^= 0xd5;
+       }
+
+       return remainder & 0xff;
+}
+static inline u8 drm_dp_calc_sb_hdr_size(struct drm_dp_sideband_msg_hdr *hdr)
+{
+       u8 size = 3;
+       size += (hdr->lct / 2);
+       return size;
+}
+
+static void drm_dp_encode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
+                                          u8 *buf, int *len)
+{
+       int idx = 0;
+       int i;
+       u8 crc4;
+       buf[idx++] = ((hdr->lct & 0xf) << 4) | (hdr->lcr & 0xf);
+       for (i = 0; i < (hdr->lct / 2); i++)
+               buf[idx++] = hdr->rad[i];
+       buf[idx++] = (hdr->broadcast << 7) | (hdr->path_msg << 6) |
+               (hdr->msg_len & 0x3f);
+       buf[idx++] = (hdr->somt << 7) | (hdr->eomt << 6) | (hdr->seqno << 4);
+
+       crc4 = drm_dp_msg_header_crc4(buf, (idx * 2) - 1);
+       buf[idx - 1] |= (crc4 & 0xf);
+
+       *len = idx;
+}
+
+static bool drm_dp_decode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
+                                          u8 *buf, int buflen, u8 *hdrlen)
+{
+       u8 crc4;
+       u8 len;
+       int i;
+       u8 idx;
+       if (buf[0] == 0)
+               return false;
+       len = 3;
+       len += ((buf[0] & 0xf0) >> 4) / 2;
+       if (len > buflen)
+               return false;
+       crc4 = drm_dp_msg_header_crc4(buf, (len * 2) - 1);
+
+       if ((crc4 & 0xf) != (buf[len - 1] & 0xf)) {
+               DRM_DEBUG_KMS("crc4 mismatch 0x%x 0x%x\n", crc4, buf[len - 1]);
+               return false;
+       }
+
+       hdr->lct = (buf[0] & 0xf0) >> 4;
+       hdr->lcr = (buf[0] & 0xf);
+       idx = 1;
+       for (i = 0; i < (hdr->lct / 2); i++)
+               hdr->rad[i] = buf[idx++];
+       hdr->broadcast = (buf[idx] >> 7) & 0x1;
+       hdr->path_msg = (buf[idx] >> 6) & 0x1;
+       hdr->msg_len = buf[idx] & 0x3f;
+       idx++;
+       hdr->somt = (buf[idx] >> 7) & 0x1;
+       hdr->eomt = (buf[idx] >> 6) & 0x1;
+       hdr->seqno = (buf[idx] >> 4) & 0x1;
+       idx++;
+       *hdrlen = idx;
+       return true;
+}
+
+static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
+                                      struct drm_dp_sideband_msg_tx *raw)
+{
+       int idx = 0;
+       int i;
+       u8 *buf = raw->msg;
+       buf[idx++] = req->req_type & 0x7f;
+
+       switch (req->req_type) {
+       case DP_ENUM_PATH_RESOURCES:
+               buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
+               idx++;
+               break;
+       case DP_ALLOCATE_PAYLOAD:
+               buf[idx] = (req->u.allocate_payload.port_number & 0xf) << 4 |
+                       (req->u.allocate_payload.number_sdp_streams & 0xf);
+               idx++;
+               buf[idx] = (req->u.allocate_payload.vcpi & 0x7f);
+               idx++;
+               buf[idx] = (req->u.allocate_payload.pbn >> 8);
+               idx++;
+               buf[idx] = (req->u.allocate_payload.pbn & 0xff);
+               idx++;
+               for (i = 0; i < req->u.allocate_payload.number_sdp_streams / 2; i++) {
+                       buf[idx] = ((req->u.allocate_payload.sdp_stream_sink[i * 2] & 0xf) << 4) |
+                               (req->u.allocate_payload.sdp_stream_sink[i * 2 + 1] & 0xf);
+                       idx++;
+               }
+               if (req->u.allocate_payload.number_sdp_streams & 1) {
+                       i = req->u.allocate_payload.number_sdp_streams - 1;
+                       buf[idx] = (req->u.allocate_payload.sdp_stream_sink[i] & 0xf) << 4;
+                       idx++;
+               }
+               break;
+       case DP_QUERY_PAYLOAD:
+               buf[idx] = (req->u.query_payload.port_number & 0xf) << 4;
+               idx++;
+               buf[idx] = (req->u.query_payload.vcpi & 0x7f);
+               idx++;
+               break;
+       case DP_REMOTE_DPCD_READ:
+               buf[idx] = (req->u.dpcd_read.port_number & 0xf) << 4;
+               buf[idx] |= ((req->u.dpcd_read.dpcd_address & 0xf0000) >> 16) & 0xf;
+               idx++;
+               buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff00) >> 8;
+               idx++;
+               buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff);
+               idx++;
+               buf[idx] = (req->u.dpcd_read.num_bytes);
+               idx++;
+               break;
+
+       case DP_REMOTE_DPCD_WRITE:
+               buf[idx] = (req->u.dpcd_write.port_number & 0xf) << 4;
+               buf[idx] |= ((req->u.dpcd_write.dpcd_address & 0xf0000) >> 16) & 0xf;
+               idx++;
+               buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff00) >> 8;
+               idx++;
+               buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff);
+               idx++;
+               buf[idx] = (req->u.dpcd_write.num_bytes);
+               idx++;
+               memcpy(&buf[idx], req->u.dpcd_write.bytes, req->u.dpcd_write.num_bytes);
+               idx += req->u.dpcd_write.num_bytes;
+               break;
+       case DP_REMOTE_I2C_READ:
+               buf[idx] = (req->u.i2c_read.port_number & 0xf) << 4;
+               buf[idx] |= (req->u.i2c_read.num_transactions & 0x3);
+               idx++;
+               for (i = 0; i < (req->u.i2c_read.num_transactions & 0x3); i++) {
+                       buf[idx] = req->u.i2c_read.transactions[i].i2c_dev_id & 0x7f;
+                       idx++;
+                       buf[idx] = req->u.i2c_read.transactions[i].num_bytes;
+                       idx++;
+                       memcpy(&buf[idx], req->u.i2c_read.transactions[i].bytes, req->u.i2c_read.transactions[i].num_bytes);
+                       idx += req->u.i2c_read.transactions[i].num_bytes;
+
+                       buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 5;
+                       buf[idx] |= (req->u.i2c_read.transactions[i].i2c_transaction_delay & 0xf);
+                       idx++;
+               }
+               buf[idx] = (req->u.i2c_read.read_i2c_device_id) & 0x7f;
+               idx++;
+               buf[idx] = (req->u.i2c_read.num_bytes_read);
+               idx++;
+               break;
+
+       case DP_REMOTE_I2C_WRITE:
+               buf[idx] = (req->u.i2c_write.port_number & 0xf) << 4;
+               idx++;
+               buf[idx] = (req->u.i2c_write.write_i2c_device_id) & 0x7f;
+               idx++;
+               buf[idx] = (req->u.i2c_write.num_bytes);
+               idx++;
+               memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes);
+               idx += req->u.i2c_write.num_bytes;
+               break;
+       }
+       raw->cur_len = idx;
+}
+
+static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len)
+{
+       u8 crc4;
+       crc4 = drm_dp_msg_data_crc4(msg, len);
+       msg[len] = crc4;
+}
+
+static void drm_dp_encode_sideband_reply(struct drm_dp_sideband_msg_reply_body *rep,
+                                        struct drm_dp_sideband_msg_tx *raw)
+{
+       int idx = 0;
+       u8 *buf = raw->msg;
+
+       buf[idx++] = (rep->reply_type & 0x1) << 7 | (rep->req_type & 0x7f);
+
+       raw->cur_len = idx;
+}
+
+/* this adds a chunk of msg to the builder to get the final msg */
+static bool drm_dp_sideband_msg_build(struct drm_dp_sideband_msg_rx *msg,
+                                     u8 *replybuf, u8 replybuflen, bool hdr)
+{
+       int ret;
+       u8 crc4;
+
+       if (hdr) {
+               u8 hdrlen;
+               struct drm_dp_sideband_msg_hdr recv_hdr;
+               ret = drm_dp_decode_sideband_msg_hdr(&recv_hdr, replybuf, replybuflen, &hdrlen);
+               if (ret == false) {
+                       print_hex_dump(KERN_DEBUG, "failed hdr", DUMP_PREFIX_NONE, 16, 1, replybuf, replybuflen, false);
+                       return false;
+               }
+
+               /* get length contained in this portion */
+               msg->curchunk_len = recv_hdr.msg_len;
+               msg->curchunk_hdrlen = hdrlen;
+
+               /* we have already gotten an somt - don't bother parsing */
+               if (recv_hdr.somt && msg->have_somt)
+                       return false;
+
+               if (recv_hdr.somt) {
+                       memcpy(&msg->initial_hdr, &recv_hdr, sizeof(struct drm_dp_sideband_msg_hdr));
+                       msg->have_somt = true;
+               }
+               if (recv_hdr.eomt)
+                       msg->have_eomt = true;
+
+               /* copy the bytes for the remainder of this header chunk */
+               msg->curchunk_idx = min(msg->curchunk_len, (u8)(replybuflen - hdrlen));
+               memcpy(&msg->chunk[0], replybuf + hdrlen, msg->curchunk_idx);
+       } else {
+               memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen);
+               msg->curchunk_idx += replybuflen;
+       }
+
+       if (msg->curchunk_idx >= msg->curchunk_len) {
+               /* do CRC */
+               crc4 = drm_dp_msg_data_crc4(msg->chunk, msg->curchunk_len - 1);
+               /* copy chunk into bigger msg */
+               memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1);
+               msg->curlen += msg->curchunk_len - 1;
+       }
+       return true;
+}
+
+static bool drm_dp_sideband_parse_link_address(struct drm_dp_sideband_msg_rx *raw,
+                                              struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+       int i;
+       memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16);
+       idx += 16;
+       repmsg->u.link_addr.nports = raw->msg[idx] & 0xf;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       for (i = 0; i < repmsg->u.link_addr.nports; i++) {
+               if (raw->msg[idx] & 0x80)
+                       repmsg->u.link_addr.ports[i].input_port = 1;
+
+               repmsg->u.link_addr.ports[i].peer_device_type = (raw->msg[idx] >> 4) & 0x7;
+               repmsg->u.link_addr.ports[i].port_number = (raw->msg[idx] & 0xf);
+
+               idx++;
+               if (idx > raw->curlen)
+                       goto fail_len;
+               repmsg->u.link_addr.ports[i].mcs = (raw->msg[idx] >> 7) & 0x1;
+               repmsg->u.link_addr.ports[i].ddps = (raw->msg[idx] >> 6) & 0x1;
+               if (repmsg->u.link_addr.ports[i].input_port == 0)
+                       repmsg->u.link_addr.ports[i].legacy_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
+               idx++;
+               if (idx > raw->curlen)
+                       goto fail_len;
+               if (repmsg->u.link_addr.ports[i].input_port == 0) {
+                       repmsg->u.link_addr.ports[i].dpcd_revision = (raw->msg[idx]);
+                       idx++;
+                       if (idx > raw->curlen)
+                               goto fail_len;
+                       memcpy(repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx], 16);
+                       idx += 16;
+                       if (idx > raw->curlen)
+                               goto fail_len;
+                       repmsg->u.link_addr.ports[i].num_sdp_streams = (raw->msg[idx] >> 4) & 0xf;
+                       repmsg->u.link_addr.ports[i].num_sdp_stream_sinks = (raw->msg[idx] & 0xf);
+                       idx++;
+
+               }
+               if (idx > raw->curlen)
+                       goto fail_len;
+       }
+
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx *raw,
+                                                  struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+       repmsg->u.remote_dpcd_read_ack.port_number = raw->msg[idx] & 0xf;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx];
+       if (idx > raw->curlen)
+               goto fail_len;
+
+       memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes);
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_remote_dpcd_write(struct drm_dp_sideband_msg_rx *raw,
+                                                     struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+       repmsg->u.remote_dpcd_write_ack.port_number = raw->msg[idx] & 0xf;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg_rx *raw,
+                                                     struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+
+       repmsg->u.remote_i2c_read_ack.port_number = (raw->msg[idx] & 0xf);
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx];
+       idx++;
+       /* TODO check */
+       memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes);
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("remote i2c reply parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband_msg_rx *raw,
+                                                         struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+       repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+       idx += 2;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+       idx += 2;
+       if (idx > raw->curlen)
+               goto fail_len;
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_msg_rx *raw,
+                                                         struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+       repmsg->u.allocate_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.allocate_payload.vcpi = raw->msg[idx];
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+       idx += 2;
+       if (idx > raw->curlen)
+               goto fail_len;
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_rx *raw,
+                                                   struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+       repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
+       idx += 2;
+       if (idx > raw->curlen)
+               goto fail_len;
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw,
+                                       struct drm_dp_sideband_msg_reply_body *msg)
+{
+       memset(msg, 0, sizeof(*msg));
+       msg->reply_type = (raw->msg[0] & 0x80) >> 7;
+       msg->req_type = (raw->msg[0] & 0x7f);
+
+       if (msg->reply_type) {
+               memcpy(msg->u.nak.guid, &raw->msg[1], 16);
+               msg->u.nak.reason = raw->msg[17];
+               msg->u.nak.nak_data = raw->msg[18];
+               return false;
+       }
+
+       switch (msg->req_type) {
+       case DP_LINK_ADDRESS:
+               return drm_dp_sideband_parse_link_address(raw, msg);
+       case DP_QUERY_PAYLOAD:
+               return drm_dp_sideband_parse_query_payload_ack(raw, msg);
+       case DP_REMOTE_DPCD_READ:
+               return drm_dp_sideband_parse_remote_dpcd_read(raw, msg);
+       case DP_REMOTE_DPCD_WRITE:
+               return drm_dp_sideband_parse_remote_dpcd_write(raw, msg);
+       case DP_REMOTE_I2C_READ:
+               return drm_dp_sideband_parse_remote_i2c_read_ack(raw, msg);
+       case DP_ENUM_PATH_RESOURCES:
+               return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg);
+       case DP_ALLOCATE_PAYLOAD:
+               return drm_dp_sideband_parse_allocate_payload_ack(raw, msg);
+       default:
+               DRM_ERROR("Got unknown reply 0x%02x\n", msg->req_type);
+               return false;
+       }
+}
+
+static bool drm_dp_sideband_parse_connection_status_notify(struct drm_dp_sideband_msg_rx *raw,
+                                                          struct drm_dp_sideband_msg_req_body *msg)
+{
+       int idx = 1;
+
+       msg->u.conn_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+
+       memcpy(msg->u.conn_stat.guid, &raw->msg[idx], 16);
+       idx += 16;
+       if (idx > raw->curlen)
+               goto fail_len;
+
+       msg->u.conn_stat.legacy_device_plug_status = (raw->msg[idx] >> 6) & 0x1;
+       msg->u.conn_stat.displayport_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
+       msg->u.conn_stat.message_capability_status = (raw->msg[idx] >> 4) & 0x1;
+       msg->u.conn_stat.input_port = (raw->msg[idx] >> 3) & 0x1;
+       msg->u.conn_stat.peer_device_type = (raw->msg[idx] & 0x7);
+       idx++;
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("connection status reply parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_resource_status_notify(struct drm_dp_sideband_msg_rx *raw,
+                                                          struct drm_dp_sideband_msg_req_body *msg)
+{
+       int idx = 1;
+
+       msg->u.resource_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+
+       memcpy(msg->u.resource_stat.guid, &raw->msg[idx], 16);
+       idx += 16;
+       if (idx > raw->curlen)
+               goto fail_len;
+
+       msg->u.resource_stat.available_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
+       idx++;
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("resource status reply parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_req(struct drm_dp_sideband_msg_rx *raw,
+                                     struct drm_dp_sideband_msg_req_body *msg)
+{
+       memset(msg, 0, sizeof(*msg));
+       msg->req_type = (raw->msg[0] & 0x7f);
+
+       switch (msg->req_type) {
+       case DP_CONNECTION_STATUS_NOTIFY:
+               return drm_dp_sideband_parse_connection_status_notify(raw, msg);
+       case DP_RESOURCE_STATUS_NOTIFY:
+               return drm_dp_sideband_parse_resource_status_notify(raw, msg);
+       default:
+               DRM_ERROR("Got unknown request 0x%02x\n", msg->req_type);
+               return false;
+       }
+}
+
+static int build_dpcd_write(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes, u8 *bytes)
+{
+       struct drm_dp_sideband_msg_req_body req;
+
+       req.req_type = DP_REMOTE_DPCD_WRITE;
+       req.u.dpcd_write.port_number = port_num;
+       req.u.dpcd_write.dpcd_address = offset;
+       req.u.dpcd_write.num_bytes = num_bytes;
+       req.u.dpcd_write.bytes = bytes;
+       drm_dp_encode_sideband_req(&req, msg);
+
+       return 0;
+}
+
+static int build_link_address(struct drm_dp_sideband_msg_tx *msg)
+{
+       struct drm_dp_sideband_msg_req_body req;
+
+       req.req_type = DP_LINK_ADDRESS;
+       drm_dp_encode_sideband_req(&req, msg);
+       return 0;
+}
+
+static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg, int port_num)
+{
+       struct drm_dp_sideband_msg_req_body req;
+
+       req.req_type = DP_ENUM_PATH_RESOURCES;
+       req.u.port_num.port_number = port_num;
+       drm_dp_encode_sideband_req(&req, msg);
+       msg->path_msg = true;
+       return 0;
+}
+
+static int build_allocate_payload(struct drm_dp_sideband_msg_tx *msg, int port_num,
+                                 u8 vcpi, uint16_t pbn)
+{
+       struct drm_dp_sideband_msg_req_body req;
+       memset(&req, 0, sizeof(req));
+       req.req_type = DP_ALLOCATE_PAYLOAD;
+       req.u.allocate_payload.port_number = port_num;
+       req.u.allocate_payload.vcpi = vcpi;
+       req.u.allocate_payload.pbn = pbn;
+       drm_dp_encode_sideband_req(&req, msg);
+       msg->path_msg = true;
+       return 0;
+}
+
+static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr,
+                                       struct drm_dp_vcpi *vcpi)
+{
+       int ret;
+
+       mutex_lock(&mgr->payload_lock);
+       ret = find_first_zero_bit(&mgr->payload_mask, mgr->max_payloads + 1);
+       if (ret > mgr->max_payloads) {
+               ret = -EINVAL;
+               DRM_DEBUG_KMS("out of payload ids %d\n", ret);
+               goto out_unlock;
+       }
+
+       set_bit(ret, &mgr->payload_mask);
+       vcpi->vcpi = ret;
+       mgr->proposed_vcpis[ret - 1] = vcpi;
+out_unlock:
+       mutex_unlock(&mgr->payload_lock);
+       return ret;
+}
+
+static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr,
+                                     int id)
+{
+       if (id == 0)
+               return;
+
+       mutex_lock(&mgr->payload_lock);
+       DRM_DEBUG_KMS("putting payload %d\n", id);
+       clear_bit(id, &mgr->payload_mask);
+       mgr->proposed_vcpis[id - 1] = NULL;
+       mutex_unlock(&mgr->payload_lock);
+}
+
+static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr,
+                             struct drm_dp_sideband_msg_tx *txmsg)
+{
+       bool ret;
+       mutex_lock(&mgr->qlock);
+       ret = (txmsg->state == DRM_DP_SIDEBAND_TX_RX ||
+              txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT);
+       mutex_unlock(&mgr->qlock);
+       return ret;
+}
+
+static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
+                                   struct drm_dp_sideband_msg_tx *txmsg)
+{
+       struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
+       int ret;
+
+       ret = wait_event_timeout(mgr->tx_waitq,
+                                check_txmsg_state(mgr, txmsg),
+                                (4 * HZ));
+       mutex_lock(&mstb->mgr->qlock);
+       if (ret > 0) {
+               if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
+                       ret = -EIO;
+                       goto out;
+               }
+       } else {
+               DRM_DEBUG_KMS("timedout msg send %p %d %d\n", txmsg, txmsg->state, txmsg->seqno);
+
+               /* dump some state */
+               ret = -EIO;
+
+               /* remove from q */
+               if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED ||
+                   txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND) {
+                       list_del(&txmsg->next);
+               }
+
+               if (txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND ||
+                   txmsg->state == DRM_DP_SIDEBAND_TX_SENT) {
+                       mstb->tx_slots[txmsg->seqno] = NULL;
+               }
+       }
+out:
+       mutex_unlock(&mgr->qlock);
+
+       return ret;
+}
+
+static struct drm_dp_mst_branch *drm_dp_add_mst_branch_device(u8 lct, u8 *rad)
+{
+       struct drm_dp_mst_branch *mstb;
+
+       mstb = kzalloc(sizeof(*mstb), GFP_KERNEL);
+       if (!mstb)
+               return NULL;
+
+       mstb->lct = lct;
+       if (lct > 1)
+               memcpy(mstb->rad, rad, lct / 2);
+       INIT_LIST_HEAD(&mstb->ports);
+       kref_init(&mstb->kref);
+       return mstb;
+}
+
+static void drm_dp_destroy_mst_branch_device(struct kref *kref)
+{
+       struct drm_dp_mst_branch *mstb = container_of(kref, struct drm_dp_mst_branch, kref);
+       struct drm_dp_mst_port *port, *tmp;
+       bool wake_tx = false;
+
+       cancel_work_sync(&mstb->mgr->work);
+
+       /*
+        * destroy all ports - don't need lock
+        * as there are no more references to the mst branch
+        * device at this point.
+        */
+       list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
+               list_del(&port->next);
+               drm_dp_put_port(port);
+       }
+
+       /* drop any tx slots msg */
+       mutex_lock(&mstb->mgr->qlock);
+       if (mstb->tx_slots[0]) {
+               mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+               mstb->tx_slots[0] = NULL;
+               wake_tx = true;
+       }
+       if (mstb->tx_slots[1]) {
+               mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+               mstb->tx_slots[1] = NULL;
+               wake_tx = true;
+       }
+       mutex_unlock(&mstb->mgr->qlock);
+
+       if (wake_tx)
+               wake_up(&mstb->mgr->tx_waitq);
+       kfree(mstb);
+}
+
+static void drm_dp_put_mst_branch_device(struct drm_dp_mst_branch *mstb)
+{
+       kref_put(&mstb->kref, drm_dp_destroy_mst_branch_device);
+}
+
+
+static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt)
+{
+       switch (old_pdt) {
+       case DP_PEER_DEVICE_DP_LEGACY_CONV:
+       case DP_PEER_DEVICE_SST_SINK:
+               /* remove i2c over sideband */
+               drm_dp_mst_unregister_i2c_bus(&port->aux);
+               break;
+       case DP_PEER_DEVICE_MST_BRANCHING:
+               drm_dp_put_mst_branch_device(port->mstb);
+               port->mstb = NULL;
+               break;
+       }
+}
+
+static void drm_dp_destroy_port(struct kref *kref)
+{
+       struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref);
+       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+       if (!port->input) {
+               port->vcpi.num_slots = 0;
+               if (port->connector)
+                       (*port->mgr->cbs->destroy_connector)(mgr, port->connector);
+               drm_dp_port_teardown_pdt(port, port->pdt);
+
+               if (!port->input && port->vcpi.vcpi > 0)
+                       drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
+       }
+       kfree(port);
+
+       (*mgr->cbs->hotplug)(mgr);
+}
+
+static void drm_dp_put_port(struct drm_dp_mst_port *port)
+{
+       kref_put(&port->kref, drm_dp_destroy_port);
+}
+
+static struct drm_dp_mst_branch *drm_dp_mst_get_validated_mstb_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_branch *to_find)
+{
+       struct drm_dp_mst_port *port;
+       struct drm_dp_mst_branch *rmstb;
+       if (to_find == mstb) {
+               kref_get(&mstb->kref);
+               return mstb;
+       }
+       list_for_each_entry(port, &mstb->ports, next) {
+               if (port->mstb) {
+                       rmstb = drm_dp_mst_get_validated_mstb_ref_locked(port->mstb, to_find);
+                       if (rmstb)
+                               return rmstb;
+               }
+       }
+       return NULL;
+}
+
+static struct drm_dp_mst_branch *drm_dp_get_validated_mstb_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb)
+{
+       struct drm_dp_mst_branch *rmstb = NULL;
+       mutex_lock(&mgr->lock);
+       if (mgr->mst_primary)
+               rmstb = drm_dp_mst_get_validated_mstb_ref_locked(mgr->mst_primary, mstb);
+       mutex_unlock(&mgr->lock);
+       return rmstb;
+}
+
+static struct drm_dp_mst_port *drm_dp_mst_get_port_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_port *to_find)
+{
+       struct drm_dp_mst_port *port, *mport;
+
+       list_for_each_entry(port, &mstb->ports, next) {
+               if (port == to_find) {
+                       kref_get(&port->kref);
+                       return port;
+               }
+               if (port->mstb) {
+                       mport = drm_dp_mst_get_port_ref_locked(port->mstb, to_find);
+                       if (mport)
+                               return mport;
+               }
+       }
+       return NULL;
+}
+
+static struct drm_dp_mst_port *drm_dp_get_validated_port_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+       struct drm_dp_mst_port *rport = NULL;
+       mutex_lock(&mgr->lock);
+       if (mgr->mst_primary)
+               rport = drm_dp_mst_get_port_ref_locked(mgr->mst_primary, port);
+       mutex_unlock(&mgr->lock);
+       return rport;
+}
+
+static struct drm_dp_mst_port *drm_dp_get_port(struct drm_dp_mst_branch *mstb, u8 port_num)
+{
+       struct drm_dp_mst_port *port;
+
+       list_for_each_entry(port, &mstb->ports, next) {
+               if (port->port_num == port_num) {
+                       kref_get(&port->kref);
+                       return port;
+               }
+       }
+
+       return NULL;
+}
+
+/*
+ * calculate a new RAD for this MST branch device
+ * if parent has an LCT of 2 then it has 1 nibble of RAD,
+ * if parent has an LCT of 3 then it has 2 nibbles of RAD,
+ */
+static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port,
+                                u8 *rad)
+{
+       int lct = port->parent->lct;
+       int shift = 4;
+       int idx = lct / 2;
+       if (lct > 1) {
+               memcpy(rad, port->parent->rad, idx);
+               shift = (lct % 2) ? 4 : 0;
+       } else
+               rad[0] = 0;
+
+       rad[idx] |= port->port_num << shift;
+       return lct + 1;
+}
+
+/*
+ * return sends link address for new mstb
+ */
+static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port)
+{
+       int ret;
+       u8 rad[6], lct;
+       bool send_link = false;
+       switch (port->pdt) {
+       case DP_PEER_DEVICE_DP_LEGACY_CONV:
+       case DP_PEER_DEVICE_SST_SINK:
+               /* add i2c over sideband */
+               ret = drm_dp_mst_register_i2c_bus(&port->aux);
+               break;
+       case DP_PEER_DEVICE_MST_BRANCHING:
+               lct = drm_dp_calculate_rad(port, rad);
+
+               port->mstb = drm_dp_add_mst_branch_device(lct, rad);
+               port->mstb->mgr = port->mgr;
+               port->mstb->port_parent = port;
+
+               send_link = true;
+               break;
+       }
+       return send_link;
+}
+
+static void drm_dp_check_port_guid(struct drm_dp_mst_branch *mstb,
+                                  struct drm_dp_mst_port *port)
+{
+       int ret;
+       if (port->dpcd_rev >= 0x12) {
+               port->guid_valid = drm_dp_validate_guid(mstb->mgr, port->guid);
+               if (!port->guid_valid) {
+                       ret = drm_dp_send_dpcd_write(mstb->mgr,
+                                                    port,
+                                                    DP_GUID,
+                                                    16, port->guid);
+                       port->guid_valid = true;
+               }
+       }
+}
+
+static void build_mst_prop_path(struct drm_dp_mst_port *port,
+                               struct drm_dp_mst_branch *mstb,
+                               char *proppath)
+{
+       int i;
+       char temp[8];
+       snprintf(proppath, 255, "mst:%d", mstb->mgr->conn_base_id);
+       for (i = 0; i < (mstb->lct - 1); i++) {
+               int shift = (i % 2) ? 0 : 4;
+               int port_num = mstb->rad[i / 2] >> shift;
+               snprintf(temp, 8, "-%d", port_num);
+               strncat(proppath, temp, 255);
+       }
+       snprintf(temp, 8, "-%d", port->port_num);
+       strncat(proppath, temp, 255);
+}
+
+static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
+                           struct device *dev,
+                           struct drm_dp_link_addr_reply_port *port_msg)
+{
+       struct drm_dp_mst_port *port;
+       bool ret;
+       bool created = false;
+       int old_pdt = 0;
+       int old_ddps = 0;
+       port = drm_dp_get_port(mstb, port_msg->port_number);
+       if (!port) {
+               port = kzalloc(sizeof(*port), GFP_KERNEL);
+               if (!port)
+                       return;
+               kref_init(&port->kref);
+               port->parent = mstb;
+               port->port_num = port_msg->port_number;
+               port->mgr = mstb->mgr;
+               port->aux.name = "DPMST";
+               port->aux.dev = dev;
+               created = true;
+       } else {
+               old_pdt = port->pdt;
+               old_ddps = port->ddps;
+       }
+
+       port->pdt = port_msg->peer_device_type;
+       port->input = port_msg->input_port;
+       port->mcs = port_msg->mcs;
+       port->ddps = port_msg->ddps;
+       port->ldps = port_msg->legacy_device_plug_status;
+       port->dpcd_rev = port_msg->dpcd_revision;
+       port->num_sdp_streams = port_msg->num_sdp_streams;
+       port->num_sdp_stream_sinks = port_msg->num_sdp_stream_sinks;
+       memcpy(port->guid, port_msg->peer_guid, 16);
+
+       /* manage mstb port lists with mgr lock - take a reference
+          for this list */
+       if (created) {
+               mutex_lock(&mstb->mgr->lock);
+               kref_get(&port->kref);
+               list_add(&port->next, &mstb->ports);
+               mutex_unlock(&mstb->mgr->lock);
+       }
+
+       if (old_ddps != port->ddps) {
+               if (port->ddps) {
+                       drm_dp_check_port_guid(mstb, port);
+                       if (!port->input)
+                               drm_dp_send_enum_path_resources(mstb->mgr, mstb, port);
+               } else {
+                       port->guid_valid = false;
+                       port->available_pbn = 0;
+                       }
+       }
+
+       if (old_pdt != port->pdt && !port->input) {
+               drm_dp_port_teardown_pdt(port, old_pdt);
+
+               ret = drm_dp_port_setup_pdt(port);
+               if (ret == true) {
+                       drm_dp_send_link_address(mstb->mgr, port->mstb);
+                       port->mstb->link_address_sent = true;
+               }
+       }
+
+       if (created && !port->input) {
+               char proppath[255];
+               build_mst_prop_path(port, mstb, proppath);
+               port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath);
+       }
+
+       /* put reference to this port */
+       drm_dp_put_port(port);
+}
+
+static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
+                              struct drm_dp_connection_status_notify *conn_stat)
+{
+       struct drm_dp_mst_port *port;
+       int old_pdt;
+       int old_ddps;
+       bool dowork = false;
+       port = drm_dp_get_port(mstb, conn_stat->port_number);
+       if (!port)
+               return;
+
+       old_ddps = port->ddps;
+       old_pdt = port->pdt;
+       port->pdt = conn_stat->peer_device_type;
+       port->mcs = conn_stat->message_capability_status;
+       port->ldps = conn_stat->legacy_device_plug_status;
+       port->ddps = conn_stat->displayport_device_plug_status;
+
+       if (old_ddps != port->ddps) {
+               if (port->ddps) {
+                       drm_dp_check_port_guid(mstb, port);
+                       dowork = true;
+               } else {
+                       port->guid_valid = false;
+                       port->available_pbn = 0;
+               }
+       }
+       if (old_pdt != port->pdt && !port->input) {
+               drm_dp_port_teardown_pdt(port, old_pdt);
+
+               if (drm_dp_port_setup_pdt(port))
+                       dowork = true;
+       }
+
+       drm_dp_put_port(port);
+       if (dowork)
+               queue_work(system_long_wq, &mstb->mgr->work);
+
+}
+
+static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr *mgr,
+                                                              u8 lct, u8 *rad)
+{
+       struct drm_dp_mst_branch *mstb;
+       struct drm_dp_mst_port *port;
+       int i;
+       /* find the port by iterating down */
+       mstb = mgr->mst_primary;
+
+       for (i = 0; i < lct - 1; i++) {
+               int shift = (i % 2) ? 0 : 4;
+               int port_num = rad[i / 2] >> shift;
+
+               list_for_each_entry(port, &mstb->ports, next) {
+                       if (port->port_num == port_num) {
+                               if (!port->mstb) {
+                                       DRM_ERROR("failed to lookup MSTB with lct %d, rad %02x\n", lct, rad[0]);
+                                       return NULL;
+                               }
+
+                               mstb = port->mstb;
+                               break;
+                       }
+               }
+       }
+       kref_get(&mstb->kref);
+       return mstb;
+}
+
+static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+                                              struct drm_dp_mst_branch *mstb)
+{
+       struct drm_dp_mst_port *port;
+
+       if (!mstb->link_address_sent) {
+               drm_dp_send_link_address(mgr, mstb);
+               mstb->link_address_sent = true;
+       }
+       list_for_each_entry(port, &mstb->ports, next) {
+               if (port->input)
+                       continue;
+
+               if (!port->ddps)
+                       continue;
+
+               if (!port->available_pbn)
+                       drm_dp_send_enum_path_resources(mgr, mstb, port);
+
+               if (port->mstb)
+                       drm_dp_check_and_send_link_address(mgr, port->mstb);
+       }
+}
+
+static void drm_dp_mst_link_probe_work(struct work_struct *work)
+{
+       struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, work);
+
+       drm_dp_check_and_send_link_address(mgr, mgr->mst_primary);
+
+}
+
+static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
+                                u8 *guid)
+{
+       static u8 zero_guid[16];
+
+       if (!memcmp(guid, zero_guid, 16)) {
+               u64 salt = get_jiffies_64();
+               memcpy(&guid[0], &salt, sizeof(u64));
+               memcpy(&guid[8], &salt, sizeof(u64));
+               return false;
+       }
+       return true;
+}
+
+#if 0
+static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes)
+{
+       struct drm_dp_sideband_msg_req_body req;
+
+       req.req_type = DP_REMOTE_DPCD_READ;
+       req.u.dpcd_read.port_number = port_num;
+       req.u.dpcd_read.dpcd_address = offset;
+       req.u.dpcd_read.num_bytes = num_bytes;
+       drm_dp_encode_sideband_req(&req, msg);
+
+       return 0;
+}
+#endif
+
+static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr,
+                                   bool up, u8 *msg, int len)
+{
+       int ret;
+       int regbase = up ? DP_SIDEBAND_MSG_UP_REP_BASE : DP_SIDEBAND_MSG_DOWN_REQ_BASE;
+       int tosend, total, offset;
+       int retries = 0;
+
+retry:
+       total = len;
+       offset = 0;
+       do {
+               tosend = min3(mgr->max_dpcd_transaction_bytes, 16, total);
+
+               ret = drm_dp_dpcd_write(mgr->aux, regbase + offset,
+                                       &msg[offset],
+                                       tosend);
+               if (ret != tosend) {
+                       if (ret == -EIO && retries < 5) {
+                               retries++;
+                               goto retry;
+                       }
+                       DRM_DEBUG_KMS("failed to dpcd write %d %d\n", tosend, ret);
+                       WARN(1, "fail\n");
+
+                       return -EIO;
+               }
+               offset += tosend;
+               total -= tosend;
+       } while (total > 0);
+       return 0;
+}
+
+static int set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr *hdr,
+                                 struct drm_dp_sideband_msg_tx *txmsg)
+{
+       struct drm_dp_mst_branch *mstb = txmsg->dst;
+
+       /* both msg slots are full */
+       if (txmsg->seqno == -1) {
+               if (mstb->tx_slots[0] && mstb->tx_slots[1]) {
+                       DRM_DEBUG_KMS("%s: failed to find slot\n", __func__);
+                       return -EAGAIN;
+               }
+               if (mstb->tx_slots[0] == NULL && mstb->tx_slots[1] == NULL) {
+                       txmsg->seqno = mstb->last_seqno;
+                       mstb->last_seqno ^= 1;
+               } else if (mstb->tx_slots[0] == NULL)
+                       txmsg->seqno = 0;
+               else
+                       txmsg->seqno = 1;
+               mstb->tx_slots[txmsg->seqno] = txmsg;
+       }
+       hdr->broadcast = 0;
+       hdr->path_msg = txmsg->path_msg;
+       hdr->lct = mstb->lct;
+       hdr->lcr = mstb->lct - 1;
+       if (mstb->lct > 1)
+               memcpy(hdr->rad, mstb->rad, mstb->lct / 2);
+       hdr->seqno = txmsg->seqno;
+       return 0;
+}
+/*
+ * process a single block of the next message in the sideband queue
+ */
+static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr,
+                                  struct drm_dp_sideband_msg_tx *txmsg,
+                                  bool up)
+{
+       u8 chunk[48];
+       struct drm_dp_sideband_msg_hdr hdr;
+       int len, space, idx, tosend;
+       int ret;
+
+       memset(&hdr, 0, sizeof(struct drm_dp_sideband_msg_hdr));
+
+       if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED) {
+               txmsg->seqno = -1;
+               txmsg->state = DRM_DP_SIDEBAND_TX_START_SEND;
+       }
+
+       /* make hdr from dst mst - for replies use seqno
+          otherwise assign one */
+       ret = set_hdr_from_dst_qlock(&hdr, txmsg);
+       if (ret < 0)
+               return ret;
+
+       /* amount left to send in this message */
+       len = txmsg->cur_len - txmsg->cur_offset;
+
+       /* 48 - sideband msg size - 1 byte for data CRC, x header bytes */
+       space = 48 - 1 - drm_dp_calc_sb_hdr_size(&hdr);
+
+       tosend = min(len, space);
+       if (len == txmsg->cur_len)
+               hdr.somt = 1;
+       if (space >= len)
+               hdr.eomt = 1;
+
+
+       hdr.msg_len = tosend + 1;
+       drm_dp_encode_sideband_msg_hdr(&hdr, chunk, &idx);
+       memcpy(&chunk[idx], &txmsg->msg[txmsg->cur_offset], tosend);
+       /* add crc at end */
+       drm_dp_crc_sideband_chunk_req(&chunk[idx], tosend);
+       idx += tosend + 1;
+
+       ret = drm_dp_send_sideband_msg(mgr, up, chunk, idx);
+       if (ret) {
+               DRM_DEBUG_KMS("sideband msg failed to send\n");
+               return ret;
+       }
+
+       txmsg->cur_offset += tosend;
+       if (txmsg->cur_offset == txmsg->cur_len) {
+               txmsg->state = DRM_DP_SIDEBAND_TX_SENT;
+               return 1;
+       }
+       return 0;
+}
+
+/* must be called holding qlock */
+static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
+{
+       struct drm_dp_sideband_msg_tx *txmsg;
+       int ret;
+
+       /* construct a chunk from the first msg in the tx_msg queue */
+       if (list_empty(&mgr->tx_msg_downq)) {
+               mgr->tx_down_in_progress = false;
+               return;
+       }
+       mgr->tx_down_in_progress = true;
+
+       txmsg = list_first_entry(&mgr->tx_msg_downq, struct drm_dp_sideband_msg_tx, next);
+       ret = process_single_tx_qlock(mgr, txmsg, false);
+       if (ret == 1) {
+               /* txmsg is sent it should be in the slots now */
+               list_del(&txmsg->next);
+       } else if (ret) {
+               DRM_DEBUG_KMS("failed to send msg in q %d\n", ret);
+               list_del(&txmsg->next);
+               if (txmsg->seqno != -1)
+                       txmsg->dst->tx_slots[txmsg->seqno] = NULL;
+               txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+               wake_up(&mgr->tx_waitq);
+       }
+       if (list_empty(&mgr->tx_msg_downq)) {
+               mgr->tx_down_in_progress = false;
+               return;
+       }
+}
+
+/* called holding qlock */
+static void process_single_up_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
+{
+       struct drm_dp_sideband_msg_tx *txmsg;
+       int ret;
+
+       /* construct a chunk from the first msg in the tx_msg queue */
+       if (list_empty(&mgr->tx_msg_upq)) {
+               mgr->tx_up_in_progress = false;
+               return;
+       }
+
+       txmsg = list_first_entry(&mgr->tx_msg_upq, struct drm_dp_sideband_msg_tx, next);
+       ret = process_single_tx_qlock(mgr, txmsg, true);
+       if (ret == 1) {
+               /* up txmsgs aren't put in slots - so free after we send it */
+               list_del(&txmsg->next);
+               kfree(txmsg);
+       } else if (ret)
+               DRM_DEBUG_KMS("failed to send msg in q %d\n", ret);
+       mgr->tx_up_in_progress = true;
+}
+
+static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
+                                struct drm_dp_sideband_msg_tx *txmsg)
+{
+       mutex_lock(&mgr->qlock);
+       list_add_tail(&txmsg->next, &mgr->tx_msg_downq);
+       if (!mgr->tx_down_in_progress)
+               process_single_down_tx_qlock(mgr);
+       mutex_unlock(&mgr->qlock);
+}
+
+static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+                                   struct drm_dp_mst_branch *mstb)
+{
+       int len;
+       struct drm_dp_sideband_msg_tx *txmsg;
+       int ret;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg)
+               return -ENOMEM;
+
+       txmsg->dst = mstb;
+       len = build_link_address(txmsg);
+
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+       if (ret > 0) {
+               int i;
+
+               if (txmsg->reply.reply_type == 1)
+                       DRM_DEBUG_KMS("link address nak received\n");
+               else {
+                       DRM_DEBUG_KMS("link address reply: %d\n", txmsg->reply.u.link_addr.nports);
+                       for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
+                               DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n", i,
+                                      txmsg->reply.u.link_addr.ports[i].input_port,
+                                      txmsg->reply.u.link_addr.ports[i].peer_device_type,
+                                      txmsg->reply.u.link_addr.ports[i].port_number,
+                                      txmsg->reply.u.link_addr.ports[i].dpcd_revision,
+                                      txmsg->reply.u.link_addr.ports[i].mcs,
+                                      txmsg->reply.u.link_addr.ports[i].ddps,
+                                      txmsg->reply.u.link_addr.ports[i].legacy_device_plug_status,
+                                      txmsg->reply.u.link_addr.ports[i].num_sdp_streams,
+                                      txmsg->reply.u.link_addr.ports[i].num_sdp_stream_sinks);
+                       }
+                       for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
+                               drm_dp_add_port(mstb, mgr->dev, &txmsg->reply.u.link_addr.ports[i]);
+                       }
+                       (*mgr->cbs->hotplug)(mgr);
+               }
+       } else
+               DRM_DEBUG_KMS("link address failed %d\n", ret);
+
+       kfree(txmsg);
+       return 0;
+}
+
+static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
+                                          struct drm_dp_mst_branch *mstb,
+                                          struct drm_dp_mst_port *port)
+{
+       int len;
+       struct drm_dp_sideband_msg_tx *txmsg;
+       int ret;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg)
+               return -ENOMEM;
+
+       txmsg->dst = mstb;
+       len = build_enum_path_resources(txmsg, port->port_num);
+
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+       if (ret > 0) {
+               if (txmsg->reply.reply_type == 1)
+                       DRM_DEBUG_KMS("enum path resources nak received\n");
+               else {
+                       if (port->port_num != txmsg->reply.u.path_resources.port_number)
+                               DRM_ERROR("got incorrect port in response\n");
+                       DRM_DEBUG_KMS("enum path resources %d: %d %d\n", txmsg->reply.u.path_resources.port_number, txmsg->reply.u.path_resources.full_payload_bw_number,
+                              txmsg->reply.u.path_resources.avail_payload_bw_number);
+                       port->available_pbn = txmsg->reply.u.path_resources.avail_payload_bw_number;
+               }
+       }
+
+       kfree(txmsg);
+       return 0;
+}
+
+static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
+                                  struct drm_dp_mst_port *port,
+                                  int id,
+                                  int pbn)
+{
+       struct drm_dp_sideband_msg_tx *txmsg;
+       struct drm_dp_mst_branch *mstb;
+       int len, ret;
+
+       mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
+       if (!mstb)
+               return -EINVAL;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg) {
+               ret = -ENOMEM;
+               goto fail_put;
+       }
+
+       txmsg->dst = mstb;
+       len = build_allocate_payload(txmsg, port->port_num,
+                                    id,
+                                    pbn);
+
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+       if (ret > 0) {
+               if (txmsg->reply.reply_type == 1) {
+                       ret = -EINVAL;
+               } else
+                       ret = 0;
+       }
+       kfree(txmsg);
+fail_put:
+       drm_dp_put_mst_branch_device(mstb);
+       return ret;
+}
+
+static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
+                                      int id,
+                                      struct drm_dp_payload *payload)
+{
+       int ret;
+
+       ret = drm_dp_dpcd_write_payload(mgr, id, payload);
+       if (ret < 0) {
+               payload->payload_state = 0;
+               return ret;
+       }
+       payload->payload_state = DP_PAYLOAD_LOCAL;
+       return 0;
+}
+
+static int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
+                                      struct drm_dp_mst_port *port,
+                                      int id,
+                                      struct drm_dp_payload *payload)
+{
+       int ret;
+       ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn);
+       if (ret < 0)
+               return ret;
+       payload->payload_state = DP_PAYLOAD_REMOTE;
+       return ret;
+}
+
+static int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
+                                       struct drm_dp_mst_port *port,
+                                       int id,
+                                       struct drm_dp_payload *payload)
+{
+       DRM_DEBUG_KMS("\n");
+       /* its okay for these to fail */
+       if (port) {
+               drm_dp_payload_send_msg(mgr, port, id, 0);
+       }
+
+       drm_dp_dpcd_write_payload(mgr, id, payload);
+       payload->payload_state = 0;
+       return 0;
+}
+
+static int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
+                                       int id,
+                                       struct drm_dp_payload *payload)
+{
+       payload->payload_state = 0;
+       return 0;
+}
+
+/**
+ * drm_dp_update_payload_part1() - Execute payload update part 1
+ * @mgr: manager to use.
+ *
+ * This iterates over all proposed virtual channels, and tries to
+ * allocate space in the link for them. For 0->slots transitions,
+ * this step just writes the VCPI to the MST device. For slots->0
+ * transitions, this writes the updated VCPIs and removes the
+ * remote VC payloads.
+ *
+ * after calling this the driver should generate ACT and payload
+ * packets.
+ */
+int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr)
+{
+       int i;
+       int cur_slots = 1;
+       struct drm_dp_payload req_payload;
+       struct drm_dp_mst_port *port;
+
+       mutex_lock(&mgr->payload_lock);
+       for (i = 0; i < mgr->max_payloads; i++) {
+               /* solve the current payloads - compare to the hw ones
+                  - update the hw view */
+               req_payload.start_slot = cur_slots;
+               if (mgr->proposed_vcpis[i]) {
+                       port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+                       req_payload.num_slots = mgr->proposed_vcpis[i]->num_slots;
+               } else {
+                       port = NULL;
+                       req_payload.num_slots = 0;
+               }
+               /* work out what is required to happen with this payload */
+               if (mgr->payloads[i].start_slot != req_payload.start_slot ||
+                   mgr->payloads[i].num_slots != req_payload.num_slots) {
+
+                       /* need to push an update for this payload */
+                       if (req_payload.num_slots) {
+                               drm_dp_create_payload_step1(mgr, i + 1, &req_payload);
+                               mgr->payloads[i].num_slots = req_payload.num_slots;
+                       } else if (mgr->payloads[i].num_slots) {
+                               mgr->payloads[i].num_slots = 0;
+                               drm_dp_destroy_payload_step1(mgr, port, i + 1, &mgr->payloads[i]);
+                               req_payload.payload_state = mgr->payloads[i].payload_state;
+                       } else
+                               req_payload.payload_state = 0;
+
+                       mgr->payloads[i].start_slot = req_payload.start_slot;
+                       mgr->payloads[i].payload_state = req_payload.payload_state;
+               }
+               cur_slots += req_payload.num_slots;
+       }
+       mutex_unlock(&mgr->payload_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_update_payload_part1);
+
+/**
+ * drm_dp_update_payload_part2() - Execute payload update part 2
+ * @mgr: manager to use.
+ *
+ * This iterates over all proposed virtual channels, and tries to
+ * allocate space in the link for them. For 0->slots transitions,
+ * this step writes the remote VC payload commands. For slots->0
+ * this just resets some internal state.
+ */
+int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr)
+{
+       struct drm_dp_mst_port *port;
+       int i;
+       int ret;
+       mutex_lock(&mgr->payload_lock);
+       for (i = 0; i < mgr->max_payloads; i++) {
+
+               if (!mgr->proposed_vcpis[i])
+                       continue;
+
+               port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+
+               DRM_DEBUG_KMS("payload %d %d\n", i, mgr->payloads[i].payload_state);
+               if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) {
+                       ret = drm_dp_create_payload_step2(mgr, port, i + 1, &mgr->payloads[i]);
+               } else if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) {
+                       ret = drm_dp_destroy_payload_step2(mgr, i + 1, &mgr->payloads[i]);
+               }
+               if (ret) {
+                       mutex_unlock(&mgr->payload_lock);
+                       return ret;
+               }
+       }
+       mutex_unlock(&mgr->payload_lock);
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_update_payload_part2);
+
+#if 0 /* unused as of yet */
+static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
+                                struct drm_dp_mst_port *port,
+                                int offset, int size)
+{
+       int len;
+       struct drm_dp_sideband_msg_tx *txmsg;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg)
+               return -ENOMEM;
+
+       len = build_dpcd_read(txmsg, port->port_num, 0, 8);
+       txmsg->dst = port->parent;
+
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       return 0;
+}
+#endif
+
+static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
+                                 struct drm_dp_mst_port *port,
+                                 int offset, int size, u8 *bytes)
+{
+       int len;
+       int ret;
+       struct drm_dp_sideband_msg_tx *txmsg;
+       struct drm_dp_mst_branch *mstb;
+
+       mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
+       if (!mstb)
+               return -EINVAL;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg) {
+               ret = -ENOMEM;
+               goto fail_put;
+       }
+
+       len = build_dpcd_write(txmsg, port->port_num, offset, size, bytes);
+       txmsg->dst = mstb;
+
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+       if (ret > 0) {
+               if (txmsg->reply.reply_type == 1) {
+                       ret = -EINVAL;
+               } else
+                       ret = 0;
+       }
+       kfree(txmsg);
+fail_put:
+       drm_dp_put_mst_branch_device(mstb);
+       return ret;
+}
+
+static int drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx *msg, u8 req_type)
+{
+       struct drm_dp_sideband_msg_reply_body reply;
+
+       reply.reply_type = 1;
+       reply.req_type = req_type;
+       drm_dp_encode_sideband_reply(&reply, msg);
+       return 0;
+}
+
+static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr,
+                                   struct drm_dp_mst_branch *mstb,
+                                   int req_type, int seqno, bool broadcast)
+{
+       struct drm_dp_sideband_msg_tx *txmsg;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg)
+               return -ENOMEM;
+
+       txmsg->dst = mstb;
+       txmsg->seqno = seqno;
+       drm_dp_encode_up_ack_reply(txmsg, req_type);
+
+       mutex_lock(&mgr->qlock);
+       list_add_tail(&txmsg->next, &mgr->tx_msg_upq);
+       if (!mgr->tx_up_in_progress) {
+               process_single_up_tx_qlock(mgr);
+       }
+       mutex_unlock(&mgr->qlock);
+       return 0;
+}
+
+static int drm_dp_get_vc_payload_bw(int dp_link_bw, int dp_link_count)
+{
+       switch (dp_link_bw) {
+       case DP_LINK_BW_1_62:
+               return 3 * dp_link_count;
+       case DP_LINK_BW_2_7:
+               return 5 * dp_link_count;
+       case DP_LINK_BW_5_4:
+               return 10 * dp_link_count;
+       }
+       return 0;
+}
+
+/**
+ * drm_dp_mst_topology_mgr_set_mst() - Set the MST state for a topology manager
+ * @mgr: manager to set state for
+ * @mst_state: true to enable MST on this connector - false to disable.
+ *
+ * This is called by the driver when it detects an MST capable device plugged
+ * into a DP MST capable port, or when a DP MST capable device is unplugged.
+ */
+int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state)
+{
+       int ret = 0;
+       struct drm_dp_mst_branch *mstb = NULL;
+
+       mutex_lock(&mgr->lock);
+       if (mst_state == mgr->mst_state)
+               goto out_unlock;
+
+       mgr->mst_state = mst_state;
+       /* set the device into MST mode */
+       if (mst_state) {
+               WARN_ON(mgr->mst_primary);
+
+               /* get dpcd info */
+               ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE);
+               if (ret != DP_RECEIVER_CAP_SIZE) {
+                       DRM_DEBUG_KMS("failed to read DPCD\n");
+                       goto out_unlock;
+               }
+
+               mgr->pbn_div = drm_dp_get_vc_payload_bw(mgr->dpcd[1], mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK);
+               mgr->total_pbn = 2560;
+               mgr->total_slots = DIV_ROUND_UP(mgr->total_pbn, mgr->pbn_div);
+               mgr->avail_slots = mgr->total_slots;
+
+               /* add initial branch device at LCT 1 */
+               mstb = drm_dp_add_mst_branch_device(1, NULL);
+               if (mstb == NULL) {
+                       ret = -ENOMEM;
+                       goto out_unlock;
+               }
+               mstb->mgr = mgr;
+
+               /* give this the main reference */
+               mgr->mst_primary = mstb;
+               kref_get(&mgr->mst_primary->kref);
+
+               {
+                       struct drm_dp_payload reset_pay;
+                       reset_pay.start_slot = 0;
+                       reset_pay.num_slots = 0x3f;
+                       drm_dp_dpcd_write_payload(mgr, 0, &reset_pay);
+               }
+
+               ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+                                        DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
+               if (ret < 0) {
+                       goto out_unlock;
+               }
+
+
+               /* sort out guid */
+               ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, mgr->guid, 16);
+               if (ret != 16) {
+                       DRM_DEBUG_KMS("failed to read DP GUID %d\n", ret);
+                       goto out_unlock;
+               }
+
+               mgr->guid_valid = drm_dp_validate_guid(mgr, mgr->guid);
+               if (!mgr->guid_valid) {
+                       ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, mgr->guid, 16);
+                       mgr->guid_valid = true;
+               }
+
+               queue_work(system_long_wq, &mgr->work);
+
+               ret = 0;
+       } else {
+               /* disable MST on the device */
+               mstb = mgr->mst_primary;
+               mgr->mst_primary = NULL;
+               /* this can fail if the device is gone */
+               drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0);
+               ret = 0;
+               memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload));
+               mgr->payload_mask = 0;
+               set_bit(0, &mgr->payload_mask);
+       }
+
+out_unlock:
+       mutex_unlock(&mgr->lock);
+       if (mstb)
+               drm_dp_put_mst_branch_device(mstb);
+       return ret;
+
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_set_mst);
+
+/**
+ * drm_dp_mst_topology_mgr_suspend() - suspend the MST manager
+ * @mgr: manager to suspend
+ *
+ * This function tells the MST device that we can't handle UP messages
+ * anymore. This should stop it from sending any since we are suspended.
+ */
+void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr)
+{
+       mutex_lock(&mgr->lock);
+       drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+                          DP_MST_EN | DP_UPSTREAM_IS_SRC);
+       mutex_unlock(&mgr->lock);
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
+
+/**
+ * drm_dp_mst_topology_mgr_resume() - resume the MST manager
+ * @mgr: manager to resume
+ *
+ * This will fetch DPCD and see if the device is still there,
+ * if it is, it will rewrite the MSTM control bits, and return.
+ *
+ * if the device fails this returns -1, and the driver should do
+ * a full MST reprobe, in case we were undocked.
+ */
+int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr)
+{
+       int ret = 0;
+
+       mutex_lock(&mgr->lock);
+
+       if (mgr->mst_primary) {
+               int sret;
+               sret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE);
+               if (sret != DP_RECEIVER_CAP_SIZE) {
+                       DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
+                       ret = -1;
+                       goto out_unlock;
+               }
+
+               ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+                                        DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
+               if (ret < 0) {
+                       DRM_DEBUG_KMS("mst write failed - undocked during suspend?\n");
+                       ret = -1;
+                       goto out_unlock;
+               }
+               ret = 0;
+       } else
+               ret = -1;
+
+out_unlock:
+       mutex_unlock(&mgr->lock);
+       return ret;
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume);
+
+static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up)
+{
+       int len;
+       u8 replyblock[32];
+       int replylen, origlen, curreply;
+       int ret;
+       struct drm_dp_sideband_msg_rx *msg;
+       int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : DP_SIDEBAND_MSG_DOWN_REP_BASE;
+       msg = up ? &mgr->up_req_recv : &mgr->down_rep_recv;
+
+       len = min(mgr->max_dpcd_transaction_bytes, 16);
+       ret = drm_dp_dpcd_read(mgr->aux, basereg,
+                              replyblock, len);
+       if (ret != len) {
+               DRM_DEBUG_KMS("failed to read DPCD down rep %d %d\n", len, ret);
+               return;
+       }
+       ret = drm_dp_sideband_msg_build(msg, replyblock, len, true);
+       if (!ret) {
+               DRM_DEBUG_KMS("sideband msg build failed %d\n", replyblock[0]);
+               return;
+       }
+       replylen = msg->curchunk_len + msg->curchunk_hdrlen;
+
+       origlen = replylen;
+       replylen -= len;
+       curreply = len;
+       while (replylen > 0) {
+               len = min3(replylen, mgr->max_dpcd_transaction_bytes, 16);
+               ret = drm_dp_dpcd_read(mgr->aux, basereg + curreply,
+                                   replyblock, len);
+               if (ret != len) {
+                       DRM_DEBUG_KMS("failed to read a chunk\n");
+               }
+               ret = drm_dp_sideband_msg_build(msg, replyblock, len, false);
+               if (ret == false)
+                       DRM_DEBUG_KMS("failed to build sideband msg\n");
+               curreply += len;
+               replylen -= len;
+       }
+}
+
+static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
+{
+       int ret = 0;
+
+       drm_dp_get_one_sb_msg(mgr, false);
+
+       if (mgr->down_rep_recv.have_eomt) {
+               struct drm_dp_sideband_msg_tx *txmsg;
+               struct drm_dp_mst_branch *mstb;
+               int slot = -1;
+               mstb = drm_dp_get_mst_branch_device(mgr,
+                                                   mgr->down_rep_recv.initial_hdr.lct,
+                                                   mgr->down_rep_recv.initial_hdr.rad);
+
+               if (!mstb) {
+                       DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->down_rep_recv.initial_hdr.lct);
+                       memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+                       return 0;
+               }
+
+               /* find the message */
+               slot = mgr->down_rep_recv.initial_hdr.seqno;
+               mutex_lock(&mgr->qlock);
+               txmsg = mstb->tx_slots[slot];
+               /* remove from slots */
+               mutex_unlock(&mgr->qlock);
+
+               if (!txmsg) {
+                       DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n",
+                              mstb,
+                              mgr->down_rep_recv.initial_hdr.seqno,
+                              mgr->down_rep_recv.initial_hdr.lct,
+                                     mgr->down_rep_recv.initial_hdr.rad[0],
+                                     mgr->down_rep_recv.msg[0]);
+                       drm_dp_put_mst_branch_device(mstb);
+                       memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+                       return 0;
+               }
+
+               drm_dp_sideband_parse_reply(&mgr->down_rep_recv, &txmsg->reply);
+               if (txmsg->reply.reply_type == 1) {
+                       DRM_DEBUG_KMS("Got NAK reply: req 0x%02x, reason 0x%02x, nak data 0x%02x\n", txmsg->reply.req_type, txmsg->reply.u.nak.reason, txmsg->reply.u.nak.nak_data);
+               }
+
+               memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+               drm_dp_put_mst_branch_device(mstb);
+
+               mutex_lock(&mgr->qlock);
+               txmsg->state = DRM_DP_SIDEBAND_TX_RX;
+               mstb->tx_slots[slot] = NULL;
+               mutex_unlock(&mgr->qlock);
+
+               wake_up(&mgr->tx_waitq);
+       }
+       return ret;
+}
+
+static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
+{
+       int ret = 0;
+       drm_dp_get_one_sb_msg(mgr, true);
+
+       if (mgr->up_req_recv.have_eomt) {
+               struct drm_dp_sideband_msg_req_body msg;
+               struct drm_dp_mst_branch *mstb;
+               bool seqno;
+               mstb = drm_dp_get_mst_branch_device(mgr,
+                                                   mgr->up_req_recv.initial_hdr.lct,
+                                                   mgr->up_req_recv.initial_hdr.rad);
+               if (!mstb) {
+                       DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
+                       memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+                       return 0;
+               }
+
+               seqno = mgr->up_req_recv.initial_hdr.seqno;
+               drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg);
+
+               if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
+                       drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false);
+                       drm_dp_update_port(mstb, &msg.u.conn_stat);
+                       DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type);
+                       (*mgr->cbs->hotplug)(mgr);
+
+               } else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
+                       drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false);
+                       DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn);
+               }
+
+               drm_dp_put_mst_branch_device(mstb);
+               memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+       }
+       return ret;
+}
+
+/**
+ * drm_dp_mst_hpd_irq() - MST hotplug IRQ notify
+ * @mgr: manager to notify irq for.
+ * @esi: 4 bytes from SINK_COUNT_ESI
+ *
+ * This should be called from the driver when it detects a short IRQ,
+ * along with the value of the DEVICE_SERVICE_IRQ_VECTOR_ESI0. The
+ * topology manager will process the sideband messages received as a result
+ * of this.
+ */
+int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled)
+{
+       int ret = 0;
+       int sc;
+       *handled = false;
+       sc = esi[0] & 0x3f;
+
+       if (sc != mgr->sink_count) {
+               mgr->sink_count = sc;
+               *handled = true;
+       }
+
+       if (esi[1] & DP_DOWN_REP_MSG_RDY) {
+               ret = drm_dp_mst_handle_down_rep(mgr);
+               *handled = true;
+       }
+
+       if (esi[1] & DP_UP_REQ_MSG_RDY) {
+               ret |= drm_dp_mst_handle_up_req(mgr);
+               *handled = true;
+       }
+
+       drm_dp_mst_kick_tx(mgr);
+       return ret;
+}
+EXPORT_SYMBOL(drm_dp_mst_hpd_irq);
+
+/**
+ * drm_dp_mst_detect_port() - get connection status for an MST port
+ * @mgr: manager for this port
+ * @port: unverified pointer to a port
+ *
+ * This returns the current connection state for a port. It validates the
+ * port pointer still exists so the caller doesn't require a reference
+ */
+enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+       enum drm_connector_status status = connector_status_disconnected;
+
+       /* we need to search for the port in the mgr in case its gone */
+       port = drm_dp_get_validated_port_ref(mgr, port);
+       if (!port)
+               return connector_status_disconnected;
+
+       if (!port->ddps)
+               goto out;
+
+       switch (port->pdt) {
+       case DP_PEER_DEVICE_NONE:
+       case DP_PEER_DEVICE_MST_BRANCHING:
+               break;
+
+       case DP_PEER_DEVICE_SST_SINK:
+               status = connector_status_connected;
+               break;
+       case DP_PEER_DEVICE_DP_LEGACY_CONV:
+               if (port->ldps)
+                       status = connector_status_connected;
+               break;
+       }
+out:
+       drm_dp_put_port(port);
+       return status;
+}
+EXPORT_SYMBOL(drm_dp_mst_detect_port);
+
+/**
+ * drm_dp_mst_get_edid() - get EDID for an MST port
+ * @connector: toplevel connector to get EDID for
+ * @mgr: manager for this port
+ * @port: unverified pointer to a port.
+ *
+ * This returns an EDID for the port connected to a connector,
+ * It validates the pointer still exists so the caller doesn't require a
+ * reference.
+ */
+struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+       struct edid *edid = NULL;
+
+       /* we need to search for the port in the mgr in case its gone */
+       port = drm_dp_get_validated_port_ref(mgr, port);
+       if (!port)
+               return NULL;
+
+       edid = drm_get_edid(connector, &port->aux.ddc);
+       drm_dp_put_port(port);
+       return edid;
+}
+EXPORT_SYMBOL(drm_dp_mst_get_edid);
+
+/**
+ * drm_dp_find_vcpi_slots() - find slots for this PBN value
+ * @mgr: manager to use
+ * @pbn: payload bandwidth to convert into slots.
+ */
+int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr,
+                          int pbn)
+{
+       int num_slots;
+
+       num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+
+       if (num_slots > mgr->avail_slots)
+               return -ENOSPC;
+       return num_slots;
+}
+EXPORT_SYMBOL(drm_dp_find_vcpi_slots);
+
+static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
+                           struct drm_dp_vcpi *vcpi, int pbn)
+{
+       int num_slots;
+       int ret;
+
+       num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+
+       if (num_slots > mgr->avail_slots)
+               return -ENOSPC;
+
+       vcpi->pbn = pbn;
+       vcpi->aligned_pbn = num_slots * mgr->pbn_div;
+       vcpi->num_slots = num_slots;
+
+       ret = drm_dp_mst_assign_payload_id(mgr, vcpi);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+/**
+ * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel
+ * @mgr: manager for this port
+ * @port: port to allocate a virtual channel for.
+ * @pbn: payload bandwidth number to request
+ * @slots: returned number of slots for this PBN.
+ */
+bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn, int *slots)
+{
+       int ret;
+
+       port = drm_dp_get_validated_port_ref(mgr, port);
+       if (!port)
+               return false;
+
+       if (port->vcpi.vcpi > 0) {
+               DRM_DEBUG_KMS("payload: vcpi %d already allocated for pbn %d - requested pbn %d\n", port->vcpi.vcpi, port->vcpi.pbn, pbn);
+               if (pbn == port->vcpi.pbn) {
+                       *slots = port->vcpi.num_slots;
+                       return true;
+               }
+       }
+
+       ret = drm_dp_init_vcpi(mgr, &port->vcpi, pbn);
+       if (ret) {
+               DRM_DEBUG_KMS("failed to init vcpi %d %d %d\n", DIV_ROUND_UP(pbn, mgr->pbn_div), mgr->avail_slots, ret);
+               goto out;
+       }
+       DRM_DEBUG_KMS("initing vcpi for %d %d\n", pbn, port->vcpi.num_slots);
+       *slots = port->vcpi.num_slots;
+
+       drm_dp_put_port(port);
+       return true;
+out:
+       return false;
+}
+EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi);
+
+/**
+ * drm_dp_mst_reset_vcpi_slots() - Reset number of slots to 0 for VCPI
+ * @mgr: manager for this port
+ * @port: unverified pointer to a port.
+ *
+ * This just resets the number of slots for the ports VCPI for later programming.
+ */
+void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+       port = drm_dp_get_validated_port_ref(mgr, port);
+       if (!port)
+               return;
+       port->vcpi.num_slots = 0;
+       drm_dp_put_port(port);
+}
+EXPORT_SYMBOL(drm_dp_mst_reset_vcpi_slots);
+
+/**
+ * drm_dp_mst_deallocate_vcpi() - deallocate a VCPI
+ * @mgr: manager for this port
+ * @port: unverified port to deallocate vcpi for
+ */
+void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+       port = drm_dp_get_validated_port_ref(mgr, port);
+       if (!port)
+               return;
+
+       drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
+       port->vcpi.num_slots = 0;
+       port->vcpi.pbn = 0;
+       port->vcpi.aligned_pbn = 0;
+       port->vcpi.vcpi = 0;
+       drm_dp_put_port(port);
+}
+EXPORT_SYMBOL(drm_dp_mst_deallocate_vcpi);
+
+static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
+                                    int id, struct drm_dp_payload *payload)
+{
+       u8 payload_alloc[3], status;
+       int ret;
+       int retries = 0;
+
+       drm_dp_dpcd_writeb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS,
+                          DP_PAYLOAD_TABLE_UPDATED);
+
+       payload_alloc[0] = id;
+       payload_alloc[1] = payload->start_slot;
+       payload_alloc[2] = payload->num_slots;
+
+       ret = drm_dp_dpcd_write(mgr->aux, DP_PAYLOAD_ALLOCATE_SET, payload_alloc, 3);
+       if (ret != 3) {
+               DRM_DEBUG_KMS("failed to write payload allocation %d\n", ret);
+               goto fail;
+       }
+
+retry:
+       ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
+       if (ret < 0) {
+               DRM_DEBUG_KMS("failed to read payload table status %d\n", ret);
+               goto fail;
+       }
+
+       if (!(status & DP_PAYLOAD_TABLE_UPDATED)) {
+               retries++;
+               if (retries < 20) {
+                       usleep_range(10000, 20000);
+                       goto retry;
+               }
+               DRM_DEBUG_KMS("status not set after read payload table status %d\n", status);
+               ret = -EINVAL;
+               goto fail;
+       }
+       ret = 0;
+fail:
+       return ret;
+}
+
+
+/**
+ * drm_dp_check_act_status() - Check ACT handled status.
+ * @mgr: manager to use
+ *
+ * Check the payload status bits in the DPCD for ACT handled completion.
+ */
+int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr)
+{
+       u8 status;
+       int ret;
+       int count = 0;
+
+       do {
+               ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
+
+               if (ret < 0) {
+                       DRM_DEBUG_KMS("failed to read payload table status %d\n", ret);
+                       goto fail;
+               }
+
+               if (status & DP_PAYLOAD_ACT_HANDLED)
+                       break;
+               count++;
+               udelay(100);
+
+       } while (count < 30);
+
+       if (!(status & DP_PAYLOAD_ACT_HANDLED)) {
+               DRM_DEBUG_KMS("failed to get ACT bit %d after %d retries\n", status, count);
+               ret = -EINVAL;
+               goto fail;
+       }
+       return 0;
+fail:
+       return ret;
+}
+EXPORT_SYMBOL(drm_dp_check_act_status);
+
+/**
+ * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode.
+ * @clock: dot clock for the mode
+ * @bpp: bpp for the mode.
+ *
+ * This uses the formula in the spec to calculate the PBN value for a mode.
+ */
+int drm_dp_calc_pbn_mode(int clock, int bpp)
+{
+       fixed20_12 pix_bw;
+       fixed20_12 fbpp;
+       fixed20_12 result;
+       fixed20_12 margin, tmp;
+       u32 res;
+
+       pix_bw.full = dfixed_const(clock);
+       fbpp.full = dfixed_const(bpp);
+       tmp.full = dfixed_const(8);
+       fbpp.full = dfixed_div(fbpp, tmp);
+
+       result.full = dfixed_mul(pix_bw, fbpp);
+       margin.full = dfixed_const(54);
+       tmp.full = dfixed_const(64);
+       margin.full = dfixed_div(margin, tmp);
+       result.full = dfixed_div(result, margin);
+
+       margin.full = dfixed_const(1006);
+       tmp.full = dfixed_const(1000);
+       margin.full = dfixed_div(margin, tmp);
+       result.full = dfixed_mul(result, margin);
+
+       result.full = dfixed_div(result, tmp);
+       result.full = dfixed_ceil(result);
+       res = dfixed_trunc(result);
+       return res;
+}
+EXPORT_SYMBOL(drm_dp_calc_pbn_mode);
+
+static int test_calc_pbn_mode(void)
+{
+       int ret;
+       ret = drm_dp_calc_pbn_mode(154000, 30);
+       if (ret != 689)
+               return -EINVAL;
+       ret = drm_dp_calc_pbn_mode(234000, 30);
+       if (ret != 1047)
+               return -EINVAL;
+       return 0;
+}
+
+/* we want to kick the TX after we've ack the up/down IRQs. */
+static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr)
+{
+       queue_work(system_long_wq, &mgr->tx_work);
+}
+
+static void drm_dp_mst_dump_mstb(struct seq_file *m,
+                                struct drm_dp_mst_branch *mstb)
+{
+       struct drm_dp_mst_port *port;
+       int tabs = mstb->lct;
+       char prefix[10];
+       int i;
+
+       for (i = 0; i < tabs; i++)
+               prefix[i] = '\t';
+       prefix[i] = '\0';
+
+       seq_printf(m, "%smst: %p, %d\n", prefix, mstb, mstb->num_ports);
+       list_for_each_entry(port, &mstb->ports, next) {
+               seq_printf(m, "%sport: %d: ddps: %d ldps: %d, %p, conn: %p\n", prefix, port->port_num, port->ddps, port->ldps, port, port->connector);
+               if (port->mstb)
+                       drm_dp_mst_dump_mstb(m, port->mstb);
+       }
+}
+
+static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
+                                 char *buf)
+{
+       int ret;
+       int i;
+       for (i = 0; i < 4; i++) {
+               ret = drm_dp_dpcd_read(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS + (i * 16), &buf[i * 16], 16);
+               if (ret != 16)
+                       break;
+       }
+       if (i == 4)
+               return true;
+       return false;
+}
+
+/**
+ * drm_dp_mst_dump_topology(): dump topology to seq file.
+ * @m: seq_file to dump output to
+ * @mgr: manager to dump current topology for.
+ *
+ * helper to dump MST topology to a seq file for debugfs.
+ */
+void drm_dp_mst_dump_topology(struct seq_file *m,
+                             struct drm_dp_mst_topology_mgr *mgr)
+{
+       int i;
+       struct drm_dp_mst_port *port;
+       mutex_lock(&mgr->lock);
+       if (mgr->mst_primary)
+               drm_dp_mst_dump_mstb(m, mgr->mst_primary);
+
+       /* dump VCPIs */
+       mutex_unlock(&mgr->lock);
+
+       mutex_lock(&mgr->payload_lock);
+       seq_printf(m, "vcpi: %lx\n", mgr->payload_mask);
+
+       for (i = 0; i < mgr->max_payloads; i++) {
+               if (mgr->proposed_vcpis[i]) {
+                       port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+                       seq_printf(m, "vcpi %d: %d %d %d\n", i, port->port_num, port->vcpi.vcpi, port->vcpi.num_slots);
+               } else
+                       seq_printf(m, "vcpi %d:unsed\n", i);
+       }
+       for (i = 0; i < mgr->max_payloads; i++) {
+               seq_printf(m, "payload %d: %d, %d, %d\n",
+                          i,
+                          mgr->payloads[i].payload_state,
+                          mgr->payloads[i].start_slot,
+                          mgr->payloads[i].num_slots);
+
+
+       }
+       mutex_unlock(&mgr->payload_lock);
+
+       mutex_lock(&mgr->lock);
+       if (mgr->mst_primary) {
+               u8 buf[64];
+               bool bret;
+               int ret;
+               ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, buf, DP_RECEIVER_CAP_SIZE);
+               seq_printf(m, "dpcd: ");
+               for (i = 0; i < DP_RECEIVER_CAP_SIZE; i++)
+                       seq_printf(m, "%02x ", buf[i]);
+               seq_printf(m, "\n");
+               ret = drm_dp_dpcd_read(mgr->aux, DP_FAUX_CAP, buf, 2);
+               seq_printf(m, "faux/mst: ");
+               for (i = 0; i < 2; i++)
+                       seq_printf(m, "%02x ", buf[i]);
+               seq_printf(m, "\n");
+               ret = drm_dp_dpcd_read(mgr->aux, DP_MSTM_CTRL, buf, 1);
+               seq_printf(m, "mst ctrl: ");
+               for (i = 0; i < 1; i++)
+                       seq_printf(m, "%02x ", buf[i]);
+               seq_printf(m, "\n");
+
+               bret = dump_dp_payload_table(mgr, buf);
+               if (bret == true) {
+                       seq_printf(m, "payload table: ");
+                       for (i = 0; i < 63; i++)
+                               seq_printf(m, "%02x ", buf[i]);
+                       seq_printf(m, "\n");
+               }
+
+       }
+
+       mutex_unlock(&mgr->lock);
+
+}
+EXPORT_SYMBOL(drm_dp_mst_dump_topology);
+
+static void drm_dp_tx_work(struct work_struct *work)
+{
+       struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, tx_work);
+
+       mutex_lock(&mgr->qlock);
+       if (mgr->tx_down_in_progress)
+               process_single_down_tx_qlock(mgr);
+       mutex_unlock(&mgr->qlock);
+}
+
+/**
+ * drm_dp_mst_topology_mgr_init - initialise a topology manager
+ * @mgr: manager struct to initialise
+ * @dev: device providing this structure - for i2c addition.
+ * @aux: DP helper aux channel to talk to this device
+ * @max_dpcd_transaction_bytes: hw specific DPCD transaction limit
+ * @max_payloads: maximum number of payloads this GPU can source
+ * @conn_base_id: the connector object ID the MST device is connected to.
+ *
+ * Return 0 for success, or negative error code on failure
+ */
+int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
+                                struct device *dev, struct drm_dp_aux *aux,
+                                int max_dpcd_transaction_bytes,
+                                int max_payloads, int conn_base_id)
+{
+       mutex_init(&mgr->lock);
+       mutex_init(&mgr->qlock);
+       mutex_init(&mgr->payload_lock);
+       INIT_LIST_HEAD(&mgr->tx_msg_upq);
+       INIT_LIST_HEAD(&mgr->tx_msg_downq);
+       INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
+       INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
+       init_waitqueue_head(&mgr->tx_waitq);
+       mgr->dev = dev;
+       mgr->aux = aux;
+       mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes;
+       mgr->max_payloads = max_payloads;
+       mgr->conn_base_id = conn_base_id;
+       mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL);
+       if (!mgr->payloads)
+               return -ENOMEM;
+       mgr->proposed_vcpis = kcalloc(max_payloads, sizeof(struct drm_dp_vcpi *), GFP_KERNEL);
+       if (!mgr->proposed_vcpis)
+               return -ENOMEM;
+       set_bit(0, &mgr->payload_mask);
+       test_calc_pbn_mode();
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
+
+/**
+ * drm_dp_mst_topology_mgr_destroy() - destroy topology manager.
+ * @mgr: manager to destroy
+ */
+void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
+{
+       mutex_lock(&mgr->payload_lock);
+       kfree(mgr->payloads);
+       mgr->payloads = NULL;
+       kfree(mgr->proposed_vcpis);
+       mgr->proposed_vcpis = NULL;
+       mutex_unlock(&mgr->payload_lock);
+       mgr->dev = NULL;
+       mgr->aux = NULL;
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
+
+/* I2C device */
+static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
+                              int num)
+{
+       struct drm_dp_aux *aux = adapter->algo_data;
+       struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port, aux);
+       struct drm_dp_mst_branch *mstb;
+       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+       unsigned int i;
+       bool reading = false;
+       struct drm_dp_sideband_msg_req_body msg;
+       struct drm_dp_sideband_msg_tx *txmsg = NULL;
+       int ret;
+
+       mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
+       if (!mstb)
+               return -EREMOTEIO;
+
+       /* construct i2c msg */
+       /* see if last msg is a read */
+       if (msgs[num - 1].flags & I2C_M_RD)
+               reading = true;
+
+       if (!reading) {
+               DRM_DEBUG_KMS("Unsupported I2C transaction for MST device\n");
+               ret = -EIO;
+               goto out;
+       }
+
+       msg.req_type = DP_REMOTE_I2C_READ;
+       msg.u.i2c_read.num_transactions = num - 1;
+       msg.u.i2c_read.port_number = port->port_num;
+       for (i = 0; i < num - 1; i++) {
+               msg.u.i2c_read.transactions[i].i2c_dev_id = msgs[i].addr;
+               msg.u.i2c_read.transactions[i].num_bytes = msgs[i].len;
+               msg.u.i2c_read.transactions[i].bytes = msgs[i].buf;
+       }
+       msg.u.i2c_read.read_i2c_device_id = msgs[num - 1].addr;
+       msg.u.i2c_read.num_bytes_read = msgs[num - 1].len;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       txmsg->dst = mstb;
+       drm_dp_encode_sideband_req(&msg, txmsg);
+
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+       if (ret > 0) {
+
+               if (txmsg->reply.reply_type == 1) { /* got a NAK back */
+                       ret = -EREMOTEIO;
+                       goto out;
+               }
+               if (txmsg->reply.u.remote_i2c_read_ack.num_bytes != msgs[num - 1].len) {
+                       ret = -EIO;
+                       goto out;
+               }
+               memcpy(msgs[num - 1].buf, txmsg->reply.u.remote_i2c_read_ack.bytes, msgs[num - 1].len);
+               ret = num;
+       }
+out:
+       kfree(txmsg);
+       drm_dp_put_mst_branch_device(mstb);
+       return ret;
+}
+
+static u32 drm_dp_mst_i2c_functionality(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+              I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+              I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+              I2C_FUNC_10BIT_ADDR;
+}
+
+static const struct i2c_algorithm drm_dp_mst_i2c_algo = {
+       .functionality = drm_dp_mst_i2c_functionality,
+       .master_xfer = drm_dp_mst_i2c_xfer,
+};
+
+/**
+ * drm_dp_mst_register_i2c_bus() - register an I2C adapter for I2C-over-AUX
+ * @aux: DisplayPort AUX channel
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux)
+{
+       aux->ddc.algo = &drm_dp_mst_i2c_algo;
+       aux->ddc.algo_data = aux;
+       aux->ddc.retries = 3;
+
+       aux->ddc.class = I2C_CLASS_DDC;
+       aux->ddc.owner = THIS_MODULE;
+       aux->ddc.dev.parent = aux->dev;
+       aux->ddc.dev.of_node = aux->dev->of_node;
+
+       strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev),
+               sizeof(aux->ddc.name));
+
+       return i2c_add_adapter(&aux->ddc);
+}
+
+/**
+ * drm_dp_mst_unregister_i2c_bus() - unregister an I2C-over-AUX adapter
+ * @aux: DisplayPort AUX channel
+ */
+static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux)
+{
+       i2c_del_adapter(&aux->ddc);
+}
index 759d6b9..150355f 100644 (file)
@@ -1,31 +1,11 @@
-/**
- * \file drm_drv.c
- * Generic driver template
- *
- * \author Rickard E. (Rik) Faith <faith@valinux.com>
- * \author Gareth Hughes <gareth@valinux.com>
- *
- * To use this template, you must at least define the following (samples
- * given for the MGA driver):
- *
- * \code
- * #define DRIVER_AUTHOR       "VA Linux Systems, Inc."
- *
- * #define DRIVER_NAME         "mga"
- * #define DRIVER_DESC         "Matrox G200/G400"
- * #define DRIVER_DATE         "20001127"
- *
- * #define drm_x               mga_##x
- * \endcode
- */
-
 /*
- * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com
+ * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org
  *
- * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
- * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California.
  * All Rights Reserved.
  *
+ * Author Rickard E. (Rik) Faith <faith@valinux.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 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
- * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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.
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <drm/drmP.h>
+#include <drm/drm_core.h>
+#include "drm_legacy.h"
+
+unsigned int drm_debug = 0;    /* 1 to enable debug output */
+EXPORT_SYMBOL(drm_debug);
+
+unsigned int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
+
+unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
+
+/*
+ * Default to use monotonic timestamps for wait-for-vblank and page-flip
+ * complete events.
+ */
+unsigned int drm_timestamp_monotonic = 1;
+
+MODULE_AUTHOR(CORE_AUTHOR);
+MODULE_DESCRIPTION(CORE_DESC);
+MODULE_LICENSE("GPL and additional rights");
+MODULE_PARM_DESC(debug, "Enable debug output");
+MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]");
+MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
+MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
+
+module_param_named(debug, drm_debug, int, 0600);
+module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
+module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
+module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
+
+#if 0
+static DEFINE_SPINLOCK(drm_minor_lock);
+static struct idr drm_minors_idr;
+#endif
+
+struct class *drm_class;
+#if 0
+static struct dentry *drm_debugfs_root;
+#endif
+
+int drm_err(const char *func, const char *format, ...)
+{
+#if 0
+       struct va_format vaf;
+       va_list args;
+       int r;
+
+       va_start(args, format);
+
+       vaf.fmt = format;
+       vaf.va = &args;
+
+       r = printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* %pV", func, &vaf);
+
+       va_end(args);
+
+       return r;
+#endif
+       return 0;
+}
+EXPORT_SYMBOL(drm_err);
+
+void drm_ut_debug_printk(const char *function_name, const char *format, ...)
+{
+#if 0
+       struct va_format vaf;
+       va_list args;
+
+       va_start(args, format);
+       vaf.fmt = format;
+       vaf.va = &args;
+
+       printk(KERN_DEBUG "[" DRM_NAME ":%s] %pV", function_name, &vaf);
+
+       va_end(args);
+#endif
+}
+EXPORT_SYMBOL(drm_ut_debug_printk);
+
+#if 0
+struct drm_master *drm_master_create(struct drm_minor *minor)
+{
+       struct drm_master *master;
+
+       master = kzalloc(sizeof(*master), GFP_KERNEL);
+       if (!master)
+               return NULL;
+
+       kref_init(&master->refcount);
+       spin_lock_init(&master->lock.spinlock);
+       init_waitqueue_head(&master->lock.lock_queue);
+       if (drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER)) {
+               kfree(master);
+               return NULL;
+       }
+       INIT_LIST_HEAD(&master->magicfree);
+       master->minor = minor;
+
+       return master;
+}
+
+struct drm_master *drm_master_get(struct drm_master *master)
+{
+       kref_get(&master->refcount);
+       return master;
+}
+EXPORT_SYMBOL(drm_master_get);
+
+static void drm_master_destroy(struct kref *kref)
+{
+       struct drm_master *master = container_of(kref, struct drm_master, refcount);
+       struct drm_magic_entry *pt, *next;
+       struct drm_device *dev = master->minor->dev;
+       struct drm_map_list *r_list, *list_temp;
+
+       mutex_lock(&dev->struct_mutex);
+       if (dev->driver->master_destroy)
+               dev->driver->master_destroy(dev, master);
+
+       list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) {
+               if (r_list->master == master) {
+                       drm_rmmap_locked(dev, r_list->map);
+                       r_list = NULL;
+               }
+       }
+
+       if (master->unique) {
+               kfree(master->unique);
+               master->unique = NULL;
+               master->unique_len = 0;
+       }
+
+       list_for_each_entry_safe(pt, next, &master->magicfree, head) {
+               list_del(&pt->head);
+               drm_ht_remove_item(&master->magiclist, &pt->hash_item);
+               kfree(pt);
+       }
+
+       drm_ht_remove(&master->magiclist);
+
+       mutex_unlock(&dev->struct_mutex);
+       kfree(master);
+}
+
+void drm_master_put(struct drm_master **master)
+{
+       kref_put(&(*master)->refcount, drm_master_destroy);
+       *master = NULL;
+}
+EXPORT_SYMBOL(drm_master_put);
+#endif
+
+int drm_setmaster_ioctl(struct drm_device *dev, void *data,
+                       struct drm_file *file_priv)
+{
+       DRM_DEBUG("setmaster\n");
+
+       if (file_priv->master != 0)
+               return (0);
+
+       return (-EPERM);
+}
+
+int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
+                        struct drm_file *file_priv)
+{
+       DRM_DEBUG("dropmaster\n");
+       if (file_priv->master != 0)
+               return -EINVAL;
+       return 0;
+}
+
+#if 0
+/*
+ * DRM Minors
+ * A DRM device can provide several char-dev interfaces on the DRM-Major. Each
+ * of them is represented by a drm_minor object. Depending on the capabilities
+ * of the device-driver, different interfaces are registered.
+ *
+ * Minors can be accessed via dev->$minor_name. This pointer is either
+ * NULL or a valid drm_minor pointer and stays valid as long as the device is
+ * valid. This means, DRM minors have the same life-time as the underlying
+ * device. However, this doesn't mean that the minor is active. Minors are
+ * registered and unregistered dynamically according to device-state.
+ */
+
+static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,
+                                            unsigned int type)
+{
+       switch (type) {
+       case DRM_MINOR_LEGACY:
+               return &dev->primary;
+       case DRM_MINOR_RENDER:
+               return &dev->render;
+       case DRM_MINOR_CONTROL:
+               return &dev->control;
+       default:
+               return NULL;
+       }
+}
+
+static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
+{
+       struct drm_minor *minor;
+       unsigned long flags;
+       int r;
+
+       minor = kzalloc(sizeof(*minor), GFP_KERNEL);
+       if (!minor)
+               return -ENOMEM;
+
+       minor->type = type;
+       minor->dev = dev;
+
+       idr_preload(GFP_KERNEL);
+       spin_lock_irqsave(&drm_minor_lock, flags);
+       r = idr_alloc(&drm_minors_idr,
+                     NULL,
+                     64 * type,
+                     64 * (type + 1),
+                     GFP_NOWAIT);
+       spin_unlock_irqrestore(&drm_minor_lock, flags);
+       idr_preload_end();
+
+       if (r < 0)
+               goto err_free;
+
+       minor->index = r;
+
+       minor->kdev = drm_sysfs_minor_alloc(minor);
+       if (IS_ERR(minor->kdev)) {
+               r = PTR_ERR(minor->kdev);
+               goto err_index;
+       }
+
+       *drm_minor_get_slot(dev, type) = minor;
+       return 0;
+
+err_index:
+       spin_lock_irqsave(&drm_minor_lock, flags);
+       idr_remove(&drm_minors_idr, minor->index);
+       spin_unlock_irqrestore(&drm_minor_lock, flags);
+err_free:
+       kfree(minor);
+       return r;
+}
+
+static void drm_minor_free(struct drm_device *dev, unsigned int type)
+{
+       struct drm_minor **slot, *minor;
+       unsigned long flags;
+
+       slot = drm_minor_get_slot(dev, type);
+       minor = *slot;
+       if (!minor)
+               return;
+
+       drm_mode_group_destroy(&minor->mode_group);
+       put_device(minor->kdev);
+
+       spin_lock_irqsave(&drm_minor_lock, flags);
+       idr_remove(&drm_minors_idr, minor->index);
+       spin_unlock_irqrestore(&drm_minor_lock, flags);
+
+       kfree(minor);
+       *slot = NULL;
+}
+
+static int drm_minor_register(struct drm_device *dev, unsigned int type)
+{
+       struct drm_minor *minor;
+       unsigned long flags;
+       int ret;
+
+       DRM_DEBUG("\n");
+
+       minor = *drm_minor_get_slot(dev, type);
+       if (!minor)
+               return 0;
+
+       ret = drm_debugfs_init(minor, minor->index, drm_debugfs_root);
+       if (ret) {
+               DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");
+               return ret;
+       }
+
+       ret = device_add(minor->kdev);
+       if (ret)
+               goto err_debugfs;
+
+       /* replace NULL with @minor so lookups will succeed from now on */
+       spin_lock_irqsave(&drm_minor_lock, flags);
+       idr_replace(&drm_minors_idr, minor, minor->index);
+       spin_unlock_irqrestore(&drm_minor_lock, flags);
+
+       DRM_DEBUG("new minor registered %d\n", minor->index);
+       return 0;
+
+err_debugfs:
+       drm_debugfs_cleanup(minor);
+       return ret;
+}
+
+static void drm_minor_unregister(struct drm_device *dev, unsigned int type)
+{
+       struct drm_minor *minor;
+       unsigned long flags;
+
+       minor = *drm_minor_get_slot(dev, type);
+       if (!minor || !device_is_registered(minor->kdev))
+               return;
+
+       /* replace @minor with NULL so lookups will fail from now on */
+       spin_lock_irqsave(&drm_minor_lock, flags);
+       idr_replace(&drm_minors_idr, NULL, minor->index);
+       spin_unlock_irqrestore(&drm_minor_lock, flags);
+
+       device_del(minor->kdev);
+       dev_set_drvdata(minor->kdev, NULL); /* safety belt */
+       drm_debugfs_cleanup(minor);
+}
+
+/**
+ * drm_minor_acquire - Acquire a DRM minor
+ * @minor_id: Minor ID of the DRM-minor
+ *
+ * Looks up the given minor-ID and returns the respective DRM-minor object. The
+ * refence-count of the underlying device is increased so you must release this
+ * object with drm_minor_release().
+ *
+ * As long as you hold this minor, it is guaranteed that the object and the
+ * minor->dev pointer will stay valid! However, the device may get unplugged and
+ * unregistered while you hold the minor.
+ *
+ * Returns:
+ * Pointer to minor-object with increased device-refcount, or PTR_ERR on
+ * failure.
+ */
+struct drm_minor *drm_minor_acquire(unsigned int minor_id)
+{
+       struct drm_minor *minor;
+       unsigned long flags;
+
+       spin_lock_irqsave(&drm_minor_lock, flags);
+       minor = idr_find(&drm_minors_idr, minor_id);
+       if (minor)
+               drm_dev_ref(minor->dev);
+       spin_unlock_irqrestore(&drm_minor_lock, flags);
+
+       if (!minor) {
+               return ERR_PTR(-ENODEV);
+       } else if (drm_device_is_unplugged(minor->dev)) {
+               drm_dev_unref(minor->dev);
+               return ERR_PTR(-ENODEV);
+       }
+
+       return minor;
+}
+
+/**
+ * drm_minor_release - Release DRM minor
+ * @minor: Pointer to DRM minor object
+ *
+ * Release a minor that was previously acquired via drm_minor_acquire().
+ */
+void drm_minor_release(struct drm_minor *minor)
+{
+       drm_dev_unref(minor->dev);
+}
+
+/**
+ * drm_put_dev - Unregister and release a DRM device
+ * @dev: DRM device
+ *
+ * Called at module unload time or when a PCI device is unplugged.
+ *
+ * Use of this function is discouraged. It will eventually go away completely.
+ * Please use drm_dev_unregister() and drm_dev_unref() explicitly instead.
+ *
+ * Cleans up all DRM device, calling drm_lastclose().
+ */
+void drm_put_dev(struct drm_device *dev)
+{
+       DRM_DEBUG("\n");
+
+       if (!dev) {
+               DRM_ERROR("cleanup called no dev\n");
+               return;
+       }
+
+       drm_dev_unregister(dev);
+       drm_dev_unref(dev);
+}
+EXPORT_SYMBOL(drm_put_dev);
+
+void drm_unplug_dev(struct drm_device *dev)
+{
+       /* for a USB device */
+       drm_minor_unregister(dev, DRM_MINOR_LEGACY);
+       drm_minor_unregister(dev, DRM_MINOR_RENDER);
+       drm_minor_unregister(dev, DRM_MINOR_CONTROL);
+
+       mutex_lock(&drm_global_mutex);
+
+       drm_device_set_unplugged(dev);
+
+       if (dev->open_count == 0) {
+               drm_put_dev(dev);
+       }
+       mutex_unlock(&drm_global_mutex);
+}
+EXPORT_SYMBOL(drm_unplug_dev);
+
+/*
+ * DRM internal mount
+ * We want to be able to allocate our own "struct address_space" to control
+ * memory-mappings in VRAM (or stolen RAM, ...). However, core MM does not allow
+ * stand-alone address_space objects, so we need an underlying inode. As there
+ * is no way to allocate an independent inode easily, we need a fake internal
+ * VFS mount-point.
+ *
+ * The drm_fs_inode_new() function allocates a new inode, drm_fs_inode_free()
+ * frees it again. You are allowed to use iget() and iput() to get references to
+ * the inode. But each drm_fs_inode_new() call must be paired with exactly one
+ * drm_fs_inode_free() call (which does not have to be the last iput()).
+ * We use drm_fs_inode_*() to manage our internal VFS mount-point and share it
+ * between multiple inode-users. You could, technically, call
+ * iget() + drm_fs_inode_free() directly after alloc and sometime later do an
+ * iput(), but this way you'd end up with a new vfsmount for each inode.
+ */
+
+static int drm_fs_cnt;
+static struct vfsmount *drm_fs_mnt;
+
+static const struct dentry_operations drm_fs_dops = {
+       .d_dname        = simple_dname,
+};
+
+static const struct super_operations drm_fs_sops = {
+       .statfs         = simple_statfs,
+};
+
+static struct dentry *drm_fs_mount(struct file_system_type *fs_type, int flags,
+                                  const char *dev_name, void *data)
+{
+       return mount_pseudo(fs_type,
+                           "drm:",
+                           &drm_fs_sops,
+                           &drm_fs_dops,
+                           0x010203ff);
+}
+
+static struct file_system_type drm_fs_type = {
+       .name           = "drm",
+       .owner          = THIS_MODULE,
+       .mount          = drm_fs_mount,
+       .kill_sb        = kill_anon_super,
+};
+
+static struct inode *drm_fs_inode_new(void)
+{
+       struct inode *inode;
+       int r;
+
+       r = simple_pin_fs(&drm_fs_type, &drm_fs_mnt, &drm_fs_cnt);
+       if (r < 0) {
+               DRM_ERROR("Cannot mount pseudo fs: %d\n", r);
+               return ERR_PTR(r);
+       }
+
+       inode = alloc_anon_inode(drm_fs_mnt->mnt_sb);
+       if (IS_ERR(inode))
+               simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);
+
+       return inode;
+}
+
+static void drm_fs_inode_free(struct inode *inode)
+{
+       if (inode) {
+               iput(inode);
+               simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);
+       }
+}
+
+/**
+ * drm_dev_alloc - Allocate new DRM device
+ * @driver: DRM driver to allocate device for
+ * @parent: Parent device object
+ *
+ * Allocate and initialize a new DRM device. No device registration is done.
+ * Call drm_dev_register() to advertice the device to user space and register it
+ * with other core subsystems.
+ *
+ * The initial ref-count of the object is 1. Use drm_dev_ref() and
+ * drm_dev_unref() to take and drop further ref-counts.
+ *
+ * RETURNS:
+ * Pointer to new DRM device, or NULL if out of memory.
+ */
+struct drm_device *drm_dev_alloc(struct drm_driver *driver,
+                                struct device *parent)
+{
+       struct drm_device *dev;
+       int ret;
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return NULL;
+
+       kref_init(&dev->ref);
+       dev->dev = parent;
+       dev->driver = driver;
+
+       INIT_LIST_HEAD(&dev->filelist);
+       INIT_LIST_HEAD(&dev->ctxlist);
+       INIT_LIST_HEAD(&dev->vmalist);
+       INIT_LIST_HEAD(&dev->maplist);
+       INIT_LIST_HEAD(&dev->vblank_event_list);
+
+       spin_lock_init(&dev->buf_lock);
+       spin_lock_init(&dev->event_lock);
+       mutex_init(&dev->struct_mutex);
+       mutex_init(&dev->ctxlist_mutex);
+       mutex_init(&dev->master_mutex);
+
+       dev->anon_inode = drm_fs_inode_new();
+       if (IS_ERR(dev->anon_inode)) {
+               ret = PTR_ERR(dev->anon_inode);
+               DRM_ERROR("Cannot allocate anonymous inode: %d\n", ret);
+               goto err_free;
+       }
+
+       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+               ret = drm_minor_alloc(dev, DRM_MINOR_CONTROL);
+               if (ret)
+                       goto err_minors;
+       }
+
+       if (drm_core_check_feature(dev, DRIVER_RENDER)) {
+               ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
+               if (ret)
+                       goto err_minors;
+       }
+
+       ret = drm_minor_alloc(dev, DRM_MINOR_LEGACY);
+       if (ret)
+               goto err_minors;
+
+       if (drm_ht_create(&dev->map_hash, 12))
+               goto err_minors;
+
+       ret = drm_legacy_ctxbitmap_init(dev);
+       if (ret) {
+               DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+               goto err_ht;
+       }
+
+       if (driver->driver_features & DRIVER_GEM) {
+               ret = drm_gem_init(dev);
+               if (ret) {
+                       DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n");
+                       goto err_ctxbitmap;
+               }
+       }
+
+       return dev;
+
+err_ctxbitmap:
+       drm_legacy_ctxbitmap_cleanup(dev);
+err_ht:
+       drm_ht_remove(&dev->map_hash);
+err_minors:
+       drm_minor_free(dev, DRM_MINOR_LEGACY);
+       drm_minor_free(dev, DRM_MINOR_RENDER);
+       drm_minor_free(dev, DRM_MINOR_CONTROL);
+       drm_fs_inode_free(dev->anon_inode);
+err_free:
+       mutex_destroy(&dev->master_mutex);
+       kfree(dev);
+       return NULL;
+}
+EXPORT_SYMBOL(drm_dev_alloc);
+
+static void drm_dev_release(struct kref *ref)
+{
+       struct drm_device *dev = container_of(ref, struct drm_device, ref);
+
+       if (dev->driver->driver_features & DRIVER_GEM)
+               drm_gem_destroy(dev);
+
+       drm_legacy_ctxbitmap_cleanup(dev);
+       drm_ht_remove(&dev->map_hash);
+       drm_fs_inode_free(dev->anon_inode);
+
+       drm_minor_free(dev, DRM_MINOR_LEGACY);
+       drm_minor_free(dev, DRM_MINOR_RENDER);
+       drm_minor_free(dev, DRM_MINOR_CONTROL);
+
+       mutex_destroy(&dev->master_mutex);
+       kfree(dev->unique);
+       kfree(dev);
+}
+
+/**
+ * drm_dev_ref - Take reference of a DRM device
+ * @dev: device to take reference of or NULL
+ *
+ * This increases the ref-count of @dev by one. You *must* already own a
+ * reference when calling this. Use drm_dev_unref() to drop this reference
+ * again.
+ *
+ * This function never fails. However, this function does not provide *any*
+ * guarantee whether the device is alive or running. It only provides a
+ * reference to the object and the memory associated with it.
  */
+void drm_dev_ref(struct drm_device *dev)
+{
+       if (dev)
+               kref_get(&dev->ref);
+}
+EXPORT_SYMBOL(drm_dev_ref);
+
+/**
+ * drm_dev_unref - Drop reference of a DRM device
+ * @dev: device to drop reference of or NULL
+ *
+ * This decreases the ref-count of @dev by one. The device is destroyed if the
+ * ref-count drops to zero.
+ */
+void drm_dev_unref(struct drm_device *dev)
+{
+       if (dev)
+               kref_put(&dev->ref, drm_dev_release);
+}
+EXPORT_SYMBOL(drm_dev_unref);
+
+/**
+ * drm_dev_register - Register DRM device
+ * @dev: Device to register
+ * @flags: Flags passed to the driver's .load() function
+ *
+ * Register the DRM device @dev with the system, advertise device to user-space
+ * and start normal device operation. @dev must be allocated via drm_dev_alloc()
+ * previously.
+ *
+ * Never call this twice on any device!
+ *
+ * RETURNS:
+ * 0 on success, negative error code on failure.
+ */
+int drm_dev_register(struct drm_device *dev, unsigned long flags)
+{
+       int ret;
+
+       mutex_lock(&drm_global_mutex);
+
+       ret = drm_minor_register(dev, DRM_MINOR_CONTROL);
+       if (ret)
+               goto err_minors;
+
+       ret = drm_minor_register(dev, DRM_MINOR_RENDER);
+       if (ret)
+               goto err_minors;
+
+       ret = drm_minor_register(dev, DRM_MINOR_LEGACY);
+       if (ret)
+               goto err_minors;
+
+       if (dev->driver->load) {
+               ret = dev->driver->load(dev, flags);
+               if (ret)
+                       goto err_minors;
+       }
+
+       /* setup grouping for legacy outputs */
+       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+               ret = drm_mode_group_init_legacy_group(dev,
+                               &dev->primary->mode_group);
+               if (ret)
+                       goto err_unload;
+       }
+
+       ret = 0;
+       goto out_unlock;
+
+err_unload:
+       if (dev->driver->unload)
+               dev->driver->unload(dev);
+err_minors:
+       drm_minor_unregister(dev, DRM_MINOR_LEGACY);
+       drm_minor_unregister(dev, DRM_MINOR_RENDER);
+       drm_minor_unregister(dev, DRM_MINOR_CONTROL);
+out_unlock:
+       mutex_unlock(&drm_global_mutex);
+       return ret;
+}
+EXPORT_SYMBOL(drm_dev_register);
+
+/**
+ * drm_dev_unregister - Unregister DRM device
+ * @dev: Device to unregister
+ *
+ * Unregister the DRM device from the system. This does the reverse of
+ * drm_dev_register() but does not deallocate the device. The caller must call
+ * drm_dev_unref() to drop their final reference.
+ */
+void drm_dev_unregister(struct drm_device *dev)
+{
+       struct drm_map_list *r_list, *list_temp;
+
+       drm_lastclose(dev);
+
+       if (dev->driver->unload)
+               dev->driver->unload(dev);
+
+       if (dev->agp)
+               drm_pci_agp_destroy(dev);
+
+       drm_vblank_cleanup(dev);
+
+       list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
+               drm_rmmap(dev, r_list->map);
+
+       drm_minor_unregister(dev, DRM_MINOR_LEGACY);
+       drm_minor_unregister(dev, DRM_MINOR_RENDER);
+       drm_minor_unregister(dev, DRM_MINOR_CONTROL);
+}
+EXPORT_SYMBOL(drm_dev_unregister);
+
+/**
+ * drm_dev_set_unique - Set the unique name of a DRM device
+ * @dev: device of which to set the unique name
+ * @fmt: format string for unique name
+ *
+ * Sets the unique name of a DRM device using the specified format string and
+ * a variable list of arguments. Drivers can use this at driver probe time if
+ * the unique name of the devices they drive is static.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_dev_set_unique(struct drm_device *dev, const char *fmt, ...)
+{
+       va_list ap;
+
+       kfree(dev->unique);
+
+       va_start(ap, fmt);
+       dev->unique = kvasprintf(GFP_KERNEL, fmt, ap);
+       va_end(ap);
+
+       return dev->unique ? 0 : -ENOMEM;
+}
+EXPORT_SYMBOL(drm_dev_set_unique);
+#endif
+
+/*
+ * DRM Core
+ * The DRM core module initializes all global DRM objects and makes them
+ * available to drivers. Once setup, drivers can probe their respective
+ * devices.
+ * Currently, core management includes:
+ *  - The "DRM-Global" key/value database
+ *  - Global ID management for connectors
+ *  - DRM major number allocation
+ *  - DRM minor management
+ *  - DRM sysfs class
+ *  - DRM debugfs root
+ *
+ * Furthermore, the DRM core provides dynamic char-dev lookups. For each
+ * interface registered on a DRM device, you can request minor numbers from DRM
+ * core. DRM core takes care of major-number management and char-dev
+ * registration. A stub ->open() callback forwards any open() requests to the
+ * registered minor.
+ */
+
+#if 0
+static int drm_stub_open(struct inode *inode, struct file *filp)
+{
+       const struct file_operations *new_fops;
+       struct drm_minor *minor;
+       int err;
+
+       DRM_DEBUG("\n");
+
+       mutex_lock(&drm_global_mutex);
+       minor = drm_minor_acquire(iminor(inode));
+       if (IS_ERR(minor)) {
+               err = PTR_ERR(minor);
+               goto out_unlock;
+       }
+
+       new_fops = fops_get(minor->dev->driver->fops);
+       if (!new_fops) {
+               err = -ENODEV;
+               goto out_release;
+       }
+
+       replace_fops(filp, new_fops);
+       if (filp->f_op->open)
+               err = filp->f_op->open(inode, filp);
+       else
+               err = 0;
+
+out_release:
+       drm_minor_release(minor);
+out_unlock:
+       mutex_unlock(&drm_global_mutex);
+       return err;
+}
+
+static const struct file_operations drm_stub_fops = {
+       .owner = THIS_MODULE,
+       .open = drm_stub_open,
+       .llseek = noop_llseek,
+};
+
+static int __init drm_core_init(void)
+{
+       int ret = -ENOMEM;
+
+       drm_global_init();
+       drm_connector_ida_init();
+       idr_init(&drm_minors_idr);
+
+       if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops))
+               goto err_p1;
+
+       drm_class = drm_sysfs_create(THIS_MODULE, "drm");
+       if (IS_ERR(drm_class)) {
+               printk(KERN_ERR "DRM: Error creating drm class.\n");
+               ret = PTR_ERR(drm_class);
+               goto err_p2;
+       }
+
+       drm_debugfs_root = debugfs_create_dir("dri", NULL);
+       if (!drm_debugfs_root) {
+               DRM_ERROR("Cannot create /sys/kernel/debug/dri\n");
+               ret = -1;
+               goto err_p3;
+       }
+
+       DRM_INFO("Initialized %s %d.%d.%d %s\n",
+                CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE);
+       return 0;
+err_p3:
+       drm_sysfs_destroy();
+err_p2:
+       unregister_chrdev(DRM_MAJOR, "drm");
+
+       idr_destroy(&drm_minors_idr);
+err_p1:
+       return ret;
+}
+
+static void __exit drm_core_exit(void)
+{
+       debugfs_remove(drm_debugfs_root);
+       drm_sysfs_destroy();
+
+       unregister_chrdev(DRM_MAJOR, "drm");
+
+       drm_connector_ida_destroy();
+       idr_destroy(&drm_minors_idr);
+}
+
+module_init(drm_core_init);
+module_exit(drm_core_exit);
+#endif
 
 #include <sys/devfs.h>
 
@@ -96,120 +948,6 @@ 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)] = {.cmd = ioctl, .func = _func, .flags = _flags, .cmd_drv = 0, .name = #ioctl}
-
-/** Ioctl table */
-static const struct drm_ioctl_desc drm_ioctls[] = {
-       DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0),
-       DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0),
-       DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_rmmap_ioctl, DRM_AUTH),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_getsareactx, DRM_AUTH),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_addctx, DRM_AUTH|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_GET_CTX, drm_getctx, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_IOCTL_SWITCH_CTX, drm_switchctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_NEW_CTX, drm_newctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_RES_CTX, drm_resctx, DRM_AUTH),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_ADD_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_RM_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_LOCK, drm_lock, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_IOCTL_UNLOCK, drm_unlock, DRM_AUTH),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_FINISH, drm_noop, DRM_AUTH),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_ADD_BUFS, drm_addbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_MARK_BUFS, drm_markbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_INFO_BUFS, drm_infobufs, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_IOCTL_MAP_BUFS, drm_mapbufs, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_IOCTL_FREE_BUFS, drm_freebufs, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_IOCTL_DMA, drm_dma, DRM_AUTH),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_AGP_ACQUIRE, drm_agp_acquire_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_AGP_RELEASE, drm_agp_release_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_AGP_ENABLE, drm_agp_enable_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_AGP_INFO, drm_agp_info_ioctl, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_IOCTL_AGP_ALLOC, drm_agp_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF(DRM_IOCTL_AGP_FREE, drm_agp_free_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       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, 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),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-
-#if 0
-       DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED),
-#endif
-
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-};
-
-#define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
-
 static struct dev_ops drm_cdevsw = {
        { "drm", 0, D_TRACKCLOSE | D_MPSAFE },
        .d_open =       drm_open,
@@ -517,12 +1255,6 @@ static int drm_load(struct drm_device *dev)
                }
        }
 
-       retcode = drm_ctxbitmap_init(dev);
-       if (retcode != 0) {
-               DRM_ERROR("Cannot allocate memory for context bitmap.\n");
-               goto error;
-       }
-
        if (dev->driver->driver_features & DRIVER_GEM) {
                retcode = drm_gem_init(dev);
                if (retcode != 0) {
@@ -556,7 +1288,6 @@ static int drm_load(struct drm_device *dev)
 error1:
        drm_gem_destroy(dev);
 error:
-       drm_ctxbitmap_cleanup(dev);
        drm_sysctl_cleanup(dev);
        DRM_LOCK(dev);
        drm_lastclose(dev);
@@ -572,42 +1303,6 @@ error:
        return retcode;
 }
 
-/**
- * Get version information
- *
- * \param inode device inode.
- * \param filp file pointer.
- * \param cmd command.
- * \param arg user argument, pointing to a drm_version structure.
- * \return zero on success or negative number on failure.
- *
- * Fills in the version information in \p arg.
- */
-int drm_version(struct drm_device *dev, void *data, struct drm_file *file_priv)
-{
-       struct drm_version *version = data;
-       int len;
-
-#define DRM_COPY( name, value )                                                \
-       len = strlen( value );                                          \
-       if ( len > name##_len ) len = name##_len;                       \
-       name##_len = strlen( value );                                   \
-       if ( len && name ) {                                            \
-               if ( copy_to_user( name, value, len ) )         \
-                       return EFAULT;                          \
-       }
-
-       version->version_major          = dev->driver->major;
-       version->version_minor          = dev->driver->minor;
-       version->version_patchlevel     = dev->driver->patchlevel;
-
-       DRM_COPY(version->name, dev->driver->name);
-       DRM_COPY(version->date, dev->driver->date);
-       DRM_COPY(version->desc, dev->driver->desc);
-
-       return 0;
-}
-
 /*
  * Stub is needed for devfs
  */
@@ -663,12 +1358,6 @@ void drm_cdevpriv_dtor(void *cd)
                                retcode = EINTR;
                                break;
                        }
-                       if (drm_lock_take(&dev->lock, DRM_KERNEL_CONTEXT)) {
-                               dev->lock.file_priv = file_priv;
-                               dev->lock.lock_time = jiffies;
-                               atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
-                               break;  /* Got lock */
-                       }
                        /* Contention */
                        retcode = DRM_LOCK_SLEEP(dev, &dev->lock.lock_queue,
                            PCATCH, "drmlk2", 0);
@@ -677,7 +1366,6 @@ void drm_cdevpriv_dtor(void *cd)
                }
                if (retcode == 0) {
                        dev->driver->reclaim_buffers_locked(dev, file_priv);
-                       drm_lock_free(&dev->lock, DRM_KERNEL_CONTEXT);
                }
        }
 
@@ -705,100 +1393,6 @@ void drm_cdevpriv_dtor(void *cd)
        DRM_UNLOCK(dev);
 }
 
-/**
- * Called whenever a process performs an ioctl on /dev/drm.
- *
- * \param inode device inode.
- * \param file_priv DRM file private.
- * \param cmd command.
- * \param arg user argument.
- * \return zero on success or negative number on failure.
- *
- * Looks up the ioctl function in the ::ioctls table, checking for root
- * previleges if so required, and dispatches to the respective function.
- */
-int drm_ioctl(struct dev_ioctl_args *ap)
-{
-       struct cdev *kdev = ap->a_head.a_dev;
-       struct drm_device *dev;
-       const struct drm_ioctl_desc *ioctl = NULL;
-       u_long cmd = ap->a_cmd;
-       unsigned int nr = DRM_IOCTL_NR(cmd);
-       int retcode = 0;
-       caddr_t data = ap->a_data;
-       int (*func)(struct drm_device *dev, void *data, struct drm_file *file_priv);
-       int is_driver_ioctl = 0;
-       struct drm_file *file_priv;
-
-       dev = drm_get_device_from_kdev(kdev);
-
-       retcode = devfs_get_cdevpriv(ap->a_fp, (void **)&file_priv);
-       if (retcode !=0) {
-               DRM_ERROR("can't find authenticator\n");
-               return EINVAL;
-       }
-
-       atomic_inc(&dev->counts[_DRM_STAT_IOCTLS]);
-
-       if (drm_device_is_unplugged(dev))
-               return -ENODEV;
-
-       if (IOCGROUP(cmd) != DRM_IOCTL_BASE) {
-               DRM_DEBUG("Bad ioctl group 0x%x\n", (int)IOCGROUP(cmd));
-               return EINVAL;
-       }
-
-       ioctl = &drm_ioctls[nr];
-       /* It's not a core DRM ioctl, try driver-specific. */
-       if (ioctl->func == NULL && nr >= DRM_COMMAND_BASE) {
-               /* The array entries begin at DRM_COMMAND_BASE ioctl nr */
-               nr -= DRM_COMMAND_BASE;
-               if (nr >= dev->driver->num_ioctls) {
-                       return EINVAL;
-               }
-               ioctl = &dev->driver->ioctls[nr];
-               is_driver_ioctl = 1;
-       }
-
-       DRM_DEBUG_VERBOSE("pid=%d, cmd=0x%02lx, nr=0x%02x, dev 0x%lx, auth=%d\n",
-           DRM_CURRENTPID, cmd, nr, (long)dev->dev,
-           file_priv->authenticated);
-
-       /* Do not trust userspace, use our own definition */
-       func = ioctl->func;
-
-       if (func == NULL) {
-               DRM_DEBUG("no function\n");
-               return EINVAL;
-       }
-
-       if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) ||
-           ((ioctl->flags & DRM_AUTH) && !file_priv->authenticated) ||
-           ((ioctl->flags & DRM_MASTER) && !file_priv->master))
-               return EACCES;
-
-       if (is_driver_ioctl) {
-               if ((ioctl->flags & DRM_UNLOCKED) == 0)
-                       mutex_lock(&drm_global_mutex);
-               /* shared code returns -errno */
-               retcode = -func(dev, data, file_priv);
-               if ((ioctl->flags & DRM_UNLOCKED) == 0)
-                       mutex_unlock(&drm_global_mutex);
-       } else {
-               retcode = func(dev, data, file_priv);
-       }
-
-       if (!ioctl)
-               DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02lx, nr=0x%02x\n",
-                         DRM_CURRENTPID,
-                         (long)dev->dev,
-                         file_priv->authenticated, cmd, nr);
-
-       if (retcode)
-               DRM_DEBUG("ret = %d\n", retcode);
-       return retcode;
-}
-
 drm_local_map_t *drm_getsarea(struct drm_device *dev)
 {
        struct drm_map_list *entry;
@@ -922,6 +1516,9 @@ SYSINIT(drm_register, SI_SUB_DRIVERS, SI_ORDER_MIDDLE,
 SYSUNINIT(drm_unregister, SI_SUB_DRIVERS, SI_ORDER_MIDDLE,
     drm_core_exit, NULL);
 
+
+#include <linux/dmi.h>
+
 /*
  * Check if dmi_system_id structure matches system DMI data
  */
index b0b9260..4a2b181 100644 (file)
@@ -3319,6 +3319,7 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
        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 && connector->eld[0])
@@ -3789,8 +3790,14 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
 
        frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
 
-       /* Populate picture aspect ratio from CEA mode list */
-       if (frame->video_code > 0)
+       /*
+        * Populate picture aspect ratio from either
+        * user input (if specified) or from the CEA mode list.
+        */
+       if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_4_3 ||
+               mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_16_9)
+               frame->picture_aspect = mode->picture_aspect_ratio;
+       else if (frame->video_code > 0)
                frame->picture_aspect = drm_get_cea_aspect_ratio(
                                                frame->video_code);
 
diff --git a/sys/dev/drm/drm_fb_cma_helper.c b/sys/dev/drm/drm_fb_cma_helper.c
new file mode 100644 (file)
index 0000000..5040dc6
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ * drm kms/fb cma (contiguous memory allocator) helper functions
+ *
+ * Copyright (C) 2012 Analog Device Inc.
+ *   Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Based on udl_fbdev.c
+ *  Copyright (C) 2012 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <linux/module.h>
+
+struct drm_fb_cma {
+       struct drm_framebuffer          fb;
+       struct drm_gem_cma_object       *obj[4];
+};
+
+struct drm_fbdev_cma {
+       struct drm_fb_helper    fb_helper;
+       struct drm_fb_cma       *fb;
+};
+
+static inline struct drm_fbdev_cma *to_fbdev_cma(struct drm_fb_helper *helper)
+{
+       return container_of(helper, struct drm_fbdev_cma, fb_helper);
+}
+
+static inline struct drm_fb_cma *to_fb_cma(struct drm_framebuffer *fb)
+{
+       return container_of(fb, struct drm_fb_cma, fb);
+}
+
+static void drm_fb_cma_destroy(struct drm_framebuffer *fb)
+{
+       struct drm_fb_cma *fb_cma = to_fb_cma(fb);
+       int i;
+
+       for (i = 0; i < 4; i++) {
+               if (fb_cma->obj[i])
+                       drm_gem_object_unreference_unlocked(&fb_cma->obj[i]->base);
+       }
+
+       drm_framebuffer_cleanup(fb);
+       kfree(fb_cma);
+}
+
+static int drm_fb_cma_create_handle(struct drm_framebuffer *fb,
+       struct drm_file *file_priv, unsigned int *handle)
+{
+       struct drm_fb_cma *fb_cma = to_fb_cma(fb);
+
+       return drm_gem_handle_create(file_priv,
+                       &fb_cma->obj[0]->base, handle);
+}
+
+static struct drm_framebuffer_funcs drm_fb_cma_funcs = {
+       .destroy        = drm_fb_cma_destroy,
+       .create_handle  = drm_fb_cma_create_handle,
+};
+
+static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev,
+       struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_cma_object **obj,
+       unsigned int num_planes)
+{
+       struct drm_fb_cma *fb_cma;
+       int ret;
+       int i;
+
+       fb_cma = kzalloc(sizeof(*fb_cma), GFP_KERNEL);
+       if (!fb_cma)
+               return ERR_PTR(-ENOMEM);
+
+       drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd);
+
+       for (i = 0; i < num_planes; i++)
+               fb_cma->obj[i] = obj[i];
+
+       ret = drm_framebuffer_init(dev, &fb_cma->fb, &drm_fb_cma_funcs);
+       if (ret) {
+               dev_err(dev->dev, "Failed to initialize framebuffer: %d\n", ret);
+               kfree(fb_cma);
+               return ERR_PTR(ret);
+       }
+
+       return fb_cma;
+}
+
+/**
+ * drm_fb_cma_create() - (struct drm_mode_config_funcs *)->fb_create callback function
+ *
+ * If your hardware has special alignment or pitch requirements these should be
+ * checked before calling this function.
+ */
+struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev,
+       struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       struct drm_fb_cma *fb_cma;
+       struct drm_gem_cma_object *objs[4];
+       struct drm_gem_object *obj;
+       unsigned int hsub;
+       unsigned int vsub;
+       int ret;
+       int i;
+
+       hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
+       vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
+
+       for (i = 0; i < drm_format_num_planes(mode_cmd->pixel_format); i++) {
+               unsigned int width = mode_cmd->width / (i ? hsub : 1);
+               unsigned int height = mode_cmd->height / (i ? vsub : 1);
+               unsigned int min_size;
+
+               obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[i]);
+               if (!obj) {
+                       dev_err(dev->dev, "Failed to lookup GEM object\n");
+                       ret = -ENXIO;
+                       goto err_gem_object_unreference;
+               }
+
+               min_size = (height - 1) * mode_cmd->pitches[i]
+                        + width * drm_format_plane_cpp(mode_cmd->pixel_format, i)
+                        + mode_cmd->offsets[i];
+
+               if (obj->size < min_size) {
+                       drm_gem_object_unreference_unlocked(obj);
+                       ret = -EINVAL;
+                       goto err_gem_object_unreference;
+               }
+               objs[i] = to_drm_gem_cma_obj(obj);
+       }
+
+       fb_cma = drm_fb_cma_alloc(dev, mode_cmd, objs, i);
+       if (IS_ERR(fb_cma)) {
+               ret = PTR_ERR(fb_cma);
+               goto err_gem_object_unreference;
+       }
+
+       return &fb_cma->fb;
+
+err_gem_object_unreference:
+       for (i--; i >= 0; i--)
+               drm_gem_object_unreference_unlocked(&objs[i]->base);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(drm_fb_cma_create);
+
+/**
+ * drm_fb_cma_get_gem_obj() - Get CMA GEM object for framebuffer
+ * @fb: The framebuffer
+ * @plane: Which plane
+ *
+ * Return the CMA GEM object for given framebuffer.
+ *
+ * This function will usually be called from the CRTC callback functions.
+ */
+struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
+       unsigned int plane)
+{
+       struct drm_fb_cma *fb_cma = to_fb_cma(fb);
+
+       if (plane >= 4)
+               return NULL;
+
+       return fb_cma->obj[plane];
+}
+EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj);
+
+#ifdef CONFIG_DEBUG_FS
+/*
+ * drm_fb_cma_describe() - Helper to dump information about a single
+ * CMA framebuffer object
+ */
+static void drm_fb_cma_describe(struct drm_framebuffer *fb, struct seq_file *m)
+{
+       struct drm_fb_cma *fb_cma = to_fb_cma(fb);
+       int i, n = drm_format_num_planes(fb->pixel_format);
+
+       seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height,
+                       (char *)&fb->pixel_format);
+
+       for (i = 0; i < n; i++) {
+               seq_printf(m, "   %d: offset=%d pitch=%d, obj: ",
+                               i, fb->offsets[i], fb->pitches[i]);
+               drm_gem_cma_describe(fb_cma->obj[i], m);
+       }
+}
+
+/**
+ * drm_fb_cma_debugfs_show() - Helper to list CMA framebuffer objects
+ * in debugfs.
+ */
+int drm_fb_cma_debugfs_show(struct seq_file *m, void *arg)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_device *dev = node->minor->dev;
+       struct drm_framebuffer *fb;
+       int ret;
+
+       ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+       if (ret)
+               return ret;
+
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret) {
+               mutex_unlock(&dev->mode_config.mutex);
+               return ret;
+       }
+
+       list_for_each_entry(fb, &dev->mode_config.fb_list, head)
+               drm_fb_cma_describe(fb, m);
+
+       mutex_unlock(&dev->struct_mutex);
+       mutex_unlock(&dev->mode_config.mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(drm_fb_cma_debugfs_show);
+#endif
+
+#if 0
+static struct fb_ops drm_fbdev_cma_ops = {
+       .owner          = THIS_MODULE,
+       .fb_fillrect    = sys_fillrect,
+       .fb_copyarea    = sys_copyarea,
+       .fb_imageblit   = sys_imageblit,
+       .fb_check_var   = drm_fb_helper_check_var,
+       .fb_set_par     = drm_fb_helper_set_par,
+       .fb_blank       = drm_fb_helper_blank,
+       .fb_pan_display = drm_fb_helper_pan_display,
+       .fb_setcmap     = drm_fb_helper_setcmap,
+};
+#endif
+
+#if 0
+static int drm_fbdev_cma_create(struct drm_fb_helper *helper,
+       struct drm_fb_helper_surface_size *sizes)
+{
+       struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+       struct drm_device *dev = helper->dev;
+       struct drm_gem_cma_object *obj;
+       unsigned int bytes_per_pixel;
+       struct fb_info *fbi;
+       size_t size;
+       int ret;
+
+       DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
+                       sizes->surface_width, sizes->surface_height,
+                       sizes->surface_bpp);
+
+       bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
+
+       mode_cmd.width = sizes->surface_width;
+       mode_cmd.height = sizes->surface_height;
+       mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
+       mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+               sizes->surface_depth);
+
+       size = mode_cmd.pitches[0] * mode_cmd.height;
+       obj = drm_gem_cma_create(dev, size);
+       if (IS_ERR(obj))
+               return -ENOMEM;
+
+#if defined(__DragonFly__)
+       fbi = kzalloc(sizeof(struct fb_info), GFP_KERNEL);
+       if (fbi)
+               fbi->device = dev->dev;
+#else
+       fbi = framebuffer_alloc(0, dev->dev);
+#endif
+       if (!fbi) {
+               dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
+               ret = -ENOMEM;
+               goto err_drm_gem_cma_free_object;
+       }
+
+#if 0
+       fbdev_cma->fb = drm_fb_cma_alloc(dev, &mode_cmd, &obj, 1);
+       if (IS_ERR(fbdev_cma->fb)) {
+               dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
+               ret = PTR_ERR(fbdev_cma->fb);
+               goto err_framebuffer_release;
+       }
+
+       fb = &fbdev_cma->fb->fb;
+       helper->fb = fb;
+       helper->fbdev = fbi;
+
+       fbi->par = helper;
+       fbi->flags = FBINFO_FLAG_DEFAULT;
+       fbi->fbops = &drm_fbdev_cma_ops;
+
+       ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+       if (ret) {
+               dev_err(dev->dev, "Failed to allocate color map.\n");
+               goto err_drm_fb_cma_destroy;
+       }
+
+       drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
+       drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
+
+       offset = fbi->var.xoffset * bytes_per_pixel;
+       offset += fbi->var.yoffset * fb->pitches[0];
+
+       dev->mode_config.fb_base = (resource_size_t)obj->paddr;
+       fbi->screen_base = obj->vaddr + offset;
+       fbi->fix.smem_start = (unsigned long)(obj->paddr + offset);
+       fbi->screen_size = size;
+       fbi->fix.smem_len = size;
+#endif
+
+       return 0;
+
+#if 0
+err_drm_fb_cma_destroy:
+       drm_framebuffer_unregister_private(fb);
+       drm_fb_cma_destroy(fb);
+err_framebuffer_release:
+       framebuffer_release(fbi);
+#endif
+err_drm_gem_cma_free_object:
+       drm_gem_cma_free_object(&obj->base);
+       return ret;
+}
+
+static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
+       .fb_probe = drm_fbdev_cma_create,
+};
+
+/**
+ * drm_fbdev_cma_init() - Allocate and initializes a drm_fbdev_cma struct
+ * @dev: DRM device
+ * @preferred_bpp: Preferred bits per pixel for the device
+ * @num_crtc: Number of CRTCs
+ * @max_conn_count: Maximum number of connectors
+ *
+ * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR.
+ */
+struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
+       unsigned int preferred_bpp, unsigned int num_crtc,
+       unsigned int max_conn_count)
+{
+       struct drm_fbdev_cma *fbdev_cma;
+       struct drm_fb_helper *helper;
+       int ret;
+
+       fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL);
+       if (!fbdev_cma) {
+               dev_err(dev->dev, "Failed to allocate drm fbdev.\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       helper = &fbdev_cma->fb_helper;
+
+       drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs);
+
+       ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
+       if (ret < 0) {
+               dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
+               goto err_free;
+       }
+
+       ret = drm_fb_helper_single_add_all_connectors(helper);
+       if (ret < 0) {
+               dev_err(dev->dev, "Failed to add connectors.\n");
+               goto err_drm_fb_helper_fini;
+
+       }
+
+       /* disable all the possible outputs/crtcs before entering KMS mode */
+       drm_helper_disable_unused_functions(dev);
+
+       ret = drm_fb_helper_initial_config(helper, preferred_bpp);
+       if (ret < 0) {
+               dev_err(dev->dev, "Failed to set initial hw configuration.\n");
+               goto err_drm_fb_helper_fini;
+       }
+
+       return fbdev_cma;
+
+err_drm_fb_helper_fini:
+       drm_fb_helper_fini(helper);
+err_free:
+       kfree(fbdev_cma);
+
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(drm_fbdev_cma_init);
+#endif
+
+/**
+ * drm_fbdev_cma_fini() - Free drm_fbdev_cma struct
+ * @fbdev_cma: The drm_fbdev_cma struct
+ */
+void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma)
+{
+#if 0
+       if (fbdev_cma->fb_helper.fbdev) {
+               struct fb_info *info;
+               int ret;
+
+               info = fbdev_cma->fb_helper.fbdev;
+               ret = unregister_framebuffer(info);
+               if (ret < 0)
+                       DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
+
+               if (info->cmap.len)
+                       fb_dealloc_cmap(&info->cmap);
+
+               framebuffer_release(info);
+       }
+#endif
+
+       if (fbdev_cma->fb) {
+               drm_framebuffer_unregister_private(&fbdev_cma->fb->fb);
+               drm_fb_cma_destroy(&fbdev_cma->fb->fb);
+       }
+
+       drm_fb_helper_fini(&fbdev_cma->fb_helper);
+       kfree(fbdev_cma);
+}
+EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini);
+
+/**
+ * drm_fbdev_cma_restore_mode() - Restores initial framebuffer mode
+ * @fbdev_cma: The drm_fbdev_cma struct, may be NULL
+ *
+ * This function is usually called from the DRM drivers lastclose callback.
+ */
+void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma)
+{
+       if (fbdev_cma)
+               drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev_cma->fb_helper);
+}
+EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode);
+
+/**
+ * drm_fbdev_cma_hotplug_event() - Poll for hotpulug events
+ * @fbdev_cma: The drm_fbdev_cma struct, may be NULL
+ *
+ * This function is usually called from the DRM drivers output_poll_changed
+ * callback.
+ */
+void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma)
+{
+       if (fbdev_cma)
+               drm_fb_helper_hotplug_event(&fbdev_cma->fb_helper);
+}
+EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event);
index 3e1b9d7..39ff2e8 100644 (file)
@@ -116,6 +116,58 @@ fail:
 }
 EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
 
+int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector)
+{
+       struct drm_fb_helper_connector **temp;
+       struct drm_fb_helper_connector *fb_helper_connector;
+
+       WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
+       if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) {
+               temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector) * (fb_helper->connector_count + 1), M_DRM, M_WAITOK);
+               if (!temp)
+                       return -ENOMEM;
+
+               fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1;
+               fb_helper->connector_info = temp;
+       }
+
+
+       fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
+       if (!fb_helper_connector)
+               return -ENOMEM;
+
+       fb_helper_connector->connector = connector;
+       fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
+       return 0;
+}
+EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
+
+int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
+                                      struct drm_connector *connector)
+{
+       struct drm_fb_helper_connector *fb_helper_connector;
+       int i, j;
+
+       WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
+
+       for (i = 0; i < fb_helper->connector_count; i++) {
+               if (fb_helper->connector_info[i]->connector == connector)
+                       break;
+       }
+
+       if (i == fb_helper->connector_count)
+               return -EINVAL;
+       fb_helper_connector = fb_helper->connector_info[i];
+
+       for (j = i + 1; j < fb_helper->connector_count; j++) {
+               fb_helper->connector_info[j - 1] = fb_helper->connector_info[j];
+       }
+       fb_helper->connector_count--;
+       kfree(fb_helper_connector);
+       return 0;
+}
+EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
+
 static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
 {
        struct drm_fb_helper_connector *fb_helper_conn;
@@ -211,9 +263,6 @@ int drm_fb_helper_debug_enter(struct fb_info *info)
        struct drm_crtc_helper_funcs *funcs;
        int i;
 
-       if (list_empty(&kernel_fb_helper_list))
-               return false;
-
        list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
                for (i = 0; i < helper->crtc_count; i++) {
                        struct drm_mode_set *mode_set =
@@ -604,6 +653,7 @@ int drm_fb_helper_init(struct drm_device *dev,
                kfree(fb_helper->crtc_info);
                return -ENOMEM;
        }
+       fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
        fb_helper->connector_count = 0;
 
        for (i = 0; i < crtc_count; i++) {
@@ -1134,7 +1184,6 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
        info->fix.ypanstep = 1; /* doing it in hw */
        info->fix.ywrapstep = 0;
        info->fix.accel = FB_ACCEL_NONE;
-       info->fix.type_aux = 0;
 
        info->fix.line_length = pitch;
        return;
@@ -1692,8 +1741,10 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
  * either the output polling work or a work item launched from the driver's
  * hotplug interrupt).
  *
- * Note that the driver must ensure that this is only called _after_ the fb has
- * been fully set up, i.e. after the call to drm_fb_helper_initial_config.
+ * Note that drivers may call this even before calling
+ * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows
+ * for a race-free fbcon setup and will make sure that the fbdev emulation will
+ * not miss any hotplug events.
  *
  * RETURNS:
  * 0 on success and a non-zero error code otherwise.
@@ -1703,11 +1754,8 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
        struct drm_device *dev = fb_helper->dev;
        u32 max_width, max_height;
 
-       if (!fb_helper->fb)
-               return 0;
-
        mutex_lock(&fb_helper->dev->mode_config.mutex);
-       if (!drm_fb_helper_is_bound(fb_helper)) {
+       if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
                fb_helper->delayed_hotplug = true;
                mutex_unlock(&fb_helper->dev->mode_config.mutex);
                return 0;
index 71af6e9..6ccdc94 100644 (file)
@@ -40,6 +40,7 @@
 
 #include <drm/drmP.h>
 #include <linux/module.h>
+#include "drm_legacy.h"
 
 /* from BKL pushdown: note that nothing else serializes idr_find() */
 DEFINE_MUTEX(drm_global_mutex);
@@ -196,98 +197,6 @@ int drm_open_helper(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p,
  * zero calls drm_lastclose().
  */
 
-#if 0 /* old drm_release equivalent from DragonFly */
-void drm_cdevpriv_dtor(void *cd)
-{
-       struct drm_file *file_priv = cd;
-       struct drm_device *dev = file_priv->dev;
-       int retcode = 0;
-
-       DRM_DEBUG("open_count = %d\n", dev->open_count);
-
-       DRM_LOCK(dev);
-
-       if (dev->driver->preclose != NULL)
-               dev->driver->preclose(dev, file_priv);
-
-       /* ========================================================
-        * Begin inline drm_release
-        */
-
-       DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
-           DRM_CURRENTPID, (long)dev->dev, dev->open_count);
-
-       if (dev->driver->driver_features & DRIVER_GEM)
-               drm_gem_release(dev, file_priv);
-
-       if (dev->primary->master->lock.hw_lock
-           && _DRM_LOCK_IS_HELD(dev->primary->master->lock.hw_lock->lock)
-           && dev->primary->master->lock.file_priv == file_priv) {
-               DRM_DEBUG("Process %d dead, freeing lock for context %d\n",
-                         DRM_CURRENTPID,
-                         _DRM_LOCKING_CONTEXT(dev->primary->master->lock.hw_lock->lock));
-               if (dev->driver->reclaim_buffers_locked != NULL)
-                       dev->driver->reclaim_buffers_locked(dev, file_priv);
-
-               drm_lock_free(&dev->primary->master->lock,
-                   _DRM_LOCKING_CONTEXT(dev->primary->master->lock.hw_lock->lock));
-
-                               /* FIXME: may require heavy-handed reset of
-                                   hardware at this point, possibly
-                                   processed via a callback to the X
-                                   server. */
-       } else if (dev->driver->reclaim_buffers_locked != NULL &&
-           dev->primary->master->lock.hw_lock != NULL) {
-               /* The lock is required to reclaim buffers */
-               for (;;) {
-                       if (!dev->primary->master->lock.hw_lock) {
-                               /* Device has been unregistered */
-                               retcode = EINTR;
-                               break;
-                       }
-                       if (drm_lock_take(&dev->primary->master->lock, DRM_KERNEL_CONTEXT)) {
-                               dev->primary->master->lock.file_priv = file_priv;
-                               dev->primary->master->lock.lock_time = jiffies;
-                               atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
-                               break;  /* Got lock */
-                       }
-                       /* Contention */
-                       retcode = DRM_LOCK_SLEEP(dev, &dev->primary->master->lock.lock_queue,
-                           PCATCH, "drmlk2", 0);
-                       if (retcode)
-                               break;
-               }
-               if (retcode == 0) {
-                       dev->driver->reclaim_buffers_locked(dev, file_priv);
-                       drm_lock_free(&dev->primary->master->lock, DRM_KERNEL_CONTEXT);
-               }
-       }
-
-       if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
-           !dev->driver->reclaim_buffers_locked)
-               drm_reclaim_buffers(dev, file_priv);
-
-       funsetown(&dev->buf_sigio);
-
-       if (dev->driver->postclose != NULL)
-               dev->driver->postclose(dev, file_priv);
-       list_del(&file_priv->lhead);
-
-
-       /* ========================================================
-        * End inline drm_release
-        */
-
-       atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
-       device_unbusy(dev->dev);
-       if (--dev->open_count == 0) {
-               retcode = drm_lastclose(dev);
-       }
-
-       DRM_UNLOCK(dev);
-}
-#endif
-
 static void drm_unload(struct drm_device *dev)
 {
        int i;
@@ -298,8 +207,6 @@ static void drm_unload(struct drm_device *dev)
        if (dev->devnode != NULL)
                destroy_dev(dev->devnode);
 
-       drm_ctxbitmap_cleanup(dev);
-
        if (dev->driver->driver_features & DRIVER_GEM)
                drm_gem_destroy(dev);
 
index c2bf923..500cf8e 100644 (file)
@@ -1,11 +1,3 @@
-/**
- * \file drm_ioctl.c
- * IOCTL processing for DRM
- *
- * \author Rickard E. (Rik) Faith <faith@valinux.com>
- * \author Gareth Hughes <gareth@valinux.com>
- */
-
 /*
  * Created: Fri Jan  8 09:01:26 1999 by faith@valinux.com
  *
@@ -13,6 +5,9 @@
  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
  * All Rights Reserved.
  *
+ * Author Rickard E. (Rik) Faith <faith@valinux.com>
+ * Author Gareth Hughes <gareth@valinux.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
  * 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.
- *
- * $FreeBSD: src/sys/dev/drm2/drm_ioctl.c,v 1.1 2012/05/22 11:07:44 kib Exp $
  */
 
+#include <sys/devfs.h>
+
 #include <drm/drmP.h>
 #include <drm/drm_core.h>
+#include "drm_legacy.h"
 
 #include <linux/export.h>
 
+static int drm_version(struct drm_device *dev, void *data,
+                      struct drm_file *file_priv);
+
+#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 const struct drm_ioctl_desc drm_ioctls[] = {
+       DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0),
+       DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0),
+       DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_rmmap_ioctl, DRM_AUTH),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_legacy_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_legacy_getsareactx, DRM_AUTH),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_legacy_addctx, DRM_AUTH|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_legacy_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_GET_CTX, drm_legacy_getctx, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_IOCTL_SWITCH_CTX, drm_legacy_switchctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_NEW_CTX, drm_legacy_newctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_RES_CTX, drm_legacy_resctx, DRM_AUTH),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_ADD_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_RM_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_LOCK, drm_lock, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_IOCTL_UNLOCK, drm_unlock, DRM_AUTH),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_FINISH, drm_noop, DRM_AUTH),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_ADD_BUFS, drm_addbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_MARK_BUFS, drm_markbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_INFO_BUFS, drm_infobufs, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_IOCTL_MAP_BUFS, drm_mapbufs, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_IOCTL_FREE_BUFS, drm_freebufs, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_IOCTL_DMA, drm_dma, DRM_AUTH),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_AGP_ACQUIRE, drm_agp_acquire_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_AGP_RELEASE, drm_agp_release_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_AGP_ENABLE, drm_agp_enable_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_AGP_INFO, drm_agp_info_ioctl, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_IOCTL_AGP_ALLOC, drm_agp_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF(DRM_IOCTL_AGP_FREE, drm_agp_free_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       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, 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),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+
+#if 0
+       DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED),
+#endif
+
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+};
+
+#define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
+
 /**
  * Get the bus id.
  *
@@ -326,8 +439,6 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
                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;
@@ -404,3 +515,211 @@ int drm_noop(struct drm_device *dev, void *data,
        return 0;
 }
 EXPORT_SYMBOL(drm_noop);
+
+/**
+ * Copy and IOCTL return string to user space
+ */
+static int drm_copy_field(char __user *buf, size_t *buf_len, const char *value)
+{
+       int len;
+
+       /* don't overflow userbuf */
+       len = strlen(value);
+       if (len > *buf_len)
+               len = *buf_len;
+
+       /* let userspace know exact length of driver value (which could be
+        * larger than the userspace-supplied buffer) */
+       *buf_len = strlen(value);
+
+       /* finally, try filling in the userbuf */
+       if (len && buf)
+               if (copy_to_user(buf, value, len))
+                       return -EFAULT;
+       return 0;
+}
+
+/**
+ * Get version information
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument, pointing to a drm_version structure.
+ * \return zero on success or negative number on failure.
+ *
+ * Fills in the version information in \p arg.
+ */
+static int drm_version(struct drm_device *dev, void *data,
+                      struct drm_file *file_priv)
+{
+       struct drm_version *version = data;
+       int err;
+
+       version->version_major = dev->driver->major;
+       version->version_minor = dev->driver->minor;
+       version->version_patchlevel = dev->driver->patchlevel;
+       err = drm_copy_field(version->name, &version->name_len,
+                       dev->driver->name);
+       if (!err)
+               err = drm_copy_field(version->date, &version->date_len,
+                               dev->driver->date);
+       if (!err)
+               err = drm_copy_field(version->desc, &version->desc_len,
+                               dev->driver->desc);
+
+       return err;
+}
+
+/**
+ * drm_ioctl_permit - Check ioctl permissions against caller
+ *
+ * @flags: ioctl permission flags.
+ * @file_priv: Pointer to struct drm_file identifying the caller.
+ *
+ * Checks whether the caller is allowed to run an ioctl with the
+ * indicated permissions. If so, returns zero. Otherwise returns an
+ * error code suitable for ioctl return.
+ */
+#if 0
+static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
+{
+       /* ROOT_ONLY is only for CAP_SYS_ADMIN */
+       if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)))
+               return -EACCES;
+
+       /* AUTH is only for authenticated or render client */
+       if (unlikely((flags & DRM_AUTH) && !drm_is_render_client(file_priv) &&
+                    !file_priv->authenticated))
+               return -EACCES;
+
+       /* MASTER is only for master or control clients */
+       if (unlikely((flags & DRM_MASTER) && !file_priv->is_master &&
+                    !drm_is_control_client(file_priv)))
+               return -EACCES;
+
+       /* Control clients must be explicitly allowed */
+       if (unlikely(!(flags & DRM_CONTROL_ALLOW) &&
+                    drm_is_control_client(file_priv)))
+               return -EACCES;
+
+       /* Render clients must be explicitly allowed */
+       if (unlikely(!(flags & DRM_RENDER_ALLOW) &&
+                    drm_is_render_client(file_priv)))
+               return -EACCES;
+
+       return 0;
+}
+#endif
+
+/**
+ * Called whenever a process performs an ioctl on /dev/drm.
+ *
+ * \param inode device inode.
+ * \param file_priv DRM file private.
+ * \param cmd command.
+ * \param arg user argument.
+ * \return zero on success or negative number on failure.
+ *
+ * Looks up the ioctl function in the ::ioctls table, checking for root
+ * previleges if so required, and dispatches to the respective function.
+ */
+int drm_ioctl(struct dev_ioctl_args *ap)
+{
+       struct cdev *kdev = ap->a_head.a_dev;
+       struct drm_device *dev;
+       const struct drm_ioctl_desc *ioctl = NULL;
+       u_long cmd = ap->a_cmd;
+       unsigned int nr = DRM_IOCTL_NR(cmd);
+       int retcode = 0;
+       caddr_t data = ap->a_data;
+       int (*func)(struct drm_device *dev, void *data, struct drm_file *file_priv);
+       int is_driver_ioctl = 0;
+       struct drm_file *file_priv;
+
+       dev = drm_get_device_from_kdev(kdev);
+
+       retcode = devfs_get_cdevpriv(ap->a_fp, (void **)&file_priv);
+       if (retcode !=0) {
+               DRM_ERROR("can't find authenticator\n");
+               return EINVAL;
+       }
+
+       atomic_inc(&dev->counts[_DRM_STAT_IOCTLS]);
+
+       if (drm_device_is_unplugged(dev))
+               return -ENODEV;
+
+       if (IOCGROUP(cmd) != DRM_IOCTL_BASE) {
+               DRM_DEBUG("Bad ioctl group 0x%x\n", (int)IOCGROUP(cmd));
+               return EINVAL;
+       }
+
+       ioctl = &drm_ioctls[nr];
+       /* It's not a core DRM ioctl, try driver-specific. */
+       if (ioctl->func == NULL && nr >= DRM_COMMAND_BASE) {
+               /* The array entries begin at DRM_COMMAND_BASE ioctl nr */
+               nr -= DRM_COMMAND_BASE;
+               if (nr >= dev->driver->num_ioctls) {
+                       return EINVAL;
+               }
+               ioctl = &dev->driver->ioctls[nr];
+               is_driver_ioctl = 1;
+       }
+
+       DRM_DEBUG_VERBOSE("pid=%d, cmd=0x%02lx, nr=0x%02x, dev 0x%lx, auth=%d\n",
+           DRM_CURRENTPID, cmd, nr, (long)dev->dev,
+           file_priv->authenticated);
+
+       /* Do not trust userspace, use our own definition */
+       func = ioctl->func;
+
+       if (func == NULL) {
+               DRM_DEBUG("no function\n");
+               return EINVAL;
+       }
+
+       if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) ||
+           ((ioctl->flags & DRM_AUTH) && !file_priv->authenticated) ||
+           ((ioctl->flags & DRM_MASTER) && !file_priv->master))
+               return EACCES;
+
+       if (is_driver_ioctl) {
+               if ((ioctl->flags & DRM_UNLOCKED) == 0)
+                       mutex_lock(&drm_global_mutex);
+               /* shared code returns -errno */
+               retcode = -func(dev, data, file_priv);
+               if ((ioctl->flags & DRM_UNLOCKED) == 0)
+                       mutex_unlock(&drm_global_mutex);
+       } else {
+               retcode = func(dev, data, file_priv);
+       }
+
+       if (!ioctl)
+               DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02lx, nr=0x%02x\n",
+                         DRM_CURRENTPID,
+                         (long)dev->dev,
+                         file_priv->authenticated, cmd, nr);
+
+       if (retcode)
+               DRM_DEBUG("ret = %d\n", retcode);
+       return retcode;
+}
+
+/**
+ * drm_ioctl_flags - Check for core ioctl and return ioctl permission flags
+ *
+ * @nr: Ioctl number.
+ * @flags: Where to return the ioctl permission flags
+ */
+bool drm_ioctl_flags(unsigned int nr, unsigned int *flags)
+{
+       if ((nr >= DRM_COMMAND_END && nr < DRM_CORE_IOCTL_COUNT) ||
+           (nr < DRM_COMMAND_BASE)) {
+               *flags = drm_ioctls[nr].flags;
+               return true;
+       }
+
+       return false;
+}
+EXPORT_SYMBOL(drm_ioctl_flags);
diff --git a/sys/dev/drm/drm_legacy.h b/sys/dev/drm/drm_legacy.h
new file mode 100644 (file)
index 0000000..d34f20a
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef __DRM_LEGACY_H__
+#define __DRM_LEGACY_H__
+
+/*
+ * Copyright (c) 2014 David Herrmann <dh.herrmann@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.
+ */
+
+struct drm_device;
+struct drm_file;
+
+/*
+ * Generic DRM Contexts
+ */
+
+#define DRM_KERNEL_CONTEXT             0
+#define DRM_RESERVED_CONTEXTS          1
+
+int drm_legacy_ctxbitmap_init(struct drm_device *dev);
+void drm_legacy_ctxbitmap_cleanup(struct drm_device *dev);
+void drm_legacy_ctxbitmap_free(struct drm_device *dev, int ctx_handle);
+void drm_legacy_ctxbitmap_flush(struct drm_device *dev, struct drm_file *file);
+
+int drm_legacy_resctx(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_addctx(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_getctx(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_switchctx(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_newctx(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_rmctx(struct drm_device *d, void *v, struct drm_file *f);
+
+int drm_legacy_setsareactx(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_getsareactx(struct drm_device *d, void *v, struct drm_file *f);
+
+#endif /* __DRM_LEGACY_H__ */
index 7e5b167..00ea5fa 100644 (file)
@@ -49,6 +49,7 @@
  */
 
 #include <drm/drmP.h>
+#include "drm_legacy.h"
 
 int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
index a4c0024..b36bd59 100644 (file)
@@ -73,6 +73,79 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
        return count;
 }
 
+/**
+ * drm_plane_helper_check_update() - Check plane update for validity
+ * @plane: plane object to update
+ * @crtc: owning CRTC of owning plane
+ * @fb: framebuffer to flip onto plane
+ * @src: source coordinates in 16.16 fixed point
+ * @dest: integer destination coordinates
+ * @clip: integer clipping coordinates
+ * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point
+ * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point
+ * @can_position: is it legal to position the plane such that it
+ *                doesn't cover the entire crtc?  This will generally
+ *                only be false for primary planes.
+ * @can_update_disabled: can the plane be updated while the crtc
+ *                       is disabled?
+ * @visible: output parameter indicating whether plane is still visible after
+ *           clipping
+ *
+ * Checks that a desired plane update is valid.  Drivers that provide
+ * their own plane handling rather than helper-provided implementations may
+ * still wish to call this function to avoid duplication of error checking
+ * code.
+ *
+ * RETURNS:
+ * Zero if update appears valid, error code on failure
+ */
+int drm_plane_helper_check_update(struct drm_plane *plane,
+                                   struct drm_crtc *crtc,
+                                   struct drm_framebuffer *fb,
+                                   struct drm_rect *src,
+                                   struct drm_rect *dest,
+                                   const struct drm_rect *clip,
+                                   int min_scale,
+                                   int max_scale,
+                                   bool can_position,
+                                   bool can_update_disabled,
+                                   bool *visible)
+{
+       int hscale, vscale;
+
+       if (!crtc->enabled && !can_update_disabled) {
+               DRM_DEBUG_KMS("Cannot update plane of a disabled CRTC.\n");
+               return -EINVAL;
+       }
+
+       /* Check scaling */
+       hscale = drm_rect_calc_hscale(src, dest, min_scale, max_scale);
+       vscale = drm_rect_calc_vscale(src, dest, min_scale, max_scale);
+       if (hscale < 0 || vscale < 0) {
+               DRM_DEBUG_KMS("Invalid scaling of plane\n");
+               return -ERANGE;
+       }
+
+       *visible = drm_rect_clip_scaled(src, dest, clip, hscale, vscale);
+       if (!*visible)
+               /*
+                * Plane isn't visible; some drivers can handle this
+                * so we just return success here.  Drivers that can't
+                * (including those that use the primary plane helper's
+                * update function) will return an error from their
+                * update_plane handler.
+                */
+               return 0;
+
+       if (!can_position && !drm_rect_equals(dest, clip)) {
+               DRM_DEBUG_KMS("Plane must cover entire CRTC\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_plane_helper_check_update);
+
 /**
  * drm_primary_helper_update() - Helper for primary plane update
  * @plane: plane object to update
@@ -299,9 +372,10 @@ struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev,
        }
 
        /* possible_crtc's will be filled in later by crtc_init */
-       ret = drm_plane_init(dev, primary, 0, &drm_primary_helper_funcs,
-                            formats, num_formats,
-                            DRM_PLANE_TYPE_PRIMARY);
+       ret = drm_universal_plane_init(dev, primary, 0,
+                                      &drm_primary_helper_funcs,
+                                      formats, num_formats,
+                                      DRM_PLANE_TYPE_PRIMARY);
        if (ret) {
                kfree(primary);
                primary = NULL;
index d974d1d..15263a5 100644 (file)
@@ -130,7 +130,14 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
        count = drm_load_edid_firmware(connector);
        if (count == 0)
 #endif
-               count = (*connector_funcs->get_modes)(connector);
+       {
+               if (connector->override_edid) {
+                       struct edid *edid = (struct edid *) connector->edid_blob_ptr->data;
+
+                       count = drm_add_edid_modes(connector, edid);
+               } else
+                       count = (*connector_funcs->get_modes)(connector);
+       }
 
        if (count == 0 && connector->status == connector_status_connected)
                count = drm_add_modes_noedid(connector, 1024, 768);
diff --git a/sys/dev/drm/drm_stub.c b/sys/dev/drm/drm_stub.c
deleted file mode 100644 (file)
index 79e33ac..0000000
+++ /dev/null
@@ -1,664 +0,0 @@
-/**
- * \file drm_stub.h
- * Stub support
- *
- * \author Rickard E. (Rik) Faith <faith@valinux.com>
- */
-
-/*
- * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org
- *
- * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California.
- * All Rights Reserved.
- *
- * 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
- * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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 <linux/module.h>
-#include <linux/moduleparam.h>
-#include <drm/drmP.h>
-#include <drm/drm_core.h>
-#include <linux/slab.h>
-
-unsigned int drm_debug = 0;    /* 1 to enable debug output */
-EXPORT_SYMBOL(drm_debug);
-
-unsigned int drm_rnodes = 0;   /* 1 to enable experimental render nodes API */
-EXPORT_SYMBOL(drm_rnodes);
-
-/* 1 to allow user space to request universal planes (experimental) */
-unsigned int drm_universal_planes = 0;
-EXPORT_SYMBOL(drm_universal_planes);
-
-unsigned int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
-EXPORT_SYMBOL(drm_vblank_offdelay);
-
-unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
-EXPORT_SYMBOL(drm_timestamp_precision);
-
-/*
- * Default to use monotonic timestamps for wait-for-vblank and page-flip
- * complete events.
- */
-unsigned int drm_timestamp_monotonic = 1;
-
-MODULE_AUTHOR(CORE_AUTHOR);
-MODULE_DESCRIPTION(CORE_DESC);
-MODULE_PARM_DESC(debug, "Enable debug output");
-MODULE_PARM_DESC(rnodes, "Enable experimental render nodes API");
-MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]");
-MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
-MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
-
-module_param_named(debug, drm_debug, int, 0600);
-module_param_named(rnodes, drm_rnodes, int, 0600);
-module_param_named(universal_planes, drm_universal_planes, int, 0600);
-module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
-module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
-module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
-
-struct idr drm_minors_idr;
-
-struct class *drm_class;
-struct dentry *drm_debugfs_root;
-
-int drm_err(const char *func, const char *format, ...)
-{
-#if 0
-       struct va_format vaf;
-       va_list args;
-       int r;
-
-       va_start(args, format);
-
-       vaf.fmt = format;
-       vaf.va = &args;
-
-       r = printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* %pV", func, &vaf);
-
-       va_end(args);
-
-       return r;
-#endif
-       return 0;
-}
-EXPORT_SYMBOL(drm_err);
-
-void drm_ut_debug_printk(unsigned int request_level,
-                        const char *prefix,
-                        const char *function_name,
-                        const char *format, ...)
-{
-#if 0
-       struct va_format vaf;
-       va_list args;
-
-       if (drm_debug & request_level) {
-               va_start(args, format);
-               vaf.fmt = format;
-               vaf.va = &args;
-
-               if (function_name)
-                       printk(KERN_DEBUG "[%s:%s], %pV", prefix,
-                              function_name, &vaf);
-               else
-                       printk(KERN_DEBUG "%pV", &vaf);
-               va_end(args);
-       }
-#endif
-}
-EXPORT_SYMBOL(drm_ut_debug_printk);
-
-#if 0
-static int drm_minor_get_id(struct drm_device *dev, int type)
-{
-       int ret;
-       int base = 0, limit = 63;
-
-       if (type == DRM_MINOR_CONTROL) {
-               base += 64;
-               limit = base + 63;
-       } else if (type == DRM_MINOR_RENDER) {
-               base += 128;
-               limit = base + 63;
-       }
-
-       mutex_lock(&dev->struct_mutex);
-       ret = idr_alloc(&drm_minors_idr, NULL, base, limit, GFP_KERNEL);
-       mutex_unlock(&dev->struct_mutex);
-
-       return ret == -ENOSPC ? -EINVAL : ret;
-}
-
-struct drm_master *drm_master_create(struct drm_minor *minor)
-{
-       struct drm_master *master;
-
-       master = kzalloc(sizeof(*master), GFP_KERNEL);
-       if (!master)
-               return NULL;
-
-       kref_init(&master->refcount);
-       spin_lock_init(&master->lock.spinlock);
-       init_waitqueue_head(&master->lock.lock_queue);
-       drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER);
-       INIT_LIST_HEAD(&master->magicfree);
-       master->minor = minor;
-
-       list_add_tail(&master->head, &minor->master_list);
-
-       return master;
-}
-
-struct drm_master *drm_master_get(struct drm_master *master)
-{
-       kref_get(&master->refcount);
-       return master;
-}
-EXPORT_SYMBOL(drm_master_get);
-
-static void drm_master_destroy(struct kref *kref)
-{
-       struct drm_master *master = container_of(kref, struct drm_master, refcount);
-       struct drm_magic_entry *pt, *next;
-       struct drm_device *dev = master->minor->dev;
-       struct drm_map_list *r_list, *list_temp;
-
-       list_del(&master->head);
-
-       if (dev->driver->master_destroy)
-               dev->driver->master_destroy(dev, master);
-
-       list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) {
-               if (r_list->master == master) {
-                       drm_rmmap_locked(dev, r_list->map);
-                       r_list = NULL;
-               }
-       }
-
-       if (master->unique) {
-               kfree(master->unique);
-               master->unique = NULL;
-               master->unique_len = 0;
-       }
-
-       kfree(dev->devname);
-       dev->devname = NULL;
-
-       list_for_each_entry_safe(pt, next, &master->magicfree, head) {
-               list_del(&pt->head);
-               drm_ht_remove_item(&master->magiclist, &pt->hash_item);
-               kfree(pt);
-       }
-
-       drm_ht_remove(&master->magiclist);
-
-       kfree(master);
-}
-
-void drm_master_put(struct drm_master **master)
-{
-       kref_put(&(*master)->refcount, drm_master_destroy);
-       *master = NULL;
-}
-EXPORT_SYMBOL(drm_master_put);
-#endif
-
-int drm_setmaster_ioctl(struct drm_device *dev, void *data,
-                       struct drm_file *file_priv)
-{
-       DRM_DEBUG("setmaster\n");
-
-       if (file_priv->master != 0)
-               return (0);
-
-       return (-EPERM);
-}
-
-int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
-                        struct drm_file *file_priv)
-{
-       DRM_DEBUG("dropmaster\n");
-       if (file_priv->master != 0)
-               return -EINVAL;
-       return 0;
-}
-
-#if 0
-/**
- * drm_get_minor - Allocate and register new DRM minor
- * @dev: DRM device
- * @minor: Pointer to where new minor is stored
- * @type: Type of minor
- *
- * Allocate a new minor of the given type and register it. A pointer to the new
- * minor is returned in @minor.
- * Caller must hold the global DRM mutex.
- *
- * RETURNS:
- * 0 on success, negative error code on failure.
- */
-static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor,
-                        int type)
-{
-       struct drm_minor *new_minor;
-       int ret;
-       int minor_id;
-
-       DRM_DEBUG("\n");
-
-       minor_id = drm_minor_get_id(dev, type);
-       if (minor_id < 0)
-               return minor_id;
-
-       new_minor = kzalloc(sizeof(struct drm_minor), GFP_KERNEL);
-       if (!new_minor) {
-               ret = -ENOMEM;
-               goto err_idr;
-       }
-
-       new_minor->type = type;
-       new_minor->device = MKDEV(DRM_MAJOR, minor_id);
-       new_minor->dev = dev;
-       new_minor->index = minor_id;
-       INIT_LIST_HEAD(&new_minor->master_list);
-
-       idr_replace(&drm_minors_idr, new_minor, minor_id);
-
-#if defined(CONFIG_DEBUG_FS)
-       ret = drm_debugfs_init(new_minor, minor_id, drm_debugfs_root);
-       if (ret) {
-               DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");
-               goto err_mem;
-       }
-#endif
-
-       ret = drm_sysfs_device_add(new_minor);
-       if (ret) {
-               printk(KERN_ERR
-                      "DRM: Error sysfs_device_add.\n");
-               goto err_debugfs;
-       }
-       *minor = new_minor;
-
-       DRM_DEBUG("new minor assigned %d\n", minor_id);
-       return 0;
-
-
-err_debugfs:
-#if defined(CONFIG_DEBUG_FS)
-       drm_debugfs_cleanup(new_minor);
-err_mem:
-#endif
-       kfree(new_minor);
-err_idr:
-       idr_remove(&drm_minors_idr, minor_id);
-       *minor = NULL;
-       return ret;
-}
-
-/**
- * drm_unplug_minor - Unplug DRM minor
- * @minor: Minor to unplug
- *
- * Unplugs the given DRM minor but keeps the object. So after this returns,
- * minor->dev is still valid so existing open-files can still access it to get
- * device information from their drm_file ojects.
- * If the minor is already unplugged or if @minor is NULL, nothing is done.
- * The global DRM mutex must be held by the caller.
- */
-static void drm_unplug_minor(struct drm_minor *minor)
-{
-       if (!minor || !minor->kdev)
-               return;
-
-#if defined(CONFIG_DEBUG_FS)
-       drm_debugfs_cleanup(minor);
-#endif
-
-       drm_sysfs_device_remove(minor);
-       idr_remove(&drm_minors_idr, minor->index);
-}
-
-/**
- * drm_put_minor - Destroy DRM minor
- * @minor: Minor to destroy
- *
- * This calls drm_unplug_minor() on the given minor and then frees it. Nothing
- * is done if @minor is NULL. It is fine to call this on already unplugged
- * minors.
- * The global DRM mutex must be held by the caller.
- */
-static void drm_put_minor(struct drm_minor *minor)
-{
-       if (!minor)
-               return;
-
-       DRM_DEBUG("release secondary minor %d\n", minor->index);
-
-       drm_unplug_minor(minor);
-       kfree(minor);
-}
-
-/**
- * Called via drm_exit() at module unload time or when pci device is
- * unplugged.
- *
- * Cleans up all DRM device, calling drm_lastclose().
- *
- */
-void drm_put_dev(struct drm_device *dev)
-{
-       DRM_DEBUG("\n");
-
-       if (!dev) {
-               DRM_ERROR("cleanup called no dev\n");
-               return;
-       }
-
-       drm_dev_unregister(dev);
-       drm_dev_free(dev);
-}
-EXPORT_SYMBOL(drm_put_dev);
-
-void drm_unplug_dev(struct drm_device *dev)
-{
-       /* for a USB device */
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               drm_unplug_minor(dev->control);
-       if (dev->render)
-               drm_unplug_minor(dev->render);
-       drm_unplug_minor(dev->primary);
-
-       mutex_lock(&drm_global_mutex);
-
-       drm_device_set_unplugged(dev);
-
-       if (dev->open_count == 0) {
-               drm_put_dev(dev);
-       }
-       mutex_unlock(&drm_global_mutex);
-}
-EXPORT_SYMBOL(drm_unplug_dev);
-#endif
-
-#if 0
-/*
- * DRM internal mount
- * We want to be able to allocate our own "struct address_space" to control
- * memory-mappings in VRAM (or stolen RAM, ...). However, core MM does not allow
- * stand-alone address_space objects, so we need an underlying inode. As there
- * is no way to allocate an independent inode easily, we need a fake internal
- * VFS mount-point.
- *
- * The drm_fs_inode_new() function allocates a new inode, drm_fs_inode_free()
- * frees it again. You are allowed to use iget() and iput() to get references to
- * the inode. But each drm_fs_inode_new() call must be paired with exactly one
- * drm_fs_inode_free() call (which does not have to be the last iput()).
- * We use drm_fs_inode_*() to manage our internal VFS mount-point and share it
- * between multiple inode-users. You could, technically, call
- * iget() + drm_fs_inode_free() directly after alloc and sometime later do an
- * iput(), but this way you'd end up with a new vfsmount for each inode.
- */
-
-static int drm_fs_cnt;
-static struct vfsmount *drm_fs_mnt;
-
-static const struct dentry_operations drm_fs_dops = {
-       .d_dname        = simple_dname,
-};
-
-static const struct super_operations drm_fs_sops = {
-       .statfs         = simple_statfs,
-};
-
-static struct dentry *drm_fs_mount(struct file_system_type *fs_type, int flags,
-                                  const char *dev_name, void *data)
-{
-       return mount_pseudo(fs_type,
-                           "drm:",
-                           &drm_fs_sops,
-                           &drm_fs_dops,
-                           0x010203ff);
-}
-
-static struct file_system_type drm_fs_type = {
-       .name           = "drm",
-       .owner          = THIS_MODULE,
-       .mount          = drm_fs_mount,
-       .kill_sb        = kill_anon_super,
-};
-
-static struct inode *drm_fs_inode_new(void)
-{
-       struct inode *inode;
-       int r;
-
-       r = simple_pin_fs(&drm_fs_type, &drm_fs_mnt, &drm_fs_cnt);
-       if (r < 0) {
-               DRM_ERROR("Cannot mount pseudo fs: %d\n", r);
-               return ERR_PTR(r);
-       }
-
-       inode = alloc_anon_inode(drm_fs_mnt->mnt_sb);
-       if (IS_ERR(inode))
-               simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);
-
-       return inode;
-}
-
-static void drm_fs_inode_free(struct inode *inode)
-{
-       if (inode) {
-               iput(inode);
-               simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);
-       }
-}
-
-/**
- * drm_dev_alloc - Allocate new drm device
- * @driver: DRM driver to allocate device for
- * @parent: Parent device object
- *
- * Allocate and initialize a new DRM device. No device registration is done.
- * Call drm_dev_register() to advertice the device to user space and register it
- * with other core subsystems.
- *
- * RETURNS:
- * Pointer to new DRM device, or NULL if out of memory.
- */
-struct drm_device *drm_dev_alloc(struct drm_driver *driver,
-                                struct device *parent)
-{
-       struct drm_device *dev;
-       int ret;
-
-       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-       if (!dev)
-               return NULL;
-
-       dev->dev = parent;
-       dev->driver = driver;
-
-       INIT_LIST_HEAD(&dev->filelist);
-       INIT_LIST_HEAD(&dev->ctxlist);
-       INIT_LIST_HEAD(&dev->vmalist);
-       INIT_LIST_HEAD(&dev->maplist);
-       INIT_LIST_HEAD(&dev->vblank_event_list);
-
-       spin_lock_init(&dev->count_lock);
-       spin_lock_init(&dev->event_lock);
-       mutex_init(&dev->struct_mutex);
-       mutex_init(&dev->ctxlist_mutex);
-
-       if (drm_ht_create(&dev->map_hash, 12))
-               goto err_free;
-
-       ret = drm_ctxbitmap_init(dev);
-       if (ret) {
-               DRM_ERROR("Cannot allocate memory for context bitmap.\n");
-               goto err_ht;
-       }
-
-       if (driver->driver_features & DRIVER_GEM) {
-               ret = drm_gem_init(dev);
-               if (ret) {
-                       DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n");
-                       goto err_ctxbitmap;
-               }
-       }
-
-       return dev;
-
-err_ctxbitmap:
-       drm_ctxbitmap_cleanup(dev);
-err_ht:
-       drm_ht_remove(&dev->map_hash);
-err_free:
-       kfree(dev);
-       return NULL;
-}
-EXPORT_SYMBOL(drm_dev_alloc);
-
-/**
- * drm_dev_free - Free DRM device
- * @dev: DRM device to free
- *
- * Free a DRM device that has previously been allocated via drm_dev_alloc().
- * You must not use kfree() instead or you will leak memory.
- *
- * This must not be called once the device got registered. Use drm_put_dev()
- * instead, which then calls drm_dev_free().
- */
-void drm_dev_free(struct drm_device *dev)
-{
-       drm_put_minor(dev->control);
-       drm_put_minor(dev->render);
-       drm_put_minor(dev->primary);
-
-       if (dev->driver->driver_features & DRIVER_GEM)
-               drm_gem_destroy(dev);
-
-       drm_ctxbitmap_cleanup(dev);
-       drm_ht_remove(&dev->map_hash);
-
-       kfree(dev->devname);
-       kfree(dev);
-}
-EXPORT_SYMBOL(drm_dev_free);
-
-/**
- * drm_dev_register - Register DRM device
- * @dev: Device to register
- *
- * Register the DRM device @dev with the system, advertise device to user-space
- * and start normal device operation. @dev must be allocated via drm_dev_alloc()
- * previously.
- *
- * Never call this twice on any device!
- *
- * RETURNS:
- * 0 on success, negative error code on failure.
- */
-int drm_dev_register(struct drm_device *dev, unsigned long flags)
-{
-       int ret;
-
-       mutex_lock(&drm_global_mutex);
-
-       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-               ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL);
-               if (ret)
-                       goto out_unlock;
-       }
-
-       if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
-               ret = drm_get_minor(dev, &dev->render, DRM_MINOR_RENDER);
-               if (ret)
-                       goto err_control_node;
-       }
-
-       ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY);
-       if (ret)
-               goto err_render_node;
-
-       if (dev->driver->load) {
-               ret = dev->driver->load(dev, flags);
-               if (ret)
-                       goto err_primary_node;
-       }
-
-       /* setup grouping for legacy outputs */
-       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-               ret = drm_mode_group_init_legacy_group(dev,
-                               &dev->primary->mode_group);
-               if (ret)
-                       goto err_unload;
-       }
-
-       ret = 0;
-       goto out_unlock;
-
-err_unload:
-       if (dev->driver->unload)
-               dev->driver->unload(dev);
-err_primary_node:
-       drm_unplug_minor(dev->primary);
-err_render_node:
-       drm_unplug_minor(dev->render);
-err_control_node:
-       drm_unplug_minor(dev->control);
-out_unlock:
-       mutex_unlock(&drm_global_mutex);
-       return ret;
-}
-EXPORT_SYMBOL(drm_dev_register);
-
-/**
- * drm_dev_unregister - Unregister DRM device
- * @dev: Device to unregister
- *
- * Unregister the DRM device from the system. This does the reverse of
- * drm_dev_register() but does not deallocate the device. The caller must call
- * drm_dev_free() to free all resources.
- */
-void drm_dev_unregister(struct drm_device *dev)
-{
-       struct drm_map_list *r_list, *list_temp;
-
-       drm_lastclose(dev);
-
-       if (dev->driver->unload)
-               dev->driver->unload(dev);
-
-       if (dev->agp)
-               drm_pci_agp_destroy(dev);
-
-       drm_vblank_cleanup(dev);
-
-       list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
-               drm_rmmap(dev, r_list->map);
-
-       drm_unplug_minor(dev->control);
-       drm_unplug_minor(dev->render);
-       drm_unplug_minor(dev->primary);
-}
-EXPORT_SYMBOL(drm_dev_unregister);
-#endif
index eda2320..e4d6b9a 100644 (file)
@@ -251,12 +251,10 @@ static int drm_bufs_info DRM_SYSCTL_HANDLER_ARGS
        DRM_SYSCTL_PRINT("\n o     size count  free      segs pages    kB\n");
        for (i = 0; i <= DRM_MAX_ORDER; i++) {
                if (dma->bufs[i].buf_count)
-                       DRM_SYSCTL_PRINT("%2d %8d %5d %5d %5d %5d %5d\n",
+                       DRM_SYSCTL_PRINT("%2d %8d %5d %5d %5d %5d\n",
                                       i,
                                       dma->bufs[i].buf_size,
                                       dma->bufs[i].buf_count,
-                                      atomic_read(&dma->bufs[i]
-                                                  .freelist.count),
                                       dma->bufs[i].seg_count,
                                       dma->bufs[i].seg_count
                                       *(1 << dma->bufs[i].page_order),
index 526a77c..6351e49 100644 (file)
@@ -426,6 +426,9 @@ static const u32 gen7_render_regs[] = {
        GEN7_SO_WRITE_OFFSET(1),
        GEN7_SO_WRITE_OFFSET(2),
        GEN7_SO_WRITE_OFFSET(3),
+       GEN7_L3SQCREG1,
+       GEN7_L3CNTLREG2,
+       GEN7_L3CNTLREG3,
 };
 
 static const u32 gen7_blt_regs[] = {
index f0cc5b7..4f5666e 100644 (file)
@@ -112,7 +112,7 @@ static void i915_free_hws(struct drm_device *dev)
        I915_WRITE(HWS_PGA, 0x1ffff000);
 }
 
-void i915_kernel_lost_context(struct drm_device * dev)
+void i915_kernel_lost_context(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_private *master_priv = dev_priv;
@@ -142,7 +142,7 @@ void i915_kernel_lost_context(struct drm_device * dev)
                master_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;
 }
 
-static int i915_dma_cleanup(struct drm_device * dev)
+static int i915_dma_cleanup(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int i;
@@ -166,7 +166,7 @@ static int i915_dma_cleanup(struct drm_device * dev)
        return 0;
 }
 
-static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
+static int i915_initialize(struct drm_device *dev, drm_i915_init_t *init)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int ret;
@@ -212,7 +212,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
        return 0;
 }
 
-static int i915_dma_resume(struct drm_device * dev)
+static int i915_dma_resume(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_engine_cs *ring = LP_RING(dev_priv);
@@ -336,7 +336,7 @@ static int validate_cmd(int cmd)
        return 0;
 }
 
-static int i915_emit_cmds(struct drm_device * dev, int *buffer, int dwords)
+static int i915_emit_cmds(struct drm_device *dev, int *buffer, int dwords)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int i, ret;
@@ -346,6 +346,7 @@ static int i915_emit_cmds(struct drm_device * dev, int *buffer, int dwords)
 
        for (i = 0; i < dwords;) {
                int sz = validate_cmd(buffer[i]);
+
                if (sz == 0 || i + sz > dwords)
                        return -EINVAL;
                i += sz;
@@ -429,7 +430,7 @@ static void i915_emit_breadcrumb(struct drm_device *dev)
        }
 }
 
-static int i915_dispatch_cmdbuffer(struct drm_device * dev,
+static int i915_dispatch_cmdbuffer(struct drm_device *dev,
                                   drm_i915_cmdbuffer_t *cmd,
                                   struct drm_clip_rect *cliprects,
                                   void *cmdbuf)
@@ -463,8 +464,8 @@ static int i915_dispatch_cmdbuffer(struct drm_device * dev,
        return 0;
 }
 
-static int i915_dispatch_batchbuffer(struct drm_device * dev,
-                                    drm_i915_batchbuffer_t * batch,
+static int i915_dispatch_batchbuffer(struct drm_device *dev,
+                                    drm_i915_batchbuffer_t *batch,
                                     struct drm_clip_rect *cliprects)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -525,7 +526,7 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev,
        return 0;
 }
 
-static int i915_dispatch_flip(struct drm_device * dev)
+static int i915_dispatch_flip(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int ret;
@@ -725,7 +726,7 @@ fail_batch_free:
        return ret;
 }
 
-static int i915_emit_irq(struct drm_device * dev)
+static int i915_emit_irq(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 #if 0
@@ -753,7 +754,7 @@ static int i915_emit_irq(struct drm_device * dev)
        return dev_priv->dri1.counter;
 }
 
-static int i915_wait_irq(struct drm_device * dev, int irq_nr)
+static int i915_wait_irq(struct drm_device *dev, int irq_nr)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 #if 0
@@ -1257,6 +1258,7 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_
 {
        struct drm_device *dev = pci_get_drvdata(pdev);
        pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
+
        if (state == VGA_SWITCHEROO_ON) {
                pr_info("switched on\n");
                dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
@@ -1328,6 +1330,13 @@ static int i915_load_modeset_init(struct drm_device *dev)
 
        intel_power_domains_init_hw(dev_priv);
 
+       /*
+        * We enable some interrupt sources in our postinstall hooks, so mark
+        * interrupts as enabled _before_ actually enabling them to avoid
+        * special cases in our ordering checks.
+        */
+       dev_priv->pm._irqs_disabled = false;
+
        ret = drm_irq_install(dev, dev->irq);
        if (ret)
                goto cleanup_gem_stolen;
@@ -1372,9 +1381,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
         */
        intel_fbdev_initial_config(dev);
 
-       /* Only enable hotplug handling once the fbdev is fully set up. */
-       dev_priv->enable_hotplug_processing = true;
-
        drm_kms_helper_poll_init(dev);
 
        return 0;
@@ -1426,15 +1432,16 @@ void i915_master_destroy(struct drm_device *dev, struct drm_master *master)
 #endif
 
 #if IS_ENABLED(CONFIG_FB)
-static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
+static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
 {
        struct apertures_struct *ap;
        struct pci_dev *pdev = dev_priv->dev->pdev;
        bool primary;
+       int ret;
 
        ap = alloc_apertures(1);
        if (!ap)
-               return;
+               return -ENOMEM;
 
        ap->ranges[0].base = dev_priv->gtt.mappable_base;
        ap->ranges[0].size = dev_priv->gtt.mappable_end;
@@ -1442,13 +1449,16 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
        primary =
                pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
 
-       remove_conflicting_framebuffers(ap, "inteldrmfb", primary);
+       ret = remove_conflicting_framebuffers(ap, "inteldrmfb", primary);
 
        kfree(ap);
+
+       return ret;
 }
 #else
-static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
+static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
 {
+       return 0;
 }
 #endif
 
@@ -1494,10 +1504,11 @@ static void i915_dump_device_info(struct drm_i915_private *dev_priv)
 #define SEP_EMPTY
 #define PRINT_FLAG(name) info->name ? #name "," : ""
 #define SEP_COMMA ,
-       DRM_DEBUG_DRIVER("i915 device info: gen=%i, pciid=0x%04x flags="
+       DRM_DEBUG_DRIVER("i915 device info: gen=%i, pciid=0x%04x rev=0x%02x flags="
                         DEV_INFO_FOR_EACH_FLAG(PRINT_S, SEP_EMPTY),
                         info->gen,
                         dev_priv->dev->pdev->device,
+                        dev_priv->dev->pdev->revision,
                         DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_COMMA));
 #undef PRINT_S
 #undef SEP_EMPTY
@@ -1601,7 +1612,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        if (dev_priv == NULL)
                return -ENOMEM;
 
-       dev->dev_private = (void *)dev_priv;
+       dev->dev_private = dev_priv;
        dev_priv->dev = dev;
 
        /* copy initial configuration to dev_priv->info */
@@ -1613,6 +1624,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        spin_init(&dev_priv->backlight_lock, "i915bl");
        lockinit(&dev_priv->uncore.lock, "915gt", 0, LK_CANRECURSE);
        spin_init(&dev_priv->mm.object_stat_lock, "i915osl");
+       spin_init(&dev_priv->mmio_flip_lock, "i915mfl");
        lockinit(&dev_priv->dpio_lock, "i915dpio", 0, LK_CANRECURSE);
        lockinit(&dev_priv->modeset_restore_lock, "i915mrl", 0, LK_CANRECURSE);
 
@@ -1679,7 +1691,11 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
                        goto out_gtt;
                }
 
-               i915_kick_out_firmware_fb(dev_priv);
+               ret = i915_kick_out_firmware_fb(dev_priv);
+               if (ret) {
+                       DRM_ERROR("failed to remove conflicting framebuffer drivers\n");
+                       goto out_gtt;
+               }
        }
 
 #if 0
@@ -1734,6 +1750,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
                goto out_mtrrfree;
        }
 
+       dev_priv->dp_wq = alloc_ordered_workqueue("i915-dp", 0);
+       if (dev_priv->dp_wq == NULL) {
+               DRM_ERROR("Failed to create our dp workqueue.\n");
+               ret = -ENOMEM;
+               goto out_freewq;
+       }
+
        intel_irq_init(dev);
        intel_uncore_sanitize(dev);
 
@@ -1810,6 +1833,8 @@ out_gem_unload:
        intel_teardown_gmbus(dev);
        intel_teardown_mchbar(dev);
        pm_qos_remove_request(&dev_priv->pm_qos);
+       destroy_workqueue(dev_priv->dp_wq);
+out_freewq:
        destroy_workqueue(dev_priv->wq);
 out_mtrrfree:
        arch_phys_wc_del(dev_priv->gtt.mtrr);
@@ -1915,6 +1940,7 @@ int i915_driver_unload(struct drm_device *dev)
        bus_generic_detach(dev->dev);
        drm_rmmap(dev, dev_priv->mmio_map);
 
+       destroy_workqueue(dev_priv->dp_wq);
        destroy_workqueue(dev_priv->wq);
        pm_qos_remove_request(&dev_priv->pm_qos);
 
@@ -1955,7 +1981,7 @@ int i915_driver_open(struct drm_device *dev, struct drm_file *file)
  * and DMA structures, since the kernel won't be using them, and clea
  * up any GEM state.
  */
-void i915_driver_lastclose(struct drm_device * dev)
+void i915_driver_lastclose(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
@@ -1978,11 +2004,11 @@ void i915_driver_lastclose(struct drm_device * dev)
        i915_dma_cleanup(dev);
 }
 
-void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv)
+void i915_driver_preclose(struct drm_device *dev, struct drm_file *file)
 {
        mutex_lock(&dev->struct_mutex);
-       i915_gem_context_close(dev, file_priv);
-       i915_gem_release(dev, file_priv);
+       i915_gem_context_close(dev, file);
+       i915_gem_release(dev, file);
        mutex_unlock(&dev->struct_mutex);
 }
 
@@ -2043,7 +2069,7 @@ const struct drm_ioctl_desc i915_ioctls[] = {
        DRM_IOCTL_DEF_DRV(I915_GEM_WAIT, i915_gem_wait_ioctl, DRM_AUTH|DRM_UNLOCKED),
        DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_CREATE, i915_gem_context_create_ioctl, DRM_UNLOCKED),
        DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_DESTROY, i915_gem_context_destroy_ioctl, DRM_UNLOCKED),
-       DRM_IOCTL_DEF_DRV(I915_REG_READ, i915_reg_read_ioctl, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(I915_REG_READ, i915_reg_read_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
        DRM_IOCTL_DEF_DRV(I915_GET_RESET_STATS, i915_get_reset_stats_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
 #if 0
        DRM_IOCTL_DEF_DRV(I915_GEM_USERPTR, i915_gem_userptr_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
@@ -2057,7 +2083,7 @@ int i915_max_ioctl = ARRAY_SIZE(i915_ioctls);
  * manage the gtt, we need to claim that all intel devices are agp.  For
  * otherwise the drm core refuses to initialize the agp support code.
  */
-int i915_driver_device_is_agp(struct drm_device * dev)
+int i915_driver_device_is_agp(struct drm_device *dev)
 {
        return 1;
 }
index 0f4f1c1..e1323d4 100644 (file)
@@ -43,8 +43,6 @@ static struct drm_driver driver;
                          PIPE_C_OFFSET, PIPE_EDP_OFFSET }, \
        .trans_offsets = { TRANSCODER_A_OFFSET, TRANSCODER_B_OFFSET, \
                           TRANSCODER_C_OFFSET, TRANSCODER_EDP_OFFSET }, \
-       .dpll_offsets = { DPLL_A_OFFSET, DPLL_B_OFFSET }, \
-       .dpll_md_offsets = { DPLL_A_MD_OFFSET, DPLL_B_MD_OFFSET }, \
        .palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET }
 
 #define GEN_CHV_PIPEOFFSETS \
@@ -52,10 +50,6 @@ static struct drm_driver driver;
                          CHV_PIPE_C_OFFSET }, \
        .trans_offsets = { TRANSCODER_A_OFFSET, TRANSCODER_B_OFFSET, \
                           CHV_TRANSCODER_C_OFFSET, }, \
-       .dpll_offsets = { DPLL_A_OFFSET, DPLL_B_OFFSET, \
-                         CHV_DPLL_C_OFFSET }, \
-       .dpll_md_offsets = { DPLL_A_MD_OFFSET, DPLL_B_MD_OFFSET, \
-                            CHV_DPLL_C_MD_OFFSET }, \
        .palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET, \
                             CHV_PALETTE_C_OFFSET }
 
@@ -305,6 +299,7 @@ static const struct intel_device_info intel_broadwell_d_info = {
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
        .has_llc = 1,
        .has_ddi = 1,
+       .has_fpga_dbg = 1,
        .has_fbc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
        IVB_CURSOR_OFFSETS,
@@ -316,6 +311,7 @@ static const struct intel_device_info intel_broadwell_m_info = {
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
        .has_llc = 1,
        .has_ddi = 1,
+       .has_fpga_dbg = 1,
        .has_fbc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
        IVB_CURSOR_OFFSETS,
@@ -327,6 +323,7 @@ static const struct intel_device_info intel_broadwell_gt3d_info = {
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
        .has_llc = 1,
        .has_ddi = 1,
+       .has_fpga_dbg = 1,
        .has_fbc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
        IVB_CURSOR_OFFSETS,
@@ -338,6 +335,7 @@ static const struct intel_device_info intel_broadwell_gt3m_info = {
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
        .has_llc = 1,
        .has_ddi = 1,
+       .has_fpga_dbg = 1,
        .has_fbc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
        IVB_CURSOR_OFFSETS,
@@ -498,12 +496,41 @@ bool i915_semaphore_is_enabled(struct drm_device *dev)
        return true;
 }
 
+void intel_hpd_cancel_work(struct drm_i915_private *dev_priv)
+{
+       lockmgr(&dev_priv->irq_lock, LK_EXCLUSIVE);
+
+       dev_priv->long_hpd_port_mask = 0;
+       dev_priv->short_hpd_port_mask = 0;
+       dev_priv->hpd_event_bits = 0;
+
+       lockmgr(&dev_priv->irq_lock, LK_RELEASE);
+
+       cancel_work_sync(&dev_priv->dig_port_work);
+       cancel_work_sync(&dev_priv->hotplug_work);
+       cancel_delayed_work_sync(&dev_priv->hotplug_reenable_work);
+}
+
+static void intel_suspend_encoders(struct drm_i915_private *dev_priv)
+{
+       struct drm_device *dev = dev_priv->dev;
+       struct drm_encoder *encoder;
+
+       drm_modeset_lock_all(dev);
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
+
+               if (intel_encoder->suspend)
+                       intel_encoder->suspend(intel_encoder);
+       }
+       drm_modeset_unlock_all(dev);
+}
+
 static int i915_drm_freeze(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_crtc *crtc;
-
-       intel_runtime_pm_get(dev_priv);
+       pci_power_t opregion_target_state;
 
        /* ignore lid events during suspend */
        mutex_lock(&dev_priv->modeset_restore_lock);
@@ -531,21 +558,28 @@ static int i915_drm_freeze(struct drm_device *dev)
                        return error;
                }
 
-               drm_irq_uninstall(dev);
-               dev_priv->enable_hotplug_processing = false;
-
-               intel_disable_gt_powersave(dev);
-
                /*
                 * Disable CRTCs directly since we want to preserve sw state
-                * for _thaw.
+                * for _thaw. Also, power gate the CRTC power wells.
                 */
                drm_modeset_lock_all(dev);
-               for_each_crtc(dev, crtc) {
-                       dev_priv->display.crtc_disable(crtc);
-               }
+               for_each_crtc(dev, crtc)
+                       intel_crtc_control(crtc, false);
                drm_modeset_unlock_all(dev);
 
+#if 0
+               intel_dp_mst_suspend(dev);
+
+               flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+#endif
+
+               intel_runtime_pm_disable_interrupts(dev);
+               intel_hpd_cancel_work(dev_priv);
+
+               intel_suspend_encoders(dev_priv);
+
+               intel_suspend_gt_powersave(dev);
+
                intel_modeset_suspend_hw(dev);
        }
 
@@ -553,8 +587,17 @@ static int i915_drm_freeze(struct drm_device *dev)
 
        i915_save_state(dev);
 
+       opregion_target_state = PCI_D3cold;
+#if IS_ENABLED(CONFIG_ACPI_SLEEP)
+       if (acpi_target_system_state() < ACPI_STATE_S3)
+               opregion_target_state = PCI_D1;
+#endif
+       intel_opregion_notify_adapter(dev, opregion_target_state);
+
+#if 0
+       intel_uncore_forcewake_reset(dev, false);
+#endif
        intel_opregion_fini(dev);
-       intel_uncore_fini(dev);
 
 #if 0
        console_lock();
@@ -564,6 +607,8 @@ static int i915_drm_freeze(struct drm_device *dev)
 
        dev_priv->suspend_count++;
 
+       intel_display_set_init_power(dev_priv, false);
+
        return 0;
 }
 
@@ -609,6 +654,20 @@ void intel_console_resume(struct work_struct *work)
        intel_fbdev_set_suspend(dev, FBINFO_STATE_RUNNING);
        console_unlock();
 }
+
+static int i915_drm_thaw_early(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (IS_HASWELL(dev) || IS_BROADWELL(dev))
+               hsw_disable_pc8(dev_priv);
+
+       intel_uncore_early_sanitize(dev, true);
+       intel_uncore_sanitize(dev);
+       intel_power_domains_init_hw(dev_priv);
+
+       return 0;
+}
 #endif
 
 static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
@@ -639,8 +698,7 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
                }
                mutex_unlock(&dev->struct_mutex);
 
-               /* We need working interrupts for modeset enabling ... */
-               drm_irq_install(dev, dev->irq);
+               intel_runtime_pm_restore_interrupts(dev);
 
                intel_modeset_init_hw(dev);
 
@@ -655,7 +713,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
                 * notifications.
                 * */
                intel_hpd_init(dev);
-               dev_priv->enable_hotplug_processing = true;
                /* Config may have changed between suspend and resume */
                drm_helper_hpd_irq_event(dev);
        }
@@ -680,7 +737,10 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
        dev_priv->modeset_restore = MODESET_DONE;
        mutex_unlock(&dev_priv->modeset_restore_lock);
 
-       intel_runtime_pm_put(dev_priv);
+#if 0
+       intel_opregion_notify_adapter(dev, PCI_D0);
+#endif
+
        return 0;
 }
 
@@ -878,6 +938,33 @@ static int i915_pm_suspend(struct device *dev)
        return i915_drm_freeze(drm_dev);
 }
 
+static int i915_pm_suspend_late(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       struct drm_i915_private *dev_priv = drm_dev->dev_private;
+
+       /*
+        * We have a suspedn ordering issue with the snd-hda driver also
+        * requiring our device to be power up. Due to the lack of a
+        * parent/child relationship we currently solve this with an late
+        * suspend hook.
+        *
+        * FIXME: This should be solved with a special hdmi sink device or
+        * similar so that power domains can be employed.
+        */
+       if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+               return 0;
+
+       if (IS_HASWELL(drm_dev) || IS_BROADWELL(drm_dev))
+               hsw_enable_pc8(dev_priv);
+
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, PCI_D3hot);
+
+       return 0;
+}
+
 static int i915_pm_resume(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
@@ -890,6 +977,7 @@ static int i915_pm_freeze(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       struct drm_i915_private *dev_priv = drm_dev->dev_private;
 
        if (!drm_dev || !drm_dev->dev_private) {
                dev_err(dev, "DRM not initialized, aborting suspend.\n");
index 64eeb53..2fff216 100644 (file)
@@ -56,7 +56,7 @@
 
 #define DRIVER_NAME            "i915"
 #define DRIVER_DESC            "Intel Graphics"
-#define DRIVER_DATE            "20080730"
+#define DRIVER_DATE            "20140725"
 
 enum i915_pipe {
        INVALID_PIPE = -1,
@@ -132,6 +132,7 @@ enum intel_display_power_domain {
        POWER_DOMAIN_PORT_OTHER,
        POWER_DOMAIN_VGA,
        POWER_DOMAIN_AUDIO,
+       POWER_DOMAIN_PLLS,
        POWER_DOMAIN_INIT,
 
        POWER_DOMAIN_NUM,
@@ -181,14 +182,20 @@ enum hpd_pin {
        list_for_each_entry((intel_connector), &(dev)->mode_config.connector_list, base.head) \
                if ((intel_connector)->base.encoder == (__encoder))
 
+#define for_each_power_domain(domain, mask)                            \
+       for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++)     \
+               if ((1 << (domain)) & (mask))
+
 struct drm_i915_private;
 struct i915_mmu_object;
 
 enum intel_dpll_id {
        DPLL_ID_PRIVATE = -1, /* non-shared dpll in use */
        /* real shared dpll ids must be >= 0 */
-       DPLL_ID_PCH_PLL_A,
-       DPLL_ID_PCH_PLL_B,
+       DPLL_ID_PCH_PLL_A = 0,
+       DPLL_ID_PCH_PLL_B = 1,
+       DPLL_ID_WRPLL1 = 0,
+       DPLL_ID_WRPLL2 = 1,
 };
 #define I915_NUM_PLLS 2
 
@@ -197,6 +204,7 @@ struct intel_dpll_hw_state {
        uint32_t dpll_md;
        uint32_t fp0;
        uint32_t fp1;
+       uint32_t wrpll;
 };
 
 struct intel_shared_dpll {
@@ -207,6 +215,8 @@ struct intel_shared_dpll {
        /* should match the index in the dev_priv->shared_dplls array */
        enum intel_dpll_id id;
        struct intel_dpll_hw_state hw_state;
+       /* The mode_set hook is optional and should be used together with the
+        * intel_prepare_shared_dpll function. */
        void (*mode_set)(struct drm_i915_private *dev_priv,
                         struct intel_shared_dpll *pll);
        void (*enable)(struct drm_i915_private *dev_priv,
@@ -231,12 +241,6 @@ void intel_link_compute_m_n(int bpp, int nlanes,
                            int pixel_clock, int link_clock,
                            struct intel_link_m_n *m_n);
 
-struct intel_ddi_plls {
-       int spll_refcount;
-       int wrpll1_refcount;
-       int wrpll2_refcount;
-};
-
 /* Interface history:
  *
  * 1.1: Original.
@@ -313,6 +317,7 @@ struct drm_i915_error_state {
        u32 eir;
        u32 pgtbl_er;
        u32 ier;
+       u32 gtier[4];
        u32 ccid;
        u32 derrmr;
        u32 forcewake;
@@ -327,6 +332,7 @@ struct drm_i915_error_state {
        u64 fence[I915_MAX_NUM_FENCES];
        struct intel_overlay_error_state *overlay;
        struct intel_display_error_state *display;
+       struct drm_i915_error_object *semaphore_obj;
 
        struct drm_i915_error_ring {
                bool valid;
@@ -438,8 +444,8 @@ struct drm_i915_display_funcs {
        void (*update_wm)(struct drm_crtc *crtc);
        void (*update_sprite_wm)(struct drm_plane *plane,
                                 struct drm_crtc *crtc,
-                                uint32_t sprite_width, int pixel_size,
-                                bool enable, bool scaled);
+                                uint32_t sprite_width, uint32_t sprite_height,
+                                int pixel_size, bool enable, bool scaled);
        void (*modeset_global_resources)(struct drm_device *dev);
        /* Returns the active state of the crtc, and if the crtc is active,
         * fills out the pipe-config with the hw state. */
@@ -555,8 +561,6 @@ struct intel_device_info {
        /* Register offsets for the various display pipes and transcoders */
        int pipe_offsets[I915_MAX_TRANSCODERS];
        int trans_offsets[I915_MAX_TRANSCODERS];
-       int dpll_offsets[I915_MAX_PIPES];
-       int dpll_md_offsets[I915_MAX_PIPES];
        int palette_offsets[I915_MAX_PIPES];
        int cursor_offsets[I915_MAX_PIPES];
 };
@@ -589,28 +593,48 @@ struct i915_ctx_hang_stats {
 };
 
 /* This must match up with the value previously used for execbuf2.rsvd1. */
-#define DEFAULT_CONTEXT_ID 0
+#define DEFAULT_CONTEXT_HANDLE 0
+/**
+ * struct intel_context - as the name implies, represents a context.
+ * @ref: reference count.
+ * @user_handle: userspace tracking identity for this context.
+ * @remap_slice: l3 row remapping information.
+ * @file_priv: filp associated with this context (NULL for global default
+ *            context).
+ * @hang_stats: information about the role of this context in possible GPU
+ *             hangs.
+ * @vm: virtual memory space used by this context.
+ * @legacy_hw_ctx: render context backing object and whether it is correctly
+ *                initialized (legacy ring submission mechanism only).
+ * @link: link in the global list of contexts.
+ *
+ * Contexts are memory images used by the hardware to store copies of their
+ * internal state.
+ */
 struct intel_context {
        struct kref ref;
-       int id;
-       bool is_initialized;
+       int user_handle;
        uint8_t remap_slice;
        struct drm_i915_file_private *file_priv;
-       struct intel_engine_cs *last_ring;
-       struct drm_i915_gem_object *obj;
        struct i915_ctx_hang_stats hang_stats;
        struct i915_address_space *vm;
 
+       struct {
+               struct drm_i915_gem_object *rcs_state;
+               bool initialized;
+       } legacy_hw_ctx;
+
        struct list_head link;
 };
 
 struct i915_fbc {
        unsigned long size;
+       unsigned threshold;
        unsigned int fb_id;
        enum plane plane;
        int y;
 
-       struct drm_mm_node *compressed_fb;
+       struct drm_mm_node compressed_fb;
        struct drm_mm_node *compressed_llb;
 
        struct intel_fbc_work {
@@ -638,9 +662,15 @@ struct i915_drrs {
        struct intel_connector *connector;
 };
 
+struct intel_dp;
 struct i915_psr {
+       struct lock lock;
        bool sink_support;
        bool source_ok;
+       struct intel_dp *enabled;
+       bool active;
+       struct delayed_work work;
+       unsigned busy_frontbuffer_bits;
 };
 
 enum intel_pch {
@@ -890,6 +920,12 @@ struct vlv_s0ix_state {
        u32 clock_gate_dis2;
 };
 
+struct intel_rps_ei {
+       u32 cz_clock;
+       u32 render_c0;
+       u32 media_c0;
+};
+
 struct intel_gen6_power_mgmt {
        /* work and pm_iir are protected by dev_priv->irq_lock */
        struct work_struct work;
@@ -913,6 +949,9 @@ struct intel_gen6_power_mgmt {
        u8 efficient_freq;      /* AKA RPe. Pre-determined balanced frequency */
        u8 rp1_freq;            /* "less than" RP0 power/freqency */
        u8 rp0_freq;            /* Non-overclocked max frequency. */
+       u32 cz_freq;
+
+       u32 ei_interrupt_count;
 
        int last_adj;
        enum { LOW_POWER, BETWEEN, HIGH_POWER } power;
@@ -920,6 +959,9 @@ struct intel_gen6_power_mgmt {
        bool enabled;
        struct delayed_work delayed_resume_work;
 
+       /* manual wa residency calculations */
+       struct intel_rps_ei up_ei, down_ei;
+
        /*
         * Protects RPS/RC6 register access and PCU communication.
         * Must be taken after struct_mutex if nested.
@@ -1239,6 +1281,7 @@ struct intel_vbt_data {
                u16 pwm_freq_hz;
                bool present;
                bool active_low_pwm;
+               u8 min_brightness;      /* min_brightness/255 of max */
        } backlight;
 
        /* MIPI DSI */
@@ -1308,7 +1351,7 @@ struct ilk_wm_values {
  */
 struct i915_runtime_pm {
        bool suspended;
-       bool irqs_disabled;
+       bool _irqs_disabled;
 };
 
 enum intel_pipe_crc_source {
@@ -1341,6 +1384,17 @@ struct intel_pipe_crc {
        wait_queue_head_t wq;
 };
 
+struct i915_frontbuffer_tracking {
+       struct lock lock;
+
+       /*
+        * Tracking bits for delayed frontbuffer flushing du to gpu activity or
+        * scheduled flips.
+        */
+       unsigned busy_bits;
+       unsigned flip_bits;
+};
+
 struct drm_i915_private {
        struct drm_device *dev;
        struct kmem_cache *slab;
@@ -1379,6 +1433,7 @@ struct drm_i915_private {
 
        struct pci_dev *bridge_dev;
        struct intel_engine_cs ring[I915_NUM_RINGS];
+       struct drm_i915_gem_object *semaphore_obj;
        uint32_t last_seqno, next_seqno;
 
        drm_dma_handle_t *status_page_dmah;
@@ -1388,6 +1443,9 @@ struct drm_i915_private {
        /* protects the irq masks */
        struct lock irq_lock;
 
+       /* protects the mmio flip data */
+       struct spinlock mmio_flip_lock;
+
        bool display_irqs_enabled;
 
        /* To control wakeup latency, e.g. for irq-driven dp aux transfers. */
@@ -1418,7 +1476,7 @@ struct drm_i915_private {
                } hpd_mark;
        } hpd_stats[HPD_NUM_PINS];
        u32 hpd_event_bits;
-       struct timer_list hotplug_reenable_timer;
+       struct delayed_work hotplug_reenable_work;
 
        struct i915_fbc fbc;
        struct i915_drrs drrs;
@@ -1484,7 +1542,6 @@ struct drm_i915_private {
 
        int num_shared_dpll;
        struct intel_shared_dpll shared_dplls[I915_NUM_PLLS];
-       struct intel_ddi_plls ddi_plls;
        int dpio_phy_iosf_port[I915_NUM_PHYS_VLV];
 
        /* Reclocking support */
@@ -1492,6 +1549,9 @@ struct drm_i915_private {
        bool lvds_downclock_avail;
        /* indicates the reduced downclock for LVDS*/
        int lvds_downclock;
+
+       struct i915_frontbuffer_tracking fb_tracking;
+
        u16 orig_clock;
 
        bool mchbar_need_disable;
@@ -1558,6 +1618,20 @@ struct drm_i915_private {
 
        struct i915_runtime_pm pm;
 
+       struct intel_digital_port *hpd_irq_port[I915_MAX_PORTS];
+       u32 long_hpd_port_mask;
+       u32 short_hpd_port_mask;
+       struct work_struct dig_port_work;
+
+       /*
+        * if we get a HPD irq from DP and a HPD irq from non-DP
+        * the non-DP HPD could block the workqueue on a mode config
+        * mutex getting, that userspace may have taken. However
+        * userspace is waiting on the DP workqueue to run which is
+        * blocked behind the non-DP one.
+        */
+       struct workqueue_struct *dp_wq;
+
        /* Old dri1 support infrastructure, beware the dragons ya fools entering
         * here! */
        struct i915_dri1_state dri1;
@@ -1609,6 +1683,28 @@ struct drm_i915_gem_object_ops {
        void (*release)(struct drm_i915_gem_object *);
 };
 
+/*
+ * Frontbuffer tracking bits. Set in obj->frontbuffer_bits while a gem bo is
+ * considered to be the frontbuffer for the given plane interface-vise. This
+ * doesn't mean that the hw necessarily already scans it out, but that any
+ * rendering (by the cpu or gpu) will land in the frontbuffer eventually.
+ *
+ * We have one bit per pipe and per scanout plane type.
+ */
+#define INTEL_FRONTBUFFER_BITS_PER_PIPE 4
+#define INTEL_FRONTBUFFER_BITS \
+       (INTEL_FRONTBUFFER_BITS_PER_PIPE * I915_MAX_PIPES)
+#define INTEL_FRONTBUFFER_PRIMARY(pipe) \
+       (1 << (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))
+#define INTEL_FRONTBUFFER_CURSOR(pipe) \
+       (1 << (1 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))))
+#define INTEL_FRONTBUFFER_SPRITE(pipe) \
+       (1 << (2 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))))
+#define INTEL_FRONTBUFFER_OVERLAY(pipe) \
+       (1 << (3 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))))
+#define INTEL_FRONTBUFFER_ALL_MASK(pipe) \
+       (0xf << (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))
+
 struct drm_i915_gem_object {
        struct drm_gem_object base;
 
@@ -1678,6 +1774,12 @@ struct drm_i915_gem_object {
        unsigned int pin_mappable:1;
        unsigned int pin_display:1;
 
+       /*
+        * Is the object to be mapped as read-only to the GPU
+        * Only honoured if hardware has relevant pte bit
+        */
+       unsigned long gt_ro:1;
+
        /*
         * Is the GPU currently using a fence to access this buffer,
         */
@@ -1690,6 +1792,8 @@ struct drm_i915_gem_object {
        unsigned int has_global_gtt_mapping:1;
        unsigned int has_dma_mapping:1;
 
+       unsigned int frontbuffer_bits:INTEL_FRONTBUFFER_BITS;
+
        vm_page_t *pages;
        int pages_pin_count;
 
@@ -1736,6 +1840,10 @@ struct drm_i915_gem_object {
 };
 #define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base)
 
+void i915_gem_track_fb(struct drm_i915_gem_object *old,
+                      struct drm_i915_gem_object *new,
+                      unsigned frontbuffer_bits);
+
 /**
  * Request queue structure.
  *
@@ -1957,10 +2065,8 @@ struct drm_i915_cmd_table {
 #define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
 
 #define HAS_HW_CONTEXTS(dev)   (INTEL_INFO(dev)->gen >= 6)
-#define HAS_ALIASING_PPGTT(dev)        (INTEL_INFO(dev)->gen >= 6 && \
-                                (!IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)))
-#define HAS_PPGTT(dev)         (INTEL_INFO(dev)->gen >= 7 \
-                                && !IS_GEN8(dev))
+#define HAS_ALIASING_PPGTT(dev)        (INTEL_INFO(dev)->gen >= 6)
+#define HAS_PPGTT(dev)         (INTEL_INFO(dev)->gen >= 7 && !IS_GEN8(dev))
 #define USES_PPGTT(dev)                intel_enable_ppgtt(dev, false)
 #define USES_FULL_PPGTT(dev)   intel_enable_ppgtt(dev, true)
 
@@