drm: Update generic, ttm and radeon code to Linux 4.9
authorFrançois Tigeot <ftigeot@wolfpond.org>
Sun, 17 Nov 2019 10:57:03 +0000 (11:57 +0100)
committerFrançois Tigeot <ftigeot@wolfpond.org>
Sun, 17 Nov 2019 10:57:26 +0000 (11:57 +0100)
* Generally cleanup non driver-specific code and increase its
  robustness

* DisplayPort support improvements

* Atomic modesetting improvements

* Radeon performance and stability improvements

* drm/i915 stays based on Linux 4.7.10 with Linux 4.19 changes
  for now

Reviewed-by: Romick
181 files changed:
sys/conf/files
sys/dev/drm/drm/Makefile
sys/dev/drm/drm_agpsupport.c
sys/dev/drm/drm_atomic.c
sys/dev/drm/drm_atomic_helper.c
sys/dev/drm/drm_auth.c
sys/dev/drm/drm_blend.c [new file with mode: 0644]
sys/dev/drm/drm_bridge.c
sys/dev/drm/drm_bufs.c
sys/dev/drm/drm_color_mgmt.c [new file with mode: 0644]
sys/dev/drm/drm_connector.c [new file with mode: 0644]
sys/dev/drm/drm_context.c
sys/dev/drm/drm_crtc.c
sys/dev/drm/drm_crtc_helper.c
sys/dev/drm/drm_crtc_helper_internal.h [copied from sys/dev/drm/drm_crtc_internal.h with 57% similarity]
sys/dev/drm/drm_crtc_internal.h
sys/dev/drm/drm_dma.c
sys/dev/drm/drm_dp_aux_dev.c
sys/dev/drm/drm_dp_dual_mode_helper.c
sys/dev/drm/drm_dp_helper.c
sys/dev/drm/drm_dp_mst_topology.c
sys/dev/drm/drm_dragonfly.c
sys/dev/drm/drm_drv.c
sys/dev/drm/drm_edid.c
sys/dev/drm/drm_encoder.c [new file with mode: 0644]
sys/dev/drm/drm_fb_helper.c
sys/dev/drm/drm_fops.c
sys/dev/drm/drm_fourcc.c [new file with mode: 0644]
sys/dev/drm/drm_framebuffer.c [new file with mode: 0644]
sys/dev/drm/drm_gem.c
sys/dev/drm/drm_global.c
sys/dev/drm/drm_hashtab.c
sys/dev/drm/drm_internal.h
sys/dev/drm/drm_ioctl.c
sys/dev/drm/drm_irq.c
sys/dev/drm/drm_legacy.h
sys/dev/drm/drm_lock.c
sys/dev/drm/drm_memory.c
sys/dev/drm/drm_mipi_dsi.c
sys/dev/drm/drm_mm.c
sys/dev/drm/drm_mode_object.c [new file with mode: 0644]
sys/dev/drm/drm_modes.c
sys/dev/drm/drm_modeset_helper.c [new file with mode: 0644]
sys/dev/drm/drm_modeset_lock.c
sys/dev/drm/drm_pci.c
sys/dev/drm/drm_plane.c [new file with mode: 0644]
sys/dev/drm/drm_plane_helper.c
sys/dev/drm/drm_prime.c
sys/dev/drm/drm_probe_helper.c
sys/dev/drm/drm_property.c [new file with mode: 0644]
sys/dev/drm/drm_rect.c
sys/dev/drm/drm_scatter.c
sys/dev/drm/drm_simple_kms_helper.c [new file with mode: 0644]
sys/dev/drm/drm_sysctl.c
sys/dev/drm/drm_vma_manager.c
sys/dev/drm/i915/intel_atomic.c
sys/dev/drm/i915/intel_atomic_plane.c
sys/dev/drm/i915/intel_color.c
sys/dev/drm/i915/intel_display.c
sys/dev/drm/i915/intel_drv.h
sys/dev/drm/i915/intel_fbc.c
sys/dev/drm/i915/intel_fbdev.c
sys/dev/drm/i915/intel_lvds.c
sys/dev/drm/i915/intel_sprite.c
sys/dev/drm/include/asm/div64.h [copied from sys/dev/drm/include/linux/kthread.h with 86% similarity]
sys/dev/drm/include/asm/io.h
sys/dev/drm/include/drm/drmP.h
sys/dev/drm/include/drm/drm_atomic.h
sys/dev/drm/include/drm/drm_atomic_helper.h
sys/dev/drm/include/drm/drm_auth.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_blend.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_bridge.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_color_mgmt.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_connector.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_crtc.h
sys/dev/drm/include/drm/drm_crtc_helper.h
sys/dev/drm/include/drm/drm_dp_helper.h
sys/dev/drm/include/drm/drm_dp_mst_helper.h
sys/dev/drm/include/drm/drm_edid.h
sys/dev/drm/include/drm/drm_encoder.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_fb_cma_helper.h
sys/dev/drm/include/drm/drm_fb_helper.h
sys/dev/drm/include/drm/drm_flip_work.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_fourcc.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_framebuffer.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_gem.h
sys/dev/drm/include/drm/drm_irq.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_legacy.h
sys/dev/drm/include/drm/drm_mipi_dsi.h
sys/dev/drm/include/drm/drm_mode_object.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_modes.h
sys/dev/drm/include/drm/drm_modeset.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_modeset_helper.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_modeset_helper_vtables.h
sys/dev/drm/include/drm/drm_os_linux.h
sys/dev/drm/include/drm/drm_panel.h
sys/dev/drm/include/drm/drm_plane.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_plane_helper.h
sys/dev/drm/include/drm/drm_property.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_simple_kms_helper.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_vma_manager.h
sys/dev/drm/include/drm/i915_drm.h
sys/dev/drm/include/drm/ttm/ttm_bo_api.h
sys/dev/drm/include/drm/ttm/ttm_bo_driver.h
sys/dev/drm/include/drm/ttm/ttm_memory.h
sys/dev/drm/include/drm/ttm/ttm_placement.h
sys/dev/drm/include/linux/bitmap.h
sys/dev/drm/include/linux/compiler.h
sys/dev/drm/include/linux/debugfs.h [copied from sys/dev/drm/include/linux/kthread.h with 83% similarity]
sys/dev/drm/include/linux/device.h
sys/dev/drm/include/linux/dma-fence.h [copied from sys/dev/drm/include/linux/kthread.h with 86% similarity]
sys/dev/drm/include/linux/errno.h
sys/dev/drm/include/linux/fb.h
sys/dev/drm/include/linux/fence.h
sys/dev/drm/include/linux/fs.h
sys/dev/drm/include/linux/i2c.h
sys/dev/drm/include/linux/irq.h [copied from sys/dev/drm/include/linux/pm.h with 80% similarity]
sys/dev/drm/include/linux/irqdomain.h [copied from sys/dev/drm/include/linux/kthread.h with 85% similarity]
sys/dev/drm/include/linux/kasan.h [copied from sys/dev/drm/include/linux/kthread.h with 86% similarity]
sys/dev/drm/include/linux/kernel.h
sys/dev/drm/include/linux/kthread.h
sys/dev/drm/include/linux/linkage.h [copied from sys/dev/drm/include/linux/kthread.h with 86% similarity]
sys/dev/drm/include/linux/mfd/core.h [copied from sys/dev/drm/include/linux/kthread.h with 86% similarity]
sys/dev/drm/include/linux/module.h
sys/dev/drm/include/linux/of.h [copied from sys/dev/drm/include/linux/pm.h with 80% similarity]
sys/dev/drm/include/linux/pci.h
sys/dev/drm/include/linux/pm.h
sys/dev/drm/include/linux/pm_runtime.h
sys/dev/drm/include/linux/power_supply.h
sys/dev/drm/include/linux/printk.h
sys/dev/drm/include/linux/sched.h
sys/dev/drm/include/linux/seq_file.h
sys/dev/drm/include/linux/slab.h
sys/dev/drm/include/linux/swiotlb.h [copied from sys/dev/drm/include/linux/kthread.h with 85% similarity]
sys/dev/drm/include/uapi/drm/drm.h
sys/dev/drm/include/uapi/drm/drm_fourcc.h
sys/dev/drm/include/uapi/drm/drm_mode.h
sys/dev/drm/include/uapi/drm/drm_sarea.h
sys/dev/drm/include/uapi/linux/media-bus-format.h [copied from sys/dev/drm/include/linux/kthread.h with 86% similarity]
sys/dev/drm/kconfig.h
sys/dev/drm/linux_fence.c
sys/dev/drm/linux_i2c.c
sys/dev/drm/linux_kobject.c
sys/dev/drm/radeon/atombios_crtc.c
sys/dev/drm/radeon/atombios_dp.c
sys/dev/drm/radeon/cik.c
sys/dev/drm/radeon/cikd.h
sys/dev/drm/radeon/evergreen.c
sys/dev/drm/radeon/evergreen_cs.c
sys/dev/drm/radeon/evergreend.h
sys/dev/drm/radeon/ni.c
sys/dev/drm/radeon/nid.h
sys/dev/drm/radeon/r100.c
sys/dev/drm/radeon/r600.c
sys/dev/drm/radeon/r600d.h
sys/dev/drm/radeon/radeon.h
sys/dev/drm/radeon/radeon_acpi.c
sys/dev/drm/radeon/radeon_connectors.c
sys/dev/drm/radeon/radeon_device.c
sys/dev/drm/radeon/radeon_display.c
sys/dev/drm/radeon/radeon_dp_auxch.c
sys/dev/drm/radeon/radeon_drv.c
sys/dev/drm/radeon/radeon_fb.c
sys/dev/drm/radeon/radeon_i2c.c
sys/dev/drm/radeon/radeon_kms.c
sys/dev/drm/radeon/radeon_legacy_crtc.c
sys/dev/drm/radeon/radeon_object.c
sys/dev/drm/radeon/radeon_pm.c
sys/dev/drm/radeon/radeon_ttm.c
sys/dev/drm/radeon/radeon_uvd.c
sys/dev/drm/radeon/radeon_vce.c
sys/dev/drm/radeon/rv770.c
sys/dev/drm/radeon/rv770d.h
sys/dev/drm/radeon/si.c
sys/dev/drm/radeon/si_dpm.c
sys/dev/drm/radeon/sid.h
sys/dev/drm/ttm/ttm_bo.c
sys/dev/drm/ttm/ttm_bo_util.c
sys/dev/drm/ttm/ttm_bo_vm.c
sys/dev/drm/ttm/ttm_memory.c
sys/dev/drm/ttm/ttm_tt.c

index 2c36bed..836aa56 100644 (file)
@@ -2235,11 +2235,14 @@ dev/drm/ati_pcigart.c                   optional drm compile-with "${NORMAL_C} -include $S/dev/d
 # drm_agpsupport.c must be included if CONFIG_AGP is set
 #dev/drm/drm_agpsupport.c              optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_atomic.c                   optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
-dev/drm/drm_bridge.c                   optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_atomic_helper.c            optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_auth.c                     optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
+dev/drm/drm_blend.c                    optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
+dev/drm/drm_bridge.c                   optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_bufs.c                     optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_cache.c                    optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
+dev/drm/drm_color_mgmt.c               optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
+dev/drm/drm_connector.c                        optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_context.c                  optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_crtc.c                     optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_crtc_helper.c              optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
@@ -2252,9 +2255,12 @@ dev/drm/drm_dragonfly.c                  optional drm
 dev/drm/drm_drv.c                      optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_edid.c                     optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_edid_load.c                        optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
+dev/drm/drm_encoder.c                  optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_encoder_slave.c            optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_fb_helper.c                        optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_fops.c                     optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
+dev/drm/drm_fourcc.c                   optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
+dev/drm/drm_framebuffer.c              optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_gem.c                      optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_global.c                   optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_hashtab.c                  optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
@@ -2264,13 +2270,17 @@ dev/drm/drm_lock.c                      optional drm compile-with "${NORMAL_C} -include $S/dev/drm/
 dev/drm/drm_memory.c                   optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_mipi_dsi.c                 optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_mm.c                       optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
+dev/drm/drm_mode_object.c              optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_modes.c                    optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
+dev/drm/drm_modeset_helper.c           optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_modeset_lock.c             optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_panel.c                    optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_pci.c                      optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
+dev/drm/drm_plane.c                    optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_plane_helper.c             optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_prime.c                    optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_probe_helper.c             optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
+dev/drm/drm_property.c                 optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_rect.c                     optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_scatter.c                  optional drm compile-with "${NORMAL_C} -include $S/dev/drm/kconfig.h ${WERROR}"
 dev/drm/drm_sysctl.c                   optional drm
index 34c79a0..d15fecd 100644 (file)
@@ -2,9 +2,15 @@
 KMOD   = drm
 SRCS   = \
        ati_pcigart.c \
+       drm_atomic.c \
+       drm_atomic_helper.c \
        drm_auth.c \
+       drm_blend.c \
+       drm_bridge.c \
        drm_bufs.c \
        drm_cache.c \
+       drm_color_mgmt.c \
+       drm_connector.c \
        drm_context.c \
        drm_crtc.c \
        drm_crtc_helper.c \
@@ -17,9 +23,12 @@ SRCS = \
        drm_drv.c \
        drm_edid.c \
        drm_edid_load.c \
+       drm_encoder.c \
        drm_encoder_slave.c \
        drm_fb_helper.c \
        drm_fops.c \
+       drm_fourcc.c \
+       drm_framebuffer.c \
        drm_gem.c \
        drm_global.c \
        drm_hashtab.c \
@@ -29,16 +38,17 @@ SRCS        = \
        drm_memory.c \
        drm_mipi_dsi.c \
        drm_mm.c \
+       drm_mode_object.c \
        drm_modes.c \
        drm_modeset_lock.c \
-       drm_atomic.c \
-       drm_bridge.c \
-       drm_atomic_helper.c \
+       drm_modeset_helper.c \
        drm_panel.c \
        drm_pci.c \
+       drm_plane.c \
        drm_plane_helper.c \
        drm_prime.c \
        drm_probe_helper.c \
+       drm_property.c \
        drm_rect.c \
        drm_scatter.c \
        drm_sysctl.c \
index a76d51e..06621ba 100644 (file)
@@ -514,9 +514,7 @@ struct drm_agp_head *drm_agp_init(struct drm_device *dev)
  * intact so it can still be used. It is safe to call this if AGP is disabled or
  * was already removed.
  *
- * If DRIVER_MODESET is active, nothing is done to protect the modesetting
- * resources from getting destroyed. Drivers are responsible of cleaning them up
- * during device shutdown.
+ * Cleanup is only done for drivers who have DRIVER_LEGACY set.
  */
 void drm_legacy_agp_clear(struct drm_device *dev)
 {
@@ -524,7 +522,7 @@ void drm_legacy_agp_clear(struct drm_device *dev)
 
        if (!dev->agp)
                return;
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        /* Remove AGP resources, but leave dev->agp intact until
index 1b0a5ad..69b9182 100644 (file)
 
 #include "drm_crtc_internal.h"
 
+static void crtc_commit_free(struct kref *kref)
+{
+       struct drm_crtc_commit *commit =
+               container_of(kref, struct drm_crtc_commit, ref);
+
+       kfree(commit);
+}
+
+void drm_crtc_commit_put(struct drm_crtc_commit *commit)
+{
+       kref_put(&commit->ref, crtc_commit_free);
+}
+EXPORT_SYMBOL(drm_crtc_commit_put);
+
 /**
  * drm_atomic_state_default_release -
  * release memory initialized by drm_atomic_state_init
 void drm_atomic_state_default_release(struct drm_atomic_state *state)
 {
        kfree(state->connectors);
-       kfree(state->connector_states);
        kfree(state->crtcs);
-       kfree(state->crtc_states);
        kfree(state->planes);
-       kfree(state->plane_states);
 }
 EXPORT_SYMBOL(drm_atomic_state_default_release);
 
@@ -72,18 +83,10 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state)
                               sizeof(*state->crtcs), GFP_KERNEL);
        if (!state->crtcs)
                goto fail;
-       state->crtc_states = kcalloc(dev->mode_config.num_crtc,
-                                    sizeof(*state->crtc_states), GFP_KERNEL);
-       if (!state->crtc_states)
-               goto fail;
        state->planes = kcalloc(dev->mode_config.num_total_plane,
                                sizeof(*state->planes), GFP_KERNEL);
        if (!state->planes)
                goto fail;
-       state->plane_states = kcalloc(dev->mode_config.num_total_plane,
-                                     sizeof(*state->plane_states), GFP_KERNEL);
-       if (!state->plane_states)
-               goto fail;
 
        state->dev = dev;
 
@@ -139,40 +142,48 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
        DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state);
 
        for (i = 0; i < state->num_connector; i++) {
-               struct drm_connector *connector = state->connectors[i];
+               struct drm_connector *connector = state->connectors[i].ptr;
 
                if (!connector)
                        continue;
 
                connector->funcs->atomic_destroy_state(connector,
-                                                      state->connector_states[i]);
-               state->connectors[i] = NULL;
-               state->connector_states[i] = NULL;
+                                                      state->connectors[i].state);
+               state->connectors[i].ptr = NULL;
+               state->connectors[i].state = NULL;
                drm_connector_unreference(connector);
        }
 
        for (i = 0; i < config->num_crtc; i++) {
-               struct drm_crtc *crtc = state->crtcs[i];
+               struct drm_crtc *crtc = state->crtcs[i].ptr;
 
                if (!crtc)
                        continue;
 
                crtc->funcs->atomic_destroy_state(crtc,
-                                                 state->crtc_states[i]);
-               state->crtcs[i] = NULL;
-               state->crtc_states[i] = NULL;
+                                                 state->crtcs[i].state);
+
+               if (state->crtcs[i].commit) {
+                       kfree(state->crtcs[i].commit->event);
+                       state->crtcs[i].commit->event = NULL;
+                       drm_crtc_commit_put(state->crtcs[i].commit);
+               }
+
+               state->crtcs[i].commit = NULL;
+               state->crtcs[i].ptr = NULL;
+               state->crtcs[i].state = NULL;
        }
 
        for (i = 0; i < config->num_total_plane; i++) {
-               struct drm_plane *plane = state->planes[i];
+               struct drm_plane *plane = state->planes[i].ptr;
 
                if (!plane)
                        continue;
 
                plane->funcs->atomic_destroy_state(plane,
-                                                  state->plane_states[i]);
-               state->planes[i] = NULL;
-               state->plane_states[i] = NULL;
+                                                  state->planes[i].state);
+               state->planes[i].ptr = NULL;
+               state->planes[i].state = NULL;
        }
 }
 EXPORT_SYMBOL(drm_atomic_state_default_clear);
@@ -270,8 +281,8 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state,
        if (!crtc_state)
                return ERR_PTR(-ENOMEM);
 
-       state->crtc_states[index] = crtc_state;
-       state->crtcs[index] = crtc;
+       state->crtcs[index].state = crtc_state;
+       state->crtcs[index].ptr = crtc;
        crtc_state->state = state;
 
        DRM_DEBUG_ATOMIC("Added [CRTC:%d:%s] %p state to %p\n",
@@ -393,8 +404,7 @@ drm_atomic_replace_property_blob(struct drm_property_blob **blob,
        if (old_blob == new_blob)
                return;
 
-       if (old_blob)
-               drm_property_unreference_blob(old_blob);
+       drm_property_unreference_blob(old_blob);
        if (new_blob)
                drm_property_reference_blob(new_blob);
        *blob = new_blob;
@@ -410,18 +420,21 @@ drm_atomic_replace_property_blob_from_id(struct drm_crtc *crtc,
                                         ssize_t expected_size,
                                         bool *replaced)
 {
-       struct drm_device *dev = crtc->dev;
        struct drm_property_blob *new_blob = NULL;
 
        if (blob_id != 0) {
-               new_blob = drm_property_lookup_blob(dev, blob_id);
+               new_blob = drm_property_lookup_blob(crtc->dev, blob_id);
                if (new_blob == NULL)
                        return -EINVAL;
-               if (expected_size > 0 && expected_size != new_blob->length)
+
+               if (expected_size > 0 && expected_size != new_blob->length) {
+                       drm_property_unreference_blob(new_blob);
                        return -EINVAL;
+               }
        }
 
        drm_atomic_replace_property_blob(blob, new_blob, replaced);
+       drm_property_unreference_blob(new_blob);
 
        return 0;
 }
@@ -632,8 +645,8 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state,
        if (!plane_state)
                return ERR_PTR(-ENOMEM);
 
-       state->plane_states[index] = plane_state;
-       state->planes[index] = plane;
+       state->planes[index].state = plane_state;
+       state->planes[index].ptr = plane;
        plane_state->state = state;
 
        DRM_DEBUG_ATOMIC("Added [PLANE:%d:%s] %p state to %p\n",
@@ -701,6 +714,8 @@ int drm_atomic_plane_set_property(struct drm_plane *plane,
                state->src_h = val;
        } else if (property == config->rotation_property) {
                state->rotation = val;
+       } else if (property == plane->zpos_property) {
+               state->zpos = val;
        } else if (plane->funcs->atomic_set_property) {
                return plane->funcs->atomic_set_property(plane, state,
                                property, val);
@@ -757,6 +772,8 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
                *val = state->src_h;
        } else if (property == config->rotation_property) {
                *val = state->rotation;
+       } else if (property == plane->zpos_property) {
+               *val = state->zpos;
        } else if (plane->funcs->atomic_get_property) {
                return plane->funcs->atomic_get_property(plane, state, property, val);
        } else {
@@ -823,8 +840,9 @@ static int drm_atomic_plane_check(struct drm_plane *plane,
        /* Check whether this plane supports the fb pixel format. */
        ret = drm_plane_check_pixel_format(plane, state->fb->pixel_format);
        if (ret) {
-               DRM_DEBUG_ATOMIC("Invalid pixel format %s\n",
-                                drm_get_format_name(state->fb->pixel_format));
+               char *format_name = drm_get_format_name(state->fb->pixel_format);
+               DRM_DEBUG_ATOMIC("Invalid pixel format %s\n", format_name);
+               kfree(format_name);
                return ret;
        }
 
@@ -897,11 +915,10 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state,
        index = drm_connector_index(connector);
 
        if (index >= state->num_connector) {
-               struct drm_connector **c;
-               struct drm_connector_state **cs;
+               struct __drm_connnectors_state *c;
                int alloc = max(index + 1, config->num_connector);
 
-               c = krealloc(state->connectors, alloc * sizeof(*state->connectors), M_DRM, M_WAITOK);
+               c = krealloc(state->connectors, alloc * sizeof(*state->connectors), M_DRM, GFP_KERNEL);
                if (!c)
                        return ERR_PTR(-ENOMEM);
 
@@ -909,26 +926,19 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state,
                memset(&state->connectors[state->num_connector], 0,
                       sizeof(*state->connectors) * (alloc - state->num_connector));
 
-               cs = krealloc(state->connector_states, alloc * sizeof(*state->connector_states), M_DRM, M_WAITOK);
-               if (!cs)
-                       return ERR_PTR(-ENOMEM);
-
-               state->connector_states = cs;
-               memset(&state->connector_states[state->num_connector], 0,
-                      sizeof(*state->connector_states) * (alloc - state->num_connector));
                state->num_connector = alloc;
        }
 
-       if (state->connector_states[index])
-               return state->connector_states[index];
+       if (state->connectors[index].state)
+               return state->connectors[index].state;
 
        connector_state = connector->funcs->atomic_duplicate_state(connector);
        if (!connector_state)
                return ERR_PTR(-ENOMEM);
 
        drm_connector_reference(connector);
-       state->connector_states[index] = connector_state;
-       state->connectors[index] = connector;
+       state->connectors[index].state = connector_state;
+       state->connectors[index].ptr = connector;
        connector_state->state = state;
 
        DRM_DEBUG_ATOMIC("Added [CONNECTOR:%d] %p state to %p\n",
@@ -1457,7 +1467,8 @@ EXPORT_SYMBOL(drm_atomic_nonblocking_commit);
  */
 
 static struct drm_pending_vblank_event *create_vblank_event(
-               struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data)
+               struct drm_device *dev, struct drm_file *file_priv,
+               struct fence *fence, uint64_t user_data)
 {
        struct drm_pending_vblank_event *e = NULL;
        int ret;
@@ -1470,12 +1481,17 @@ static struct drm_pending_vblank_event *create_vblank_event(
        e->event.base.length = sizeof(e->event);
        e->event.user_data = user_data;
 
-       ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base);
-       if (ret) {
-               kfree(e);
-               return NULL;
+       if (file_priv) {
+               ret = drm_event_reserve_init(dev, file_priv, &e->base,
+                                            &e->event.base);
+               if (ret) {
+                       kfree(e);
+                       return NULL;
+               }
        }
 
+       e->base.fence = fence;
+
        return e;
 }
 
@@ -1678,7 +1694,7 @@ retry:
                                goto out;
                        }
 
-                       prop = drm_property_find(dev, prop_id);
+                       prop = drm_mode_obj_find_prop_id(obj, prop_id);
                        if (!prop) {
                                drm_mode_object_unreference(obj);
                                ret = -ENOENT;
@@ -1715,7 +1731,8 @@ retry:
                for_each_crtc_in_state(state, crtc, crtc_state, i) {
                        struct drm_pending_vblank_event *e;
 
-                       e = create_vblank_event(dev, file_priv, arg->user_data);
+                       e = create_vblank_event(dev, file_priv, NULL,
+                                               arg->user_data);
                        if (!e) {
                                ret = -ENOMEM;
                                goto out;
index 48c7804..cee4b07 100644 (file)
@@ -32,6 +32,8 @@
 #include <drm/drm_atomic_helper.h>
 #include <linux/fence.h>
 
+#include "drm_crtc_internal.h"
+
 /**
  * DOC: overview
  *
@@ -110,8 +112,10 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state,
 
                if (funcs->atomic_best_encoder)
                        new_encoder = funcs->atomic_best_encoder(connector, conn_state);
-               else
+               else if (funcs->best_encoder)
                        new_encoder = funcs->best_encoder(connector);
+               else
+                       new_encoder = drm_atomic_helper_best_encoder(connector);
 
                if (new_encoder) {
                        if (encoder_mask & (1 << drm_encoder_index(new_encoder))) {
@@ -298,8 +302,10 @@ update_connector_routing(struct drm_atomic_state *state,
        if (funcs->atomic_best_encoder)
                new_encoder = funcs->atomic_best_encoder(connector,
                                                         connector_state);
-       else
+       else if (funcs->best_encoder)
                new_encoder = funcs->best_encoder(connector);
+       else
+               new_encoder = drm_atomic_helper_best_encoder(connector);
 
        if (!new_encoder) {
                DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
@@ -414,6 +420,9 @@ mode_fixup(struct drm_atomic_state *state)
        for_each_crtc_in_state(state, crtc, crtc_state, i) {
                const struct drm_crtc_helper_funcs *funcs;
 
+               if (!crtc_state->enable)
+                       continue;
+
                if (!crtc_state->mode_changed &&
                    !crtc_state->connectors_changed)
                        continue;
@@ -458,7 +467,7 @@ mode_fixup(struct drm_atomic_state *state)
  * times for the same update, e.g. when the ->atomic_check functions depend upon
  * the adjusted dotclock for fifo space allocation and watermark computation.
  *
- * RETURNS
+ * RETURNS:
  * Zero for success or -errno
  */
 int
@@ -572,7 +581,7 @@ EXPORT_SYMBOL(drm_atomic_helper_check_modeset);
  * It also sets crtc_state->planes_changed to indicate that a crtc has
  * updated planes.
  *
- * RETURNS
+ * RETURNS:
  * Zero for success or -errno
  */
 int
@@ -611,7 +620,7 @@ drm_atomic_helper_check_planes(struct drm_device *dev,
                if (!funcs || !funcs->atomic_check)
                        continue;
 
-               ret = funcs->atomic_check(crtc, state->crtc_states[i]);
+               ret = funcs->atomic_check(crtc, crtc_state);
                if (ret) {
                        DRM_DEBUG_ATOMIC("[CRTC:%d:%s] atomic driver check failed\n",
                                         crtc->base.id, crtc->name);
@@ -640,7 +649,7 @@ EXPORT_SYMBOL(drm_atomic_helper_check_planes);
  * ->atomic_check functions depend upon an updated adjusted_mode.clock to
  * e.g. properly compute watermarks.
  *
- * RETURNS
+ * RETURNS:
  * Zero for success or -errno
  */
 int drm_atomic_helper_check(struct drm_device *dev,
@@ -736,6 +745,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
                /* Right function depends upon target state. */
                if (crtc->state->enable && funcs->prepare)
                        funcs->prepare(crtc);
+               else if (funcs->atomic_disable)
+                       funcs->atomic_disable(crtc, old_crtc_state);
                else if (funcs->disable)
                        funcs->disable(crtc);
                else
@@ -873,8 +884,12 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
                 * Each encoder has at most one connector (since we always steal
                 * it away), so we won't call mode_set hooks twice.
                 */
-               if (funcs && funcs->mode_set)
+               if (funcs && funcs->atomic_mode_set) {
+                       funcs->atomic_mode_set(encoder, new_crtc_state,
+                                              connector->state);
+               } else if (funcs && funcs->mode_set) {
                        funcs->mode_set(encoder, mode, adjusted_mode);
+               }
 
                drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode);
        }
@@ -990,29 +1005,46 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
  * drm_atomic_helper_wait_for_fences - wait for fences stashed in plane state
  * @dev: DRM device
  * @state: atomic state object with old state structures
+ * @pre_swap: if true, do an interruptible wait
  *
  * For implicit sync, driver should fish the exclusive fence out from the
  * incoming fb's and stash it in the drm_plane_state.  This is called after
  * drm_atomic_helper_swap_state() so it uses the current plane state (and
  * just uses the atomic state to find the changed planes)
+ *
+ * Returns zero if success or < 0 if fence_wait() fails.
  */
-void drm_atomic_helper_wait_for_fences(struct drm_device *dev,
-                           struct drm_atomic_state *state)
+int drm_atomic_helper_wait_for_fences(struct drm_device *dev,
+                                     struct drm_atomic_state *state,
+                                     bool pre_swap)
 {
        struct drm_plane *plane;
        struct drm_plane_state *plane_state;
-       int i;
+       int i, ret;
 
        for_each_plane_in_state(state, plane, plane_state, i) {
-               if (!plane->state->fence)
+               if (!pre_swap)
+                       plane_state = plane->state;
+
+               if (!plane_state->fence)
                        continue;
 
-               WARN_ON(!plane->state->fb);
+               WARN_ON(!plane_state->fb);
 
-               fence_wait(plane->state->fence, false);
-               fence_put(plane->state->fence);
-               plane->state->fence = NULL;
+               /*
+                * If waiting for fences pre-swap (ie: nonblock), userspace can
+                * still interrupt the operation. Instead of blocking until the
+                * timer expires, make the wait interruptible.
+                */
+               ret = fence_wait(plane_state->fence, pre_swap);
+               if (ret)
+                       return ret;
+
+               fence_put(plane_state->fence);
+               plane_state->fence = NULL;
        }
+
+       return 0;
 }
 EXPORT_SYMBOL(drm_atomic_helper_wait_for_fences);
 
@@ -1113,32 +1145,94 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
 EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
 
 /**
- * drm_atomic_helper_commit - commit validated state object
- * @dev: DRM device
- * @state: the driver state object
- * @nonblocking: whether nonblocking behavior is requested.
+ * drm_atomic_helper_commit_tail - commit atomic update to hardware
+ * @state: new modeset state to be committed
  *
- * This function commits a with drm_atomic_helper_check() pre-validated state
- * object. This can still fail when e.g. the framebuffer reservation fails. For
- * now this doesn't implement nonblocking commits.
+ * This is the default implemenation for the ->atomic_commit_tail() hook of the
+ * &drm_mode_config_helper_funcs vtable.
  *
- * Note that right now this function does not support nonblocking commits, hence
- * driver writers must implement their own version for now. Also note that the
- * default ordering of how the various stages are called is to match the legacy
- * modeset helper library closest. One peculiarity of that is that it doesn't
- * mesh well with runtime PM at all.
+ * Note that the default ordering of how the various stages are called is to
+ * match the legacy modeset helper library closest. One peculiarity of that is
+ * that it doesn't mesh well with runtime PM at all.
  *
- * For drivers supporting runtime PM the recommended sequence is
+ * For drivers supporting runtime PM the recommended sequence is instead ::
  *
  *     drm_atomic_helper_commit_modeset_disables(dev, state);
  *
  *     drm_atomic_helper_commit_modeset_enables(dev, state);
  *
- *     drm_atomic_helper_commit_planes(dev, state, true);
+ *     drm_atomic_helper_commit_planes(dev, state,
+ *                                     DRM_PLANE_COMMIT_ACTIVE_ONLY);
+ *
+ * for committing the atomic update to hardware.  See the kerneldoc entries for
+ * these three functions for more details.
+ */
+void drm_atomic_helper_commit_tail(struct drm_atomic_state *state)
+{
+       struct drm_device *dev = state->dev;
+
+       drm_atomic_helper_commit_modeset_disables(dev, state);
+
+       drm_atomic_helper_commit_planes(dev, state, 0);
+
+       drm_atomic_helper_commit_modeset_enables(dev, state);
+
+       drm_atomic_helper_commit_hw_done(state);
+
+       drm_atomic_helper_wait_for_vblanks(dev, state);
+
+       drm_atomic_helper_cleanup_planes(dev, state);
+}
+EXPORT_SYMBOL(drm_atomic_helper_commit_tail);
+
+static void commit_tail(struct drm_atomic_state *state)
+{
+       struct drm_device *dev = state->dev;
+       struct drm_mode_config_helper_funcs *funcs;
+
+       funcs = dev->mode_config.helper_private;
+
+       drm_atomic_helper_wait_for_fences(dev, state, false);
+
+       drm_atomic_helper_wait_for_dependencies(state);
+
+       if (funcs && funcs->atomic_commit_tail)
+               funcs->atomic_commit_tail(state);
+       else
+               drm_atomic_helper_commit_tail(state);
+
+       drm_atomic_helper_commit_cleanup_done(state);
+
+       drm_atomic_state_free(state);
+}
+
+static void commit_work(struct work_struct *work)
+{
+       struct drm_atomic_state *state = container_of(work,
+                                                     struct drm_atomic_state,
+                                                     commit_work);
+       commit_tail(state);
+}
+
+/**
+ * drm_atomic_helper_commit - commit validated state object
+ * @dev: DRM device
+ * @state: the driver state object
+ * @nonblock: whether nonblocking behavior is requested.
+ *
+ * This function commits a with drm_atomic_helper_check() pre-validated state
+ * object. This can still fail when e.g. the framebuffer reservation fails. This
+ * function implements nonblocking commits, using
+ * drm_atomic_helper_setup_commit() and related functions.
+ *
+ * Note that right now this function does not support nonblocking commits, hence
+ * driver writers must implement their own version for now.
  *
- * See the kerneldoc entries for these three functions for more details.
+ * Committing the actual hardware state is done through the
+ * ->atomic_commit_tail() callback of the &drm_mode_config_helper_funcs vtable,
+ * or it's default implementation drm_atomic_helper_commit_tail().
  *
- * RETURNS
+ * RETURNS:
  * Zero for success or -errno.
  */
 int drm_atomic_helper_commit(struct drm_device *dev,
@@ -1147,20 +1241,29 @@ int drm_atomic_helper_commit(struct drm_device *dev,
 {
        int ret;
 
-       if (nonblock)
-               return -EBUSY;
+       ret = drm_atomic_helper_setup_commit(state, nonblock);
+       if (ret)
+               return ret;
+
+       INIT_WORK(&state->commit_work, commit_work);
 
        ret = drm_atomic_helper_prepare_planes(dev, state);
        if (ret)
                return ret;
 
+       if (!nonblock) {
+               ret = drm_atomic_helper_wait_for_fences(dev, state, true);
+               if (ret)
+                       return ret;
+       }
+
        /*
         * This is the point of no return - everything below never fails except
         * when the hw goes bonghits. Which means we can commit the new state on
         * the software side now.
         */
 
-       drm_atomic_helper_swap_state(dev, state);
+       drm_atomic_helper_swap_state(state, true);
 
        /*
         * Everything below can be run asynchronously without the need to grab
@@ -1176,21 +1279,16 @@ int drm_atomic_helper_commit(struct drm_device *dev,
         * update. Which is important since compositors need to figure out the
         * composition of the next frame right after having submitted the
         * current layout.
+        *
+        * NOTE: Commit work has multiple phases, first hardware commit, then
+        * cleanup. We want them to overlap, hence need system_unbound_wq to
+        * make sure work items don't artifically stall on each another.
         */
 
-       drm_atomic_helper_wait_for_fences(dev, state);
-
-       drm_atomic_helper_commit_modeset_disables(dev, state);
-
-       drm_atomic_helper_commit_planes(dev, state, false);
-
-       drm_atomic_helper_commit_modeset_enables(dev, state);
-
-       drm_atomic_helper_wait_for_vblanks(dev, state);
-
-       drm_atomic_helper_cleanup_planes(dev, state);
-
-       drm_atomic_state_free(state);
+       if (nonblock)
+               queue_work(system_unbound_wq, &state->commit_work);
+       else
+               commit_tail(state);
 
        return 0;
 }
@@ -1199,12 +1297,7 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
 /**
  * DOC: implementing nonblocking commit
  *
- * For now the atomic helpers don't support nonblocking commit directly. If
- * there is real need it could be added though, using the dma-buf fence
- * infrastructure for generic synchronization with outstanding rendering.
- *
- * For now drivers have to implement nonblocking commit themselves, with the
- * following sequence being the recommended one:
+ * Nonblocking atomic commits have to be implemented in the following sequence:
  *
  * 1. Run drm_atomic_helper_prepare_planes() first. This is the only function
  * which commit needs to call which can fail, so we want to run it first and
@@ -1216,10 +1309,14 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
  * cancelled updates. Note that it is important to ensure that the framebuffer
  * cleanup is still done when cancelling.
  *
- * For sufficient parallelism it is recommended to have a work item per crtc
- * (for updates which don't touch global state) and a global one. Then we only
- * need to synchronize with the crtc work items for changed crtcs and the global
- * work item, which allows nice concurrent updates on disjoint sets of crtcs.
+ * Asynchronous workers need to have sufficient parallelism to be able to run
+ * different atomic commits on different CRTCs in parallel. The simplest way to
+ * achive this is by running them on the &system_unbound_wq work queue. Note
+ * that drivers are not required to split up atomic commits and run an
+ * individual commit in parallel - userspace is supposed to do that if it cares.
+ * But it might be beneficial to do that for modesets, since those necessarily
+ * must be done as one global operation, and enabling or disabling a CRTC can
+ * take a long time. But even that is not required.
  *
  * 3. The software state is updated synchronously with
  * drm_atomic_helper_swap_state(). Doing this under the protection of all modeset
@@ -1232,7 +1329,309 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
  * commit helpers: a) pre-plane commit b) plane commit c) post-plane commit and
  * then cleaning up the framebuffers after the old framebuffer is no longer
  * being displayed.
+ *
+ * The above scheme is implemented in the atomic helper libraries in
+ * drm_atomic_helper_commit() using a bunch of helper functions. See
+ * drm_atomic_helper_setup_commit() for a starting point.
+ */
+
+static int stall_checks(struct drm_crtc *crtc, bool nonblock)
+{
+       struct drm_crtc_commit *commit, *stall_commit = NULL;
+       bool completed = true;
+       int i;
+       long ret = 0;
+
+       spin_lock(&crtc->commit_lock);
+       i = 0;
+       list_for_each_entry(commit, &crtc->commit_list, commit_entry) {
+               if (i == 0) {
+                       completed = try_wait_for_completion(&commit->flip_done);
+                       /* Userspace is not allowed to get ahead of the previous
+                        * commit with nonblocking ones. */
+                       if (!completed && nonblock) {
+                               spin_unlock(&crtc->commit_lock);
+                               return -EBUSY;
+                       }
+               } else if (i == 1) {
+                       stall_commit = commit;
+                       drm_crtc_commit_get(stall_commit);
+                       break;
+               }
+
+               i++;
+       }
+       spin_unlock(&crtc->commit_lock);
+
+       if (!stall_commit)
+               return 0;
+
+       /* We don't want to let commits get ahead of cleanup work too much,
+        * stalling on 2nd previous commit means triple-buffer won't ever stall.
+        */
+       ret = wait_for_completion_interruptible_timeout(&stall_commit->cleanup_done,
+                                                       10*HZ);
+       if (ret == 0)
+               DRM_ERROR("[CRTC:%d:%s] cleanup_done timed out\n",
+                         crtc->base.id, crtc->name);
+
+       drm_crtc_commit_put(stall_commit);
+
+       return ret < 0 ? ret : 0;
+}
+
+/**
+ * drm_atomic_helper_setup_commit - setup possibly nonblocking commit
+ * @state: new modeset state to be committed
+ * @nonblock: whether nonblocking behavior is requested.
+ *
+ * This function prepares @state to be used by the atomic helper's support for
+ * nonblocking commits. Drivers using the nonblocking commit infrastructure
+ * should always call this function from their ->atomic_commit hook.
+ *
+ * To be able to use this support drivers need to use a few more helper
+ * functions. drm_atomic_helper_wait_for_dependencies() must be called before
+ * actually committing the hardware state, and for nonblocking commits this call
+ * must be placed in the async worker. See also drm_atomic_helper_swap_state()
+ * and it's stall parameter, for when a driver's commit hooks look at the
+ * ->state pointers of struct &drm_crtc, &drm_plane or &drm_connector directly.
+ *
+ * Completion of the hardware commit step must be signalled using
+ * drm_atomic_helper_commit_hw_done(). After this step the driver is not allowed
+ * to read or change any permanent software or hardware modeset state. The only
+ * exception is state protected by other means than &drm_modeset_lock locks.
+ * Only the free standing @state with pointers to the old state structures can
+ * be inspected, e.g. to clean up old buffers using
+ * drm_atomic_helper_cleanup_planes().
+ *
+ * At the very end, before cleaning up @state drivers must call
+ * drm_atomic_helper_commit_cleanup_done().
+ *
+ * This is all implemented by in drm_atomic_helper_commit(), giving drivers a
+ * complete and esay-to-use default implementation of the atomic_commit() hook.
+ *
+ * The tracking of asynchronously executed and still pending commits is done
+ * using the core structure &drm_crtc_commit.
+ *
+ * By default there's no need to clean up resources allocated by this function
+ * explicitly: drm_atomic_state_default_clear() will take care of that
+ * automatically.
+ *
+ * Returns:
+ *
+ * 0 on success. -EBUSY when userspace schedules nonblocking commits too fast,
+ * -ENOMEM on allocation failures and -EINTR when a signal is pending.
+ */
+int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
+                                  bool nonblock)
+{
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *crtc_state;
+       struct drm_crtc_commit *commit;
+       int i, ret;
+
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
+               commit = kzalloc(sizeof(*commit), GFP_KERNEL);
+               if (!commit)
+                       return -ENOMEM;
+
+               init_completion(&commit->flip_done);
+               init_completion(&commit->hw_done);
+               init_completion(&commit->cleanup_done);
+               INIT_LIST_HEAD(&commit->commit_entry);
+               kref_init(&commit->ref);
+               commit->crtc = crtc;
+
+               state->crtcs[i].commit = commit;
+
+               ret = stall_checks(crtc, nonblock);
+               if (ret)
+                       return ret;
+
+               /* Drivers only send out events when at least either current or
+                * new CRTC state is active. Complete right away if everything
+                * stays off. */
+               if (!crtc->state->active && !crtc_state->active) {
+                       complete_all(&commit->flip_done);
+                       continue;
+               }
+
+               /* Legacy cursor updates are fully unsynced. */
+               if (state->legacy_cursor_update) {
+                       complete_all(&commit->flip_done);
+                       continue;
+               }
+
+               if (!crtc_state->event) {
+                       commit->event = kzalloc(sizeof(*commit->event),
+                                               GFP_KERNEL);
+                       if (!commit->event)
+                               return -ENOMEM;
+
+                       crtc_state->event = commit->event;
+               }
+
+               crtc_state->event->base.completion = &commit->flip_done;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
+
+
+static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc)
+{
+       struct drm_crtc_commit *commit;
+       int i = 0;
+
+       list_for_each_entry(commit, &crtc->commit_list, commit_entry) {
+               /* skip the first entry, that's the current commit */
+               if (i == 1)
+                       return commit;
+               i++;
+       }
+
+       return NULL;
+}
+
+/**
+ * drm_atomic_helper_wait_for_dependencies - wait for required preceeding commits
+ * @state: new modeset state to be committed
+ *
+ * This function waits for all preceeding commits that touch the same CRTC as
+ * @state to both be committed to the hardware (as signalled by
+ * drm_atomic_helper_commit_hw_done) and executed by the hardware (as signalled
+ * by calling drm_crtc_vblank_send_event on the event member of
+ * &drm_crtc_state).
+ *
+ * This is part of the atomic helper support for nonblocking commits, see
+ * drm_atomic_helper_setup_commit() for an overview.
  */
+void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *state)
+{
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *crtc_state;
+       struct drm_crtc_commit *commit;
+       int i;
+       long ret;
+
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
+               spin_lock(&crtc->commit_lock);
+               commit = preceeding_commit(crtc);
+               if (commit)
+                       drm_crtc_commit_get(commit);
+               spin_unlock(&crtc->commit_lock);
+
+               if (!commit)
+                       continue;
+
+               ret = wait_for_completion_timeout(&commit->hw_done,
+                                                 10*HZ);
+               if (ret == 0)
+                       DRM_ERROR("[CRTC:%d:%s] hw_done timed out\n",
+                                 crtc->base.id, crtc->name);
+
+               /* Currently no support for overwriting flips, hence
+                * stall for previous one to execute completely. */
+               ret = wait_for_completion_timeout(&commit->flip_done,
+                                                 10*HZ);
+               if (ret == 0)
+                       DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
+                                 crtc->base.id, crtc->name);
+
+               drm_crtc_commit_put(commit);
+       }
+}
+EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
+
+/**
+ * drm_atomic_helper_commit_hw_done - setup possible nonblocking commit
+ * @state: new modeset state to be committed
+ *
+ * This function is used to signal completion of the hardware commit step. After
+ * this step the driver is not allowed to read or change any permanent software
+ * or hardware modeset state. The only exception is state protected by other
+ * means than &drm_modeset_lock locks.
+ *
+ * Drivers should try to postpone any expensive or delayed cleanup work after
+ * this function is called.
+ *
+ * This is part of the atomic helper support for nonblocking commits, see
+ * drm_atomic_helper_setup_commit() for an overview.
+ */
+void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *state)
+{
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *crtc_state;
+       struct drm_crtc_commit *commit;
+       int i;
+
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
+               commit = state->crtcs[i].commit;
+               if (!commit)
+                       continue;
+
+               /* backend must have consumed any event by now */
+               WARN_ON(crtc->state->event);
+               spin_lock(&crtc->commit_lock);
+               complete_all(&commit->hw_done);
+               spin_unlock(&crtc->commit_lock);
+       }
+}
+EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done);
+
+/**
+ * drm_atomic_helper_commit_cleanup_done - signal completion of commit
+ * @state: new modeset state to be committed
+ *
+ * This signals completion of the atomic update @state, including any cleanup
+ * work. If used, it must be called right before calling
+ * drm_atomic_state_free().
+ *
+ * This is part of the atomic helper support for nonblocking commits, see
+ * drm_atomic_helper_setup_commit() for an overview.
+ */
+void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *state)
+{
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *crtc_state;
+       struct drm_crtc_commit *commit;
+       int i;
+       long ret;
+
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
+               commit = state->crtcs[i].commit;
+               if (WARN_ON(!commit))
+                       continue;
+
+               spin_lock(&crtc->commit_lock);
+               complete_all(&commit->cleanup_done);
+               WARN_ON(!try_wait_for_completion(&commit->hw_done));
+
+               /* commit_list borrows our reference, need to remove before we
+                * clean up our drm_atomic_state. But only after it actually
+                * completed, otherwise subsequent commits won't stall properly. */
+               if (try_wait_for_completion(&commit->flip_done))
+                       goto del_commit;
+
+               spin_unlock(&crtc->commit_lock);
+
+               /* We must wait for the vblank event to signal our completion
+                * before releasing our reference, since the vblank work does
+                * not hold a reference of its own. */
+               ret = wait_for_completion_timeout(&commit->flip_done,
+                                                 10*HZ);
+               if (ret == 0)
+                       DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
+                                 crtc->base.id, crtc->name);
+
+               spin_lock(&crtc->commit_lock);
+del_commit:
+               list_del(&commit->commit_entry);
+               spin_unlock(&crtc->commit_lock);
+       }
+}
+EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done);
 
 /**
  * drm_atomic_helper_prepare_planes - prepare plane resources before commit
@@ -1249,19 +1648,18 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
 int drm_atomic_helper_prepare_planes(struct drm_device *dev,
                                     struct drm_atomic_state *state)
 {
-       int nplanes = dev->mode_config.num_total_plane;
-       int ret, i;
+       struct drm_plane *plane;
+       struct drm_plane_state *plane_state;
+       int ret, i, j;
 
-       for (i = 0; i < nplanes; i++) {
+       for_each_plane_in_state(state, plane, plane_state, i) {
                const struct drm_plane_helper_funcs *funcs;
-               struct drm_plane *plane = state->planes[i];
-               struct drm_plane_state *plane_state = state->plane_states[i];
-
-               if (!plane)
-                       continue;
 
                funcs = plane->helper_private;
 
+               if (!drm_atomic_helper_framebuffer_changed(dev, state, plane_state->crtc))
+                       continue;
+
                if (funcs->prepare_fb) {
                        ret = funcs->prepare_fb(plane, plane_state);
                        if (ret)
@@ -1272,19 +1670,19 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev,
        return 0;
 
 fail:
-       for (i--; i >= 0; i--) {
+       for_each_plane_in_state(state, plane, plane_state, j) {
                const struct drm_plane_helper_funcs *funcs;
-               struct drm_plane *plane = state->planes[i];
-               struct drm_plane_state *plane_state = state->plane_states[i];
 
-               if (!plane)
+               if (j >= i)
+                       continue;
+
+               if (!drm_atomic_helper_framebuffer_changed(dev, state, plane_state->crtc))
                        continue;
 
                funcs = plane->helper_private;
 
                if (funcs->cleanup_fb)
                        funcs->cleanup_fb(plane, plane_state);
-
        }
 
        return ret;
@@ -1300,7 +1698,7 @@ static bool plane_crtc_active(struct drm_plane_state *state)
  * drm_atomic_helper_commit_planes - commit plane state
  * @dev: DRM device
  * @old_state: atomic state object with old state structures
- * @active_only: Only commit on active CRTC if set
+ * @flags: flags for committing plane state
  *
  * This function commits the new plane state using the plane and atomic helper
  * functions for planes and crtcs. It assumes that the atomic state has already
@@ -1320,25 +1718,34 @@ static bool plane_crtc_active(struct drm_plane_state *state)
  * most drivers don't need to be immediately notified of plane updates for a
  * disabled CRTC.
  *
- * Unless otherwise needed, drivers are advised to set the @active_only
- * parameters to true in order not to receive plane update notifications related
- * to a disabled CRTC. This avoids the need to manually ignore plane updates in
+ * Unless otherwise needed, drivers are advised to set the ACTIVE_ONLY flag in
+ * @flags in order not to receive plane update notifications related to a
+ * disabled CRTC. This avoids the need to manually ignore plane updates in
  * driver code when the driver and/or hardware can't or just don't need to deal
  * with updates on disabled CRTCs, for example when supporting runtime PM.
  *
- * The drm_atomic_helper_commit() default implementation only sets @active_only
- * to false to most closely match the behaviour of the legacy helpers. This should
- * not be copied blindly by drivers.
+ * Drivers may set the NO_DISABLE_AFTER_MODESET flag in @flags if the relevant
+ * display controllers require to disable a CRTC's planes when the CRTC is
+ * disabled. This function would skip the ->atomic_disable call for a plane if
+ * the CRTC of the old plane state needs a modesetting operation. Of course,
+ * the drivers need to disable the planes in their CRTC disable callbacks
+ * since no one else would do that.
+ *
+ * The drm_atomic_helper_commit() default implementation doesn't set the
+ * ACTIVE_ONLY flag to most closely match the behaviour of the legacy helpers.
+ * This should not be copied blindly by drivers.
  */
 void drm_atomic_helper_commit_planes(struct drm_device *dev,
                                     struct drm_atomic_state *old_state,
-                                    bool active_only)
+                                    uint32_t flags)
 {
        struct drm_crtc *crtc;
        struct drm_crtc_state *old_crtc_state;
        struct drm_plane *plane;
        struct drm_plane_state *old_plane_state;
        int i;
+       bool active_only = flags & DRM_PLANE_COMMIT_ACTIVE_ONLY;
+       bool no_disable = flags & DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET;
 
        for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
                const struct drm_crtc_helper_funcs *funcs;
@@ -1382,10 +1789,19 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
                /*
                 * Special-case disabling the plane if drivers support it.
                 */
-               if (disabling && funcs->atomic_disable)
+               if (disabling && funcs->atomic_disable) {
+                       struct drm_crtc_state *crtc_state;
+
+                       crtc_state = old_plane_state->crtc->state;
+
+                       if (drm_atomic_crtc_needs_modeset(crtc_state) &&
+                           no_disable)
+                               continue;
+
                        funcs->atomic_disable(plane, old_plane_state);
-               else if (plane->state->crtc || disabling)
+               } else if (plane->state->crtc || disabling) {
                        funcs->atomic_update(plane, old_plane_state);
+               }
        }
 
        for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
@@ -1464,12 +1880,12 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc);
 
 /**
  * drm_atomic_helper_disable_planes_on_crtc - helper to disable CRTC's planes
- * @crtc: CRTC
+ * @old_crtc_state: atomic state object with the old CRTC state
  * @atomic: if set, synchronize with CRTC's atomic_begin/flush hooks
  *
  * Disables all planes associated with the given CRTC. This can be
- * used for instance in the CRTC helper disable callback to disable
- * all planes before shutting down the display pipeline.
+ * used for instance in the CRTC helper atomic_disable callback to disable
+ * all planes.
  *
  * If the atomic-parameter is set the function calls the CRTC's
  * atomic_begin hook before and atomic_flush hook after disabling the
@@ -1478,9 +1894,11 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc);
  * It is a bug to call this function without having implemented the
  * ->atomic_disable() plane hook.
  */
-void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc,
-                                             bool atomic)
+void
+drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc_state *old_crtc_state,
+                                        bool atomic)
 {
+       struct drm_crtc *crtc = old_crtc_state->crtc;
        const struct drm_crtc_helper_funcs *crtc_funcs =
                crtc->helper_private;
        struct drm_plane *plane;
@@ -1488,11 +1906,11 @@ void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc,
        if (atomic && crtc_funcs && crtc_funcs->atomic_begin)
                crtc_funcs->atomic_begin(crtc, NULL);
 
-       drm_for_each_plane(plane, crtc->dev) {
+       drm_atomic_crtc_state_for_each_plane(plane, old_crtc_state) {
                const struct drm_plane_helper_funcs *plane_funcs =
                        plane->helper_private;
 
-               if (plane->state->crtc != crtc || !plane_funcs)
+               if (!plane_funcs)
                        continue;
 
                WARN_ON(!plane_funcs->atomic_disable);
@@ -1527,6 +1945,9 @@ void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
        for_each_plane_in_state(old_state, plane, plane_state, i) {
                const struct drm_plane_helper_funcs *funcs;
 
+               if (!drm_atomic_helper_framebuffer_changed(dev, old_state, plane_state->crtc))
+                       continue;
+
                funcs = plane->helper_private;
 
                if (funcs->cleanup_fb)
@@ -1537,8 +1958,8 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
 
 /**
  * drm_atomic_helper_swap_state - store atomic state into current sw state
- * @dev: DRM device
  * @state: atomic state
+ * @stall: stall for proceeding commits
  *
  * This function stores the atomic state into the current state pointers in all
  * driver objects. It should be called after all failing steps have been done
@@ -1559,42 +1980,70 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
  *
  * 5. Call drm_atomic_helper_cleanup_planes() with @state, which since step 3
  * contains the old state. Also do any other cleanup required with that state.
+ *
+ * @stall must be set when nonblocking commits for this driver directly access
+ * the ->state pointer of &drm_plane, &drm_crtc or &drm_connector. With the
+ * current atomic helpers this is almost always the case, since the helpers
+ * don't pass the right state structures to the callbacks.
  */
-void drm_atomic_helper_swap_state(struct drm_device *dev,
-                                 struct drm_atomic_state *state)
+void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
+                                 bool stall)
 {
        int i;
+       long ret;
+       struct drm_connector *connector;
+       struct drm_connector_state *conn_state;
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *crtc_state;
+       struct drm_plane *plane;
+       struct drm_plane_state *plane_state;
+       struct drm_crtc_commit *commit;
+
+       if (stall) {
+               for_each_crtc_in_state(state, crtc, crtc_state, i) {
+                       spin_lock(&crtc->commit_lock);
+                       commit = list_first_entry_or_null(&crtc->commit_list,
+                                       struct drm_crtc_commit, commit_entry);
+                       if (commit)
+                               drm_crtc_commit_get(commit);
+                       spin_unlock(&crtc->commit_lock);
+
+                       if (!commit)
+                               continue;
 
-       for (i = 0; i < state->num_connector; i++) {
-               struct drm_connector *connector = state->connectors[i];
-
-               if (!connector)
-                       continue;
+                       ret = wait_for_completion_timeout(&commit->hw_done,
+                                                         10*HZ);
+                       if (ret == 0)
+                               DRM_ERROR("[CRTC:%d:%s] hw_done timed out\n",
+                                         crtc->base.id, crtc->name);
+                       drm_crtc_commit_put(commit);
+               }
+       }
 
+       for_each_connector_in_state(state, connector, conn_state, i) {
                connector->state->state = state;
-               swap(state->connector_states[i], connector->state);
+               swap(state->connectors[i].state, connector->state);
                connector->state->state = NULL;
        }
 
-       for (i = 0; i < dev->mode_config.num_crtc; i++) {
-               struct drm_crtc *crtc = state->crtcs[i];
-
-               if (!crtc)
-                       continue;
-
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
                crtc->state->state = state;
-               swap(state->crtc_states[i], crtc->state);
+               swap(state->crtcs[i].state, crtc->state);
                crtc->state->state = NULL;
-       }
 
-       for (i = 0; i < dev->mode_config.num_total_plane; i++) {
-               struct drm_plane *plane = state->planes[i];
+               if (state->crtcs[i].commit) {
+                       spin_lock(&crtc->commit_lock);
+                       list_add(&state->crtcs[i].commit->commit_entry,
+                                &crtc->commit_list);
+                       spin_unlock(&crtc->commit_lock);
 
-               if (!plane)
-                       continue;
+                       state->crtcs[i].commit->event = NULL;
+               }
+       }
 
+       for_each_plane_in_state(state, plane, plane_state, i) {
                plane->state->state = state;
-               swap(state->plane_states[i], plane->state);
+               swap(state->planes[i].state, plane->state);
                plane->state->state = NULL;
        }
 }
@@ -1959,7 +2408,7 @@ int __drm_atomic_helper_set_config(struct drm_mode_set *set,
        primary_state->crtc_h = vdisplay;
        primary_state->src_x = set->x << 16;
        primary_state->src_y = set->y << 16;
-       if (primary_state->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) {
+       if (primary_state->rotation & (DRM_ROTATE_90 | DRM_ROTATE_270)) {
                primary_state->src_w = vdisplay << 16;
                primary_state->src_h = hdisplay << 16;
        } else {
@@ -2409,7 +2858,7 @@ EXPORT_SYMBOL(drm_atomic_helper_page_flip);
  * This is the main helper function provided by the atomic helper framework for
  * implementing the legacy DPMS connector interface. It computes the new desired
  * ->active state for the corresponding CRTC (if the connector is enabled) and
- *  updates it.
+ * updates it.
  *
  * Returns:
  * Returns 0 on success, negative errno numbers on failure.
@@ -2566,6 +3015,7 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
        state->planes_changed = false;
        state->connectors_changed = false;
        state->color_mgmt_changed = false;
+       state->zpos_changed = false;
        state->event = NULL;
 }
 EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state);
@@ -2643,7 +3093,7 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane)
 
        if (plane->state) {
                plane->state->plane = plane;
-               plane->state->rotation = BIT(DRM_ROTATE_0);
+               plane->state->rotation = DRM_ROTATE_0;
        }
 }
 EXPORT_SYMBOL(drm_atomic_helper_plane_reset);
@@ -2930,16 +3380,15 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state);
  * @red: red correction table
  * @green: green correction table
  * @blue: green correction table
- * @start:
  * @size: size of the tables
  *
  * Implements support for legacy gamma correction table for drivers
  * that support color management through the DEGAMMA_LUT/GAMMA_LUT
  * properties.
  */
-void drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
-                                       u16 *red, u16 *green, u16 *blue,
-                                       uint32_t start, uint32_t size)
+int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
+                                      u16 *red, u16 *green, u16 *blue,
+                                      uint32_t size)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_mode_config *config = &dev->mode_config;
@@ -2951,7 +3400,7 @@ void drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
 
        state = drm_atomic_state_alloc(crtc->dev);
        if (!state)
-               return;
+               return -ENOMEM;
 
        blob = drm_property_create_blob(dev,
                                        sizeof(struct drm_color_lut) * size,
@@ -3002,7 +3451,7 @@ retry:
 
        drm_property_unreference_blob(blob);
 
-       return;
+       return 0;
 fail:
        if (ret == -EDEADLK)
                goto backoff;
@@ -3010,7 +3459,7 @@ fail:
        drm_atomic_state_free(state);
        drm_property_unreference_blob(blob);
 
-       return;
+       return ret;
 backoff:
        drm_atomic_state_clear(state);
        drm_atomic_legacy_backoff(state);
index ea40672..bc2930c 100644 (file)
 
 #include <drm/drmP.h>
 #include "drm_internal.h"
+#include "drm_legacy.h"
 
 /**
- * drm_getmagic - Get unique magic of a client
- * @dev: DRM device to operate on
- * @data: ioctl data containing the drm_auth object
- * @file_priv: DRM file that performs the operation
+ * DOC: master and authentication
  *
- * This looks up the unique magic of the passed client and returns it. If the
- * client did not have a magic assigned, yet, a new one is registered. The magic
- * is stored in the passed drm_auth object.
+ * struct &drm_master is used to track groups of clients with open
+ * primary/legacy device nodes. For every struct &drm_file which has had at
+ * least once successfully became the device master (either through the
+ * SET_MASTER IOCTL, or implicitly through opening the primary device node when
+ * no one else is the current master that time) there exists one &drm_master.
+ * This is noted in the is_master member of &drm_file. All other clients have
+ * just a pointer to the &drm_master they are associated with.
  *
- * Returns: 0 on success, negative error code on failure.
+ * In addition only one &drm_master can be the current master for a &drm_device.
+ * It can be switched through the DROP_MASTER and SET_MASTER IOCTL, or
+ * implicitly through closing/openeing the primary device node. See also
+ * drm_is_current_master().
+ *
+ * Clients can authenticate against the current master (if it matches their own)
+ * using the GETMAGIC and AUTHMAGIC IOCTLs. Together with exchanging masters,
+ * this allows controlled access to the device for an entire group of mutually
+ * trusted clients.
  */
+
 int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
        struct drm_auth *auth = data;
        int ret = 0;
 
-       mutex_lock(&dev->struct_mutex);
+       mutex_lock(&dev->master_mutex);
        if (!file_priv->magic) {
                ret = idr_alloc(&dev->magic_map, file_priv,
                                1, 0, GFP_KERNEL);
@@ -56,23 +67,13 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
                        file_priv->magic = ret;
        }
        auth->magic = file_priv->magic;
-       mutex_unlock(&dev->struct_mutex);
+       mutex_unlock(&dev->master_mutex);
 
        DRM_DEBUG("%u\n", auth->magic);
 
        return ret < 0 ? ret : 0;
 }
 
-/**
- * drm_authmagic - Authenticate client with a magic
- * @dev: DRM device to operate on
- * @data: ioctl data containing the drm_auth object
- * @file_priv: DRM file that performs the operation
- *
- * This looks up a DRM client by the passed magic and authenticates it.
- *
- * Returns: 0 on success, negative error code on failure.
- */
 int drm_authmagic(struct drm_device *dev, void *data,
                  struct drm_file *file_priv)
 {
@@ -81,13 +82,255 @@ int drm_authmagic(struct drm_device *dev, void *data,
 
        DRM_DEBUG("%u\n", auth->magic);
 
-       mutex_lock(&dev->struct_mutex);
+       mutex_lock(&dev->master_mutex);
        file = idr_find(&dev->magic_map, auth->magic);
        if (file) {
                file->authenticated = 1;
                idr_replace(&dev->magic_map, NULL, auth->magic);
        }
-       mutex_unlock(&dev->struct_mutex);
+       mutex_unlock(&dev->master_mutex);
 
        return file ? 0 : -EINVAL;
 }
+
+static struct drm_master *drm_master_create(struct drm_device *dev)
+{
+       struct drm_master *master;
+
+       master = kzalloc(sizeof(*master), GFP_KERNEL);
+       if (!master)
+               return NULL;
+
+       kref_init(&master->refcount);
+       spin_init(&master->lock.spinlock, "drmmls");
+       init_waitqueue_head(&master->lock.lock_queue);
+       idr_init(&master->magic_map);
+       master->dev = dev;
+
+       return master;
+}
+
+static int drm_set_master(struct drm_device *dev, struct drm_file *fpriv,
+                         bool new_master)
+{
+       int ret = 0;
+
+       dev->master = drm_master_get(fpriv->master);
+       if (dev->driver->master_set) {
+               ret = dev->driver->master_set(dev, fpriv, new_master);
+               if (unlikely(ret != 0)) {
+                       drm_master_put(&dev->master);
+               }
+       }
+
+       return ret;
+}
+
+static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
+{
+       struct drm_master *old_master;
+       int ret;
+
+#if 0
+       lockdep_assert_held_once(&dev->master_mutex);
+#endif
+
+       old_master = fpriv->master;
+       fpriv->master = drm_master_create(dev);
+       if (!fpriv->master) {
+               fpriv->master = old_master;
+               return -ENOMEM;
+       }
+
+       if (dev->driver->master_create) {
+               ret = dev->driver->master_create(dev, fpriv->master);
+               if (ret)
+                       goto out_err;
+       }
+       fpriv->is_master = 1;
+       fpriv->authenticated = 1;
+
+       ret = drm_set_master(dev, fpriv, true);
+       if (ret)
+               goto out_err;
+
+       if (old_master)
+               drm_master_put(&old_master);
+
+       return 0;
+
+out_err:
+       /* drop references and restore old master on failure */
+       drm_master_put(&fpriv->master);
+       fpriv->master = old_master;
+
+       return ret;
+}
+
+int drm_setmaster_ioctl(struct drm_device *dev, void *data,
+                       struct drm_file *file_priv)
+{
+       int ret = 0;
+
+       mutex_lock(&dev->master_mutex);
+       if (drm_is_current_master(file_priv))
+               goto out_unlock;
+
+       if (dev->master) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       if (!file_priv->master) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       if (!file_priv->is_master) {
+               ret = drm_new_set_master(dev, file_priv);
+               goto out_unlock;
+       }
+
+       ret = drm_set_master(dev, file_priv, false);
+out_unlock:
+       mutex_unlock(&dev->master_mutex);
+       return ret;
+}
+
+static void drm_drop_master(struct drm_device *dev,
+                           struct drm_file *fpriv)
+{
+       if (dev->driver->master_drop)
+               dev->driver->master_drop(dev, fpriv);
+       drm_master_put(&dev->master);
+}
+
+int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
+                        struct drm_file *file_priv)
+{
+       int ret = -EINVAL;
+
+       mutex_lock(&dev->master_mutex);
+       if (!drm_is_current_master(file_priv))
+               goto out_unlock;
+
+       if (!dev->master)
+               goto out_unlock;
+
+       ret = 0;
+       drm_drop_master(dev, file_priv);
+out_unlock:
+       mutex_unlock(&dev->master_mutex);
+       return ret;
+}
+
+int drm_master_open(struct drm_file *file_priv)
+{
+       struct drm_device *dev = file_priv->minor->dev;
+       int ret = 0;
+
+       /* if there is no current master make this fd it, but do not create
+        * any master object for render clients */
+       mutex_lock(&dev->master_mutex);
+       if (!dev->master)
+               ret = drm_new_set_master(dev, file_priv);
+       else
+               file_priv->master = drm_master_get(dev->master);
+       mutex_unlock(&dev->master_mutex);
+
+       return ret;
+}
+
+void drm_master_release(struct drm_file *file_priv)
+{
+       struct drm_device *dev = file_priv->minor->dev;
+       struct drm_master *master = file_priv->master;
+
+       mutex_lock(&dev->master_mutex);
+       if (file_priv->magic)
+               idr_remove(&file_priv->master->magic_map, file_priv->magic);
+
+       if (!drm_is_current_master(file_priv))
+               goto out;
+
+       if (drm_core_check_feature(dev, DRIVER_LEGACY)) {
+               /*
+                * Since the master is disappearing, so is the
+                * possibility to lock.
+                */
+               mutex_lock(&dev->struct_mutex);
+               if (master->lock.hw_lock) {
+                       if (dev->sigdata.lock == master->lock.hw_lock)
+                               dev->sigdata.lock = NULL;
+                       master->lock.hw_lock = NULL;
+                       master->lock.file_priv = NULL;
+                       wake_up_interruptible_all(&master->lock.lock_queue);
+               }
+               mutex_unlock(&dev->struct_mutex);
+       }
+
+       if (dev->master == file_priv->master)
+               drm_drop_master(dev, file_priv);
+out:
+       /* drop the master reference held by the file priv */
+       if (file_priv->master)
+               drm_master_put(&file_priv->master);
+       mutex_unlock(&dev->master_mutex);
+}
+
+/**
+ * drm_is_current_master - checks whether @priv is the current master
+ * @fpriv: DRM file private
+ *
+ * Checks whether @fpriv is current master on its device. This decides whether a
+ * client is allowed to run DRM_MASTER IOCTLs.
+ *
+ * Most of the modern IOCTL which require DRM_MASTER are for kernel modesetting
+ * - the current master is assumed to own the non-shareable display hardware.
+ */
+bool drm_is_current_master(struct drm_file *fpriv)
+{
+       return fpriv->is_master;
+}
+EXPORT_SYMBOL(drm_is_current_master);
+
+/**
+ * drm_master_get - reference a master pointer
+ * @master: struct &drm_master
+ *
+ * Increments the reference count of @master and returns a pointer to @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_device *dev = master->dev;
+
+       if (dev->driver->master_destroy)
+               dev->driver->master_destroy(dev, master);
+
+       drm_legacy_master_rmmaps(dev, master);
+
+       idr_destroy(&master->magic_map);
+       kfree(master->unique);
+       kfree(master);
+}
+
+/**
+ * drm_master_put - unreference and clear a master pointer
+ * @master: pointer to a pointer of struct &drm_master
+ *
+ * This decrements the &drm_master behind @master and sets it to NULL.
+ */
+void drm_master_put(struct drm_master **master)
+{
+       kref_put(&(*master)->refcount, drm_master_destroy);
+       *master = NULL;
+}
+EXPORT_SYMBOL(drm_master_put);
diff --git a/sys/dev/drm/drm_blend.c b/sys/dev/drm/drm_blend.c
new file mode 100644 (file)
index 0000000..85172a9
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2016 Samsung Electronics Co.Ltd
+ * Authors:
+ *     Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * DRM core plane blending related functions
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_blend.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+
+#include "drm_crtc_internal.h"
+
+/**
+ * DOC: overview
+ *
+ * The basic plane composition model supported by standard plane properties only
+ * has a source rectangle (in logical pixels within the &drm_framebuffer), with
+ * sub-pixel accuracy, which is scaled up to a pixel-aligned destination
+ * rectangle in the visible area of a &drm_crtc. The visible area of a CRTC is
+ * defined by the horizontal and vertical visible pixels (stored in @hdisplay
+ * and @vdisplay) of the requested mode (stored in @mode in the
+ * &drm_crtc_state). These two rectangles are both stored in the
+ * &drm_plane_state.
+ *
+ * For the atomic ioctl the following standard (atomic) properties on the plane object
+ * encode the basic plane composition model:
+ *
+ * SRC_X:
+ *     X coordinate offset for the source rectangle within the
+ *     &drm_framebuffer, in 16.16 fixed point. Must be positive.
+ * SRC_Y:
+ *     Y coordinate offset for the source rectangle within the
+ *     &drm_framebuffer, in 16.16 fixed point. Must be positive.
+ * SRC_W:
+ *     Width for the source rectangle within the &drm_framebuffer, in 16.16
+ *     fixed point. SRC_X plus SRC_W must be within the width of the source
+ *     framebuffer. Must be positive.
+ * SRC_H:
+ *     Height for the source rectangle within the &drm_framebuffer, in 16.16
+ *     fixed point. SRC_Y plus SRC_H must be within the height of the source
+ *     framebuffer. Must be positive.
+ * CRTC_X:
+ *     X coordinate offset for the destination rectangle. Can be negative.
+ * CRTC_Y:
+ *     Y coordinate offset for the destination rectangle. Can be negative.
+ * CRTC_W:
+ *     Width for the destination rectangle. CRTC_X plus CRTC_W can extend past
+ *     the currently visible horizontal area of the &drm_crtc.
+ * CRTC_H:
+ *     Height for the destination rectangle. CRTC_Y plus CRTC_H can extend past
+ *     the currently visible vertical area of the &drm_crtc.
+ * FB_ID:
+ *     Mode object ID of the &drm_framebuffer this plane should scan out.
+ * CRTC_ID:
+ *     Mode object ID of the &drm_crtc this plane should be connected to.
+ *
+ * Note that the source rectangle must fully lie within the bounds of the
+ * &drm_framebuffer. The destination rectangle can lie outside of the visible
+ * area of the current mode of the CRTC. It must be apprpriately clipped by the
+ * driver, which can be done by calling drm_plane_helper_check_update(). Drivers
+ * are also allowed to round the subpixel sampling positions appropriately, but
+ * only to the next full pixel. No pixel outside of the source rectangle may
+ * ever be sampled, which is important when applying more sophisticated
+ * filtering than just a bilinear one when scaling. The filtering mode when
+ * scaling is unspecified.
+ *
+ * On top of this basic transformation additional properties can be exposed by
+ * the driver:
+ *
+ * - Rotation is set up with drm_mode_create_rotation_property(). It adds a
+ *   rotation and reflection step between the source and destination rectangles.
+ *   Without this property the rectangle is only scaled, but not rotated or
+ *   reflected.
+ *
+ * - Z position is set up with drm_plane_create_zpos_immutable_property() and
+ *   drm_plane_create_zpos_property(). It controls the visibility of overlapping
+ *   planes. Without this property the primary plane is always below the cursor
+ *   plane, and ordering between all other planes is undefined.
+ *
+ * Note that all the property extensions described here apply either to the
+ * plane or the CRTC (e.g. for the background color, which currently is not
+ * exposed and assumed to be black).
+ */
+
+/**
+ * drm_mode_create_rotation_property - create a new rotation property
+ * @dev: DRM device
+ * @supported_rotations: bitmask of supported rotations and reflections
+ *
+ * This creates a new property with the selected support for transformations.
+ * The resulting property should be stored in @rotation_property in
+ * &drm_mode_config. It then must be attached to each plane which supports
+ * rotations using drm_object_attach_property().
+ *
+ * FIXME: Probably better if the rotation property is created on each plane,
+ * like the zpos property. Otherwise it's not possible to allow different
+ * rotation modes on different planes.
+ *
+ * Since a rotation by 180° degress is the same as reflecting both along the x
+ * and the y axis the rotation property is somewhat redundant. Drivers can use
+ * drm_rotation_simplify() to normalize values of this property.
+ *
+ * The property exposed to userspace is a bitmask property (see
+ * drm_property_create_bitmask()) called "rotation" and has the following
+ * bitmask enumaration values:
+ *
+ * DRM_ROTATE_0:
+ *     "rotate-0"
+ * DRM_ROTATE_90:
+ *     "rotate-90"
+ * DRM_ROTATE_180:
+ *     "rotate-180"
+ * DRM_ROTATE_270:
+ *     "rotate-270"
+ * DRM_REFLECT_X:
+ *     "reflect-x"
+ * DRM_REFELCT_Y:
+ *     "reflect-y"
+ *
+ * Rotation is the specified amount in degrees in counter clockwise direction,
+ * the X and Y axis are within the source rectangle, i.e.  the X/Y axis before
+ * rotation. After reflection, the rotation is applied to the image sampled from
+ * the source rectangle, before scaling it to fit the destination rectangle.
+ */
+struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev,
+                                                      unsigned int supported_rotations)
+{
+       static const struct drm_prop_enum_list props[] = {
+               { __builtin_ffs(DRM_ROTATE_0) - 1,   "rotate-0" },
+               { __builtin_ffs(DRM_ROTATE_90) - 1,  "rotate-90" },
+               { __builtin_ffs(DRM_ROTATE_180) - 1, "rotate-180" },
+               { __builtin_ffs(DRM_ROTATE_270) - 1, "rotate-270" },
+               { __builtin_ffs(DRM_REFLECT_X) - 1,  "reflect-x" },
+               { __builtin_ffs(DRM_REFLECT_Y) - 1,  "reflect-y" },
+       };
+
+       return drm_property_create_bitmask(dev, 0, "rotation",
+                                          props, ARRAY_SIZE(props),
+                                          supported_rotations);
+}
+EXPORT_SYMBOL(drm_mode_create_rotation_property);
+
+/**
+ * drm_rotation_simplify() - Try to simplify the rotation
+ * @rotation: Rotation to be simplified
+ * @supported_rotations: Supported rotations
+ *
+ * Attempt to simplify the rotation to a form that is supported.
+ * Eg. if the hardware supports everything except DRM_REFLECT_X
+ * one could call this function like this:
+ *
+ * drm_rotation_simplify(rotation, DRM_ROTATE_0 |
+ *                       DRM_ROTATE_90 | DRM_ROTATE_180 |
+ *                       DRM_ROTATE_270 | DRM_REFLECT_Y);
+ *
+ * to eliminate the DRM_ROTATE_X flag. Depending on what kind of
+ * transforms the hardware supports, this function may not
+ * be able to produce a supported transform, so the caller should
+ * check the result afterwards.
+ */
+unsigned int drm_rotation_simplify(unsigned int rotation,
+                                  unsigned int supported_rotations)
+{
+       if (rotation & ~supported_rotations) {
+               rotation ^= DRM_REFLECT_X | DRM_REFLECT_Y;
+               rotation = (rotation & DRM_REFLECT_MASK) |
+                          BIT((ffs(rotation & DRM_ROTATE_MASK) + 1) % 4);
+       }
+
+       return rotation;
+}
+EXPORT_SYMBOL(drm_rotation_simplify);
+
+/**
+ * drm_plane_create_zpos_property - create mutable zpos property
+ * @plane: drm plane
+ * @zpos: initial value of zpos property
+ * @min: minimal possible value of zpos property
+ * @max: maximal possible value of zpos property
+ *
+ * This function initializes generic mutable zpos property and enables support
+ * for it in drm core. Drivers can then attach this property to planes to enable
+ * support for configurable planes arrangement during blending operation.
+ * Once mutable zpos property has been enabled, the DRM core will automatically
+ * calculate drm_plane_state->normalized_zpos values. Usually min should be set
+ * to 0 and max to maximal number of planes for given crtc - 1.
+ *
+ * If zpos of some planes cannot be changed (like fixed background or
+ * cursor/topmost planes), driver should adjust min/max values and assign those
+ * planes immutable zpos property with lower or higher values (for more
+ * information, see drm_plane_create_zpos_immutable_property() function). In such
+ * case driver should also assign proper initial zpos values for all planes in
+ * its plane_reset() callback, so the planes will be always sorted properly.
+ *
+ * See also drm_atomic_normalize_zpos().
+ *
+ * The property exposed to userspace is called "zpos".
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_plane_create_zpos_property(struct drm_plane *plane,
+                                  unsigned int zpos,
+                                  unsigned int min, unsigned int max)
+{
+       struct drm_property *prop;
+
+       prop = drm_property_create_range(plane->dev, 0, "zpos", min, max);
+       if (!prop)
+               return -ENOMEM;
+
+       drm_object_attach_property(&plane->base, prop, zpos);
+
+       plane->zpos_property = prop;
+
+       if (plane->state) {
+               plane->state->zpos = zpos;
+               plane->state->normalized_zpos = zpos;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_plane_create_zpos_property);
+
+/**
+ * drm_plane_create_zpos_immutable_property - create immuttable zpos property
+ * @plane: drm plane
+ * @zpos: value of zpos property
+ *
+ * This function initializes generic immutable zpos property and enables
+ * support for it in drm core. Using this property driver lets userspace
+ * to get the arrangement of the planes for blending operation and notifies
+ * it that the hardware (or driver) doesn't support changing of the planes'
+ * order. For mutable zpos see drm_plane_create_zpos_property().
+ *
+ * The property exposed to userspace is called "zpos".
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_plane_create_zpos_immutable_property(struct drm_plane *plane,
+                                            unsigned int zpos)
+{
+       struct drm_property *prop;
+
+       prop = drm_property_create_range(plane->dev, DRM_MODE_PROP_IMMUTABLE,
+                                        "zpos", zpos, zpos);
+       if (!prop)
+               return -ENOMEM;
+
+       drm_object_attach_property(&plane->base, prop, zpos);
+
+       plane->zpos_property = prop;
+
+       if (plane->state) {
+               plane->state->zpos = zpos;
+               plane->state->normalized_zpos = zpos;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_plane_create_zpos_immutable_property);
+
+static int drm_atomic_state_zpos_cmp(const void *a, const void *b)
+{
+       const struct drm_plane_state *sa = *(struct drm_plane_state **)a;
+       const struct drm_plane_state *sb = *(struct drm_plane_state **)b;
+
+       if (sa->zpos != sb->zpos)
+               return sa->zpos - sb->zpos;
+       else
+               return sa->plane->base.id - sb->plane->base.id;
+}
+
+static int drm_atomic_helper_crtc_normalize_zpos(struct drm_crtc *crtc,
+                                         struct drm_crtc_state *crtc_state)
+{
+       struct drm_atomic_state *state = crtc_state->state;
+       struct drm_device *dev = crtc->dev;
+       int total_planes = dev->mode_config.num_total_plane;
+       struct drm_plane_state **states;
+       struct drm_plane *plane;
+       int i, n = 0;
+       int ret = 0;
+
+       DRM_DEBUG_ATOMIC("[CRTC:%d:%s] calculating normalized zpos values\n",
+                        crtc->base.id, crtc->name);
+
+       states = kmalloc_array(total_planes, sizeof(*states), GFP_TEMPORARY);
+       if (!states)
+               return -ENOMEM;
+
+       /*
+        * Normalization process might create new states for planes which
+        * normalized_zpos has to be recalculated.
+        */
+       drm_for_each_plane_mask(plane, dev, crtc_state->plane_mask) {
+               struct drm_plane_state *plane_state =
+                       drm_atomic_get_plane_state(state, plane);
+               if (IS_ERR(plane_state)) {
+                       ret = PTR_ERR(plane_state);
+                       goto done;
+               }
+               states[n++] = plane_state;
+               DRM_DEBUG_ATOMIC("[PLANE:%d:%s] processing zpos value %d\n",
+                                plane->base.id, plane->name,
+                                plane_state->zpos);
+       }
+
+       sort(states, n, sizeof(*states), drm_atomic_state_zpos_cmp, NULL);
+
+       for (i = 0; i < n; i++) {
+               plane = states[i]->plane;
+
+               states[i]->normalized_zpos = i;
+               DRM_DEBUG_ATOMIC("[PLANE:%d:%s] normalized zpos value %d\n",
+                                plane->base.id, plane->name, i);
+       }
+       crtc_state->zpos_changed = true;
+
+done:
+       kfree(states);
+       return ret;
+}
+
+/**
+ * drm_atomic_normalize_zpos - calculate normalized zpos values for all crtcs
+ * @dev: DRM device
+ * @state: atomic state of DRM device
+ *
+ * This function calculates normalized zpos value for all modified planes in
+ * the provided atomic state of DRM device.
+ *
+ * For every CRTC this function checks new states of all planes assigned to
+ * it and calculates normalized zpos value for these planes. Planes are compared
+ * first by their zpos values, then by plane id (if zpos is equal). The plane
+ * with lowest zpos value is at the bottom. The plane_state->normalized_zpos is
+ * then filled with unique values from 0 to number of active planes in crtc
+ * minus one.
+ *
+ * RETURNS
+ * Zero for success or -errno
+ */
+int drm_atomic_normalize_zpos(struct drm_device *dev,
+                             struct drm_atomic_state *state)
+{
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *crtc_state;
+       struct drm_plane *plane;
+       struct drm_plane_state *plane_state;
+       int i, ret = 0;
+
+       for_each_plane_in_state(state, plane, plane_state, i) {
+               crtc = plane_state->crtc;
+               if (!crtc)
+                       continue;
+               if (plane->state->zpos != plane_state->zpos) {
+                       crtc_state =
+                               drm_atomic_get_existing_crtc_state(state, crtc);
+                       crtc_state->zpos_changed = true;
+               }
+       }
+
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
+               if (crtc_state->plane_mask != crtc->state->plane_mask ||
+                   crtc_state->zpos_changed) {
+                       ret = drm_atomic_helper_crtc_normalize_zpos(crtc,
+                                                                   crtc_state);
+                       if (ret)
+                               return ret;
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL(drm_atomic_normalize_zpos);
index f016194..448929e 100644 (file)
@@ -23,9 +23,9 @@
 
 #include <linux/err.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 
-
-#include "drm/drmP.h"
+#include <drm/drm_bridge.h>
 
 /**
  * DOC: overview
@@ -35,7 +35,7 @@
  * encoder chain.
  *
  * A bridge is always attached to a single &drm_encoder at a time, but can be
- * either connected to it directly, or through an intermediate bridge:
+ * either connected to it directly, or through an intermediate bridge::
  *
  *     encoder ---> bridge B ---> bridge A
  *
@@ -97,11 +97,11 @@ EXPORT_SYMBOL(drm_bridge_remove);
  * @dev: DRM device
  * @bridge: bridge control structure
  *
- * called by a kms driver to link one of our encoder/bridge to the given
+ * Called by a kms driver to link one of our encoder/bridge to the given
  * bridge.
  *
  * Note that setting up links between the bridge and our encoder/bridge
- * objects needs to be handled by the kms driver itself
+ * objects needs to be handled by the kms driver itself.
  *
  * RETURNS:
  * Zero on success, error code on failure
@@ -123,6 +123,31 @@ int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge)
 }
 EXPORT_SYMBOL(drm_bridge_attach);
 
+/**
+ * drm_bridge_detach - deassociate given bridge from its DRM device
+ *
+ * @bridge: bridge control structure
+ *
+ * Called by a kms driver to unlink the given bridge from its DRM device.
+ *
+ * Note that tearing down links between the bridge and our encoder/bridge
+ * objects needs to be handled by the kms driver itself.
+ */
+void drm_bridge_detach(struct drm_bridge *bridge)
+{
+       if (WARN_ON(!bridge))
+               return;
+
+       if (WARN_ON(!bridge->dev))
+               return;
+
+       if (bridge->funcs->detach)
+               bridge->funcs->detach(bridge);
+
+       bridge->dev = NULL;
+}
+EXPORT_SYMBOL(drm_bridge_detach);
+
 /**
  * DOC: bridge callbacks
  *
index 15f7da9..ae61f6b 100644 (file)
@@ -54,7 +54,7 @@ static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
                 */
                if (!entry->map ||
                    map->type != entry->map->type ||
-                   entry->master != dev->primary->master)
+                   entry->master != dev->master)
                        continue;
                switch (map->type) {
                case _DRM_SHM:
@@ -248,12 +248,12 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
                map->offset = (unsigned long)map->handle;
                if (map->flags & _DRM_CONTAINS_LOCK) {
                        /* Prevent a 2nd X Server from creating a 2nd lock */
-                       if (dev->primary->master->lock.hw_lock != NULL) {
+                       if (dev->master->lock.hw_lock != NULL) {
                                vfree(map->handle);
                                kfree(map);
                                return -EBUSY;
                        }
-                       dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle;   /* Pointer to lock */
+                       dev->sigdata.lock = dev->master->lock.hw_lock = map->handle;    /* Pointer to lock */
                }
                break;
        case _DRM_AGP: {
@@ -364,7 +364,7 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
        mutex_unlock(&dev->struct_mutex);
 
        if (!(map->flags & _DRM_DRIVER))
-               list->master = dev->primary->master;
+               list->master = dev->master;
        *maplist = list;
        return 0;
 }
@@ -405,7 +405,7 @@ int drm_legacy_addmap_ioctl(struct drm_device *dev, void *data,
                return -EPERM;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        err = drm_addmap_core(dev, map->offset, map->size, map->type,
@@ -451,7 +451,7 @@ int drm_legacy_getmap_ioctl(struct drm_device *dev, void *data,
        int i;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        idx = map->offset;
@@ -553,7 +553,7 @@ EXPORT_SYMBOL(drm_legacy_rmmap_locked);
 void drm_legacy_rmmap(struct drm_device *dev, struct drm_local_map *map)
 {
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        mutex_lock(&dev->struct_mutex);
@@ -562,12 +562,11 @@ void drm_legacy_rmmap(struct drm_device *dev, struct drm_local_map *map)
 }
 EXPORT_SYMBOL(drm_legacy_rmmap);
 
-#if 0
 void drm_legacy_master_rmmaps(struct drm_device *dev, struct drm_master *master)
 {
        struct drm_map_list *r_list, *list_temp;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        mutex_lock(&dev->struct_mutex);
@@ -579,7 +578,6 @@ void drm_legacy_master_rmmaps(struct drm_device *dev, struct drm_master *master)
        }
        mutex_unlock(&dev->struct_mutex);
 }
-#endif
 
 /* The rmmap ioctl appears to be unnecessary.  All mappings are torn down on
  * the last close of the device, and this is necessary for cleanup when things
@@ -605,7 +603,7 @@ int drm_legacy_rmmap_ioctl(struct drm_device *dev, void *data,
        int ret;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        mutex_lock(&dev->struct_mutex);
@@ -772,7 +770,7 @@ int drm_legacy_addbufs_agp(struct drm_device *dev,
                return -EINVAL;
        }
 
-       entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL);
+       entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL);
        if (!entry->buflist) {
                mutex_unlock(&dev->struct_mutex);
                atomic_dec(&dev->buf_alloc);
@@ -922,14 +920,14 @@ int drm_legacy_addbufs_pci(struct drm_device *dev,
                return -EINVAL;
        }
 
-       entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL);
+       entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL);
        if (!entry->buflist) {
                mutex_unlock(&dev->struct_mutex);
                atomic_dec(&dev->buf_alloc);
                return -ENOMEM;
        }
 
-       entry->seglist = kzalloc(count * sizeof(*entry->seglist), GFP_KERNEL);
+       entry->seglist = kcalloc(count, sizeof(*entry->seglist), GFP_KERNEL);
        if (!entry->seglist) {
                kfree(entry->buflist);
                mutex_unlock(&dev->struct_mutex);
@@ -940,8 +938,9 @@ int drm_legacy_addbufs_pci(struct drm_device *dev,
        /* Keep the original pagelist until we know all the allocations
         * have succeeded
         */
-       temp_pagelist = kmalloc((dma->page_count + (count << page_order)) *
-                              sizeof(*dma->pagelist), M_DRM, M_WAITOK );
+       temp_pagelist = kmalloc_array(dma->page_count + (count << page_order),
+                                     sizeof(*dma->pagelist),
+                                     GFP_KERNEL);
        if (!temp_pagelist) {
                kfree(entry->buflist);
                kfree(entry->seglist);
@@ -1133,8 +1132,7 @@ static int drm_legacy_addbufs_sg(struct drm_device *dev,
                return -EINVAL;
        }
 
-       entry->buflist = kzalloc(count * sizeof(*entry->buflist),
-                               GFP_KERNEL);
+       entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL);
        if (!entry->buflist) {
                mutex_unlock(&dev->struct_mutex);
                atomic_dec(&dev->buf_alloc);
@@ -1236,7 +1234,7 @@ int drm_legacy_addbufs(struct drm_device *dev, void *data,
        struct drm_buf_desc *request = data;
        int ret;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
@@ -1282,7 +1280,7 @@ int drm_legacy_infobufs(struct drm_device *dev, void *data,
        int i;
        int count;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
@@ -1363,7 +1361,7 @@ int drm_legacy_markbufs(struct drm_device *dev, void *data,
        int order;
        struct drm_buf_entry *entry;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
@@ -1411,7 +1409,7 @@ int drm_legacy_freebufs(struct drm_device *dev, void *data,
        int idx;
        struct drm_buf *buf;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
@@ -1467,7 +1465,7 @@ int drm_legacy_mapbufs(struct drm_device *dev, void *data,
        struct drm_buf_map *request = data;
        int i;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
@@ -1552,7 +1550,7 @@ int drm_legacy_mapbufs(struct drm_device *dev, void *data,
 int drm_legacy_dma_ioctl(struct drm_device *dev, void *data,
                  struct drm_file *file_priv)
 {
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (dev->driver->dma_ioctl)
diff --git a/sys/dev/drm/drm_color_mgmt.c b/sys/dev/drm/drm_color_mgmt.c
new file mode 100644 (file)
index 0000000..d28ffdd
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_color_mgmt.h>
+
+#include "drm_crtc_internal.h"
+
+/**
+ * DOC: overview
+ *
+ * Color management or color space adjustments is supported through a set of 5
+ * properties on the &drm_crtc object. They are set up by calling
+ * drm_crtc_enable_color_mgmt().
+ *
+ * "DEGAMMA_LUT”:
+ *     Blob property to set the degamma lookup table (LUT) mapping pixel data
+ *     from the framebuffer before it is given to the transformation matrix.
+ *     The data is interpreted as an array of struct &drm_color_lut elements.
+ *     Hardware might choose not to use the full precision of the LUT elements
+ *     nor use all the elements of the LUT (for example the hardware might
+ *     choose to interpolate between LUT[0] and LUT[4]).
+ *
+ * “DEGAMMA_LUT_SIZE”:
+ *     Unsinged range property to give the size of the lookup table to be set
+ *     on the DEGAMMA_LUT property (the size depends on the underlying
+ *     hardware). If drivers support multiple LUT sizes then they should
+ *     publish the largest size, and sub-sample smaller sized LUTs (e.g. for
+ *     split-gamma modes) appropriately.
+ *
+ * “CTM”:
+ *     Blob property to set the current transformation matrix (CTM) apply to
+ *     pixel data after the lookup through the degamma LUT and before the
+ *     lookup through the gamma LUT. The data is interpreted as a struct
+ *     &drm_color_ctm.
+ *
+ * “GAMMA_LUT”:
+ *     Blob property to set the gamma lookup table (LUT) mapping pixel data
+ *     after the transformation matrix to data sent to the connector. The
+ *     data is interpreted as an array of struct &drm_color_lut elements.
+ *     Hardware might choose not to use the full precision of the LUT elements
+ *     nor use all the elements of the LUT (for example the hardware might
+ *     choose to interpolate between LUT[0] and LUT[4]).
+ *
+ * “GAMMA_LUT_SIZE”:
+ *     Unsigned range property to give the size of the lookup table to be set
+ *     on the GAMMA_LUT property (the size depends on the underlying hardware).
+ *     If drivers support multiple LUT sizes then they should publish the
+ *     largest size, and sub-sample smaller sized LUTs (e.g. for split-gamma
+ *     modes) appropriately.
+ *
+ * There is also support for a legacy gamma table, which is set up by calling
+ * drm_mode_crtc_set_gamma_size(). Drivers which support both should use
+ * drm_atomic_helper_legacy_gamma_set() to alias the legacy gamma ramp with the
+ * "GAMMA_LUT" property above.
+ */
+
+/**
+ * drm_crtc_enable_color_mgmt - enable color management properties
+ * @crtc: DRM CRTC
+ * @degamma_lut_size: the size of the degamma lut (before CSC)
+ * @has_ctm: whether to attach ctm_property for CSC matrix
+ * @gamma_lut_size: the size of the gamma lut (after CSC)
+ *
+ * This function lets the driver enable the color correction
+ * properties on a CRTC. This includes 3 degamma, csc and gamma
+ * properties that userspace can set and 2 size properties to inform
+ * the userspace of the lut sizes. Each of the properties are
+ * optional. The gamma and degamma properties are only attached if
+ * their size is not 0 and ctm_property is only attached if has_ctm is
+ * true.
+ */
+void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,
+                               uint degamma_lut_size,
+                               bool has_ctm,
+                               uint gamma_lut_size)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_mode_config *config = &dev->mode_config;
+
+       if (degamma_lut_size) {
+               drm_object_attach_property(&crtc->base,
+                                          config->degamma_lut_property, 0);
+               drm_object_attach_property(&crtc->base,
+                                          config->degamma_lut_size_property,
+                                          degamma_lut_size);
+       }
+
+       if (has_ctm)
+               drm_object_attach_property(&crtc->base,
+                                          config->ctm_property, 0);
+
+       if (gamma_lut_size) {
+               drm_object_attach_property(&crtc->base,
+                                          config->gamma_lut_property, 0);
+               drm_object_attach_property(&crtc->base,
+                                          config->gamma_lut_size_property,
+                                          gamma_lut_size);
+       }
+}
+EXPORT_SYMBOL(drm_crtc_enable_color_mgmt);
+
+/**
+ * drm_mode_crtc_set_gamma_size - set the gamma table size
+ * @crtc: CRTC to set the gamma table size for
+ * @gamma_size: size of the gamma table
+ *
+ * Drivers which support gamma tables should set this to the supported gamma
+ * table size when initializing the CRTC. Currently the drm core only supports a
+ * fixed gamma table size.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
+                                int gamma_size)
+{
+       uint16_t *r_base, *g_base, *b_base;
+       int i;
+
+       crtc->gamma_size = gamma_size;
+
+       crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3,
+                                   GFP_KERNEL);
+       if (!crtc->gamma_store) {
+               crtc->gamma_size = 0;
+               return -ENOMEM;
+       }
+
+       r_base = crtc->gamma_store;
+       g_base = r_base + gamma_size;
+       b_base = g_base + gamma_size;
+       for (i = 0; i < gamma_size; i++) {
+               r_base[i] = i << 8;
+               g_base[i] = i << 8;
+               b_base[i] = i << 8;
+       }
+
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
+
+/**
+ * drm_mode_gamma_set_ioctl - set the gamma table
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Set the gamma table of a CRTC to the one passed in by the user. Userspace can
+ * inquire the required gamma table size through drm_mode_gamma_get_ioctl.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_gamma_set_ioctl(struct drm_device *dev,
+                            void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_crtc_lut *crtc_lut = data;
+       struct drm_crtc *crtc;
+       void *r_base, *g_base, *b_base;
+       int size;
+       int ret = 0;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       drm_modeset_lock_all(dev);
+       crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+       if (!crtc) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       if (crtc->funcs->gamma_set == NULL) {
+               ret = -ENOSYS;
+               goto out;
+       }
+
+       /* memcpy into gamma store */
+       if (crtc_lut->gamma_size != crtc->gamma_size) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       size = crtc_lut->gamma_size * (sizeof(uint16_t));
+       r_base = crtc->gamma_store;
+       if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       g_base = r_base + size;
+       if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       b_base = g_base + size;
+       if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size);
+
+out:
+       drm_modeset_unlock_all(dev);
+       return ret;
+
+}
+
+/**
+ * drm_mode_gamma_get_ioctl - get the gamma table
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Copy the current gamma table into the storage provided. This also provides
+ * the gamma table size the driver expects, which can be used to size the
+ * allocated storage.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_gamma_get_ioctl(struct drm_device *dev,
+                            void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_crtc_lut *crtc_lut = data;
+       struct drm_crtc *crtc;
+       void *r_base, *g_base, *b_base;
+       int size;
+       int ret = 0;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       drm_modeset_lock_all(dev);
+       crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+       if (!crtc) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       /* memcpy into gamma store */
+       if (crtc_lut->gamma_size != crtc->gamma_size) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       size = crtc_lut->gamma_size * (sizeof(uint16_t));
+       r_base = crtc->gamma_store;
+       if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       g_base = r_base + size;
+       if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       b_base = g_base + size;
+       if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+out:
+       drm_modeset_unlock_all(dev);
+       return ret;
+}
diff --git a/sys/dev/drm/drm_connector.c b/sys/dev/drm/drm_connector.c
new file mode 100644 (file)
index 0000000..2db7fb5
--- /dev/null
@@ -0,0 +1,1123 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_edid.h>
+
+#include "drm_crtc_internal.h"
+#include "drm_internal.h"
+
+/**
+ * DOC: overview
+ *
+ * In DRM connectors are the general abstraction for display sinks, and include
+ * als fixed panels or anything else that can display pixels in some form. As
+ * opposed to all other KMS objects representing hardware (like CRTC, encoder or
+ * plane abstractions) connectors can be hotplugged and unplugged at runtime.
+ * Hence they are reference-counted using drm_connector_reference() and
+ * drm_connector_unreference().
+ *
+ * KMS driver must create, initialize, register and attach at a struct
+ * &drm_connector for each such sink. The instance is created as other KMS
+ * objects and initialized by setting the following fields.
+ *
+ * The connector is then registered with a call to drm_connector_init() with a
+ * pointer to the connector functions and a connector type, and exposed through
+ * sysfs with a call to drm_connector_register().
+ *
+ * Connectors must be attached to an encoder to be used. For devices that map
+ * connectors to encoders 1:1, the connector should be attached at
+ * initialization time with a call to drm_mode_connector_attach_encoder(). The
+ * driver must also set the struct &drm_connector encoder field to point to the
+ * attached encoder.
+ *
+ * For connectors which are not fixed (like built-in panels) the driver needs to
+ * support hotplug notifications. The simplest way to do that is by using the
+ * probe helpers, see drm_kms_helper_poll_init() for connectors which don't have
+ * hardware support for hotplug interrupts. Connectors with hardware hotplug
+ * support can instead use e.g. drm_helper_hpd_irq_event().
+ */
+
+struct drm_conn_prop_enum_list {
+       int type;
+       const char *name;
+       struct ida ida;
+};
+
+/*
+ * Connector and encoder types.
+ */
+static struct drm_conn_prop_enum_list drm_connector_enum_list[] = {
+       { DRM_MODE_CONNECTOR_Unknown, "Unknown" },
+       { DRM_MODE_CONNECTOR_VGA, "VGA" },
+       { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
+       { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
+       { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
+       { DRM_MODE_CONNECTOR_Composite, "Composite" },
+       { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" },
+       { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
+       { DRM_MODE_CONNECTOR_Component, "Component" },
+       { DRM_MODE_CONNECTOR_9PinDIN, "DIN" },
+       { DRM_MODE_CONNECTOR_DisplayPort, "DP" },
+       { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
+       { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
+       { DRM_MODE_CONNECTOR_TV, "TV" },
+       { DRM_MODE_CONNECTOR_eDP, "eDP" },
+       { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
+       { DRM_MODE_CONNECTOR_DSI, "DSI" },
+       { DRM_MODE_CONNECTOR_DPI, "DPI" },
+};
+
+void drm_connector_ida_init(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
+               ida_init(&drm_connector_enum_list[i].ida);
+}
+
+void drm_connector_ida_destroy(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
+               ida_destroy(&drm_connector_enum_list[i].ida);
+}
+
+/**
+ * drm_connector_get_cmdline_mode - reads the user's cmdline mode
+ * @connector: connector to quwery
+ *
+ * The kernel supports per-connector configuration of its consoles through
+ * use of the video= parameter. This function parses that option and
+ * extracts the user's specified mode (or enable/disable status) for a
+ * particular connector. This is typically only used during the early fbdev
+ * setup.
+ */
+static void drm_connector_get_cmdline_mode(struct drm_connector *connector)
+{
+       struct drm_cmdline_mode *mode = &connector->cmdline_mode;
+       char *option = NULL;
+
+       if (fb_get_options(connector->name, &option))
+               return;
+
+       if (!drm_mode_parse_command_line_for_connector(option,
+                                                      connector,
+                                                      mode))
+               return;
+
+       if (mode->force) {
+               const char *s;
+
+               switch (mode->force) {
+               case DRM_FORCE_OFF:
+                       s = "OFF";
+                       break;
+               case DRM_FORCE_ON_DIGITAL:
+                       s = "ON - dig";
+                       break;
+               default:
+               case DRM_FORCE_ON:
+                       s = "ON";
+                       break;
+               }
+
+               DRM_INFO("forcing %s connector %s\n", connector->name, s);
+               connector->force = mode->force;
+       }
+
+       DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
+                     connector->name,
+                     mode->xres, mode->yres,
+                     mode->refresh_specified ? mode->refresh : 60,
+                     mode->rb ? " reduced blanking" : "",
+                     mode->margins ? " with margins" : "",
+                     mode->interlace ?  " interlaced" : "");
+}
+
+static void drm_connector_free(struct kref *kref)
+{
+       struct drm_connector *connector =
+               container_of(kref, struct drm_connector, base.refcount);
+       struct drm_device *dev = connector->dev;
+
+       drm_mode_object_unregister(dev, &connector->base);
+       connector->funcs->destroy(connector);
+}
+
+/**
+ * drm_connector_init - Init a preallocated connector
+ * @dev: DRM device
+ * @connector: the connector to init
+ * @funcs: callbacks for this connector
+ * @connector_type: user visible type of the connector
+ *
+ * Initialises a preallocated connector. Connectors should be
+ * subclassed as part of driver connector objects.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_connector_init(struct drm_device *dev,
+                      struct drm_connector *connector,
+                      const struct drm_connector_funcs *funcs,
+                      int connector_type)
+{
+       struct drm_mode_config *config = &dev->mode_config;
+       int ret;
+       struct ida *connector_ida =
+               &drm_connector_enum_list[connector_type].ida;
+
+       drm_modeset_lock_all(dev);
+
+       ret = drm_mode_object_get_reg(dev, &connector->base,
+                                     DRM_MODE_OBJECT_CONNECTOR,
+                                     false, drm_connector_free);
+       if (ret)
+               goto out_unlock;
+
+       connector->base.properties = &connector->properties;
+       connector->dev = dev;
+       connector->funcs = funcs;
+
+       ret = ida_simple_get(&config->connector_ida, 0, 0, GFP_KERNEL);
+       if (ret < 0)
+               goto out_put;
+       connector->index = ret;
+       ret = 0;
+
+       connector->connector_type = connector_type;
+       connector->connector_type_id =
+               ida_simple_get(connector_ida, 1, 0, GFP_KERNEL);
+       if (connector->connector_type_id < 0) {
+               ret = connector->connector_type_id;
+               goto out_put_id;
+       }
+       connector->name =
+               kasprintf(GFP_KERNEL, "%s-%d",
+                         drm_connector_enum_list[connector_type].name,
+                         connector->connector_type_id);
+       if (!connector->name) {
+               ret = -ENOMEM;
+               goto out_put_type_id;
+       }
+
+       INIT_LIST_HEAD(&connector->probed_modes);
+       INIT_LIST_HEAD(&connector->modes);
+       connector->edid_blob_ptr = NULL;
+       connector->status = connector_status_unknown;
+
+       drm_connector_get_cmdline_mode(connector);
+
+       /* We should add connectors at the end to avoid upsetting the connector
+        * index too much. */
+       list_add_tail(&connector->head, &config->connector_list);
+       config->num_connector++;
+
+       if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL)
+               drm_object_attach_property(&connector->base,
+                                             config->edid_property,
+                                             0);
+
+       drm_object_attach_property(&connector->base,
+                                     config->dpms_property, 0);
+
+       if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
+               drm_object_attach_property(&connector->base, config->prop_crtc_id, 0);
+       }
+
+       connector->debugfs_entry = NULL;
+out_put_type_id:
+       if (ret)
+               ida_simple_remove(connector_ida, connector->connector_type_id);
+out_put_id:
+       if (ret)
+               ida_simple_remove(&config->connector_ida, connector->index);
+out_put:
+       if (ret)
+               drm_mode_object_unregister(dev, &connector->base);
+
+out_unlock:
+       drm_modeset_unlock_all(dev);
+
+       return ret;
+}
+EXPORT_SYMBOL(drm_connector_init);
+
+/**
+ * drm_mode_connector_attach_encoder - attach a connector to an encoder
+ * @connector: connector to attach
+ * @encoder: encoder to attach @connector to
+ *
+ * This function links up a connector to an encoder. Note that the routing
+ * restrictions between encoders and crtcs are exposed to userspace through the
+ * possible_clones and possible_crtcs bitmasks.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_connector_attach_encoder(struct drm_connector *connector,
+                                     struct drm_encoder *encoder)
+{
+       int i;
+
+       /*
+        * In the past, drivers have attempted to model the static association
+        * of connector to encoder in simple connector/encoder devices using a
+        * direct assignment of connector->encoder = encoder. This connection
+        * is a logical one and the responsibility of the core, so drivers are
+        * expected not to mess with this.
+        *
+        * Note that the error return should've been enough here, but a large
+        * majority of drivers ignores the return value, so add in a big WARN
+        * to get people's attention.
+        */
+       if (WARN_ON(connector->encoder))
+               return -EINVAL;
+
+       for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+               if (connector->encoder_ids[i] == 0) {
+                       connector->encoder_ids[i] = encoder->base.id;
+                       return 0;
+               }
+       }
+       return -ENOMEM;
+}
+EXPORT_SYMBOL(drm_mode_connector_attach_encoder);
+
+static void drm_mode_remove(struct drm_connector *connector,
+                           struct drm_display_mode *mode)
+{
+       list_del(&mode->head);
+       drm_mode_destroy(connector->dev, mode);
+}
+
+/**
+ * drm_connector_cleanup - cleans up an initialised connector
+ * @connector: connector to cleanup
+ *
+ * Cleans up the connector but doesn't free the object.
+ */
+void drm_connector_cleanup(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_display_mode *mode, *t;
+
+       /* The connector should have been removed from userspace long before
+        * it is finally destroyed.
+        */
+       if (WARN_ON(connector->registered))
+               drm_connector_unregister(connector);
+
+       if (connector->tile_group) {
+               drm_mode_put_tile_group(dev, connector->tile_group);
+               connector->tile_group = NULL;
+       }
+
+       list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
+               drm_mode_remove(connector, mode);
+
+       list_for_each_entry_safe(mode, t, &connector->modes, head)
+               drm_mode_remove(connector, mode);
+
+       ida_simple_remove(&drm_connector_enum_list[connector->connector_type].ida,
+                         connector->connector_type_id);
+
+       ida_simple_remove(&dev->mode_config.connector_ida,
+                         connector->index);
+
+       kfree(connector->display_info.bus_formats);
+       drm_mode_object_unregister(dev, &connector->base);
+       kfree(connector->name);
+       connector->name = NULL;
+       list_del(&connector->head);
+       dev->mode_config.num_connector--;
+
+       WARN_ON(connector->state && !connector->funcs->atomic_destroy_state);
+       if (connector->state && connector->funcs->atomic_destroy_state)
+               connector->funcs->atomic_destroy_state(connector,
+                                                      connector->state);
+
+       memset(connector, 0, sizeof(*connector));
+}
+EXPORT_SYMBOL(drm_connector_cleanup);
+
+/**
+ * drm_connector_register - register a connector
+ * @connector: the connector to register
+ *
+ * Register userspace interfaces for a connector
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_connector_register(struct drm_connector *connector)
+{
+       int ret;
+
+       if (connector->registered)
+               return 0;
+
+       ret = drm_sysfs_connector_add(connector);
+       if (ret)
+               return ret;
+
+       ret = drm_debugfs_connector_add(connector);
+       if (ret) {
+               goto err_sysfs;
+       }
+
+       if (connector->funcs->late_register) {
+               ret = connector->funcs->late_register(connector);
+               if (ret)
+                       goto err_debugfs;
+       }
+
+       drm_mode_object_register(connector->dev, &connector->base);
+
+       connector->registered = true;
+       return 0;
+
+err_debugfs:
+       drm_debugfs_connector_remove(connector);
+err_sysfs:
+       drm_sysfs_connector_remove(connector);
+       return ret;
+}
+EXPORT_SYMBOL(drm_connector_register);
+
+/**
+ * drm_connector_unregister - unregister a connector
+ * @connector: the connector to unregister
+ *
+ * Unregister userspace interfaces for a connector
+ */
+void drm_connector_unregister(struct drm_connector *connector)
+{
+       if (!connector->registered)
+               return;
+
+       if (connector->funcs->early_unregister)
+               connector->funcs->early_unregister(connector);
+
+       drm_sysfs_connector_remove(connector);
+       drm_debugfs_connector_remove(connector);
+
+       connector->registered = false;
+}
+EXPORT_SYMBOL(drm_connector_unregister);
+
+void drm_connector_unregister_all(struct drm_device *dev)
+{
+       struct drm_connector *connector;
+
+       /* FIXME: taking the mode config mutex ends up in a clash with sysfs */
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+               drm_connector_unregister(connector);
+}
+
+int drm_connector_register_all(struct drm_device *dev)
+{
+       struct drm_connector *connector;
+       int ret;
+
+       /* FIXME: taking the mode config mutex ends up in a clash with
+        * fbcon/backlight registration */
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               ret = drm_connector_register(connector);
+               if (ret)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       mutex_unlock(&dev->mode_config.mutex);
+       drm_connector_unregister_all(dev);
+       return ret;
+}
+
+/**
+ * drm_get_connector_status_name - return a string for connector status
+ * @status: connector status to compute name of
+ *
+ * In contrast to the other drm_get_*_name functions this one here returns a
+ * const pointer and hence is threadsafe.
+ */
+const char *drm_get_connector_status_name(enum drm_connector_status status)
+{
+       if (status == connector_status_connected)
+               return "connected";
+       else if (status == connector_status_disconnected)
+               return "disconnected";
+       else
+               return "unknown";
+}
+EXPORT_SYMBOL(drm_get_connector_status_name);
+
+static const struct drm_prop_enum_list drm_subpixel_enum_list[] = {
+       { SubPixelUnknown, "Unknown" },
+       { SubPixelHorizontalRGB, "Horizontal RGB" },
+       { SubPixelHorizontalBGR, "Horizontal BGR" },
+       { SubPixelVerticalRGB, "Vertical RGB" },
+       { SubPixelVerticalBGR, "Vertical BGR" },
+       { SubPixelNone, "None" },
+};
+
+/**
+ * drm_get_subpixel_order_name - return a string for a given subpixel enum
+ * @order: enum of subpixel_order
+ *
+ * Note you could abuse this and return something out of bounds, but that
+ * would be a caller error.  No unscrubbed user data should make it here.
+ */
+const char *drm_get_subpixel_order_name(enum subpixel_order order)
+{
+       return drm_subpixel_enum_list[order].name;
+}
+EXPORT_SYMBOL(drm_get_subpixel_order_name);
+
+static const struct drm_prop_enum_list drm_dpms_enum_list[] = {
+       { DRM_MODE_DPMS_ON, "On" },
+       { DRM_MODE_DPMS_STANDBY, "Standby" },
+       { DRM_MODE_DPMS_SUSPEND, "Suspend" },
+       { DRM_MODE_DPMS_OFF, "Off" }
+};
+DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
+
+/**
+ * drm_display_info_set_bus_formats - set the supported bus formats
+ * @info: display info to store bus formats in
+ * @formats: array containing the supported bus formats
+ * @num_formats: the number of entries in the fmts array
+ *
+ * Store the supported bus formats in display info structure.
+ * See MEDIA_BUS_FMT_* definitions in include/uapi/linux/media-bus-format.h for
+ * a full list of available formats.
+ */
+int drm_display_info_set_bus_formats(struct drm_display_info *info,
+                                    const u32 *formats,
+                                    unsigned int num_formats)
+{
+       u32 *fmts = NULL;
+
+       if (!formats && num_formats)
+               return -EINVAL;
+
+       if (formats && num_formats) {
+               fmts = kmemdup(formats, sizeof(*formats) * num_formats,
+                              GFP_KERNEL);
+               if (!fmts)
+                       return -ENOMEM;
+       }
+
+       kfree(info->bus_formats);
+       info->bus_formats = fmts;
+       info->num_bus_formats = num_formats;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_display_info_set_bus_formats);
+
+/* Optional connector properties. */
+static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = {
+       { DRM_MODE_SCALE_NONE, "None" },
+       { DRM_MODE_SCALE_FULLSCREEN, "Full" },
+       { DRM_MODE_SCALE_CENTER, "Center" },
+       { 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" },
+};
+
+static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = {
+       { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
+       { DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
+       { DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
+};
+DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
+
+static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = {
+       { DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
+       { DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
+       { DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
+};
+DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name,
+                drm_dvi_i_subconnector_enum_list)
+
+static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
+       { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
+       { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
+};
+DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
+
+static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
+       { DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
+       { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
+};
+DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
+                drm_tv_subconnector_enum_list)
+
+int drm_connector_create_standard_properties(struct drm_device *dev)
+{
+       struct drm_property *prop;
+
+       prop = drm_property_create(dev, DRM_MODE_PROP_BLOB |
+                                  DRM_MODE_PROP_IMMUTABLE,
+                                  "EDID", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.edid_property = prop;
+
+       prop = drm_property_create_enum(dev, 0,
+                                  "DPMS", drm_dpms_enum_list,
+                                  ARRAY_SIZE(drm_dpms_enum_list));
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.dpms_property = prop;
+
+       prop = drm_property_create(dev,
+                                  DRM_MODE_PROP_BLOB |
+                                  DRM_MODE_PROP_IMMUTABLE,
+                                  "PATH", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.path_property = prop;
+
+       prop = drm_property_create(dev,
+                                  DRM_MODE_PROP_BLOB |
+                                  DRM_MODE_PROP_IMMUTABLE,
+                                  "TILE", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.tile_property = prop;
+
+       return 0;
+}
+
+/**
+ * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
+ * @dev: DRM device
+ *
+ * Called by a driver the first time a DVI-I connector is made.
+ */
+int drm_mode_create_dvi_i_properties(struct drm_device *dev)
+{
+       struct drm_property *dvi_i_selector;
+       struct drm_property *dvi_i_subconnector;
+
+       if (dev->mode_config.dvi_i_select_subconnector_property)
+               return 0;
+
+       dvi_i_selector =
+               drm_property_create_enum(dev, 0,
+                                   "select subconnector",
+                                   drm_dvi_i_select_enum_list,
+                                   ARRAY_SIZE(drm_dvi_i_select_enum_list));
+       dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector;
+
+       dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
+                                   "subconnector",
+                                   drm_dvi_i_subconnector_enum_list,
+                                   ARRAY_SIZE(drm_dvi_i_subconnector_enum_list));
+       dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
+
+/**
+ * drm_create_tv_properties - create TV specific connector properties
+ * @dev: DRM device
+ * @num_modes: number of different TV formats (modes) supported
+ * @modes: array of pointers to strings containing name of each format
+ *
+ * Called by a driver's TV initialization routine, this function creates
+ * the TV specific connector properties for a given device.  Caller is
+ * responsible for allocating a list of format names and passing them to
+ * this routine.
+ */
+int drm_mode_create_tv_properties(struct drm_device *dev,
+                                 unsigned int num_modes,
+                                 const char * const modes[])
+{
+       struct drm_property *tv_selector;
+       struct drm_property *tv_subconnector;
+       unsigned int i;
+
+       if (dev->mode_config.tv_select_subconnector_property)
+               return 0;
+
+       /*
+        * Basic connector properties
+        */
+       tv_selector = drm_property_create_enum(dev, 0,
+                                         "select subconnector",
+                                         drm_tv_select_enum_list,
+                                         ARRAY_SIZE(drm_tv_select_enum_list));
+       if (!tv_selector)
+               goto nomem;
+
+       dev->mode_config.tv_select_subconnector_property = tv_selector;
+
+       tv_subconnector =
+               drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
+                                   "subconnector",
+                                   drm_tv_subconnector_enum_list,
+                                   ARRAY_SIZE(drm_tv_subconnector_enum_list));
+       if (!tv_subconnector)
+               goto nomem;
+       dev->mode_config.tv_subconnector_property = tv_subconnector;
+
+       /*
+        * Other, TV specific properties: margins & TV modes.
+        */
+       dev->mode_config.tv_left_margin_property =
+               drm_property_create_range(dev, 0, "left margin", 0, 100);
+       if (!dev->mode_config.tv_left_margin_property)
+               goto nomem;
+
+       dev->mode_config.tv_right_margin_property =
+               drm_property_create_range(dev, 0, "right margin", 0, 100);
+       if (!dev->mode_config.tv_right_margin_property)
+               goto nomem;
+
+       dev->mode_config.tv_top_margin_property =
+               drm_property_create_range(dev, 0, "top margin", 0, 100);
+       if (!dev->mode_config.tv_top_margin_property)
+               goto nomem;
+
+       dev->mode_config.tv_bottom_margin_property =
+               drm_property_create_range(dev, 0, "bottom margin", 0, 100);
+       if (!dev->mode_config.tv_bottom_margin_property)
+               goto nomem;
+
+       dev->mode_config.tv_mode_property =
+               drm_property_create(dev, DRM_MODE_PROP_ENUM,
+                                   "mode", num_modes);
+       if (!dev->mode_config.tv_mode_property)
+               goto nomem;
+
+       for (i = 0; i < num_modes; i++)
+               drm_property_add_enum(dev->mode_config.tv_mode_property, i,
+                                     i, modes[i]);
+
+       dev->mode_config.tv_brightness_property =
+               drm_property_create_range(dev, 0, "brightness", 0, 100);
+       if (!dev->mode_config.tv_brightness_property)
+               goto nomem;
+
+       dev->mode_config.tv_contrast_property =
+               drm_property_create_range(dev, 0, "contrast", 0, 100);
+       if (!dev->mode_config.tv_contrast_property)
+               goto nomem;
+
+       dev->mode_config.tv_flicker_reduction_property =
+               drm_property_create_range(dev, 0, "flicker reduction", 0, 100);
+       if (!dev->mode_config.tv_flicker_reduction_property)
+               goto nomem;
+
+       dev->mode_config.tv_overscan_property =
+               drm_property_create_range(dev, 0, "overscan", 0, 100);
+       if (!dev->mode_config.tv_overscan_property)
+               goto nomem;
+
+       dev->mode_config.tv_saturation_property =
+               drm_property_create_range(dev, 0, "saturation", 0, 100);
+       if (!dev->mode_config.tv_saturation_property)
+               goto nomem;
+
+       dev->mode_config.tv_hue_property =
+               drm_property_create_range(dev, 0, "hue", 0, 100);
+       if (!dev->mode_config.tv_hue_property)
+               goto nomem;
+
+       return 0;
+nomem:
+       return -ENOMEM;
+}
+EXPORT_SYMBOL(drm_mode_create_tv_properties);
+
+/**
+ * drm_mode_create_scaling_mode_property - create scaling mode property
+ * @dev: DRM device
+ *
+ * Called by a driver the first time it's needed, must be attached to desired
+ * connectors.
+ */
+int drm_mode_create_scaling_mode_property(struct drm_device *dev)
+{
+       struct drm_property *scaling_mode;
+
+       if (dev->mode_config.scaling_mode_property)
+               return 0;
+
+       scaling_mode =
+               drm_property_create_enum(dev, 0, "scaling mode",
+                               drm_scaling_mode_enum_list,
+                                   ARRAY_SIZE(drm_scaling_mode_enum_list));
+
+       dev->mode_config.scaling_mode_property = scaling_mode;
+
+       return 0;
+}
+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, negative 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_suggested_offset_properties - create suggests offset properties
+ * @dev: DRM device
+ *
+ * Create the the suggested x/y offset property for connectors.
+ */
+int drm_mode_create_suggested_offset_properties(struct drm_device *dev)
+{
+       if (dev->mode_config.suggested_x_property && dev->mode_config.suggested_y_property)
+               return 0;
+
+       dev->mode_config.suggested_x_property =
+               drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested X", 0, 0xffffffff);
+
+       dev->mode_config.suggested_y_property =
+               drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested Y", 0, 0xffffffff);
+
+       if (dev->mode_config.suggested_x_property == NULL ||
+           dev->mode_config.suggested_y_property == NULL)
+               return -ENOMEM;
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_suggested_offset_properties);
+
+/**
+ * drm_mode_connector_set_path_property - set tile property on connector
+ * @connector: connector to set property on.
+ * @path: path to use for property; must not be NULL.
+ *
+ * This creates a property to expose to userspace to specify a
+ * connector path. This is mainly used for DisplayPort MST where
+ * connectors have a topology and we want to allow userspace to give
+ * them more meaningful names.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_connector_set_path_property(struct drm_connector *connector,
+                                        const char *path)
+{
+       struct drm_device *dev = connector->dev;
+       int ret;
+
+       ret = drm_property_replace_global_blob(dev,
+                                              &connector->path_blob_ptr,
+                                              strlen(path) + 1,
+                                              path,
+                                              &connector->base,
+                                              dev->mode_config.path_property);
+       return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_set_path_property);
+
+/**
+ * drm_mode_connector_set_tile_property - set tile property on connector
+ * @connector: connector to set property on.
+ *
+ * This looks up the tile information for a connector, and creates a
+ * property for userspace to parse if it exists. The property is of
+ * the form of 8 integers using ':' as a separator.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_connector_set_tile_property(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       char tile[256];
+       int ret;
+
+       if (!connector->has_tile) {
+               ret  = drm_property_replace_global_blob(dev,
+                                                       &connector->tile_blob_ptr,
+                                                       0,
+                                                       NULL,
+                                                       &connector->base,
+                                                       dev->mode_config.tile_property);
+               return ret;
+       }
+
+       snprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d",
+                connector->tile_group->id, connector->tile_is_single_monitor,
+                connector->num_h_tile, connector->num_v_tile,
+                connector->tile_h_loc, connector->tile_v_loc,
+                connector->tile_h_size, connector->tile_v_size);
+
+       ret = drm_property_replace_global_blob(dev,
+                                              &connector->tile_blob_ptr,
+                                              strlen(tile) + 1,
+                                              tile,
+                                              &connector->base,
+                                              dev->mode_config.tile_property);
+       return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_set_tile_property);
+
+/**
+ * drm_mode_connector_update_edid_property - update the edid property of a connector
+ * @connector: drm connector
+ * @edid: new value of the edid property
+ *
+ * This function creates a new blob modeset object and assigns its id to the
+ * connector's edid property.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_connector_update_edid_property(struct drm_connector *connector,
+                                           const struct edid *edid)
+{
+       struct drm_device *dev = connector->dev;
+       size_t size = 0;
+       int ret;
+
+       /* ignore requests to set edid when overridden */
+       if (connector->override_edid)
+               return 0;
+
+       if (edid)
+               size = EDID_LENGTH * (1 + edid->extensions);
+
+       ret = drm_property_replace_global_blob(dev,
+                                              &connector->edid_blob_ptr,
+                                              size,
+                                              edid,
+                                              &connector->base,
+                                              dev->mode_config.edid_property);
+       return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_update_edid_property);
+
+int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
+                                   struct drm_property *property,
+                                   uint64_t value)
+{
+       int ret = -EINVAL;
+       struct drm_connector *connector = obj_to_connector(obj);
+
+       /* Do DPMS ourselves */
+       if (property == connector->dev->mode_config.dpms_property) {
+               ret = (*connector->funcs->dpms)(connector, (int)value);
+       } else if (connector->funcs->set_property)
+               ret = connector->funcs->set_property(connector, property, value);
+
+       /* store the property value if successful */
+       if (!ret)
+               drm_object_property_set_value(&connector->base, property, value);
+       return ret;
+}
+
+int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
+                                      void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_connector_set_property *conn_set_prop = data;
+       struct drm_mode_obj_set_property obj_set_prop = {
+               .value = conn_set_prop->value,
+               .prop_id = conn_set_prop->prop_id,
+               .obj_id = conn_set_prop->connector_id,
+               .obj_type = DRM_MODE_OBJECT_CONNECTOR
+       };
+
+       /* It does all the locking and checking we need */
+       return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
+}
+
+static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector)
+{
+       /* For atomic drivers only state objects are synchronously updated and
+        * protected by modeset locks, so check those first. */
+       if (connector->state)
+               return connector->state->best_encoder;
+       return connector->encoder;
+}
+
+static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
+                                        const struct drm_file *file_priv)
+{
+       /*
+        * If user-space hasn't configured the driver to expose the stereo 3D
+        * modes, don't expose them.
+        */
+       if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode))
+               return false;
+
+       return true;
+}
+
+int drm_mode_getconnector(struct drm_device *dev, void *data,
+                         struct drm_file *file_priv)
+{
+       struct drm_mode_get_connector *out_resp = data;
+       struct drm_connector *connector;
+       struct drm_encoder *encoder;
+       struct drm_display_mode *mode;
+       int mode_count = 0;
+       int encoders_count = 0;
+       int ret = 0;
+       int copied = 0;
+       int i;
+       struct drm_mode_modeinfo u_mode;
+       struct drm_mode_modeinfo __user *mode_ptr;
+       uint32_t __user *encoder_ptr;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
+
+       mutex_lock(&dev->mode_config.mutex);
+
+       connector = drm_connector_lookup(dev, out_resp->connector_id);
+       if (!connector) {
+               ret = -ENOENT;
+               goto out_unlock;
+       }
+
+       for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++)
+               if (connector->encoder_ids[i] != 0)
+                       encoders_count++;
+
+       if (out_resp->count_modes == 0) {
+               connector->funcs->fill_modes(connector,
+                                            dev->mode_config.max_width,
+                                            dev->mode_config.max_height);
+       }
+
+       /* delayed so we get modes regardless of pre-fill_modes state */
+       list_for_each_entry(mode, &connector->modes, head)
+               if (drm_mode_expose_to_userspace(mode, file_priv))
+                       mode_count++;
+
+       out_resp->connector_id = connector->base.id;
+       out_resp->connector_type = connector->connector_type;
+       out_resp->connector_type_id = connector->connector_type_id;
+       out_resp->mm_width = connector->display_info.width_mm;
+       out_resp->mm_height = connector->display_info.height_mm;
+       out_resp->subpixel = connector->display_info.subpixel_order;
+       out_resp->connection = connector->status;
+
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+       encoder = drm_connector_get_encoder(connector);
+       if (encoder)
+               out_resp->encoder_id = encoder->base.id;
+       else
+               out_resp->encoder_id = 0;
+
+       /*
+        * This ioctl is called twice, once to determine how much space is
+        * needed, and the 2nd time to fill it.
+        */
+       if ((out_resp->count_modes >= mode_count) && mode_count) {
+               copied = 0;
+               mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr;
+               list_for_each_entry(mode, &connector->modes, head) {
+                       if (!drm_mode_expose_to_userspace(mode, file_priv))
+                               continue;
+
+                       drm_mode_convert_to_umode(&u_mode, mode);
+                       if (copy_to_user(mode_ptr + copied,
+                                        &u_mode, sizeof(u_mode))) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+                       copied++;
+               }
+       }
+       out_resp->count_modes = mode_count;
+
+       ret = drm_mode_object_get_properties(&connector->base, file_priv->atomic,
+                       (uint32_t __user *)(unsigned long)(out_resp->props_ptr),
+                       (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr),
+                       &out_resp->count_props);
+       if (ret)
+               goto out;
+
+       if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
+               copied = 0;
+               encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
+               for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+                       if (connector->encoder_ids[i] != 0) {
+                               if (put_user(connector->encoder_ids[i],
+                                            encoder_ptr + copied)) {
+                                       ret = -EFAULT;
+                                       goto out;
+                               }
+                               copied++;
+                       }
+               }
+       }
+       out_resp->count_encoders = encoders_count;
+
+out:
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
+
+       drm_connector_unreference(connector);
+out_unlock:
+       mutex_unlock(&dev->mode_config.mutex);
+
+       return ret;
+}
+
index b58abbc..76ac608 100644 (file)
@@ -54,7 +54,7 @@ struct drm_ctx_list {
 void drm_legacy_ctxbitmap_free(struct drm_device * dev, int ctx_handle)
 {
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        mutex_lock(&dev->struct_mutex);
@@ -92,7 +92,7 @@ static int drm_legacy_ctxbitmap_next(struct drm_device * dev)
 void drm_legacy_ctxbitmap_init(struct drm_device * dev)
 {
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        idr_init(&dev->ctx_idr);
@@ -109,7 +109,7 @@ void drm_legacy_ctxbitmap_init(struct drm_device * dev)
 void drm_legacy_ctxbitmap_cleanup(struct drm_device * dev)
 {
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        mutex_lock(&dev->struct_mutex);
@@ -131,7 +131,7 @@ void drm_legacy_ctxbitmap_flush(struct drm_device *dev, struct drm_file *file)
        struct drm_ctx_list *pos, *tmp;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        mutex_lock(&dev->ctxlist_mutex);
@@ -177,7 +177,7 @@ int drm_legacy_getsareactx(struct drm_device *dev, void *data,
        struct drm_map_list *_entry;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        mutex_lock(&dev->struct_mutex);
@@ -225,7 +225,7 @@ int drm_legacy_setsareactx(struct drm_device *dev, void *data,
        struct drm_map_list *r_list = NULL;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        mutex_lock(&dev->struct_mutex);
@@ -329,7 +329,7 @@ int drm_legacy_resctx(struct drm_device *dev, void *data,
        int i;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (res->count >= DRM_RESERVED_CONTEXTS) {
@@ -363,7 +363,7 @@ int drm_legacy_addctx(struct drm_device *dev, void *data,
        struct drm_ctx *ctx = data;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        ctx->handle = drm_legacy_ctxbitmap_next(dev);
@@ -410,7 +410,7 @@ int drm_legacy_getctx(struct drm_device *dev, void *data,
        struct drm_ctx *ctx = data;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        /* This is 0, because we don't handle any context flags */
@@ -436,7 +436,7 @@ int drm_legacy_switchctx(struct drm_device *dev, void *data,
        struct drm_ctx *ctx = data;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        DRM_DEBUG("%d\n", ctx->handle);
@@ -460,7 +460,7 @@ int drm_legacy_newctx(struct drm_device *dev, void *data,
        struct drm_ctx *ctx = data;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        DRM_DEBUG("%d\n", ctx->handle);
@@ -486,7 +486,7 @@ int drm_legacy_rmctx(struct drm_device *dev, void *data,
        struct drm_ctx *ctx = data;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        DRM_DEBUG("%d\n", ctx->handle);
index d062e4e..c5954b4 100644 (file)
 #include <linux/slab.h>
 #include <drm/drm_modeset_lock.h>
 #include <drm/drm_atomic.h>
+#include <drm/drm_auth.h>
+#include <drm/drm_framebuffer.h>
 
 #include "drm_crtc_internal.h"
 #include "drm_internal.h"
 
-static struct drm_framebuffer *
-internal_framebuffer_create(struct drm_device *dev,
-                           const struct drm_mode_fb_cmd2 *r,
-                           struct drm_file *file_priv);
-
-/* Avoid boilerplate.  I'm tired of typing. */
-#define DRM_ENUM_NAME_FN(fnname, list)                         \
-       const char *fnname(int val)                             \
-       {                                                       \
-               int i;                                          \
-               for (i = 0; i < ARRAY_SIZE(list); i++) {        \
-                       if (list[i].type == val)                \
-                               return list[i].name;            \
-               }                                               \
-               return "(unknown)";                             \
-       }
-
 /*
  * Global properties
  */
-static const struct drm_prop_enum_list drm_dpms_enum_list[] = {
-       { DRM_MODE_DPMS_ON, "On" },
-       { DRM_MODE_DPMS_STANDBY, "Standby" },
-       { DRM_MODE_DPMS_SUSPEND, "Suspend" },
-       { DRM_MODE_DPMS_OFF, "Off" }
-};
-
-DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
-
 static const struct drm_prop_enum_list drm_plane_type_enum_list[] = {
        { DRM_PLANE_TYPE_OVERLAY, "Overlay" },
        { DRM_PLANE_TYPE_PRIMARY, "Primary" },
@@ -81,563 +57,88 @@ static const struct drm_prop_enum_list drm_plane_type_enum_list[] = {
 /*
  * Optional properties
  */
-static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = {
-       { DRM_MODE_SCALE_NONE, "None" },
-       { DRM_MODE_SCALE_FULLSCREEN, "Full" },
-       { DRM_MODE_SCALE_CENTER, "Center" },
-       { 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.
- */
-static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = {
-       { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
-       { DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
-       { DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
-};
-
-DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
-
-static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = {
-       { DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
-       { DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
-       { DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
-};
-
-DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name,
-                drm_dvi_i_subconnector_enum_list)
-
-static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
-       { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
-       { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
-       { DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
-       { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
-       { DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
-};
-
-DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
-
-static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
-       { DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
-       { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
-       { DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
-       { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
-       { DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
-};
-
-DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
-                drm_tv_subconnector_enum_list)
-
-static const struct drm_prop_enum_list drm_dirty_info_enum_list[] = {
-       { DRM_MODE_DIRTY_OFF,      "Off"      },
-       { DRM_MODE_DIRTY_ON,       "On"       },
-       { DRM_MODE_DIRTY_ANNOTATE, "Annotate" },
-};
-
-struct drm_conn_prop_enum_list {
-       int type;
-       const char *name;
-       struct ida ida;
-       int count;
-};
-
-/*
- * Connector and encoder types.
- */
-static struct drm_conn_prop_enum_list drm_connector_enum_list[] = {
-       { DRM_MODE_CONNECTOR_Unknown, "Unknown" },
-       { DRM_MODE_CONNECTOR_VGA, "VGA" },
-       { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
-       { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
-       { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
-       { DRM_MODE_CONNECTOR_Composite, "Composite" },
-       { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" },
-       { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
-       { DRM_MODE_CONNECTOR_Component, "Component" },
-       { DRM_MODE_CONNECTOR_9PinDIN, "DIN" },
-       { DRM_MODE_CONNECTOR_DisplayPort, "DP" },
-       { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
-       { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
-       { DRM_MODE_CONNECTOR_TV, "TV" },
-       { DRM_MODE_CONNECTOR_eDP, "eDP" },
-       { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
-       { DRM_MODE_CONNECTOR_DSI, "DSI" },
-       { DRM_MODE_CONNECTOR_DPI, "DPI" },
-};
-
-static const struct drm_prop_enum_list drm_encoder_enum_list[] = {
-       { DRM_MODE_ENCODER_NONE, "None" },
-       { DRM_MODE_ENCODER_DAC, "DAC" },
-       { DRM_MODE_ENCODER_TMDS, "TMDS" },
-       { DRM_MODE_ENCODER_LVDS, "LVDS" },
-       { DRM_MODE_ENCODER_TVDAC, "TV" },
-       { DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
-       { DRM_MODE_ENCODER_DSI, "DSI" },
-       { DRM_MODE_ENCODER_DPMST, "DP MST" },
-       { DRM_MODE_ENCODER_DPI, "DPI" },
-};
-
-static const struct drm_prop_enum_list drm_subpixel_enum_list[] = {
-       { SubPixelUnknown, "Unknown" },
-       { SubPixelHorizontalRGB, "Horizontal RGB" },
-       { SubPixelHorizontalBGR, "Horizontal BGR" },
-       { SubPixelVerticalRGB, "Vertical RGB" },
-       { SubPixelVerticalBGR, "Vertical BGR" },
-       { SubPixelNone, "None" },
-};
-
-void drm_connector_ida_init(void)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
-               ida_init(&drm_connector_enum_list[i].ida);
-}
-
-void drm_connector_ida_destroy(void)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
-               ida_destroy(&drm_connector_enum_list[i].ida);
-}
-
-/**
- * drm_get_connector_status_name - return a string for connector status
- * @status: connector status to compute name of
- *
- * In contrast to the other drm_get_*_name functions this one here returns a
- * const pointer and hence is threadsafe.
- */
-const char *drm_get_connector_status_name(enum drm_connector_status status)
-{
-       if (status == connector_status_connected)
-               return "connected";
-       else if (status == connector_status_disconnected)
-               return "disconnected";
-       else
-               return "unknown";
-}
-EXPORT_SYMBOL(drm_get_connector_status_name);
-
-/**
- * drm_get_subpixel_order_name - return a string for a given subpixel enum
- * @order: enum of subpixel_order
- *
- * Note you could abuse this and return something out of bounds, but that
- * would be a caller error.  No unscrubbed user data should make it here.
- */
-const char *drm_get_subpixel_order_name(enum subpixel_order order)
-{
-       return drm_subpixel_enum_list[order].name;
-}
-EXPORT_SYMBOL(drm_get_subpixel_order_name);
-
-static char printable_char(int c)
-{
-       return isascii(c) && isprint(c) ? c : '?';
-}
-
-/**
- * drm_get_format_name - return a string for drm fourcc format
- * @format: format to compute name of
- *
- * Note that the buffer used by this function is globally shared and owned by
- * the function itself.
- *
- * FIXME: This isn't really multithreading safe.
- */
-const char *drm_get_format_name(uint32_t format)
-{
-       static char buf[32];
-
-       ksnprintf(buf, sizeof(buf),
-                "%c%c%c%c %s-endian (0x%08x)",
-                printable_char(format & 0xff),
-                printable_char((format >> 8) & 0xff),
-                printable_char((format >> 16) & 0xff),
-                printable_char((format >> 24) & 0x7f),
-                format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little",
-                format);
-
-       return buf;
-}
-EXPORT_SYMBOL(drm_get_format_name);
-
-/*
- * Internal function to assign a slot in the object idr and optionally
- * register the object into the idr.
- */
-static int drm_mode_object_get_reg(struct drm_device *dev,
-                                  struct drm_mode_object *obj,
-                                  uint32_t obj_type,
-                                  bool register_obj,
-                                  void (*obj_free_cb)(struct kref *kref))
-{
-       int ret;
-
-       mutex_lock(&dev->mode_config.idr_mutex);
-       ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL);
-       if (ret >= 0) {
-               /*
-                * Set up the object linking under the protection of the idr
-                * lock so that other users can't see inconsistent state.
-                */
-               obj->id = ret;
-               obj->type = obj_type;
-               if (obj_free_cb) {
-                       obj->free_cb = obj_free_cb;
-                       kref_init(&obj->refcount);
-               }
-       }
-       mutex_unlock(&dev->mode_config.idr_mutex);
-
-       return ret < 0 ? ret : 0;
-}
-
 /**
- * drm_mode_object_get - allocate a new modeset identifier
- * @dev: DRM device
- * @obj: object pointer, used to generate unique ID
- * @obj_type: object type
- *
- * Create a unique identifier based on @ptr in @dev's identifier space.  Used
- * for tracking modes, CRTCs and connectors. Note that despite the _get postfix
- * modeset identifiers are _not_ reference counted. Hence don't use this for
- * reference counted modeset objects like framebuffers.
+ * drm_crtc_force_disable - Forcibly turn off a CRTC
+ * @crtc: CRTC to turn off
  *
  * Returns:
  * Zero on success, error code on failure.
  */
-int drm_mode_object_get(struct drm_device *dev,
-                       struct drm_mode_object *obj, uint32_t obj_type)
-{
-       return drm_mode_object_get_reg(dev, obj, obj_type, true, NULL);
-}
-
-static void drm_mode_object_register(struct drm_device *dev,
-                                    struct drm_mode_object *obj)
-{
-       mutex_lock(&dev->mode_config.idr_mutex);
-       idr_replace(&dev->mode_config.crtc_idr, obj, obj->id);
-       mutex_unlock(&dev->mode_config.idr_mutex);
-}
-
-/**
- * drm_mode_object_unregister - free a modeset identifer
- * @dev: DRM device
- * @object: object to free
- *
- * Free @id from @dev's unique identifier pool.
- * This function can be called multiple times, and guards against
- * multiple removals.
- * These modeset identifiers are _not_ reference counted. Hence don't use this
- * for reference counted modeset objects like framebuffers.
- */
-void drm_mode_object_unregister(struct drm_device *dev,
-                        struct drm_mode_object *object)
-{
-       mutex_lock(&dev->mode_config.idr_mutex);
-       if (object->id) {
-               idr_remove(&dev->mode_config.crtc_idr, object->id);
-               object->id = 0;
-       }
-       mutex_unlock(&dev->mode_config.idr_mutex);
-}
-
-static struct drm_mode_object *_object_find(struct drm_device *dev,
-               uint32_t id, uint32_t type)
-{
-       struct drm_mode_object *obj = NULL;
-
-       mutex_lock(&dev->mode_config.idr_mutex);
-       obj = idr_find(&dev->mode_config.crtc_idr, id);
-       if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type)
-               obj = NULL;
-       if (obj && obj->id != id)
-               obj = NULL;
-
-       if (obj && obj->free_cb) {
-               if (!kref_get_unless_zero(&obj->refcount))
-                       obj = NULL;
-       }
-       mutex_unlock(&dev->mode_config.idr_mutex);
-
-       return obj;
-}
-
-/**
- * drm_mode_object_find - look up a drm object with static lifetime
- * @dev: drm device
- * @id: id of the mode object
- * @type: type of the mode object
- *
- * This function is used to look up a modeset object. It will acquire a
- * reference for reference counted objects. This reference must be dropped again
- * by callind drm_mode_object_unreference().
- */
-struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
-               uint32_t id, uint32_t type)
-{
-       struct drm_mode_object *obj = NULL;
-
-       obj = _object_find(dev, id, type);
-       return obj;
-}
-EXPORT_SYMBOL(drm_mode_object_find);
-
-/**
- * drm_mode_object_unreference - decr the object refcnt
- * @obj: mode_object
- *
- * This functions decrements the object's refcount if it is a refcounted modeset
- * object. It is a no-op on any other object. This is used to drop references
- * acquired with drm_mode_object_reference().
- */
-void drm_mode_object_unreference(struct drm_mode_object *obj)
-{
-       if (obj->free_cb) {
-               DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount));
-               kref_put(&obj->refcount, obj->free_cb);
-       }
-}
-EXPORT_SYMBOL(drm_mode_object_unreference);
-
-/**
- * drm_mode_object_reference - incr the object refcnt
- * @obj: mode_object
- *
- * This functions increments the object's refcount if it is a refcounted modeset
- * object. It is a no-op on any other object. References should be dropped again
- * by calling drm_mode_object_unreference().
- */
-void drm_mode_object_reference(struct drm_mode_object *obj)
-{
-       if (obj->free_cb) {
-               DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount));
-               kref_get(&obj->refcount);
-       }
-}
-EXPORT_SYMBOL(drm_mode_object_reference);
-
-static void drm_framebuffer_free(struct kref *kref)
+int drm_crtc_force_disable(struct drm_crtc *crtc)
 {
-       struct drm_framebuffer *fb =
-                       container_of(kref, struct drm_framebuffer, base.refcount);
-       struct drm_device *dev = fb->dev;
-
-       /*
-        * The lookup idr holds a weak reference, which has not necessarily been
-        * removed at this point. Check for that.
-        */
-       drm_mode_object_unregister(dev, &fb->base);
+       struct drm_mode_set set = {
+               .crtc = crtc,
+       };
 
-       fb->funcs->destroy(fb);
+       return drm_mode_set_config_internal(&set);
 }
+EXPORT_SYMBOL(drm_crtc_force_disable);
 
 /**
- * drm_framebuffer_init - initialize a framebuffer
- * @dev: DRM device
- * @fb: framebuffer to be initialized
- * @funcs: ... with these functions
+ * drm_crtc_force_disable_all - Forcibly turn off all enabled CRTCs
+ * @dev: DRM device whose CRTCs to turn off
  *
- * Allocates an ID for the framebuffer's parent mode object, sets its mode
- * functions & device file and adds it to the master fd list.
- *
- * IMPORTANT:
- * This functions publishes the fb and makes it available for concurrent access
- * by other users. Which means by this point the fb _must_ be fully set up -
- * since all the fb attributes are invariant over its lifetime, no further
- * locking but only correct reference counting is required.
+ * Drivers may want to call this on unload to ensure that all displays are
+ * unlit and the GPU is in a consistent, low power state. Takes modeset locks.
  *
  * Returns:
  * Zero on success, error code on failure.
  */
-int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
-                        const struct drm_framebuffer_funcs *funcs)
+int drm_crtc_force_disable_all(struct drm_device *dev)
 {
-       int ret;
-
-       INIT_LIST_HEAD(&fb->filp_head);
-       fb->dev = dev;
-       fb->funcs = funcs;
-
-       ret = drm_mode_object_get_reg(dev, &fb->base, DRM_MODE_OBJECT_FB,
-                                     false, drm_framebuffer_free);
-       if (ret)
-               goto out;
-
-       mutex_lock(&dev->mode_config.fb_lock);
-       dev->mode_config.num_fb++;
-       list_add(&fb->head, &dev->mode_config.fb_list);
-       mutex_unlock(&dev->mode_config.fb_lock);
+       struct drm_crtc *crtc;
+       int ret = 0;
 
-       drm_mode_object_register(dev, &fb->base);
+       drm_modeset_lock_all(dev);
+       drm_for_each_crtc(crtc, dev)
+               if (crtc->enabled) {
+                       ret = drm_crtc_force_disable(crtc);
+                       if (ret)
+                               goto out;
+               }
 out:
+       drm_modeset_unlock_all(dev);
        return ret;
 }
-EXPORT_SYMBOL(drm_framebuffer_init);
-
-/**
- * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference
- * @dev: drm device
- * @id: id of the fb object
- *
- * If successful, this grabs an additional reference to the framebuffer -
- * callers need to make sure to eventually unreference the returned framebuffer
- * again, using @drm_framebuffer_unreference.
- */
-struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
-                                              uint32_t id)
-{
-       struct drm_mode_object *obj;
-       struct drm_framebuffer *fb = NULL;
+EXPORT_SYMBOL(drm_crtc_force_disable_all);
 
-       obj = _object_find(dev, id, DRM_MODE_OBJECT_FB);
-       if (obj)
-               fb = obj_to_fb(obj);
-       return fb;
-}
-EXPORT_SYMBOL(drm_framebuffer_lookup);
+DEFINE_WW_CLASS(crtc_ww_class);
 
-/**
- * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
- * @fb: fb to unregister
- *
- * Drivers need to call this when cleaning up driver-private framebuffers, e.g.
- * those used for fbdev. Note that the caller must hold a reference of it's own,
- * i.e. the object may not be destroyed through this call (since it'll lead to a
- * locking inversion).
- */
-void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
+static unsigned int drm_num_crtcs(struct drm_device *dev)
 {
-       struct drm_device *dev;
-
-       if (!fb)
-               return;
-
-       dev = fb->dev;
-
-       /* Mark fb as reaped and drop idr ref. */
-       drm_mode_object_unregister(dev, &fb->base);
-}
-EXPORT_SYMBOL(drm_framebuffer_unregister_private);
+       unsigned int num = 0;
+       struct drm_crtc *tmp;
 
-/**
- * drm_framebuffer_cleanup - remove a framebuffer object
- * @fb: framebuffer to remove
- *
- * Cleanup framebuffer. This function is intended to be used from the drivers
- * ->destroy callback. It can also be used to clean up driver private
- *  framebuffers embedded into a larger structure.
- *
- * Note that this function does not remove the fb from active usuage - if it is
- * still used anywhere, hilarity can ensue since userspace could call getfb on
- * the id and get back -EINVAL. Obviously no concern at driver unload time.
- *
- * Also, the framebuffer will not be removed from the lookup idr - for
- * user-created framebuffers this will happen in in the rmfb ioctl. For
- * driver-private objects (e.g. for fbdev) drivers need to explicitly call
- * drm_framebuffer_unregister_private.
- */
-void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
-{
-       struct drm_device *dev = fb->dev;
+       drm_for_each_crtc(tmp, dev) {
+               num++;
+       }
 
-       mutex_lock(&dev->mode_config.fb_lock);
-       list_del(&fb->head);
-       dev->mode_config.num_fb--;
-       mutex_unlock(&dev->mode_config.fb_lock);
+       return num;
 }
-EXPORT_SYMBOL(drm_framebuffer_cleanup);
 
-/**
- * drm_framebuffer_remove - remove and unreference a framebuffer object
- * @fb: framebuffer to remove
- *
- * Scans all the CRTCs and planes in @dev's mode_config.  If they're
- * using @fb, removes it, setting it to NULL. Then drops the reference to the
- * passed-in framebuffer. Might take the modeset locks.
- *
- * Note that this function optimizes the cleanup away if the caller holds the
- * last reference to the framebuffer. It is also guaranteed to not take the
- * modeset locks in this case.
- */
-void drm_framebuffer_remove(struct drm_framebuffer *fb)
+static int drm_crtc_register_all(struct drm_device *dev)
 {
-       struct drm_device *dev;
        struct drm_crtc *crtc;
-       struct drm_plane *plane;
-       struct drm_mode_set set;
-       int ret;
-
-       if (!fb)
-               return;
-
-       dev = fb->dev;
-
-       WARN_ON(!list_empty(&fb->filp_head));
-
-       /*
-        * drm ABI mandates that we remove any deleted framebuffers from active
-        * useage. But since most sane clients only remove framebuffers they no
-        * longer need, try to optimize this away.
-        *
-        * Since we're holding a reference ourselves, observing a refcount of 1
-        * means that we're the last holder and can skip it. Also, the refcount
-        * can never increase from 1 again, so we don't need any barriers or
-        * locks.
-        *
-        * Note that userspace could try to race with use and instate a new
-        * usage _after_ we've cleared all current ones. End result will be an
-        * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot
-        * in this manner.
-        */
-       if (drm_framebuffer_read_refcount(fb) > 1) {
-               drm_modeset_lock_all(dev);
-               /* remove from any CRTC */
-               drm_for_each_crtc(crtc, dev) {
-                       if (crtc->primary->fb == fb) {
-                               /* should turn off the crtc */
-                               memset(&set, 0, sizeof(struct drm_mode_set));
-                               set.crtc = crtc;
-                               set.fb = NULL;
-                               ret = drm_mode_set_config_internal(&set);
-                               if (ret)
-                                       DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
-                       }
-               }
+       int ret = 0;
 
-               drm_for_each_plane(plane, dev) {
-                       if (plane->fb == fb)
-                               drm_plane_force_disable(plane);
-               }
-               drm_modeset_unlock_all(dev);
+       drm_for_each_crtc(crtc, dev) {
+               if (crtc->funcs->late_register)
+                       ret = crtc->funcs->late_register(crtc);
+               if (ret)
+                       return ret;
        }
 
-       drm_framebuffer_unreference(fb);
+       return 0;
 }
-EXPORT_SYMBOL(drm_framebuffer_remove);
-
-DEFINE_WW_CLASS(crtc_ww_class);
 
-static unsigned int drm_num_crtcs(struct drm_device *dev)
+static void drm_crtc_unregister_all(struct drm_device *dev)
 {
-       unsigned int num = 0;
-       struct drm_crtc *tmp;
+       struct drm_crtc *crtc;
 
-       drm_for_each_crtc(tmp, dev) {
-               num++;
+       drm_for_each_crtc(crtc, dev) {
+               if (crtc->funcs->early_unregister)
+                       crtc->funcs->early_unregister(crtc);
        }
-
-       return num;
 }
 
 /**
@@ -650,7 +151,11 @@ static unsigned int drm_num_crtcs(struct drm_device *dev)
  * @funcs: callbacks for the new CRTC
  * @name: printf style format string for the CRTC name, or NULL for default name
  *
- * Inits a new object created as base part of a driver crtc object.
+ * Inits a new object created as base part of a driver crtc object. Drivers
+ * should use this function instead of drm_crtc_init(), which is only provided
+ * for backwards compatibility with drivers which do not yet support universal
+ * planes). For really simple hardware which has only 1 plane look at
+ * drm_simple_display_pipe_init() instead.
  *
  * Returns:
  * Zero on success, error code on failure.
@@ -670,6 +175,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
        crtc->dev = dev;
        crtc->funcs = funcs;
 
+       INIT_LIST_HEAD(&crtc->commit_list);
+       spin_init(&crtc->commit_lock, "dccl");
+
        drm_modeset_lock_init(&crtc->mutex);
        ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
        if (ret)
@@ -693,7 +201,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
        crtc->base.properties = &crtc->properties;
 
        list_add_tail(&crtc->head, &config->crtc_list);
-       config->num_crtc++;
+       crtc->index = config->num_crtc++;
 
        crtc->primary = primary;
        crtc->cursor = cursor;
@@ -723,6 +231,11 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
 
+       /* Note that the crtc_list is considered to be static; should we
+        * remove the drm_crtc at runtime we would have to decrement all
+        * the indices on the drm_crtc after us in the crtc_list.
+        */
+
        kfree(crtc->gamma_store);
        crtc->gamma_store = NULL;
 
@@ -743,19 +256,22 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
 EXPORT_SYMBOL(drm_crtc_cleanup);
 
 /**
- * drm_crtc_index - find the index of a registered CRTC
- * @crtc: CRTC to find index for
+ * drm_connector_index - find the index of a registered connector
+ * @connector: connector to find index for
  *
- * Given a registered CRTC, return the index of that CRTC within a DRM
- * device's list of CRTCs.
+ * Given a registered connector, return the index of that connector within a DRM
+ * device's list of connectors.
  */
-unsigned int drm_crtc_index(struct drm_crtc *crtc)
+unsigned int drm_connector_index(struct drm_connector *connector)
 {
        unsigned int index = 0;
-       struct drm_crtc *tmp;
+       struct drm_connector *tmp;
+       struct drm_mode_config *config = &connector->dev->mode_config;
 
-       drm_for_each_crtc(tmp, crtc->dev) {
-               if (tmp == crtc)
+       WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));
+
+       drm_for_each_connector(tmp, connector->dev) {
+               if (tmp == connector)
                        return index;
 
                index++;
@@ -763,4640 +279,652 @@ unsigned int drm_crtc_index(struct drm_crtc *crtc)
 
        BUG();
 }
-EXPORT_SYMBOL(drm_crtc_index);
+EXPORT_SYMBOL(drm_connector_index);
 
-/*
- * drm_mode_remove - remove and free a mode
- * @connector: connector list to modify
- * @mode: mode to remove
- *
- * Remove @mode from @connector's mode list, then free it.
- */
-static void drm_mode_remove(struct drm_connector *connector,
-                           struct drm_display_mode *mode)
+int drm_modeset_register_all(struct drm_device *dev)
 {
-       list_del(&mode->head);
-       drm_mode_destroy(connector->dev, mode);
-}
+       int ret;
 
-/**
- * drm_display_info_set_bus_formats - set the supported bus formats
- * @info: display info to store bus formats in
- * @formats: array containing the supported bus formats
- * @num_formats: the number of entries in the fmts array
- *
- * Store the supported bus formats in display info structure.
- * See MEDIA_BUS_FMT_* definitions in include/uapi/linux/media-bus-format.h for
- * a full list of available formats.
- */
-int drm_display_info_set_bus_formats(struct drm_display_info *info,
-                                    const u32 *formats,
-                                    unsigned int num_formats)
-{
-       u32 *fmts = NULL;
+       ret = drm_plane_register_all(dev);
+       if (ret)
+               goto err_plane;
 
-       if (!formats && num_formats)
-               return -EINVAL;
+       ret = drm_crtc_register_all(dev);
+       if  (ret)
+               goto err_crtc;
 
-       if (formats && num_formats) {
-               fmts = kmemdup(formats, sizeof(*formats) * num_formats,
-                              GFP_KERNEL);
-               if (!fmts)
-                       return -ENOMEM;
-       }
+       ret = drm_encoder_register_all(dev);
+       if (ret)
+               goto err_encoder;
 
-       kfree(info->bus_formats);
-       info->bus_formats = fmts;
-       info->num_bus_formats = num_formats;
+       ret = drm_connector_register_all(dev);
+       if (ret)
+               goto err_connector;
 
        return 0;
+
+err_connector:
+       drm_encoder_unregister_all(dev);
+err_encoder:
+       drm_crtc_unregister_all(dev);
+err_crtc:
+       drm_plane_unregister_all(dev);
+err_plane:
+       return ret;
 }
-EXPORT_SYMBOL(drm_display_info_set_bus_formats);
 
-/**
- * drm_connector_get_cmdline_mode - reads the user's cmdline mode
- * @connector: connector to quwery
- *
- * The kernel supports per-connector configration of its consoles through
- * use of the video= parameter. This function parses that option and
- * extracts the user's specified mode (or enable/disable status) for a
- * particular connector. This is typically only used during the early fbdev
- * setup.
- */
-static void drm_connector_get_cmdline_mode(struct drm_connector *connector)
+void drm_modeset_unregister_all(struct drm_device *dev)
 {
-       struct drm_cmdline_mode *mode = &connector->cmdline_mode;
-       char *option = NULL;
-
-       if (fb_get_options(connector->name, &option))
-               return;
-
-       if (!drm_mode_parse_command_line_for_connector(option,
-                                                      connector,
-                                                      mode))
-               return;
-
-       if (mode->force) {
-               const char *s;
-
-               switch (mode->force) {
-               case DRM_FORCE_OFF:
-                       s = "OFF";
-                       break;
-               case DRM_FORCE_ON_DIGITAL:
-                       s = "ON - dig";
-                       break;
-               default:
-               case DRM_FORCE_ON:
-                       s = "ON";
-                       break;
-               }
-
-               DRM_INFO("forcing %s connector %s\n", connector->name, s);
-               connector->force = mode->force;
-       }
-
-       DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
-                     connector->name,
-                     mode->xres, mode->yres,
-                     mode->refresh_specified ? mode->refresh : 60,
-                     mode->rb ? " reduced blanking" : "",
-                     mode->margins ? " with margins" : "",
-                     mode->interlace ?  " interlaced" : "");
+       drm_connector_unregister_all(dev);
+       drm_encoder_unregister_all(dev);
+       drm_crtc_unregister_all(dev);
+       drm_plane_unregister_all(dev);
 }
 
-static void drm_connector_free(struct kref *kref)
-{
-       struct drm_connector *connector =
-               container_of(kref, struct drm_connector, base.refcount);
-       struct drm_device *dev = connector->dev;
-
-       drm_mode_object_unregister(dev, &connector->base);
-       connector->funcs->destroy(connector);
-}
-
-/**
- * drm_connector_init - Init a preallocated connector
- * @dev: DRM device
- * @connector: the connector to init
- * @funcs: callbacks for this connector
- * @connector_type: user visible type of the connector
- *
- * Initialises a preallocated connector. Connectors should be
- * subclassed as part of driver connector objects.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_connector_init(struct drm_device *dev,
-                      struct drm_connector *connector,
-                      const struct drm_connector_funcs *funcs,
-                      int connector_type)
+static int drm_mode_create_standard_properties(struct drm_device *dev)
 {
-       struct drm_mode_config *config = &dev->mode_config;
+       struct drm_property *prop;
        int ret;
 
-       drm_modeset_lock_all(dev);
-
-       ret = drm_mode_object_get_reg(dev, &connector->base,
-                                     DRM_MODE_OBJECT_CONNECTOR,
-                                     true, drm_connector_free);
+       ret = drm_connector_create_standard_properties(dev);
        if (ret)
-               goto out_unlock;
-
-       connector->base.properties = &connector->properties;
-       connector->dev = dev;
-       connector->funcs = funcs;
-
-       connector->connector_type = connector_type;
-       connector->connector_type_id =
-               ++drm_connector_enum_list[connector_type].count; /* TODO */
-       if (connector->connector_type_id < 0) {
-               ret = connector->connector_type_id;
-               goto out_put;
-       }
-       connector->name =
-               kasprintf(GFP_KERNEL, "%s-%d",
-                         drm_connector_enum_list[connector_type].name,
-                         connector->connector_type_id);
-       if (!connector->name) {
-               ret = -ENOMEM;
-               goto out_put;
-       }
-
-       INIT_LIST_HEAD(&connector->probed_modes);
-       INIT_LIST_HEAD(&connector->modes);
-       connector->edid_blob_ptr = NULL;
-       connector->status = connector_status_unknown;
-
-       drm_connector_get_cmdline_mode(connector);
-
-       /* We should add connectors at the end to avoid upsetting the connector
-        * index too much. */
-       list_add_tail(&connector->head, &config->connector_list);
-       config->num_connector++;
-
-       if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL)
-               drm_object_attach_property(&connector->base,
-                                             config->edid_property,
-                                             0);
-
-       drm_object_attach_property(&connector->base,
-                                     config->dpms_property, 0);
-
-       if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
-               drm_object_attach_property(&connector->base, config->prop_crtc_id, 0);
-       }
-
-       connector->debugfs_entry = NULL;
-
-out_put:
-       if (ret)
-               drm_mode_object_unregister(dev, &connector->base);
-
-out_unlock:
-       drm_modeset_unlock_all(dev);
+               return ret;
 
-       return ret;
-}
-EXPORT_SYMBOL(drm_connector_init);
+       prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
+                                       "type", drm_plane_type_enum_list,
+                                       ARRAY_SIZE(drm_plane_type_enum_list));
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.plane_type_property = prop;
 
-/**
- * drm_connector_cleanup - cleans up an initialised connector
- * @connector: connector to cleanup
- *
- * Cleans up the connector but doesn't free the object.
- */
-void drm_connector_cleanup(struct drm_connector *connector)
-{
-       struct drm_device *dev = connector->dev;
-       struct drm_display_mode *mode, *t;
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "SRC_X", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_src_x = prop;
 
-       list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
-               drm_mode_remove(connector, mode);
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "SRC_Y", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_src_y = prop;
 
-       list_for_each_entry_safe(mode, t, &connector->modes, head)
-               drm_mode_remove(connector, mode);
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "SRC_W", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_src_w = prop;
 
-       ida_remove(&drm_connector_enum_list[connector->connector_type].ida,
-                  connector->connector_type_id);
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "SRC_H", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_src_h = prop;
 
-       ida_remove(&dev->mode_config.connector_ida,
-                  connector->connector_id);
+       prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_X", INT_MIN, INT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_x = prop;
 
-       kfree(connector->display_info.bus_formats);
-       drm_mode_object_unregister(dev, &connector->base);
-       kfree(connector->name);
-       connector->name = NULL;
-       list_del(&connector->head);
-       dev->mode_config.num_connector--;
+       prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_Y", INT_MIN, INT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_y = prop;
 
-       WARN_ON(connector->state && !connector->funcs->atomic_destroy_state);
-       if (connector->state && connector->funcs->atomic_destroy_state)
-               connector->funcs->atomic_destroy_state(connector,
-                                                      connector->state);
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_W", 0, INT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_w = prop;
 
-       memset(connector, 0, sizeof(*connector));
-}
-EXPORT_SYMBOL(drm_connector_cleanup);
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_H", 0, INT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_h = prop;
 
-/**
- * drm_connector_index - find the index of a registered connector
- * @connector: connector to find index for
- *
- * Given a registered connector, return the index of that connector within a DRM
- * device's list of connectors.
- */
-unsigned int drm_connector_index(struct drm_connector *connector)
-{
-       unsigned int index = 0;
-       struct drm_connector *tmp;
-       struct drm_mode_config *config = &connector->dev->mode_config;
+       prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
+                       "FB_ID", DRM_MODE_OBJECT_FB);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_fb_id = prop;
 
-       WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));
+       prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_ID", DRM_MODE_OBJECT_CRTC);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_id = prop;
 
-       drm_for_each_connector(tmp, connector->dev) {
-               if (tmp == connector)
-                       return index;
+       prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC,
+                       "ACTIVE");
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_active = prop;
 
-               index++;
-       }
+       prop = drm_property_create(dev,
+                       DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB,
+                       "MODE_ID", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_mode_id = prop;
 
-       BUG();
-}
-EXPORT_SYMBOL(drm_connector_index);
+       prop = drm_property_create(dev,
+                       DRM_MODE_PROP_BLOB,
+                       "DEGAMMA_LUT", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.degamma_lut_property = prop;
 
-/**
- * drm_connector_register - register a connector
- * @connector: the connector to register
- *
- * Register userspace interfaces for a connector
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_connector_register(struct drm_connector *connector)
-{
-       int ret;
+       prop = drm_property_create_range(dev,
+                       DRM_MODE_PROP_IMMUTABLE,
+                       "DEGAMMA_LUT_SIZE", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.degamma_lut_size_property = prop;
 
-       ret = drm_sysfs_connector_add(connector);
-       if (ret)
-               return ret;
+       prop = drm_property_create(dev,
+                       DRM_MODE_PROP_BLOB,
+                       "CTM", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.ctm_property = prop;
 
-       ret = drm_debugfs_connector_add(connector);
-       if (ret) {
-               drm_sysfs_connector_remove(connector);
-               return ret;
-       }
+       prop = drm_property_create(dev,
+                       DRM_MODE_PROP_BLOB,
+                       "GAMMA_LUT", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.gamma_lut_property = prop;
 
-       drm_mode_object_register(connector->dev, &connector->base);
+       prop = drm_property_create_range(dev,
+                       DRM_MODE_PROP_IMMUTABLE,
+                       "GAMMA_LUT_SIZE", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.gamma_lut_size_property = prop;
 
        return 0;
 }
-EXPORT_SYMBOL(drm_connector_register);
-
-/**
- * drm_connector_unregister - unregister a connector
- * @connector: the connector to unregister
- *
- * Unregister userspace interfaces for a connector
- */
-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);
 
 /**
- * drm_connector_register_all - register all connectors
- * @dev: drm device
+ * drm_mode_getresources - get graphics configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
- * This function registers all connectors in sysfs and other places so that
- * userspace can start to access them. Drivers can call it after calling
- * drm_dev_register() to complete the device registration, if they don't call
- * drm_connector_register() on each connector individually.
+ * Construct a set of configuration description structures and return
+ * them to the user, including CRTC, connector and framebuffer configuration.
  *
- * When a device is unplugged and should be removed from userspace access,
- * call drm_connector_unregister_all(), which is the inverse of this
- * function.
+ * Called by the user via ioctl.
  *
  * Returns:
- * Zero on success, error code on failure.
- */
-int drm_connector_register_all(struct drm_device *dev)
-{
-       struct drm_connector *connector;
-       int ret;
-
-       mutex_lock(&dev->mode_config.mutex);
-
-       drm_for_each_connector(connector, dev) {
-               ret = drm_connector_register(connector);
-               if (ret)
-                       goto err;
-       }
-
-       mutex_unlock(&dev->mode_config.mutex);
-
-       return 0;
-
-err:
-       mutex_unlock(&dev->mode_config.mutex);
-       drm_connector_unregister_all(dev);
-       return ret;
-}
-EXPORT_SYMBOL(drm_connector_register_all);
-
-/**
- * drm_connector_unregister_all - unregister connector userspace interfaces
- * @dev: drm device
- *
- * This functions unregisters all connectors from sysfs and other places so
- * that userspace can no longer access them. Drivers should call this as the
- * first step tearing down the device instace, or when the underlying
- * physical device disappeared (e.g. USB unplug), right before calling
- * drm_dev_unregister().
+ * Zero on success, negative errno on failure.
  */
-void drm_connector_unregister_all(struct drm_device *dev)
+int drm_mode_getresources(struct drm_device *dev, void *data,
+                         struct drm_file *file_priv)
 {
+       struct drm_mode_card_res *card_res = data;
+       struct list_head *lh;
+       struct drm_framebuffer *fb;
        struct drm_connector *connector;
+       struct drm_crtc *crtc;
+       struct drm_encoder *encoder;
+       int ret = 0;
+       int connector_count = 0;
+       int crtc_count = 0;
+       int fb_count = 0;
+       int encoder_count = 0;
+       int copied = 0;
+       uint32_t __user *fb_id;
+       uint32_t __user *crtc_id;
+       uint32_t __user *connector_id;
+       uint32_t __user *encoder_id;
 
-       /* FIXME: taking the mode config mutex ends up in a clash with sysfs */
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
-               drm_connector_unregister(connector);
-}
-EXPORT_SYMBOL(drm_connector_unregister_all);
-
-/**
- * drm_encoder_init - Init a preallocated encoder
- * @dev: drm device
- * @encoder: the encoder to init
- * @funcs: callbacks for this encoder
- * @encoder_type: user visible type of the encoder
- * @name: printf style format string for the encoder name, or NULL for default name
- *
- * Initialises a preallocated encoder. Encoder should be
- * subclassed as part of driver encoder objects.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_encoder_init(struct drm_device *dev,
-                     struct drm_encoder *encoder,
-                     const struct drm_encoder_funcs *funcs,
-                     int encoder_type, const char *name, ...)
-{
-       int ret;
-
-       drm_modeset_lock_all(dev);
-
-       ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
-       if (ret)
-               goto out_unlock;
-
-       encoder->dev = dev;
-       encoder->encoder_type = encoder_type;
-       encoder->funcs = funcs;
-       if (name) {
-               va_list ap;
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
 
-               va_start(ap, name);
-               encoder->name = kvasprintf(GFP_KERNEL, name, ap);
-               va_end(ap);
-       } else {
-               encoder->name = kasprintf(GFP_KERNEL, "%s-%d",
-                                         drm_encoder_enum_list[encoder_type].name,
-                                         encoder->base.id);
-       }
-       if (!encoder->name) {
-               ret = -ENOMEM;
-               goto out_put;
-       }
 
-       list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
-       dev->mode_config.num_encoder++;
+       mutex_lock(&file_priv->fbs_lock);
+       /*
+        * For the non-control nodes we need to limit the list of resources
+        * by IDs in the group list for this node
+        */
+       list_for_each(lh, &file_priv->fbs)
+               fb_count++;
 
-out_put:
-       if (ret)
-               drm_mode_object_unregister(dev, &encoder->base);
-
-out_unlock:
-       drm_modeset_unlock_all(dev);
-
-       return ret;
-}
-EXPORT_SYMBOL(drm_encoder_init);
-
-/**
- * drm_encoder_index - find the index of a registered encoder
- * @encoder: encoder to find index for
- *
- * Given a registered encoder, return the index of that encoder within a DRM
- * device's list of encoders.
- */
-unsigned int drm_encoder_index(struct drm_encoder *encoder)
-{
-       unsigned int index = 0;
-       struct drm_encoder *tmp;
-
-       drm_for_each_encoder(tmp, encoder->dev) {
-               if (tmp == encoder)
-                       return index;
-
-               index++;
-       }
-
-       BUG();
-}
-EXPORT_SYMBOL(drm_encoder_index);
-
-/**
- * drm_encoder_cleanup - cleans up an initialised encoder
- * @encoder: encoder to cleanup
- *
- * Cleans up the encoder but doesn't free the object.
- */
-void drm_encoder_cleanup(struct drm_encoder *encoder)
-{
-       struct drm_device *dev = encoder->dev;
-
-       drm_modeset_lock_all(dev);
-       drm_mode_object_unregister(dev, &encoder->base);
-       kfree(encoder->name);
-       list_del(&encoder->head);
-       dev->mode_config.num_encoder--;
-       drm_modeset_unlock_all(dev);
-
-       memset(encoder, 0, sizeof(*encoder));
-}
-EXPORT_SYMBOL(drm_encoder_cleanup);
-
-static unsigned int drm_num_planes(struct drm_device *dev)
-{
-       unsigned int num = 0;
-       struct drm_plane *tmp;
-
-       drm_for_each_plane(tmp, dev) {
-               num++;
-       }
-
-       return num;
-}
-
-/**
- * drm_universal_plane_init - Initialize a new universal plane object
- * @dev: DRM device
- * @plane: plane object to init
- * @possible_crtcs: bitmask of possible CRTCs
- * @funcs: callbacks for the new plane
- * @formats: array of supported formats (%DRM_FORMAT_*)
- * @format_count: number of elements in @formats
- * @type: type of plane (overlay, primary, cursor)
- * @name: printf style format string for the plane name, or NULL for default name
- *
- * Initializes a plane object of type @type.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
-                            unsigned long possible_crtcs,
-                            const struct drm_plane_funcs *funcs,
-                            const uint32_t *formats, unsigned int format_count,
-                            enum drm_plane_type type,
-                            const char *name, ...)
-{
-       struct drm_mode_config *config = &dev->mode_config;
-       int ret;
-
-       ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
-       if (ret)
-               return ret;
-
-       drm_modeset_lock_init(&plane->mutex);
-
-       plane->base.properties = &plane->properties;
-       plane->dev = dev;
-       plane->funcs = funcs;
-       plane->format_types = kmalloc(format_count * sizeof(uint32_t),
-                                     M_DRM, M_WAITOK);
-       if (!plane->format_types) {
-               DRM_DEBUG_KMS("out of memory when allocating plane\n");
-               drm_mode_object_unregister(dev, &plane->base);
-               return -ENOMEM;
-       }
-
-       if (name) {
-               va_list ap;
-
-               va_start(ap, name);
-               plane->name = kvasprintf(GFP_KERNEL, name, ap);
-               va_end(ap);
-       } else {
-               plane->name = kasprintf(GFP_KERNEL, "plane-%d",
-                                       drm_num_planes(dev));
-       }
-       if (!plane->name) {
-               kfree(plane->format_types);
-               drm_mode_object_unregister(dev, &plane->base);
-               return -ENOMEM;
-       }
-
-       memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
-       plane->format_count = format_count;
-       plane->possible_crtcs = possible_crtcs;
-       plane->type = type;
-
-       list_add_tail(&plane->head, &config->plane_list);
-       config->num_total_plane++;
-       if (plane->type == DRM_PLANE_TYPE_OVERLAY)
-               config->num_overlay_plane++;
-
-       drm_object_attach_property(&plane->base,
-                                  config->plane_type_property,
-                                  plane->type);
-
-       if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
-               drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
-               drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
-               drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
-               drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
-               drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
-               drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
-               drm_object_attach_property(&plane->base, config->prop_src_x, 0);
-               drm_object_attach_property(&plane->base, config->prop_src_y, 0);
-               drm_object_attach_property(&plane->base, config->prop_src_w, 0);
-               drm_object_attach_property(&plane->base, config->prop_src_h, 0);
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_universal_plane_init);
-
-/**
- * drm_plane_init - Initialize a legacy plane
- * @dev: DRM device
- * @plane: plane object to init
- * @possible_crtcs: bitmask of possible CRTCs
- * @funcs: callbacks for the new plane
- * @formats: array of supported formats (%DRM_FORMAT_*)
- * @format_count: number of elements in @formats
- * @is_primary: plane type (primary vs overlay)
- *
- * Legacy API to initialize a DRM plane.
- *
- * New drivers should call drm_universal_plane_init() instead.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
-                  unsigned long possible_crtcs,
-                  const struct drm_plane_funcs *funcs,
-                  const uint32_t *formats, unsigned int format_count,
-                  bool is_primary)
-{
-       enum drm_plane_type type;
-
-       type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
-       return drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
-                                       formats, format_count, type, NULL);
-}
-EXPORT_SYMBOL(drm_plane_init);
-
-/**
- * drm_plane_cleanup - Clean up the core plane usage
- * @plane: plane to cleanup
- *
- * This function cleans up @plane and removes it from the DRM mode setting
- * core. Note that the function does *not* free the plane structure itself,
- * this is the responsibility of the caller.
- */
-void drm_plane_cleanup(struct drm_plane *plane)
-{
-       struct drm_device *dev = plane->dev;
-
-       drm_modeset_lock_all(dev);
-       kfree(plane->format_types);
-       drm_mode_object_unregister(dev, &plane->base);
-
-       BUG_ON(list_empty(&plane->head));
-
-       list_del(&plane->head);
-       dev->mode_config.num_total_plane--;
-       if (plane->type == DRM_PLANE_TYPE_OVERLAY)
-               dev->mode_config.num_overlay_plane--;
-       drm_modeset_unlock_all(dev);
-
-       WARN_ON(plane->state && !plane->funcs->atomic_destroy_state);
-       if (plane->state && plane->funcs->atomic_destroy_state)
-               plane->funcs->atomic_destroy_state(plane, plane->state);
-
-       kfree(plane->name);
-
-       memset(plane, 0, sizeof(*plane));
-}
-EXPORT_SYMBOL(drm_plane_cleanup);
-
-/**
- * drm_plane_index - find the index of a registered plane
- * @plane: plane to find index for
- *
- * Given a registered plane, return the index of that CRTC within a DRM
- * device's list of planes.
- */
-unsigned int drm_plane_index(struct drm_plane *plane)
-{
-       unsigned int index = 0;
-       struct drm_plane *tmp;
-
-       drm_for_each_plane(tmp, plane->dev) {
-               if (tmp == plane)
-                       return index;
-
-               index++;
-       }
-
-       BUG();
-}
-EXPORT_SYMBOL(drm_plane_index);
-
-/**
- * drm_plane_from_index - find the registered plane at an index
- * @dev: DRM device
- * @idx: index of registered plane to find for
- *
- * Given a plane index, return the registered plane from DRM device's
- * list of planes with matching index.
- */
-struct drm_plane *
-drm_plane_from_index(struct drm_device *dev, int idx)
-{
-       struct drm_plane *plane;
-       unsigned int i = 0;
-
-       drm_for_each_plane(plane, dev) {
-               if (i == idx)
-                       return plane;
-               i++;
-       }
-       return NULL;
-}
-EXPORT_SYMBOL(drm_plane_from_index);
-
-/**
- * drm_plane_force_disable - Forcibly disable a plane
- * @plane: plane to disable
- *
- * Forces the plane to be disabled.
- *
- * Used when the plane's current framebuffer is destroyed,
- * and when restoring fbdev mode.
- */
-void drm_plane_force_disable(struct drm_plane *plane)
-{
-       int ret;
-
-       if (!plane->fb)
-               return;
-
-       plane->old_fb = plane->fb;
-       ret = plane->funcs->disable_plane(plane);
-       if (ret) {
-               DRM_ERROR("failed to disable plane with busy fb\n");
-               plane->old_fb = NULL;
-               return;
-       }
-       /* disconnect the plane from the fb and crtc: */
-       drm_framebuffer_unreference(plane->old_fb);
-       plane->old_fb = NULL;
-       plane->fb = NULL;
-       plane->crtc = NULL;
-}
-EXPORT_SYMBOL(drm_plane_force_disable);
-
-static int drm_mode_create_standard_properties(struct drm_device *dev)
-{
-       struct drm_property *prop;
-
-       /*
-        * Standard properties (apply to all connectors)
-        */
-       prop = drm_property_create(dev, DRM_MODE_PROP_BLOB |
-                                  DRM_MODE_PROP_IMMUTABLE,
-                                  "EDID", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.edid_property = prop;
-
-       prop = drm_property_create_enum(dev, 0,
-                                  "DPMS", drm_dpms_enum_list,
-                                  ARRAY_SIZE(drm_dpms_enum_list));
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.dpms_property = prop;
-
-       prop = drm_property_create(dev,
-                                  DRM_MODE_PROP_BLOB |
-                                  DRM_MODE_PROP_IMMUTABLE,
-                                  "PATH", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.path_property = prop;
-
-       prop = drm_property_create(dev,
-                                  DRM_MODE_PROP_BLOB |
-                                  DRM_MODE_PROP_IMMUTABLE,
-                                  "TILE", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.tile_property = prop;
-
-       prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
-                                       "type", drm_plane_type_enum_list,
-                                       ARRAY_SIZE(drm_plane_type_enum_list));
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.plane_type_property = prop;
-
-       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "SRC_X", 0, UINT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_src_x = prop;
-
-       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "SRC_Y", 0, UINT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_src_y = prop;
-
-       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "SRC_W", 0, UINT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_src_w = prop;
-
-       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "SRC_H", 0, UINT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_src_h = prop;
-
-       prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "CRTC_X", INT_MIN, INT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_crtc_x = prop;
-
-       prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "CRTC_Y", INT_MIN, INT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_crtc_y = prop;
-
-       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "CRTC_W", 0, INT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_crtc_w = prop;
-
-       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "CRTC_H", 0, INT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_crtc_h = prop;
-
-       prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
-                       "FB_ID", DRM_MODE_OBJECT_FB);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_fb_id = prop;
-
-       prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
-                       "CRTC_ID", DRM_MODE_OBJECT_CRTC);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_crtc_id = prop;
-
-       prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC,
-                       "ACTIVE");
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_active = prop;
-
-       prop = drm_property_create(dev,
-                       DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB,
-                       "MODE_ID", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_mode_id = prop;
-
-       prop = drm_property_create(dev,
-                       DRM_MODE_PROP_BLOB,
-                       "DEGAMMA_LUT", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.degamma_lut_property = prop;
-
-       prop = drm_property_create_range(dev,
-                       DRM_MODE_PROP_IMMUTABLE,
-                       "DEGAMMA_LUT_SIZE", 0, UINT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.degamma_lut_size_property = prop;
-
-       prop = drm_property_create(dev,
-                       DRM_MODE_PROP_BLOB,
-                       "CTM", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.ctm_property = prop;
-
-       prop = drm_property_create(dev,
-                       DRM_MODE_PROP_BLOB,
-                       "GAMMA_LUT", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.gamma_lut_property = prop;
-
-       prop = drm_property_create_range(dev,
-                       DRM_MODE_PROP_IMMUTABLE,
-                       "GAMMA_LUT_SIZE", 0, UINT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.gamma_lut_size_property = prop;
-
-       return 0;
-}
-
-/**
- * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
- * @dev: DRM device
- *
- * Called by a driver the first time a DVI-I connector is made.
- */
-int drm_mode_create_dvi_i_properties(struct drm_device *dev)
-{
-       struct drm_property *dvi_i_selector;
-       struct drm_property *dvi_i_subconnector;
-
-       if (dev->mode_config.dvi_i_select_subconnector_property)
-               return 0;
-
-       dvi_i_selector =
-               drm_property_create_enum(dev, 0,
-                                   "select subconnector",
-                                   drm_dvi_i_select_enum_list,
-                                   ARRAY_SIZE(drm_dvi_i_select_enum_list));
-       dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector;
-
-       dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
-                                   "subconnector",
-                                   drm_dvi_i_subconnector_enum_list,
-                                   ARRAY_SIZE(drm_dvi_i_subconnector_enum_list));
-       dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
-
-/**
- * drm_create_tv_properties - create TV specific connector properties
- * @dev: DRM device
- * @num_modes: number of different TV formats (modes) supported
- * @modes: array of pointers to strings containing name of each format
- *
- * Called by a driver's TV initialization routine, this function creates
- * the TV specific connector properties for a given device.  Caller is
- * responsible for allocating a list of format names and passing them to
- * this routine.
- */
-int drm_mode_create_tv_properties(struct drm_device *dev,
-                                 unsigned int num_modes,
-                                 const char * const modes[])
-{
-       struct drm_property *tv_selector;
-       struct drm_property *tv_subconnector;
-       unsigned int i;
-
-       if (dev->mode_config.tv_select_subconnector_property)
-               return 0;
-
-       /*
-        * Basic connector properties
-        */
-       tv_selector = drm_property_create_enum(dev, 0,
-                                         "select subconnector",
-                                         drm_tv_select_enum_list,
-                                         ARRAY_SIZE(drm_tv_select_enum_list));
-       if (!tv_selector)
-               goto nomem;
-
-       dev->mode_config.tv_select_subconnector_property = tv_selector;
-
-       tv_subconnector =
-               drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
-                                   "subconnector",
-                                   drm_tv_subconnector_enum_list,
-                                   ARRAY_SIZE(drm_tv_subconnector_enum_list));
-       if (!tv_subconnector)
-               goto nomem;
-       dev->mode_config.tv_subconnector_property = tv_subconnector;
-
-       /*
-        * Other, TV specific properties: margins & TV modes.
-        */
-       dev->mode_config.tv_left_margin_property =
-               drm_property_create_range(dev, 0, "left margin", 0, 100);
-       if (!dev->mode_config.tv_left_margin_property)
-               goto nomem;
-
-       dev->mode_config.tv_right_margin_property =
-               drm_property_create_range(dev, 0, "right margin", 0, 100);
-       if (!dev->mode_config.tv_right_margin_property)
-               goto nomem;
-
-       dev->mode_config.tv_top_margin_property =
-               drm_property_create_range(dev, 0, "top margin", 0, 100);
-       if (!dev->mode_config.tv_top_margin_property)
-               goto nomem;
-
-       dev->mode_config.tv_bottom_margin_property =
-               drm_property_create_range(dev, 0, "bottom margin", 0, 100);
-       if (!dev->mode_config.tv_bottom_margin_property)
-               goto nomem;
-
-       dev->mode_config.tv_mode_property =
-               drm_property_create(dev, DRM_MODE_PROP_ENUM,
-                                   "mode", num_modes);
-       if (!dev->mode_config.tv_mode_property)
-               goto nomem;
-
-       for (i = 0; i < num_modes; i++)
-               drm_property_add_enum(dev->mode_config.tv_mode_property, i,
-                                     i, modes[i]);
-
-       dev->mode_config.tv_brightness_property =
-               drm_property_create_range(dev, 0, "brightness", 0, 100);
-       if (!dev->mode_config.tv_brightness_property)
-               goto nomem;
-
-       dev->mode_config.tv_contrast_property =
-               drm_property_create_range(dev, 0, "contrast", 0, 100);
-       if (!dev->mode_config.tv_contrast_property)
-               goto nomem;
-
-       dev->mode_config.tv_flicker_reduction_property =
-               drm_property_create_range(dev, 0, "flicker reduction", 0, 100);
-       if (!dev->mode_config.tv_flicker_reduction_property)
-               goto nomem;
-
-       dev->mode_config.tv_overscan_property =
-               drm_property_create_range(dev, 0, "overscan", 0, 100);
-       if (!dev->mode_config.tv_overscan_property)
-               goto nomem;
-
-       dev->mode_config.tv_saturation_property =
-               drm_property_create_range(dev, 0, "saturation", 0, 100);
-       if (!dev->mode_config.tv_saturation_property)
-               goto nomem;
-
-       dev->mode_config.tv_hue_property =
-               drm_property_create_range(dev, 0, "hue", 0, 100);
-       if (!dev->mode_config.tv_hue_property)
-               goto nomem;
-
-       return 0;
-nomem:
-       return -ENOMEM;
-}
-EXPORT_SYMBOL(drm_mode_create_tv_properties);
-
-/**
- * drm_mode_create_scaling_mode_property - create scaling mode property
- * @dev: DRM device
- *
- * Called by a driver the first time it's needed, must be attached to desired
- * connectors.
- */
-int drm_mode_create_scaling_mode_property(struct drm_device *dev)
-{
-       struct drm_property *scaling_mode;
-
-       if (dev->mode_config.scaling_mode_property)
-               return 0;
-
-       scaling_mode =
-               drm_property_create_enum(dev, 0, "scaling mode",
-                               drm_scaling_mode_enum_list,
-                                   ARRAY_SIZE(drm_scaling_mode_enum_list));
-
-       dev->mode_config.scaling_mode_property = scaling_mode;
-
-       return 0;
-}
-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, negative 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
- *
- * Called by a driver the first time it's needed, must be attached to desired
- * connectors.
- */
-int drm_mode_create_dirty_info_property(struct drm_device *dev)
-{
-       struct drm_property *dirty_info;
-
-       if (dev->mode_config.dirty_info_property)
-               return 0;
-
-       dirty_info =
-               drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
-                                   "dirty",
-                                   drm_dirty_info_enum_list,
-                                   ARRAY_SIZE(drm_dirty_info_enum_list));
-       dev->mode_config.dirty_info_property = dirty_info;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_mode_create_dirty_info_property);
-
-/**
- * drm_mode_create_suggested_offset_properties - create suggests offset properties
- * @dev: DRM device
- *
- * Create the the suggested x/y offset property for connectors.
- */
-int drm_mode_create_suggested_offset_properties(struct drm_device *dev)
-{
-       if (dev->mode_config.suggested_x_property && dev->mode_config.suggested_y_property)
-               return 0;
-
-       dev->mode_config.suggested_x_property =
-               drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested X", 0, 0xffffffff);
-
-       dev->mode_config.suggested_y_property =
-               drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested Y", 0, 0xffffffff);
-
-       if (dev->mode_config.suggested_x_property == NULL ||
-           dev->mode_config.suggested_y_property == NULL)
-               return -ENOMEM;
-       return 0;
-}
-EXPORT_SYMBOL(drm_mode_create_suggested_offset_properties);
-
-/**
- * drm_mode_getresources - get graphics configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Construct a set of configuration description structures and return
- * them to the user, including CRTC, connector and framebuffer configuration.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getresources(struct drm_device *dev, void *data,
-                         struct drm_file *file_priv)
-{
-       struct drm_mode_card_res *card_res = data;
-       struct list_head *lh;
-       struct drm_framebuffer *fb;
-       struct drm_connector *connector;
-       struct drm_crtc *crtc;
-       struct drm_encoder *encoder;
-       int ret = 0;
-       int connector_count = 0;
-       int crtc_count = 0;
-       int fb_count = 0;
-       int encoder_count = 0;
-       int copied = 0;
-       uint32_t __user *fb_id;
-       uint32_t __user *crtc_id;
-       uint32_t __user *connector_id;
-       uint32_t __user *encoder_id;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-
-       mutex_lock(&file_priv->fbs_lock);
-       /*
-        * For the non-control nodes we need to limit the list of resources
-        * by IDs in the group list for this node
-        */
-       list_for_each(lh, &file_priv->fbs)
-               fb_count++;
-
-       /* handle this in 4 parts */
-       /* FBs */
-       if (card_res->count_fbs >= fb_count) {
-               copied = 0;
-               fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr;
-               list_for_each_entry(fb, &file_priv->fbs, filp_head) {
-                       if (put_user(fb->base.id, fb_id + copied)) {
-                               mutex_unlock(&file_priv->fbs_lock);
-                               return -EFAULT;
-                       }
-                       copied++;
-               }
-       }
-       card_res->count_fbs = fb_count;
-       mutex_unlock(&file_priv->fbs_lock);
-
-       /* mode_config.mutex protects the connector list against e.g. DP MST
-        * connector hot-adding. CRTC/Plane lists are invariant. */
-       mutex_lock(&dev->mode_config.mutex);
-       drm_for_each_crtc(crtc, dev)
-               crtc_count++;
-
-       drm_for_each_connector(connector, dev)
-               connector_count++;
-
-       drm_for_each_encoder(encoder, dev)
-               encoder_count++;
-
-       card_res->max_height = dev->mode_config.max_height;
-       card_res->min_height = dev->mode_config.min_height;
-       card_res->max_width = dev->mode_config.max_width;
-       card_res->min_width = dev->mode_config.min_width;
-
-       /* CRTCs */
-       if (card_res->count_crtcs >= crtc_count) {
-               copied = 0;
-               crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
-               drm_for_each_crtc(crtc, dev) {
-                       if (put_user(crtc->base.id, crtc_id + copied)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       copied++;
-               }
-       }
-       card_res->count_crtcs = crtc_count;
-
-       /* Encoders */
-       if (card_res->count_encoders >= encoder_count) {
-               copied = 0;
-               encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
-               drm_for_each_encoder(encoder, dev) {
-                       if (put_user(encoder->base.id, encoder_id +
-                                    copied)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       copied++;
-               }
-       }
-       card_res->count_encoders = encoder_count;
-
-       /* Connectors */
-       if (card_res->count_connectors >= connector_count) {
-               copied = 0;
-               connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
-               drm_for_each_connector(connector, dev) {
-                       if (put_user(connector->base.id,
-                                    connector_id + copied)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       copied++;
-               }
-       }
-       card_res->count_connectors = connector_count;
-
-out:
-       mutex_unlock(&dev->mode_config.mutex);
-       return ret;
-}
-
-/**
- * drm_mode_getcrtc - get CRTC configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Construct a CRTC configuration structure to return to the user.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getcrtc(struct drm_device *dev,
-                    void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_crtc *crtc_resp = data;
-       struct drm_crtc *crtc;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
-       if (!crtc)
-               return -ENOENT;
-
-       drm_modeset_lock_crtc(crtc, crtc->primary);
-       crtc_resp->gamma_size = crtc->gamma_size;
-       if (crtc->primary->fb)
-               crtc_resp->fb_id = crtc->primary->fb->base.id;
-       else
-               crtc_resp->fb_id = 0;
-
-       if (crtc->state) {
-               crtc_resp->x = crtc->primary->state->src_x >> 16;
-               crtc_resp->y = crtc->primary->state->src_y >> 16;
-               if (crtc->state->enable) {
-                       drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->state->mode);
-                       crtc_resp->mode_valid = 1;
-
-               } else {
-                       crtc_resp->mode_valid = 0;
-               }
-       } else {
-               crtc_resp->x = crtc->x;
-               crtc_resp->y = crtc->y;
-               if (crtc->enabled) {
-                       drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->mode);
-                       crtc_resp->mode_valid = 1;
-
-               } else {
-                       crtc_resp->mode_valid = 0;
-               }
-       }
-       drm_modeset_unlock_crtc(crtc);
-
-       return 0;
-}
-
-static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
-                                        const struct drm_file *file_priv)
-{
-       /*
-        * If user-space hasn't configured the driver to expose the stereo 3D
-        * modes, don't expose them.
-        */
-       if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode))
-               return false;
-
-       return true;
-}
-
-static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector)
-{
-       /* For atomic drivers only state objects are synchronously updated and
-        * protected by modeset locks, so check those first. */
-       if (connector->state)
-               return connector->state->best_encoder;
-       return connector->encoder;
-}
-
-/* helper for getconnector and getproperties ioctls */
-static int get_properties(struct drm_mode_object *obj, bool atomic,
-               uint32_t __user *prop_ptr, uint64_t __user *prop_values,
-               uint32_t *arg_count_props)
-{
-       int props_count;
-       int i, ret, copied;
-
-       props_count = obj->properties->count;
-       if (!atomic)
-               props_count -= obj->properties->atomic_count;
-
-       if ((*arg_count_props >= props_count) && props_count) {
-               for (i = 0, copied = 0; copied < props_count; i++) {
-                       struct drm_property *prop = obj->properties->properties[i];
-                       uint64_t val;
-
-                       if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic)
-                               continue;
-
-                       ret = drm_object_property_get_value(obj, prop, &val);
-                       if (ret)
-                               return ret;
-
-                       if (put_user(prop->base.id, prop_ptr + copied))
-                               return -EFAULT;
-
-                       if (put_user(val, prop_values + copied))
-                               return -EFAULT;
-
-                       copied++;
-               }
-       }
-       *arg_count_props = props_count;
-
-       return 0;
-}
-
-/**
- * drm_mode_getconnector - get connector configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Construct a connector configuration structure to return to the user.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getconnector(struct drm_device *dev, void *data,
-                         struct drm_file *file_priv)
-{
-       struct drm_mode_get_connector *out_resp = data;
-       struct drm_connector *connector;
-       struct drm_encoder *encoder;
-       struct drm_display_mode *mode;
-       int mode_count = 0;
-       int encoders_count = 0;
-       int ret = 0;
-       int copied = 0;
-       int i;
-       struct drm_mode_modeinfo u_mode;
-       struct drm_mode_modeinfo __user *mode_ptr;
-       uint32_t __user *encoder_ptr;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
-
-       mutex_lock(&dev->mode_config.mutex);
-
-       connector = drm_connector_lookup(dev, out_resp->connector_id);
-       if (!connector) {
-               ret = -ENOENT;
-               goto out_unlock;
-       }
-
-       for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++)
-               if (connector->encoder_ids[i] != 0)
-                       encoders_count++;
-
-       if (out_resp->count_modes == 0) {
-               connector->funcs->fill_modes(connector,
-                                            dev->mode_config.max_width,
-                                            dev->mode_config.max_height);
-       }
-
-       /* delayed so we get modes regardless of pre-fill_modes state */
-       list_for_each_entry(mode, &connector->modes, head)
-               if (drm_mode_expose_to_userspace(mode, file_priv))
-                       mode_count++;
-
-       out_resp->connector_id = connector->base.id;
-       out_resp->connector_type = connector->connector_type;
-       out_resp->connector_type_id = connector->connector_type_id;
-       out_resp->mm_width = connector->display_info.width_mm;
-       out_resp->mm_height = connector->display_info.height_mm;
-       out_resp->subpixel = connector->display_info.subpixel_order;
-       out_resp->connection = connector->status;
-
-       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
-       encoder = drm_connector_get_encoder(connector);
-       if (encoder)
-               out_resp->encoder_id = encoder->base.id;
-       else
-               out_resp->encoder_id = 0;
-
-       /*
-        * This ioctl is called twice, once to determine how much space is
-        * needed, and the 2nd time to fill it.
-        */
-       if ((out_resp->count_modes >= mode_count) && mode_count) {
-               copied = 0;
-               mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr;
-               list_for_each_entry(mode, &connector->modes, head) {
-                       if (!drm_mode_expose_to_userspace(mode, file_priv))
-                               continue;
-
-                       drm_mode_convert_to_umode(&u_mode, mode);
-                       if (copy_to_user(mode_ptr + copied,
-                                        &u_mode, sizeof(u_mode))) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       copied++;
-               }
-       }
-       out_resp->count_modes = mode_count;
-
-       ret = get_properties(&connector->base, file_priv->atomic,
-                       (uint32_t __user *)(unsigned long)(out_resp->props_ptr),
-                       (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr),
-                       &out_resp->count_props);
-       if (ret)
-               goto out;
-
-       if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
-               copied = 0;
-               encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
-               for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
-                       if (connector->encoder_ids[i] != 0) {
-                               if (put_user(connector->encoder_ids[i],
-                                            encoder_ptr + copied)) {
-                                       ret = -EFAULT;
-                                       goto out;
-                               }
-                               copied++;
-                       }
-               }
-       }
-       out_resp->count_encoders = encoders_count;
-
-out:
-       drm_modeset_unlock(&dev->mode_config.connection_mutex);
-
-       drm_connector_unreference(connector);
-out_unlock:
-       mutex_unlock(&dev->mode_config.mutex);
-
-       return ret;
-}
-
-static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder)
-{
-       struct drm_connector *connector;
-       struct drm_device *dev = encoder->dev;
-       bool uses_atomic = false;
-
-       /* For atomic drivers only state objects are synchronously updated and
-        * protected by modeset locks, so check those first. */
-       drm_for_each_connector(connector, dev) {
-               if (!connector->state)
-                       continue;
-
-               uses_atomic = true;
-
-               if (connector->state->best_encoder != encoder)
-                       continue;
-
-               return connector->state->crtc;
-       }
-
-       /* Don't return stale data (e.g. pending async disable). */
-       if (uses_atomic)
-               return NULL;
-
-       return encoder->crtc;
-}
-
-/**
- * drm_mode_getencoder - get encoder configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Construct a encoder configuration structure to return to the user.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getencoder(struct drm_device *dev, void *data,
-                       struct drm_file *file_priv)
-{
-       struct drm_mode_get_encoder *enc_resp = data;
-       struct drm_encoder *encoder;
-       struct drm_crtc *crtc;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       encoder = drm_encoder_find(dev, enc_resp->encoder_id);
-       if (!encoder)
-               return -ENOENT;
-
-       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
-       crtc = drm_encoder_get_crtc(encoder);
-       if (crtc)
-               enc_resp->crtc_id = crtc->base.id;
-       else
-               enc_resp->crtc_id = 0;
-       drm_modeset_unlock(&dev->mode_config.connection_mutex);
-
-       enc_resp->encoder_type = encoder->encoder_type;
-       enc_resp->encoder_id = encoder->base.id;
-       enc_resp->possible_crtcs = encoder->possible_crtcs;
-       enc_resp->possible_clones = encoder->possible_clones;
-
-       return 0;
-}
-
-/**
- * drm_mode_getplane_res - enumerate all plane resources
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
- *
- * Construct a list of plane ids to return to the user.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getplane_res(struct drm_device *dev, void *data,
-                         struct drm_file *file_priv)
-{
-       struct drm_mode_get_plane_res *plane_resp = data;
-       struct drm_mode_config *config;
-       struct drm_plane *plane;
-       uint32_t __user *plane_ptr;
-       int copied = 0;
-       unsigned num_planes;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       config = &dev->mode_config;
-
-       if (file_priv->universal_planes)
-               num_planes = config->num_total_plane;
-       else
-               num_planes = config->num_overlay_plane;
-
-       /*
-        * This ioctl is called twice, once to determine how much space is
-        * needed, and the 2nd time to fill it.
-        */
-       if (num_planes &&
-           (plane_resp->count_planes >= num_planes)) {
-               plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr;
-
-               /* Plane lists are invariant, no locking needed. */
-               drm_for_each_plane(plane, dev) {
-                       /*
-                        * Unless userspace set the 'universal planes'
-                        * capability bit, only advertise overlays.
-                        */
-                       if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
-                           !file_priv->universal_planes)
-                               continue;
-
-                       if (put_user(plane->base.id, plane_ptr + copied))
-                               return -EFAULT;
-                       copied++;
-               }
-       }
-       plane_resp->count_planes = num_planes;
-
-       return 0;
-}
-
-/**
- * drm_mode_getplane - get plane configuration
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
- *
- * Construct a plane configuration structure to return to the user.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getplane(struct drm_device *dev, void *data,
-                     struct drm_file *file_priv)
-{
-       struct drm_mode_get_plane *plane_resp = data;
-       struct drm_plane *plane;
-       uint32_t __user *format_ptr;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       plane = drm_plane_find(dev, plane_resp->plane_id);
-       if (!plane)
-               return -ENOENT;
-
-       drm_modeset_lock(&plane->mutex, NULL);
-       if (plane->crtc)
-               plane_resp->crtc_id = plane->crtc->base.id;
-       else
-               plane_resp->crtc_id = 0;
-
-       if (plane->fb)
-               plane_resp->fb_id = plane->fb->base.id;
-       else
-               plane_resp->fb_id = 0;
-       drm_modeset_unlock(&plane->mutex);
-
-       plane_resp->plane_id = plane->base.id;
-       plane_resp->possible_crtcs = plane->possible_crtcs;
-       plane_resp->gamma_size = 0;
-
-       /*
-        * This ioctl is called twice, once to determine how much space is
-        * needed, and the 2nd time to fill it.
-        */
-       if (plane->format_count &&
-           (plane_resp->count_format_types >= plane->format_count)) {
-               format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr;
-               if (copy_to_user(format_ptr,
-                                plane->format_types,
-                                sizeof(uint32_t) * plane->format_count)) {
-                       return -EFAULT;
-               }
-       }
-       plane_resp->count_format_types = plane->format_count;
-
-       return 0;
-}
-
-/**
- * drm_plane_check_pixel_format - Check if the plane supports the pixel format
- * @plane: plane to check for format support
- * @format: the pixel format
- *
- * Returns:
- * Zero of @plane has @format in its list of supported pixel formats, -EINVAL
- * otherwise.
- */
-int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format)
-{
-       unsigned int i;
-
-       for (i = 0; i < plane->format_count; i++) {
-               if (format == plane->format_types[i])
-                       return 0;
-       }
-
-       return -EINVAL;
-}
-
-static int check_src_coords(uint32_t src_x, uint32_t src_y,
-                           uint32_t src_w, uint32_t src_h,
-                           const struct drm_framebuffer *fb)
-{
-       unsigned int fb_width, fb_height;
-
-       fb_width = fb->width << 16;
-       fb_height = fb->height << 16;
-
-       /* Make sure source coordinates are inside the fb. */
-       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",
-                             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);
-               return -ENOSPC;
-       }
-
-       return 0;
-}
-
-/*
- * setplane_internal - setplane handler for internal callers
- *
- * 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.
- *
- * src_{x,y,w,h} are provided in 16.16 fixed point format
- */
-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)
-{
-       int ret = 0;
-
-       /* No fb means shut it down */
-       if (!fb) {
-               plane->old_fb = plane->fb;
-               ret = plane->funcs->disable_plane(plane);
-               if (!ret) {
-                       plane->crtc = NULL;
-                       plane->fb = NULL;
-               } else {
-                       plane->old_fb = NULL;
-               }
-               goto out;
-       }
-
-       /* 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;
-       }
-
-       /* Check whether this plane supports the fb pixel format. */
-       ret = drm_plane_check_pixel_format(plane, fb->pixel_format);
-       if (ret) {
-               DRM_DEBUG_KMS("Invalid pixel format %s\n",
-                             drm_get_format_name(fb->pixel_format));
-               goto out;
-       }
-
-       /* Give drivers some help against integer overflows */
-       if (crtc_w > INT_MAX ||
-           crtc_x > INT_MAX - (int32_t) crtc_w ||
-           crtc_h > INT_MAX ||
-           crtc_y > INT_MAX - (int32_t) crtc_h) {
-               DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
-                             crtc_w, crtc_h, crtc_x, crtc_y);
-               ret = -ERANGE;
-               goto out;
-       }
-
-       ret = check_src_coords(src_x, src_y, src_w, src_h, fb);
-       if (ret)
-               goto out;
-
-       plane->old_fb = plane->fb;
-       ret = plane->funcs->update_plane(plane, crtc, fb,
-                                        crtc_x, crtc_y, crtc_w, crtc_h,
-                                        src_x, src_y, src_w, src_h);
-       if (!ret) {
-               plane->crtc = crtc;
-               plane->fb = fb;
-               fb = NULL;
-       } else {
-               plane->old_fb = NULL;
-       }
-
-out:
-       if (fb)
-               drm_framebuffer_unreference(fb);
-       if (plane->old_fb)
-               drm_framebuffer_unreference(plane->old_fb);
-       plane->old_fb = NULL;
-
-       return ret;
-}
-
-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)
-{
-       int ret;
-
-       drm_modeset_lock_all(plane->dev);
-       ret = __setplane_internal(plane, crtc, fb,
-                                 crtc_x, crtc_y, crtc_w, crtc_h,
-                                 src_x, src_y, src_w, src_h);
-       drm_modeset_unlock_all(plane->dev);
-
-       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, negative 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_plane *plane;
-       struct drm_crtc *crtc = NULL;
-       struct drm_framebuffer *fb = NULL;
-
-       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;
-       }
-
-       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;
-               }
-
-               crtc = drm_crtc_find(dev, plane_req->crtc_id);
-               if (!crtc) {
-                       DRM_DEBUG_KMS("Unknown crtc ID %d\n",
-                                     plane_req->crtc_id);
-                       return -ENOENT;
-               }
-       }
-
-       /*
-        * 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);
-}
-
-/**
- * drm_mode_set_config_internal - helper to call ->set_config
- * @set: modeset config to set
- *
- * This is a little helper to wrap internal calls to the ->set_config driver
- * interface. The only thing it adds is correct refcounting dance.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_set_config_internal(struct drm_mode_set *set)
-{
-       struct drm_crtc *crtc = set->crtc;
-       struct drm_framebuffer *fb;
-       struct drm_crtc *tmp;
-       int ret;
-
-       /*
-        * NOTE: ->set_config can also disable other crtcs (if we steal all
-        * connectors from it), hence we need to refcount the fbs across all
-        * crtcs. Atomic modeset will have saner semantics ...
-        */
-       drm_for_each_crtc(tmp, crtc->dev)
-               tmp->primary->old_fb = tmp->primary->fb;
-
-       fb = set->fb;
-
-       ret = crtc->funcs->set_config(set);
-       if (ret == 0) {
-               crtc->primary->crtc = crtc;
-               crtc->primary->fb = fb;
-       }
-
-       drm_for_each_crtc(tmp, crtc->dev) {
-               if (tmp->primary->fb)
-                       drm_framebuffer_reference(tmp->primary->fb);
-               if (tmp->primary->old_fb)
-                       drm_framebuffer_unreference(tmp->primary->old_fb);
-               tmp->primary->old_fb = NULL;
-       }
-
-       return ret;
-}
-EXPORT_SYMBOL(drm_mode_set_config_internal);
-
-/**
- * drm_crtc_get_hv_timing - Fetches hdisplay/vdisplay for given mode
- * @mode: mode to query
- * @hdisplay: hdisplay value to fill in
- * @vdisplay: vdisplay value to fill in
- *
- * The vdisplay value will be doubled if the specified mode is a stereo mode of
- * the appropriate layout.
- */
-void drm_crtc_get_hv_timing(const struct drm_display_mode *mode,
-                           int *hdisplay, int *vdisplay)
-{
-       struct drm_display_mode adjusted;
-
-       drm_mode_copy(&adjusted, mode);
-       drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY);
-       *hdisplay = adjusted.crtc_hdisplay;
-       *vdisplay = adjusted.crtc_vdisplay;
-}
-EXPORT_SYMBOL(drm_crtc_get_hv_timing);
-
-/**
- * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the
- *     CRTC viewport
- * @crtc: CRTC that framebuffer will be displayed on
- * @x: x panning
- * @y: y panning
- * @mode: mode that framebuffer will be displayed under
- * @fb: framebuffer to check size of
- */
-int drm_crtc_check_viewport(const struct drm_crtc *crtc,
-                           int x, int y,
-                           const struct drm_display_mode *mode,
-                           const struct drm_framebuffer *fb)
-
-{
-       int hdisplay, vdisplay;
-
-       drm_crtc_get_hv_timing(mode, &hdisplay, &vdisplay);
-
-       if (crtc->state &&
-           crtc->primary->state->rotation & (BIT(DRM_ROTATE_90) |
-                                             BIT(DRM_ROTATE_270)))
-               swap(hdisplay, vdisplay);
-
-       return check_src_coords(x << 16, y << 16,
-                               hdisplay << 16, vdisplay << 16, fb);
-}
-EXPORT_SYMBOL(drm_crtc_check_viewport);
-
-/**
- * drm_mode_setcrtc - set CRTC configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Build a new CRTC configuration based on user request.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_setcrtc(struct drm_device *dev, void *data,
-                    struct drm_file *file_priv)
-{
-       struct drm_mode_config *config = &dev->mode_config;
-       struct drm_mode_crtc *crtc_req = data;
-       struct drm_crtc *crtc;
-       struct drm_connector **connector_set = NULL, *connector;
-       struct drm_framebuffer *fb = NULL;
-       struct drm_display_mode *mode = NULL;
-       struct drm_mode_set set;
-       uint32_t __user *set_connectors_ptr;
-       int ret;
-       int i;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       /*
-        * Universal plane src offsets are only 16.16, prevent havoc for
-        * drivers using universal plane code internally.
-        */
-       if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000)
-               return -ERANGE;
-
-       drm_modeset_lock_all(dev);
-       crtc = drm_crtc_find(dev, crtc_req->crtc_id);
-       if (!crtc) {
-               DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
-               ret = -ENOENT;
-               goto out;
-       }
-       DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
-
-       if (crtc_req->mode_valid) {
-               /* If we have a mode we need a framebuffer. */
-               /* If we pass -1, set the mode with the currently bound fb */
-               if (crtc_req->fb_id == -1) {
-                       if (!crtc->primary->fb) {
-                               DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
-                               ret = -EINVAL;
-                               goto out;
-                       }
-                       fb = crtc->primary->fb;
-                       /* Make refcounting symmetric with the lookup path. */
-                       drm_framebuffer_reference(fb);
-               } else {
-                       fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
-                       if (!fb) {
-                               DRM_DEBUG_KMS("Unknown FB ID%d\n",
-                                               crtc_req->fb_id);
-                               ret = -ENOENT;
-                               goto out;
-                       }
-               }
-
-               mode = drm_mode_create(dev);
-               if (!mode) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-
-               ret = drm_mode_convert_umode(mode, &crtc_req->mode);
-               if (ret) {
-                       DRM_DEBUG_KMS("Invalid mode\n");
-                       goto out;
-               }
-
-               /*
-                * Check whether the primary plane supports the fb pixel format.
-                * Drivers not implementing the universal planes API use a
-                * default formats list provided by the DRM core which doesn't
-                * match real hardware capabilities. Skip the check in that
-                * case.
-                */
-               if (!crtc->primary->format_default) {
-                       ret = drm_plane_check_pixel_format(crtc->primary,
-                                                          fb->pixel_format);
-                       if (ret) {
-                               DRM_DEBUG_KMS("Invalid pixel format %s\n",
-                                       drm_get_format_name(fb->pixel_format));
-                               goto out;
-                       }
-               }
-
-               ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
-                                             mode, fb);
-               if (ret)
-                       goto out;
-
-       }
-
-       if (crtc_req->count_connectors == 0 && mode) {
-               DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
-               ret = -EINVAL;
-               goto out;
-       }
-
-       if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
-               DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
-                         crtc_req->count_connectors);
-               ret = -EINVAL;
-               goto out;
-       }
-
-       if (crtc_req->count_connectors > 0) {
-               u32 out_id;
-
-               /* Avoid unbounded kernel memory allocation */
-               if (crtc_req->count_connectors > config->num_connector) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               connector_set = kmalloc(crtc_req->count_connectors *
-                                       sizeof(struct drm_connector *),
-                                       M_DRM, M_WAITOK);
-               if (!connector_set) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-
-               for (i = 0; i < crtc_req->count_connectors; i++) {
-                       connector_set[i] = NULL;
-                       set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
-                       if (get_user(out_id, &set_connectors_ptr[i])) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-
-                       connector = drm_connector_lookup(dev, out_id);
-                       if (!connector) {
-                               DRM_DEBUG_KMS("Connector id %d unknown\n",
-                                               out_id);
-                               ret = -ENOENT;
-                               goto out;
-                       }
-                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-                                       connector->base.id,
-                                       connector->name);
-
-                       connector_set[i] = connector;
-               }
-       }
-
-       set.crtc = crtc;
-       set.x = crtc_req->x;
-       set.y = crtc_req->y;
-       set.mode = mode;
-       set.connectors = connector_set;
-       set.num_connectors = crtc_req->count_connectors;
-       set.fb = fb;
-       ret = drm_mode_set_config_internal(&set);
-
-out:
-       if (fb)
-               drm_framebuffer_unreference(fb);
-
-       if (connector_set) {
-               for (i = 0; i < crtc_req->count_connectors; i++) {
-                       if (connector_set[i])
-                               drm_connector_unreference(connector_set[i]);
-               }
-       }
-       kfree(connector_set);
-       drm_mode_destroy(dev, mode);
-       drm_modeset_unlock_all(dev);
-       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, negative 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);
-       WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL);
-
-       /*
-        * 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 = internal_framebuffer_create(dev, &fbreq, file_priv);
-                       if (IS_ERR(fb)) {
-                               DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n");
-                               return PTR_ERR(fb);
-                       }
-               } else {
-                       fb = NULL;
-               }
-       } else {
-               fb = crtc->cursor->fb;
-               if (fb)
-                       drm_framebuffer_reference(fb);
-       }
-
-       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)
-{
-       struct drm_crtc *crtc;
-       int ret = 0;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
-               return -EINVAL;
-
-       crtc = drm_crtc_find(dev, req->crtc_id);
-       if (!crtc) {
-               DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
-               return -ENOENT;
-       }
-
-       /*
-        * If this crtc has a universal cursor plane, call that plane's update
-        * handler rather than using legacy cursor handlers.
-        */
-       drm_modeset_lock_crtc(crtc, crtc->cursor);
-       if (crtc->cursor) {
-               ret = drm_mode_cursor_universal(crtc, req, file_priv);
-               goto out;
-       }
-
-       if (req->flags & DRM_MODE_CURSOR_BO) {
-               if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
-                       ret = -ENXIO;
-                       goto out;
-               }
-               /* Turns off the cursor if handle is 0 */
-               if (crtc->funcs->cursor_set2)
-                       ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle,
-                                                     req->width, req->height, req->hot_x, req->hot_y);
-               else
-                       ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle,
-                                                     req->width, req->height);
-       }
-
-       if (req->flags & DRM_MODE_CURSOR_MOVE) {
-               if (crtc->funcs->cursor_move) {
-                       ret = crtc->funcs->cursor_move(crtc, req->x, req->y);
-               } else {
-                       ret = -EFAULT;
-                       goto out;
-               }
-       }
-out:
-       drm_modeset_unlock_crtc(crtc);
-
-       return ret;
-
-}
-
-
-/**
- * drm_mode_cursor_ioctl - set CRTC's cursor configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Set the cursor configuration based on user request.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_cursor_ioctl(struct drm_device *dev,
-                         void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_cursor *req = data;
-       struct drm_mode_cursor2 new_req;
-
-       memcpy(&new_req, req, sizeof(struct drm_mode_cursor));
-       new_req.hot_x = new_req.hot_y = 0;
-
-       return drm_mode_cursor_common(dev, &new_req, file_priv);
-}
-
-/**
- * drm_mode_cursor2_ioctl - set CRTC's cursor configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Set the cursor configuration based on user request. This implements the 2nd
- * version of the cursor ioctl, which allows userspace to additionally specify
- * the hotspot of the pointer.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_cursor2_ioctl(struct drm_device *dev,
-                          void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_cursor2 *req = data;
-
-       return drm_mode_cursor_common(dev, req, file_priv);
-}
-
-/**
- * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description
- * @bpp: bits per pixels
- * @depth: bit depth per pixel
- *
- * Computes a drm fourcc pixel format code for the given @bpp/@depth values.
- * Useful in fbdev emulation code, since that deals in those values.
- */
-uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
-{
-       uint32_t fmt;
-
-       switch (bpp) {
-       case 8:
-               fmt = DRM_FORMAT_C8;
-               break;
-       case 16:
-               if (depth == 15)
-                       fmt = DRM_FORMAT_XRGB1555;
-               else
-                       fmt = DRM_FORMAT_RGB565;
-               break;
-       case 24:
-               fmt = DRM_FORMAT_RGB888;
-               break;
-       case 32:
-               if (depth == 24)
-                       fmt = DRM_FORMAT_XRGB8888;
-               else if (depth == 30)
-                       fmt = DRM_FORMAT_XRGB2101010;
-               else
-                       fmt = DRM_FORMAT_ARGB8888;
-               break;
-       default:
-               DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n");
-               fmt = DRM_FORMAT_XRGB8888;
-               break;
-       }
-
-       return fmt;
-}
-EXPORT_SYMBOL(drm_mode_legacy_fb_format);
-
-/**
- * drm_mode_addfb - 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. This is the
- * original addfb ioctl which only supported RGB formats.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_addfb(struct drm_device *dev,
-                  void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_fb_cmd *or = data;
-       struct drm_mode_fb_cmd2 r = {};
-       int ret;
-
-       /* convert to new format and call new ioctl */
-       r.fb_id = or->fb_id;
-       r.width = or->width;
-       r.height = or->height;
-       r.pitches[0] = or->pitch;
-       r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth);
-       r.handles[0] = or->handle;
-
-       ret = drm_mode_addfb2(dev, &r, file_priv);
-       if (ret)
-               return ret;
-
-       or->fb_id = r.fb_id;
-
-       return 0;
-}
-
-static int format_check(const struct drm_mode_fb_cmd2 *r)
-{
-       uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN;
-
-       switch (format) {
-       case DRM_FORMAT_C8:
-       case DRM_FORMAT_RGB332:
-       case DRM_FORMAT_BGR233:
-       case DRM_FORMAT_XRGB4444:
-       case DRM_FORMAT_XBGR4444:
-       case DRM_FORMAT_RGBX4444:
-       case DRM_FORMAT_BGRX4444:
-       case DRM_FORMAT_ARGB4444:
-       case DRM_FORMAT_ABGR4444:
-       case DRM_FORMAT_RGBA4444:
-       case DRM_FORMAT_BGRA4444:
-       case DRM_FORMAT_XRGB1555:
-       case DRM_FORMAT_XBGR1555:
-       case DRM_FORMAT_RGBX5551:
-       case DRM_FORMAT_BGRX5551:
-       case DRM_FORMAT_ARGB1555:
-       case DRM_FORMAT_ABGR1555:
-       case DRM_FORMAT_RGBA5551:
-       case DRM_FORMAT_BGRA5551:
-       case DRM_FORMAT_RGB565:
-       case DRM_FORMAT_BGR565:
-       case DRM_FORMAT_RGB888:
-       case DRM_FORMAT_BGR888:
-       case DRM_FORMAT_XRGB8888:
-       case DRM_FORMAT_XBGR8888:
-       case DRM_FORMAT_RGBX8888:
-       case DRM_FORMAT_BGRX8888:
-       case DRM_FORMAT_ARGB8888:
-       case DRM_FORMAT_ABGR8888:
-       case DRM_FORMAT_RGBA8888:
-       case DRM_FORMAT_BGRA8888:
-       case DRM_FORMAT_XRGB2101010:
-       case DRM_FORMAT_XBGR2101010:
-       case DRM_FORMAT_RGBX1010102:
-       case DRM_FORMAT_BGRX1010102:
-       case DRM_FORMAT_ARGB2101010:
-       case DRM_FORMAT_ABGR2101010:
-       case DRM_FORMAT_RGBA1010102:
-       case DRM_FORMAT_BGRA1010102:
-       case DRM_FORMAT_YUYV:
-       case DRM_FORMAT_YVYU:
-       case DRM_FORMAT_UYVY:
-       case DRM_FORMAT_VYUY:
-       case DRM_FORMAT_AYUV:
-       case DRM_FORMAT_NV12:
-       case DRM_FORMAT_NV21:
-       case DRM_FORMAT_NV16:
-       case DRM_FORMAT_NV61:
-       case DRM_FORMAT_NV24:
-       case DRM_FORMAT_NV42:
-       case DRM_FORMAT_YUV410:
-       case DRM_FORMAT_YVU410:
-       case DRM_FORMAT_YUV411:
-       case DRM_FORMAT_YVU411:
-       case DRM_FORMAT_YUV420:
-       case DRM_FORMAT_YVU420:
-       case DRM_FORMAT_YUV422:
-       case DRM_FORMAT_YVU422:
-       case DRM_FORMAT_YUV444:
-       case DRM_FORMAT_YVU444:
-               return 0;
-       default:
-               DRM_DEBUG_KMS("invalid pixel format %s\n",
-                             drm_get_format_name(r->pixel_format));
-               return -EINVAL;
-       }
-}
-
-static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
-{
-       int ret, hsub, vsub, num_planes, i;
-
-       ret = format_check(r);
-       if (ret) {
-               DRM_DEBUG_KMS("bad framebuffer format %s\n",
-                             drm_get_format_name(r->pixel_format));
-               return ret;
-       }
-
-   &nb