drm/i915: Update to Linux 4.4
authorFrançois Tigeot <ftigeot@wolfpond.org>
Thu, 21 Jul 2016 04:01:12 +0000 (06:01 +0200)
committerFrançois Tigeot <ftigeot@wolfpond.org>
Thu, 21 Jul 2016 04:01:48 +0000 (06:01 +0200)
* Broxton and Skylake support improvements

* Cherryview specific fixes

* Atomic modesetting conversion progress

* Improved validation of video modes. Some low-power chips can't
  drive all DP screens and this is now detected by the driver.

* PSR and FBC improvements and bug fixes

* Workarounds for some specific HDMI monitors needing more time than
  allowed by the spec to handle hot-plug events

* As usual, various fixes for little issues here and there

109 files changed:
sys/conf/files
sys/dev/drm/drm_atomic.c
sys/dev/drm/drm_atomic_helper.c
sys/dev/drm/drm_crtc.c
sys/dev/drm/drm_dp_helper.c
sys/dev/drm/drm_drv.c
sys/dev/drm/drm_edid.c
sys/dev/drm/drm_fb_helper.c
sys/dev/drm/drm_gem.c
sys/dev/drm/drm_internal.h
sys/dev/drm/drm_ioctl.c
sys/dev/drm/drm_irq.c
sys/dev/drm/drm_lock.c
sys/dev/drm/drm_memory.c
sys/dev/drm/drm_mm.c
sys/dev/drm/drm_modeset_lock.c
sys/dev/drm/drm_plane_helper.c
sys/dev/drm/drm_probe_helper.c
sys/dev/drm/drm_rect.c
sys/dev/drm/drm_vma_manager.c
sys/dev/drm/i915/Makefile
sys/dev/drm/i915/dvo.h
sys/dev/drm/i915/dvo_ch7017.c
sys/dev/drm/i915/dvo_ch7xxx.c
sys/dev/drm/i915/dvo_ivch.c
sys/dev/drm/i915/dvo_ns2501.c
sys/dev/drm/i915/dvo_sil164.c
sys/dev/drm/i915/dvo_tfp410.c
sys/dev/drm/i915/i915_cmd_parser.c
sys/dev/drm/i915/i915_dma.c
sys/dev/drm/i915/i915_drv.c
sys/dev/drm/i915/i915_drv.h
sys/dev/drm/i915/i915_gem.c
sys/dev/drm/i915/i915_gem_context.c
sys/dev/drm/i915/i915_gem_evict.c
sys/dev/drm/i915/i915_gem_execbuffer.c
sys/dev/drm/i915/i915_gem_fence.c
sys/dev/drm/i915/i915_gem_gtt.c
sys/dev/drm/i915/i915_gem_gtt.h
sys/dev/drm/i915/i915_gem_shrinker.c
sys/dev/drm/i915/i915_gem_stolen.c
sys/dev/drm/i915/i915_gem_userptr.c
sys/dev/drm/i915/i915_guc_reg.h
sys/dev/drm/i915/i915_guc_submission.c [new file with mode: 0644]
sys/dev/drm/i915/i915_irq.c
sys/dev/drm/i915/i915_params.c
sys/dev/drm/i915/i915_reg.h
sys/dev/drm/i915/i915_suspend.c
sys/dev/drm/i915/i915_sysfs.c
sys/dev/drm/i915/i915_trace.h
sys/dev/drm/i915/i915_vgpu.h
sys/dev/drm/i915/intel_acpi.c
sys/dev/drm/i915/intel_atomic.c
sys/dev/drm/i915/intel_atomic_plane.c
sys/dev/drm/i915/intel_audio.c
sys/dev/drm/i915/intel_bios.c
sys/dev/drm/i915/intel_bios.h
sys/dev/drm/i915/intel_crt.c
sys/dev/drm/i915/intel_csr.c
sys/dev/drm/i915/intel_ddi.c
sys/dev/drm/i915/intel_display.c
sys/dev/drm/i915/intel_dp.c
sys/dev/drm/i915/intel_dp_mst.c
sys/dev/drm/i915/intel_drv.h
sys/dev/drm/i915/intel_dsi.c
sys/dev/drm/i915/intel_dsi.h
sys/dev/drm/i915/intel_dsi_pll.c
sys/dev/drm/i915/intel_dvo.c
sys/dev/drm/i915/intel_fbc.c
sys/dev/drm/i915/intel_fbdev.c
sys/dev/drm/i915/intel_guc.h [new file with mode: 0644]
sys/dev/drm/i915/intel_guc_fwif.h
sys/dev/drm/i915/intel_guc_loader.c [new file with mode: 0644]
sys/dev/drm/i915/intel_hdmi.c
sys/dev/drm/i915/intel_i2c.c
sys/dev/drm/i915/intel_lrc.c
sys/dev/drm/i915/intel_lrc.h
sys/dev/drm/i915/intel_lvds.c
sys/dev/drm/i915/intel_modes.c
sys/dev/drm/i915/intel_opregion.c
sys/dev/drm/i915/intel_panel.c
sys/dev/drm/i915/intel_pm.c
sys/dev/drm/i915/intel_psr.c
sys/dev/drm/i915/intel_ringbuffer.c
sys/dev/drm/i915/intel_ringbuffer.h
sys/dev/drm/i915/intel_runtime_pm.c
sys/dev/drm/i915/intel_sdvo.c
sys/dev/drm/i915/intel_sprite.c
sys/dev/drm/i915/intel_tv.c
sys/dev/drm/i915/intel_uncore.c
sys/dev/drm/include/drm/drmP.h
sys/dev/drm/include/drm/drm_atomic.h
sys/dev/drm/include/drm/drm_atomic_helper.h
sys/dev/drm/include/drm/drm_crtc.h
sys/dev/drm/include/drm/drm_displayid.h [new file with mode: 0644]
sys/dev/drm/include/drm/drm_dp_helper.h
sys/dev/drm/include/drm/drm_edid.h
sys/dev/drm/include/drm/drm_fb_helper.h
sys/dev/drm/include/drm/drm_plane_helper.h
sys/dev/drm/include/drm/i915_component.h
sys/dev/drm/include/drm/i915_pciids.h
sys/dev/drm/include/uapi_drm/i915_drm.h
sys/dev/drm/include/uapi_linux/fb.h
sys/dev/drm/radeon/radeon_display.c
sys/dev/drm/radeon/radeon_drv.c
sys/dev/drm/radeon/radeon_kms.c
sys/dev/drm/radeon/radeon_kms.h
sys/dev/drm/radeon/radeon_mode.h
sys/dev/drm/radeon/radeon_pm.c

index 6ba3b10..ce44e7a 100644 (file)
@@ -2078,6 +2078,7 @@ dev/drm/i915/i915_gem_render_state.c      optional i915 drm
 dev/drm/i915/i915_gem_shrinker.c       optional i915 drm
 dev/drm/i915/i915_gem_tiling.c         optional i915 drm
 dev/drm/i915/i915_gem_userptr.c                optional i915 drm
+dev/drm/i915/i915_guc_submission.c     optional i915 drm
 dev/drm/i915/i915_irq.c                        optional i915 drm
 dev/drm/i915/i915_params.c             optional i915 drm
 dev/drm/i915/i915_suspend.c            optional i915 drm
@@ -2102,6 +2103,7 @@ dev/drm/i915/intel_fbdev.c                optional i915 drm
 dev/drm/i915/intel_fbc.c               optional i915 drm
 dev/drm/i915/intel_fifo_underrun.c     optional i915 drm
 dev/drm/i915/intel_frontbuffer.c       optional i915 drm
+dev/drm/i915/intel_guc_loader.c                optional i915 drm
 dev/drm/i915/intel_hdmi.c              optional i915 drm
 dev/drm/i915/intel_hotplug.c           optional i915 drm
 dev/drm/i915/intel_i2c.c               optional i915 drm
index 6d20ae1..e2d79da 100644 (file)
@@ -440,7 +440,8 @@ EXPORT_SYMBOL(drm_atomic_crtc_set_property);
  * consistent behavior you must call this function rather than the
  * driver hook directly.
  */
-static int drm_atomic_crtc_get_property(struct drm_crtc *crtc,
+static int
+drm_atomic_crtc_get_property(struct drm_crtc *crtc,
                const struct drm_crtc_state *state,
                struct drm_property *property, uint64_t *val)
 {
@@ -665,6 +666,25 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
        return 0;
 }
 
+static bool
+plane_switching_crtc(struct drm_atomic_state *state,
+                    struct drm_plane *plane,
+                    struct drm_plane_state *plane_state)
+{
+       if (!plane->state->crtc || !plane_state->crtc)
+               return false;
+
+       if (plane->state->crtc == plane_state->crtc)
+               return false;
+
+       /* This could be refined, but currently there's no helper or driver code
+        * to implement direct switching of active planes nor userspace to take
+        * advantage of more direct plane switching without the intermediate
+        * full OFF state.
+        */
+       return true;
+}
+
 /**
  * drm_atomic_plane_check - check plane state
  * @plane: plane to check
@@ -736,6 +756,12 @@ static int drm_atomic_plane_check(struct drm_plane *plane,
                return -ENOSPC;
        }
 
+       if (plane_switching_crtc(state->state, plane, state)) {
+               DRM_DEBUG_ATOMIC("[PLANE:%d] switching CRTC directly\n",
+                                plane->base.id);
+               return -EINVAL;
+       }
+
        return 0;
 }
 
@@ -1424,6 +1450,45 @@ static int atomic_set_prop(struct drm_atomic_state *state,
        return ret;
 }
 
+/**
+ * drm_atomic_update_old_fb -- Unset old_fb pointers and set plane->fb pointers.
+ *
+ * @dev: drm device to check.
+ * @plane_mask: plane mask for planes that were updated.
+ * @ret: return value, can be -EDEADLK for a retry.
+ *
+ * Before doing an update plane->old_fb is set to plane->fb,
+ * but before dropping the locks old_fb needs to be set to NULL
+ * and plane->fb updated. This is a common operation for each
+ * atomic update, so this call is split off as a helper.
+ */
+void drm_atomic_clean_old_fb(struct drm_device *dev,
+                            unsigned plane_mask,
+                            int ret)
+{
+       struct drm_plane *plane;
+
+       /* if succeeded, fixup legacy plane crtc/fb ptrs before dropping
+        * locks (ie. while it is still safe to deref plane->state).  We
+        * need to do this here because the driver entry points cannot
+        * distinguish between legacy and atomic ioctls.
+        */
+       drm_for_each_plane_mask(plane, dev, plane_mask) {
+               if (ret == 0) {
+                       struct drm_framebuffer *new_fb = plane->state->fb;
+                       if (new_fb)
+                               drm_framebuffer_reference(new_fb);
+                       plane->fb = new_fb;
+                       plane->crtc = plane->state->crtc;
+
+                       if (plane->old_fb)
+                               drm_framebuffer_unreference(plane->old_fb);
+               }
+               plane->old_fb = NULL;
+       }
+}
+EXPORT_SYMBOL(drm_atomic_clean_old_fb);
+
 int drm_mode_atomic_ioctl(struct drm_device *dev,
                          void *data, struct drm_file *file_priv)
 {
@@ -1438,7 +1503,7 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
        struct drm_plane *plane;
        struct drm_crtc *crtc;
        struct drm_crtc_state *crtc_state;
-       unsigned plane_mask = 0;
+       unsigned plane_mask;
        int ret = 0;
        unsigned int i, j;
 
@@ -1478,6 +1543,7 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
        state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET);
 
 retry:
+       plane_mask = 0;
        copied_objs = 0;
        copied_props = 0;
 
@@ -1568,24 +1634,7 @@ retry:
        }
 
 out:
-       /* if succeeded, fixup legacy plane crtc/fb ptrs before dropping
-        * locks (ie. while it is still safe to deref plane->state).  We
-        * need to do this here because the driver entry points cannot
-        * distinguish between legacy and atomic ioctls.
-        */
-       drm_for_each_plane_mask(plane, dev, plane_mask) {
-               if (ret == 0) {
-                       struct drm_framebuffer *new_fb = plane->state->fb;
-                       if (new_fb)
-                               drm_framebuffer_reference(new_fb);
-                       plane->fb = new_fb;
-                       plane->crtc = plane->state->crtc;
-
-                       if (plane->old_fb)
-                               drm_framebuffer_unreference(plane->old_fb);
-               }
-               plane->old_fb = NULL;
-       }
+       drm_atomic_clean_old_fb(dev, plane_mask, ret);
 
        if (ret && arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
                /*
index 3b3fa47..ad88fca 100644 (file)
  * add their own additional internal state.
  *
  * This library also provides default implementations for the check callback in
- * drm_atomic_helper_check and for the commit callback with
- * drm_atomic_helper_commit. But the individual stages and callbacks are expose
- * to allow drivers to mix and match and e.g. use the plane helpers only
+ * drm_atomic_helper_check() and for the commit callback with
+ * drm_atomic_helper_commit(). But the individual stages and callbacks are
+ * exposed to allow drivers to mix and match and e.g. use the plane helpers only
  * together with a driver private modeset implementation.
  *
  * This library also provides implementations for all the legacy driver
- * interfaces on top of the atomic interface. See drm_atomic_helper_set_config,
- * drm_atomic_helper_disable_plane, drm_atomic_helper_disable_plane and the
+ * interfaces on top of the atomic interface. See drm_atomic_helper_set_config(),
+ * drm_atomic_helper_disable_plane(), drm_atomic_helper_disable_plane() and the
  * various functions to implement set_property callbacks. New drivers must not
  * implement these functions themselves but must use the provided helpers.
  */
@@ -210,6 +210,14 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
                return -EINVAL;
        }
 
+       if (!drm_encoder_crtc_ok(new_encoder, connector_state->crtc)) {
+               DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] incompatible with [CRTC:%d]\n",
+                                new_encoder->base.id,
+                                new_encoder->name,
+                                connector_state->crtc->base.id);
+               return -EINVAL;
+       }
+
        if (new_encoder == connector_state->best_encoder) {
                DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d]\n",
                                 connector->base.id,
@@ -996,6 +1004,22 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
  * object. This can still fail when e.g. the framebuffer reservation fails. For
  * now this doesn't implement asynchronous commits.
  *
+ * Note that right now this function does not support async commits, and 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.
+ *
+ * For drivers supporting runtime PM the recommended sequence is
+ *
+ *     drm_atomic_helper_commit_modeset_disables(dev, state);
+ *
+ *     drm_atomic_helper_commit_modeset_enables(dev, state);
+ *
+ *     drm_atomic_helper_commit_planes(dev, state, true);
+ *
+ * See the kerneldoc entries for these three functions for more details.
+ *
  * RETURNS
  * Zero for success or -errno.
  */
@@ -1040,7 +1064,7 @@ int drm_atomic_helper_commit(struct drm_device *dev,
 
        drm_atomic_helper_commit_modeset_disables(dev, state);
 
-       drm_atomic_helper_commit_planes(dev, state);
+       drm_atomic_helper_commit_planes(dev, state, false);
 
        drm_atomic_helper_commit_modeset_enables(dev, state);
 
@@ -1080,7 +1104,7 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
  * work item, which allows nice concurrent updates on disjoint sets of crtcs.
  *
  * 3. The software state is updated synchronously with
- * drm_atomic_helper_swap_state. Doing this under the protection of all modeset
+ * drm_atomic_helper_swap_state(). Doing this under the protection of all modeset
  * locks means concurrent callers never see inconsistent state. And doing this
  * while it's guaranteed that no relevant async worker runs means that async
  * workers do not need grab any locks. Actually they must not grab locks, for
@@ -1114,17 +1138,14 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev,
                const struct drm_plane_helper_funcs *funcs;
                struct drm_plane *plane = state->planes[i];
                struct drm_plane_state *plane_state = state->plane_states[i];
-               struct drm_framebuffer *fb;
 
                if (!plane)
                        continue;
 
                funcs = plane->helper_private;
 
-               fb = plane_state->fb;
-
-               if (fb && funcs->prepare_fb) {
-                       ret = funcs->prepare_fb(plane, fb, plane_state);
+               if (funcs->prepare_fb) {
+                       ret = funcs->prepare_fb(plane, plane_state);
                        if (ret)
                                goto fail;
                }
@@ -1137,17 +1158,14 @@ fail:
                const struct drm_plane_helper_funcs *funcs;
                struct drm_plane *plane = state->planes[i];
                struct drm_plane_state *plane_state = state->plane_states[i];
-               struct drm_framebuffer *fb;
 
                if (!plane)
                        continue;
 
                funcs = plane->helper_private;
 
-               fb = state->plane_states[i]->fb;
-
-               if (fb && funcs->cleanup_fb)
-                       funcs->cleanup_fb(plane, fb, plane_state);
+               if (funcs->cleanup_fb)
+                       funcs->cleanup_fb(plane, plane_state);
 
        }
 
@@ -1155,10 +1173,16 @@ fail:
 }
 EXPORT_SYMBOL(drm_atomic_helper_prepare_planes);
 
+static bool plane_crtc_active(struct drm_plane_state *state)
+{
+       return state->crtc && state->crtc->state->active;
+}
+
 /**
  * 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
  *
  * 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
@@ -1171,9 +1195,26 @@ EXPORT_SYMBOL(drm_atomic_helper_prepare_planes);
  * Note that this function does all plane updates across all CRTCs in one step.
  * If the hardware can't support this approach look at
  * drm_atomic_helper_commit_planes_on_crtc() instead.
+ *
+ * Plane parameters can be updated by applications while the associated CRTC is
+ * disabled. The DRM/KMS core will store the parameters in the plane state,
+ * which will be available to the driver when the CRTC is turned on. As a result
+ * 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
+ * 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.
  */
 void drm_atomic_helper_commit_planes(struct drm_device *dev,
-                                    struct drm_atomic_state *old_state)
+                                    struct drm_atomic_state *old_state,
+                                    bool active_only)
 {
        struct drm_crtc *crtc;
        struct drm_crtc_state *old_crtc_state;
@@ -1189,25 +1230,43 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
                if (!funcs || !funcs->atomic_begin)
                        continue;
 
+               if (active_only && !crtc->state->active)
+                       continue;
+
                funcs->atomic_begin(crtc, old_crtc_state);
        }
 
        for_each_plane_in_state(old_state, plane, old_plane_state, i) {
                const struct drm_plane_helper_funcs *funcs;
+               bool disabling;
 
                funcs = plane->helper_private;
 
                if (!funcs)
                        continue;
 
+               disabling = drm_atomic_plane_disabling(plane, old_plane_state);
+
+               if (active_only) {
+                       /*
+                        * Skip planes related to inactive CRTCs. If the plane
+                        * is enabled use the state of the current CRTC. If the
+                        * plane is being disabled use the state of the old
+                        * CRTC to avoid skipping planes being disabled on an
+                        * active CRTC.
+                        */
+                       if (!disabling && !plane_crtc_active(plane->state))
+                               continue;
+                       if (disabling && !plane_crtc_active(old_plane_state))
+                               continue;
+               }
+
                /*
                 * Special-case disabling the plane if drivers support it.
                 */
-               if (drm_atomic_plane_disabling(plane, old_plane_state) &&
-                   funcs->atomic_disable)
+               if (disabling && funcs->atomic_disable)
                        funcs->atomic_disable(plane, old_plane_state);
-               else if (plane->state->crtc ||
-                        drm_atomic_plane_disabling(plane, old_plane_state))
+               else if (plane->state->crtc || disabling)
                        funcs->atomic_update(plane, old_plane_state);
        }
 
@@ -1219,6 +1278,9 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
                if (!funcs || !funcs->atomic_flush)
                        continue;
 
+               if (active_only && !crtc->state->active)
+                       continue;
+
                funcs->atomic_flush(crtc, old_crtc_state);
        }
 }
@@ -1303,14 +1365,11 @@ 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;
-               struct drm_framebuffer *old_fb;
 
                funcs = plane->helper_private;
 
-               old_fb = plane_state->fb;
-
-               if (old_fb && funcs->cleanup_fb)
-                       funcs->cleanup_fb(plane, old_fb, plane_state);
+               if (funcs->cleanup_fb)
+                       funcs->cleanup_fb(plane, plane_state);
        }
 }
 EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
@@ -1337,7 +1396,7 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
  *
  * 4. Actually commit the hardware state.
  *
- * 5. Call drm_atomic_helper_cleanup_planes with @state, which since step 3
+ * 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.
  */
 void drm_atomic_helper_swap_state(struct drm_device *dev,
@@ -1505,21 +1564,12 @@ retry:
                goto fail;
        }
 
-       ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
+       if (plane_state->crtc && (plane == plane->crtc->cursor))
+               plane_state->state->legacy_cursor_update = true;
+
+       ret = __drm_atomic_helper_disable_plane(plane, plane_state);
        if (ret != 0)
                goto fail;
-       drm_atomic_set_fb_for_plane(plane_state, NULL);
-       plane_state->crtc_x = 0;
-       plane_state->crtc_y = 0;
-       plane_state->crtc_h = 0;
-       plane_state->crtc_w = 0;
-       plane_state->src_x = 0;
-       plane_state->src_y = 0;
-       plane_state->src_h = 0;
-       plane_state->src_w = 0;
-
-       if (plane == plane->crtc->cursor)
-               state->legacy_cursor_update = true;
 
        ret = drm_atomic_commit(state);
        if (ret != 0)
@@ -1549,6 +1599,29 @@ backoff:
 }
 EXPORT_SYMBOL(drm_atomic_helper_disable_plane);
 
+/* just used from fb-helper and atomic-helper: */
+int __drm_atomic_helper_disable_plane(struct drm_plane *plane,
+               struct drm_plane_state *plane_state)
+{
+       int ret;
+
+       ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
+       if (ret != 0)
+               return ret;
+
+       drm_atomic_set_fb_for_plane(plane_state, NULL);
+       plane_state->crtc_x = 0;
+       plane_state->crtc_y = 0;
+       plane_state->crtc_h = 0;
+       plane_state->crtc_w = 0;
+       plane_state->src_x = 0;
+       plane_state->src_y = 0;
+       plane_state->src_h = 0;
+       plane_state->src_w = 0;
+
+       return 0;
+}
+
 static int update_output_state(struct drm_atomic_state *state,
                               struct drm_mode_set *set)
 {
@@ -1632,8 +1705,6 @@ int drm_atomic_helper_set_config(struct drm_mode_set *set)
 {
        struct drm_atomic_state *state;
        struct drm_crtc *crtc = set->crtc;
-       struct drm_crtc_state *crtc_state;
-       struct drm_plane_state *primary_state;
        int ret = 0;
 
        state = drm_atomic_state_alloc(crtc->dev);
@@ -1642,17 +1713,55 @@ int drm_atomic_helper_set_config(struct drm_mode_set *set)
 
        state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc);
 retry:
-       crtc_state = drm_atomic_get_crtc_state(state, crtc);
-       if (IS_ERR(crtc_state)) {
-               ret = PTR_ERR(crtc_state);
+       ret = __drm_atomic_helper_set_config(set, state);
+       if (ret != 0)
                goto fail;
-       }
 
-       primary_state = drm_atomic_get_plane_state(state, crtc->primary);
-       if (IS_ERR(primary_state)) {
-               ret = PTR_ERR(primary_state);
+       ret = drm_atomic_commit(state);
+       if (ret != 0)
                goto fail;
-       }
+
+       /* Driver takes ownership of state on successful commit. */
+       return 0;
+fail:
+       if (ret == -EDEADLK)
+               goto backoff;
+
+       drm_atomic_state_free(state);
+
+       return ret;
+backoff:
+       drm_atomic_state_clear(state);
+       drm_atomic_legacy_backoff(state);
+
+       /*
+        * Someone might have exchanged the framebuffer while we dropped locks
+        * in the backoff code. We need to fix up the fb refcount tracking the
+        * core does for us.
+        */
+       crtc->primary->old_fb = crtc->primary->fb;
+
+       goto retry;
+}
+EXPORT_SYMBOL(drm_atomic_helper_set_config);
+
+/* just used from fb-helper and atomic-helper: */
+int __drm_atomic_helper_set_config(struct drm_mode_set *set,
+               struct drm_atomic_state *state)
+{
+       struct drm_crtc_state *crtc_state;
+       struct drm_plane_state *primary_state;
+       struct drm_crtc *crtc = set->crtc;
+       int hdisplay, vdisplay;
+       int ret;
+
+       crtc_state = drm_atomic_get_crtc_state(state, crtc);
+       if (IS_ERR(crtc_state))
+               return PTR_ERR(crtc_state);
+
+       primary_state = drm_atomic_get_plane_state(state, crtc->primary);
+       if (IS_ERR(primary_state))
+               return PTR_ERR(primary_state);
 
        if (!set->mode) {
                WARN_ON(set->fb);
@@ -1660,13 +1769,13 @@ retry:
 
                ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
                if (ret != 0)
-                       goto fail;
+                       return ret;
 
                crtc_state->active = false;
 
                ret = drm_atomic_set_crtc_for_plane(primary_state, NULL);
                if (ret != 0)
-                       goto fail;
+                       return ret;
 
                drm_atomic_set_fb_for_plane(primary_state, NULL);
 
@@ -1678,55 +1787,38 @@ retry:
 
        ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode);
        if (ret != 0)
-               goto fail;
+               return ret;
 
        crtc_state->active = true;
 
        ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
        if (ret != 0)
-               goto fail;
+               return ret;
+
+       drm_crtc_get_hv_timing(set->mode, &hdisplay, &vdisplay);
+
        drm_atomic_set_fb_for_plane(primary_state, set->fb);
        primary_state->crtc_x = 0;
        primary_state->crtc_y = 0;
-       primary_state->crtc_h = set->mode->vdisplay;
-       primary_state->crtc_w = set->mode->hdisplay;
+       primary_state->crtc_h = vdisplay;
+       primary_state->crtc_w = hdisplay;
        primary_state->src_x = set->x << 16;
        primary_state->src_y = set->y << 16;
-       primary_state->src_h = set->mode->vdisplay << 16;
-       primary_state->src_w = set->mode->hdisplay << 16;
+       if (primary_state->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) {
+               primary_state->src_h = hdisplay << 16;
+               primary_state->src_w = vdisplay << 16;
+       } else {
+               primary_state->src_h = vdisplay << 16;
+               primary_state->src_w = hdisplay << 16;
+       }
 
 commit:
        ret = update_output_state(state, set);
        if (ret)
-               goto fail;
-
-       ret = drm_atomic_commit(state);
-       if (ret != 0)
-               goto fail;
+               return ret;
 
-       /* Driver takes ownership of state on successful commit. */
        return 0;
-fail:
-       if (ret == -EDEADLK)
-               goto backoff;
-
-       drm_atomic_state_free(state);
-
-       return ret;
-backoff:
-       drm_atomic_state_clear(state);
-       drm_atomic_legacy_backoff(state);
-
-       /*
-        * Someone might have exchanged the framebuffer while we dropped locks
-        * in the backoff code. We need to fix up the fb refcount tracking the
-        * core does for us.
-        */
-       crtc->primary->old_fb = crtc->primary->fb;
-
-       goto retry;
 }
-EXPORT_SYMBOL(drm_atomic_helper_set_config);
 
 /**
  * drm_atomic_helper_crtc_set_property - helper for crtc properties
@@ -2335,6 +2427,84 @@ drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector)
 }
 EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
 
+/**
+ * drm_atomic_helper_duplicate_state - duplicate an atomic state object
+ * @dev: DRM device
+ * @ctx: lock acquisition context
+ *
+ * Makes a copy of the current atomic state by looping over all objects and
+ * duplicating their respective states.
+ *
+ * Note that this treats atomic state as persistent between save and restore.
+ * Drivers must make sure that this is possible and won't result in confusion
+ * or erroneous behaviour.
+ *
+ * Note that if callers haven't already acquired all modeset locks this might
+ * return -EDEADLK, which must be handled by calling drm_modeset_backoff().
+ *
+ * Returns:
+ * A pointer to the copy of the atomic state object on success or an
+ * ERR_PTR()-encoded error code on failure.
+ */
+struct drm_atomic_state *
+drm_atomic_helper_duplicate_state(struct drm_device *dev,
+                                 struct drm_modeset_acquire_ctx *ctx)
+{
+       struct drm_atomic_state *state;
+       struct drm_connector *conn;
+       struct drm_plane *plane;
+       struct drm_crtc *crtc;
+       int err = 0;
+
+       state = drm_atomic_state_alloc(dev);
+       if (!state)
+               return ERR_PTR(-ENOMEM);
+
+       state->acquire_ctx = ctx;
+
+       drm_for_each_crtc(crtc, dev) {
+               struct drm_crtc_state *crtc_state;
+
+               crtc_state = drm_atomic_get_crtc_state(state, crtc);
+               if (IS_ERR(crtc_state)) {
+                       err = PTR_ERR(crtc_state);
+                       goto free;
+               }
+       }
+
+       drm_for_each_plane(plane, dev) {
+               struct drm_plane_state *plane_state;
+
+               plane_state = drm_atomic_get_plane_state(state, plane);
+               if (IS_ERR(plane_state)) {
+                       err = PTR_ERR(plane_state);
+                       goto free;
+               }
+       }
+
+       drm_for_each_connector(conn, dev) {
+               struct drm_connector_state *conn_state;
+
+               conn_state = drm_atomic_get_connector_state(state, conn);
+               if (IS_ERR(conn_state)) {
+                       err = PTR_ERR(conn_state);
+                       goto free;
+               }
+       }
+
+       /* clear the acquire context so that it isn't accidentally reused */
+       state->acquire_ctx = NULL;
+
+free:
+       if (err < 0) {
+               drm_atomic_state_free(state);
+               state = ERR_PTR(err);
+       }
+
+       return state;
+}
+EXPORT_SYMBOL(drm_atomic_helper_duplicate_state);
+
 /**
  * __drm_atomic_helper_connector_destroy_state - release connector state
  * @connector: connector object
index 11f3165..f4adb96 100644 (file)
@@ -310,8 +310,7 @@ static int drm_mode_object_get_reg(struct drm_device *dev,
  * reference counted modeset objects like framebuffers.
  *
  * Returns:
- * New unique (relative to other objects in @dev) integer identifier for the
- * object.
+ * 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)
@@ -427,7 +426,7 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
 out:
        mutex_unlock(&dev->mode_config.fb_lock);
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL(drm_framebuffer_init);
 
@@ -542,7 +541,12 @@ EXPORT_SYMBOL(drm_framebuffer_reference);
  */
 void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
 {
-       struct drm_device *dev = fb->dev;
+       struct drm_device *dev;
+
+       if (!fb)
+               return;
+
+       dev = fb->dev;
 
        mutex_lock(&dev->mode_config.fb_lock);
        /* Mark fb as reaped and drop idr ref. */
@@ -593,12 +597,17 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
  */
 void drm_framebuffer_remove(struct drm_framebuffer *fb)
 {
-       struct drm_device *dev = fb->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));
 
        /*
@@ -671,7 +680,6 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 
        crtc->dev = dev;
        crtc->funcs = funcs;
-       crtc->invert_dimensions = false;
 
        drm_modeset_lock_init(&crtc->mutex);
        ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
@@ -1505,7 +1513,7 @@ EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
  */
 int drm_mode_create_tv_properties(struct drm_device *dev,
                                  unsigned int num_modes,
-                                 char *modes[])
+                                 const char * const modes[])
 {
        struct drm_property *tv_selector;
        struct drm_property *tv_subconnector;
@@ -1521,6 +1529,9 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
                                          "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 =
@@ -1528,6 +1539,8 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
                                    "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;
 
        /*
@@ -1535,42 +1548,67 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
         */
        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);
 
@@ -2272,6 +2310,32 @@ int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format)
        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
  *
@@ -2291,7 +2355,6 @@ static int __setplane_internal(struct drm_plane *plane,
                               uint32_t src_w, uint32_t src_h)
 {
        int ret = 0;
-       unsigned int fb_width, fb_height;
 
        /* No fb means shut it down */
        if (!fb) {
@@ -2328,27 +2391,13 @@ static int __setplane_internal(struct drm_plane *plane,
            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);
-               return -ERANGE;
+               ret = -ERANGE;
+               goto out;
        }
 
-
-       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);
-               ret = -ENOSPC;
+       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,
@@ -2539,20 +2588,13 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc,
 
        drm_crtc_get_hv_timing(mode, &hdisplay, &vdisplay);
 
-       if (crtc->invert_dimensions)
+       if (crtc->state &&
+           crtc->primary->state->rotation & (BIT(DRM_ROTATE_90) |
+                                             BIT(DRM_ROTATE_270)))
                swap(hdisplay, vdisplay);
 
-       if (hdisplay > fb->width ||
-           vdisplay > fb->height ||
-           x > fb->width - hdisplay ||
-           y > fb->height - vdisplay) {
-               DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
-                             fb->width, fb->height, hdisplay, vdisplay, x, y,
-                             crtc->invert_dimensions ? " (inverted)" : "");
-               return -ENOSPC;
-       }
-
-       return 0;
+       return check_src_coords(x << 16, y << 16,
+                               hdisplay << 16, vdisplay << 16, fb);
 }
 EXPORT_SYMBOL(drm_crtc_check_viewport);
 
@@ -3306,14 +3348,11 @@ int drm_mode_rmfb(struct drm_device *dev,
        if (!found)
                goto fail_lookup;
 
-       /* Mark fb as reaped, we still have a ref from fpriv->fbs. */
-       __drm_framebuffer_unregister(dev, fb);
-
        list_del_init(&fb->filp_head);
        mutex_unlock(&dev->mode_config.fb_lock);
        mutex_unlock(&file_priv->fbs_lock);
 
-       drm_framebuffer_remove(fb);
+       drm_framebuffer_unreference(fb);
 
        return 0;
 
@@ -3468,7 +3507,6 @@ out_err1:
  */
 void drm_fb_release(struct drm_file *priv)
 {
-       struct drm_device *dev = priv->dev;
        struct drm_framebuffer *fb, *tfb;
 
        /*
@@ -3482,16 +3520,10 @@ void drm_fb_release(struct drm_file *priv)
         * at it any more.
         */
        list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
-
-               mutex_lock(&dev->mode_config.fb_lock);
-               /* Mark fb as reaped, we still have a ref from fpriv->fbs. */
-               __drm_framebuffer_unregister(dev, fb);
-               mutex_unlock(&dev->mode_config.fb_lock);
-
                list_del_init(&fb->filp_head);
 
-               /* This will also drop the fpriv->fbs reference. */
-               drm_framebuffer_remove(fb);
+               /* This drops the fpriv->fbs reference. */
+               drm_framebuffer_unreference(fb);
        }
 }
 
@@ -5177,7 +5209,14 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                goto out;
        }
 
-       ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
+       if (crtc->state) {
+               const struct drm_plane_state *state = crtc->primary->state;
+
+               ret = check_src_coords(state->src_x, state->src_y,
+                                      state->src_w, state->src_h, fb);
+       } else {
+               ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
+       }
        if (ret)
                goto out;
 
@@ -5632,7 +5671,8 @@ unsigned int drm_rotation_simplify(unsigned int rotation,
 {
        if (rotation & ~supported_rotations) {
                rotation ^= BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y);
-               rotation = (rotation & ~0xf) | BIT((ffs(rotation & 0xf) + 1) % 4);
+               rotation = (rotation & DRM_REFLECT_MASK) |
+                          BIT((ffs(rotation & DRM_ROTATE_MASK) + 1) % 4);
        }
 
        return rotation;
@@ -5735,7 +5775,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
         */
        WARN_ON(!list_empty(&dev->mode_config.fb_list));
        list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
-               drm_framebuffer_remove(fb);
+               drm_framebuffer_free(&fb->refcount);
        }
 
        list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list,
index 5c301dc..a1ac2bd 100644 (file)
@@ -158,8 +158,6 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw)
 }
 EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate);
 
-#define AUX_RETRY_INTERVAL 500 /* us */
-
 /**
  * DOC: dp helpers
  *
@@ -214,7 +212,7 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
                        return -EIO;
 
                case DP_AUX_NATIVE_REPLY_DEFER:
-                       usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
+                       usleep_range(400, 500);
                        break;
                }
        }
@@ -424,90 +422,19 @@ static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter)
               I2C_FUNC_10BIT_ADDR;
 }
 
-#define AUX_PRECHARGE_LEN 10 /* 10 to 16 */
-#define AUX_SYNC_LEN (16 + 4) /* preamble + AUX_SYNC_END */
-#define AUX_STOP_LEN 4
-#define AUX_CMD_LEN 4
-#define AUX_ADDRESS_LEN 20
-#define AUX_REPLY_PAD_LEN 4
-#define AUX_LENGTH_LEN 8
-
-/*
- * Calculate the duration of the AUX request/reply in usec. Gives the
- * "best" case estimate, ie. successful while as short as possible.
- */
-static int drm_dp_aux_req_duration(const struct drm_dp_aux_msg *msg)
-{
-       int len = AUX_PRECHARGE_LEN + AUX_SYNC_LEN + AUX_STOP_LEN +
-               AUX_CMD_LEN + AUX_ADDRESS_LEN + AUX_LENGTH_LEN;
-
-       if ((msg->request & DP_AUX_I2C_READ) == 0)
-               len += msg->size * 8;
-
-       return len;
-}
-
-static int drm_dp_aux_reply_duration(const struct drm_dp_aux_msg *msg)
+static void drm_dp_i2c_msg_write_status_update(struct drm_dp_aux_msg *msg)
 {
-       int len = AUX_PRECHARGE_LEN + AUX_SYNC_LEN + AUX_STOP_LEN +
-               AUX_CMD_LEN + AUX_REPLY_PAD_LEN;
-
        /*
-        * For read we expect what was asked. For writes there will
-        * be 0 or 1 data bytes. Assume 0 for the "best" case.
+        * In case of i2c defer or short i2c ack reply to a write,
+        * we need to switch to WRITE_STATUS_UPDATE to drain the
+        * rest of the message
         */
-       if (msg->request & DP_AUX_I2C_READ)
-               len += msg->size * 8;
-
-       return len;
-}
-
-#define I2C_START_LEN 1
-#define I2C_STOP_LEN 1
-#define I2C_ADDR_LEN 9 /* ADDRESS + R/W + ACK/NACK */
-#define I2C_DATA_LEN 9 /* DATA + ACK/NACK */
-
-/*
- * Calculate the length of the i2c transfer in usec, assuming
- * the i2c bus speed is as specified. Gives the the "worst"
- * case estimate, ie. successful while as long as possible.
- * Doesn't account the the "MOT" bit, and instead assumes each
- * message includes a START, ADDRESS and STOP. Neither does it
- * account for additional random variables such as clock stretching.
- */
-static int drm_dp_i2c_msg_duration(const struct drm_dp_aux_msg *msg,
-                                  int i2c_speed_khz)
-{
-       /* AUX bitrate is 1MHz, i2c bitrate as specified */
-       return DIV_ROUND_UP((I2C_START_LEN + I2C_ADDR_LEN +
-                            msg->size * I2C_DATA_LEN +
-                            I2C_STOP_LEN) * 1000, i2c_speed_khz);
-}
-
-/*
- * Deterine how many retries should be attempted to successfully transfer
- * the specified message, based on the estimated durations of the
- * i2c and AUX transfers.
- */
-static int drm_dp_i2c_retry_count(const struct drm_dp_aux_msg *msg,
-                             int i2c_speed_khz)
-{
-       int aux_time_us = drm_dp_aux_req_duration(msg) +
-               drm_dp_aux_reply_duration(msg);
-       int i2c_time_us = drm_dp_i2c_msg_duration(msg, i2c_speed_khz);
-
-       return DIV_ROUND_UP(i2c_time_us, aux_time_us + AUX_RETRY_INTERVAL);
+       if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_WRITE) {
+               msg->request &= DP_AUX_I2C_MOT;
+               msg->request |= DP_AUX_I2C_WRITE_STATUS_UPDATE;
+       }
 }
 
-/*
- * FIXME currently assumes 10 kHz as some real world devices seem
- * to require it. We should query/set the speed via DPCD if supported.
- */
-static int dp_aux_i2c_speed_khz __read_mostly = 10;
-module_param_unsafe(dp_aux_i2c_speed_khz, int, 0644);
-MODULE_PARM_DESC(dp_aux_i2c_speed_khz,
-                "Assumed speed of the i2c bus in kHz, (1-400, default 10)");
-
 /*
  * Transfer a single I2C-over-AUX message and handle various error conditions,
  * retrying the transaction as appropriate.  It is assumed that the
@@ -520,16 +447,13 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
 {
        unsigned int retry, defer_i2c;
        int ret;
+
        /*
         * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device
         * is required to retry at least seven times upon receiving AUX_DEFER
         * before giving up the AUX transaction.
-        *
-        * We also try to account for the i2c bus speed.
         */
-       int max_retries = max(7, drm_dp_i2c_retry_count(msg, dp_aux_i2c_speed_khz));
-
-       for (retry = 0, defer_i2c = 0; retry < (max_retries + defer_i2c); retry++) {
+       for (retry = 0, defer_i2c = 0; retry < (7 + defer_i2c); retry++) {
                mutex_lock(&aux->hw_mutex);
                ret = aux->transfer(aux, msg);
                mutex_unlock(&aux->hw_mutex);
@@ -565,7 +489,7 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
                         * For now just defer for long enough to hopefully be
                         * safe for all use-cases.
                         */
-                       usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
+                       usleep_range(500, 600);
                        continue;
 
                default:
@@ -579,6 +503,8 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
                         * Both native ACK and I2C ACK replies received. We
                         * can assume the transfer was successful.
                         */
+                       if (ret != msg->size)
+                               drm_dp_i2c_msg_write_status_update(msg);
                        return ret;
 
                case DP_AUX_I2C_REPLY_NACK:
@@ -595,7 +521,8 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
                        aux->i2c_defer_count++;
                        if (defer_i2c < 7)
                                defer_i2c++;
-                       usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
+                       usleep_range(400, 500);
+                       drm_dp_i2c_msg_write_status_update(msg);
                        continue;
 
                default:
@@ -608,6 +535,14 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
        return -EREMOTEIO;
 }
 
+static void drm_dp_i2c_msg_set_request(struct drm_dp_aux_msg *msg,
+                                      const struct i2c_msg *i2c_msg)
+{
+       msg->request = (i2c_msg->flags & I2C_M_RD) ?
+               DP_AUX_I2C_READ : DP_AUX_I2C_WRITE;
+       msg->request |= DP_AUX_I2C_MOT;
+}
+
 /*
  * Keep retrying drm_dp_i2c_do_msg until all data has been transferred.
  *
@@ -661,10 +596,7 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
 
        for (i = 0; i < num; i++) {
                msg.address = msgs[i].addr;
-               msg.request = (msgs[i].flags & I2C_M_RD) ?
-                       DP_AUX_I2C_READ :
-                       DP_AUX_I2C_WRITE;
-               msg.request |= DP_AUX_I2C_MOT;
+               drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
                /* Send a bare address packet to start the transaction.
                 * Zero sized messages specify an address only (bare
                 * address) transaction.
@@ -672,6 +604,13 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
                msg.buffer = NULL;
                msg.size = 0;
                err = drm_dp_i2c_do_msg(aux, &msg);
+
+               /*
+                * Reset msg.request in case in case it got
+                * changed into a WRITE_STATUS_UPDATE.
+                */
+               drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
+
                if (err < 0)
                        break;
                /* We want each transaction to be as large as possible, but
@@ -684,6 +623,13 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
                        msg.size = min(transfer_size, msgs[i].len - j);
 
                        err = drm_dp_i2c_drain_msg(aux, &msg);
+
+                       /*
+                        * Reset msg.request in case in case it got
+                        * changed into a WRITE_STATUS_UPDATE.
+                        */
+                       drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
+
                        if (err < 0)
                                break;
                        transfer_size = err;
@@ -718,7 +664,7 @@ static const struct i2c_algorithm drm_dp_i2c_algo = {
  */
 int drm_dp_aux_register(struct drm_dp_aux *aux)
 {
-       lockinit(&aux->hw_mutex, "ahm", 0, LK_CANRECURSE);
+       mutex_init(&aux->hw_mutex);
 
        aux->ddc.algo = &drm_dp_i2c_algo;
        aux->ddc.algo_data = aux;
index d27006e..b002f0f 100644 (file)
@@ -47,7 +47,7 @@
 #endif
 unsigned int drm_debug = DRM_DEBUGBITS_ON;     /* defaults to 0 */
 #else
-unsigned int drm_debug = 0;    /* 1 to enable debug output */
+unsigned int drm_debug = 0;    /* bitmask of DRM_UT_x */
 #endif /* __DragonFly__ */
 EXPORT_SYMBOL(drm_debug);
 
@@ -65,7 +65,6 @@ static DEFINE_SPINLOCK(drm_minor_lock);
 static struct idr drm_minors_idr;
 #endif
 
-struct class *drm_class;
 #if 0
 static struct dentry *drm_debugfs_root;
 #endif
@@ -381,16 +380,52 @@ void drm_minor_release(struct drm_minor *minor)
        drm_dev_unref(minor->dev);
 }
 
+/**
+ * DOC: driver instance overview
+ *
+ * A device instance for a drm driver is represented by struct &drm_device. This
+ * is allocated with drm_dev_alloc(), usually from bus-specific ->probe()
+ * callbacks implemented by the driver. The driver then needs to initialize all
+ * the various subsystems for the drm device like memory management, vblank
+ * handling, modesetting support and intial output configuration plus obviously
+ * initialize all the corresponding hardware bits. An important part of this is
+ * also calling drm_dev_set_unique() to set the userspace-visible unique name of
+ * this device instance. Finally when everything is up and running and ready for
+ * userspace the device instance can be published using drm_dev_register().
+ *
+ * There is also deprecated support for initalizing device instances using
+ * bus-specific helpers and the ->load() callback. But due to
+ * backwards-compatibility needs the device instance have to be published too
+ * early, which requires unpretty global locking to make safe and is therefore
+ * only support for existing drivers not yet converted to the new scheme.
+ *
+ * When cleaning up a device instance everything needs to be done in reverse:
+ * First unpublish the device instance with drm_dev_unregister(). Then clean up
+ * any other resources allocated at device initialization and drop the driver's
+ * reference to &drm_device using drm_dev_unref().
+ *
+ * Note that the lifetime rules for &drm_device instance has still a lot of
+ * historical baggage. Hence use the reference counting provided by
+ * drm_dev_ref() and drm_dev_unref() only carefully.
+ *
+ * Also note that embedding of &drm_device is currently not (yet) supported (but
+ * it would be easy to add). Drivers can store driver-private data in the
+ * dev_priv field of &drm_device.
+ */
+
 /**
  * drm_put_dev - Unregister and release a DRM device
  * @dev: DRM device
  *
  * Called at module unload time or when a PCI device is unplugged.
  *
- * Use of this function is discouraged. It will eventually go away completely.
- * Please use drm_dev_unregister() and drm_dev_unref() explicitly instead.
- *
  * Cleans up all DRM device, calling drm_lastclose().
+ *
+ * Note: Use of this function is deprecated. It will eventually go away
+ * completely.  Please use drm_dev_unregister() and drm_dev_unref() explicitly
+ * instead to make sure that the device isn't userspace accessible any more
+ * while teardown is in progress, ensuring that userspace can't access an
+ * inconsistent state.
  */
 void drm_put_dev(struct drm_device *dev)
 {
@@ -503,7 +538,9 @@ static void drm_fs_inode_free(struct inode *inode)
  *
  * Allocate and initialize a new DRM device. No device registration is done.
  * Call drm_dev_register() to advertice the device to user space and register it
- * with other core subsystems.
+ * with other core subsystems. This should be done last in the device
+ * initialization sequence to make sure userspace can't access an inconsistent
+ * state.
  *
  * The initial ref-count of the object is 1. Use drm_dev_ref() and
  * drm_dev_unref() to take and drop further ref-counts.
@@ -550,6 +587,8 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
                ret = drm_minor_alloc(dev, DRM_MINOR_CONTROL);
                if (ret)
                        goto err_minors;
+
+               WARN_ON(driver->suspend || driver->resume);
        }
 
        if (drm_core_check_feature(dev, DRIVER_RENDER)) {
@@ -656,6 +695,12 @@ EXPORT_SYMBOL(drm_dev_unref);
  *
  * Never call this twice on any device!
  *
+ * NOTE: To ensure backward compatibility with existing drivers method this
+ * function calls the ->load() method after registering the device nodes,
+ * creating race conditions. Usage of the ->load() methods is therefore
+ * deprecated, drivers must perform all initialization before calling
+ * drm_dev_register().
+ *
  * RETURNS:
  * 0 on success, negative error code on failure.
  */
@@ -703,6 +748,9 @@ EXPORT_SYMBOL(drm_dev_register);
  * Unregister the DRM device from the system. This does the reverse of
  * drm_dev_register() but does not deallocate the device. The caller must call
  * drm_dev_unref() to drop their final reference.
+ *
+ * This should be called first in the device teardown code to make sure
+ * userspace can't access the device instance any more.
  */
 void drm_dev_unregister(struct drm_device *dev)
 {
@@ -825,10 +873,9 @@ static int __init drm_core_init(void)
        if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops))
                goto err_p1;
 
-       drm_class = drm_sysfs_create(THIS_MODULE, "drm");
-       if (IS_ERR(drm_class)) {
+       ret = drm_sysfs_init();
+       if (ret < 0) {
                printk(KERN_ERR "DRM: Error creating drm class.\n");
-               ret = PTR_ERR(drm_class);
                goto err_p2;
        }
 
@@ -1078,11 +1125,6 @@ static int drm_load(struct drm_device *dev)
        if (drm_core_check_feature(dev, DRIVER_USE_AGP)) {
                if (drm_pci_device_is_agp(dev))
                        dev->agp = drm_agp_init(dev);
-               if (dev->agp != NULL && dev->agp->agp_info.ai_aperture_base != 0) {
-                       if (drm_mtrr_add(dev->agp->agp_info.ai_aperture_base,
-                           dev->agp->agp_info.ai_aperture_size, DRM_MTRR_WC) == 0)
-                               dev->agp->agp_mtrr = 1;
-               }
        }
 
        if (dev->driver->driver_features & DRIVER_GEM) {
index 7835d79..9d6e040 100644 (file)
@@ -2419,6 +2419,8 @@ add_cvt_modes(struct drm_connector *connector, struct edid *edid)
        return closure.modes;
 }
 
+static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode);
+
 static void
 do_detailed_mode(struct detailed_timing *timing, void *c)
 {
@@ -2435,6 +2437,13 @@ do_detailed_mode(struct detailed_timing *timing, void *c)
                if (closure->preferred)
                        newmode->type |= DRM_MODE_TYPE_PREFERRED;
 
+               /*
+                * Detailed modes are limited to 10kHz pixel clock resolution,
+                * so fix up anything that looks like CEA/HDMI mode, but the clock
+                * is just slightly off.
+                */
+               fixup_detailed_cea_mode_clock(newmode);
+
                drm_mode_probed_add(closure->connector, newmode);
                closure->modes++;
                closure->preferred = 0;
@@ -2520,9 +2529,9 @@ cea_mode_alternate_clock(const struct drm_display_mode *cea_mode)
         * and the 60Hz variant otherwise.
         */
        if (cea_mode->vdisplay == 240 || cea_mode->vdisplay == 480)
-               clock = clock * 1001 / 1000;
+               clock = DIV_ROUND_CLOSEST(clock * 1001, 1000);
        else
-               clock = DIV_ROUND_UP(clock * 1000, 1001);
+               clock = DIV_ROUND_CLOSEST(clock * 1000, 1001);
 
        return clock;
 }
@@ -3094,6 +3103,45 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid)
        return modes;
 }
 
+static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode)
+{
+       const struct drm_display_mode *cea_mode;
+       int clock1, clock2, clock;
+       u8 mode_idx;
+       const char *type;
+
+       mode_idx = drm_match_cea_mode(mode) - 1;
+       if (mode_idx < ARRAY_SIZE(edid_cea_modes)) {
+               type = "CEA";
+               cea_mode = &edid_cea_modes[mode_idx];
+               clock1 = cea_mode->clock;
+               clock2 = cea_mode_alternate_clock(cea_mode);
+       } else {
+               mode_idx = drm_match_hdmi_mode(mode) - 1;
+               if (mode_idx < ARRAY_SIZE(edid_4k_modes)) {
+                       type = "HDMI";
+                       cea_mode = &edid_4k_modes[mode_idx];
+                       clock1 = cea_mode->clock;
+                       clock2 = hdmi_mode_alternate_clock(cea_mode);
+               } else {
+                       return;
+               }
+       }
+
+       /* pick whichever is closest */
+       if (abs(mode->clock - clock1) < abs(mode->clock - clock2))
+               clock = clock1;
+       else
+               clock = clock2;
+
+       if (mode->clock == clock)
+               return;
+
+       DRM_DEBUG("detailed mode matches %s VIC %d, adjusting clock %d -> %d\n",
+                 type, mode_idx + 1, mode->clock, clock);
+       mode->clock = clock;
+}
+
 static void
 parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db)
 {
@@ -3349,7 +3397,7 @@ EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
  * the sink doesn't support audio or video.
  */
 int drm_av_sync_delay(struct drm_connector *connector,
-                     struct drm_display_mode *mode)
+                     const struct drm_display_mode *mode)
 {
        int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
        int a, v;
@@ -3384,7 +3432,6 @@ EXPORT_SYMBOL(drm_av_sync_delay);
 /**
  * drm_select_eld - select one ELD from multiple HDMI/DP sinks
  * @encoder: the encoder just changed display mode
- * @mode: the adjusted display mode
  *
  * It's possible for one encoder to be associated with multiple HDMI/DP sinks.
  * The policy is now hard coded to simply use the first HDMI/DP sink's ELD.
@@ -3392,8 +3439,7 @@ EXPORT_SYMBOL(drm_av_sync_delay);
  * Return: The connector associated with the first HDMI/DP sink that has ELD
  * attached to it.
  */
-struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
-                                    struct drm_display_mode *mode)
+struct drm_connector *drm_select_eld(struct drm_encoder *encoder)
 {
        struct drm_connector *connector;
        struct drm_device *dev = encoder->dev;
index caee9d7..f68b1d4 100644 (file)
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+
+static bool drm_fbdev_emulation = true;
+module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
+MODULE_PARM_DESC(fbdev_emulation,
+                "Enable legacy fbdev emulation [default=true]");
 
 static LINUX_LIST_HEAD(kernel_fb_helper_list);
 
@@ -96,6 +103,9 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
        struct drm_connector *connector;
        int i;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        mutex_lock(&dev->mode_config.mutex);
        drm_for_each_connector(connector, dev) {
                struct drm_fb_helper_connector *fb_helper_connector;
@@ -126,6 +136,9 @@ int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_
        struct drm_fb_helper_connector **temp;
        struct drm_fb_helper_connector *fb_helper_connector;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
        if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) {
                temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector *) * (fb_helper->connector_count + 1), M_DRM, M_WAITOK);
@@ -181,6 +194,9 @@ int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
        struct drm_fb_helper_connector *fb_helper_connector;
        int i, j;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
 
        for (i = 0; i < fb_helper->connector_count; i++) {
@@ -319,6 +335,72 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
 EXPORT_SYMBOL(drm_fb_helper_debug_leave);
 #endif
 
+static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper)
+{
+       struct drm_device *dev = fb_helper->dev;
+       struct drm_plane *plane;
+       struct drm_atomic_state *state;
+       int i, ret;
+       unsigned plane_mask;
+
+       state = drm_atomic_state_alloc(dev);
+       if (!state)
+               return -ENOMEM;
+
+       state->acquire_ctx = dev->mode_config.acquire_ctx;
+retry:
+       plane_mask = 0;
+       drm_for_each_plane(plane, dev) {
+               struct drm_plane_state *plane_state;
+
+               plane_state = drm_atomic_get_plane_state(state, plane);
+               if (IS_ERR(plane_state)) {
+                       ret = PTR_ERR(plane_state);
+                       goto fail;
+               }
+
+               plane_state->rotation = BIT(DRM_ROTATE_0);
+
+               plane->old_fb = plane->fb;
+               plane_mask |= 1 << drm_plane_index(plane);
+
+               /* disable non-primary: */
+               if (plane->type == DRM_PLANE_TYPE_PRIMARY)
+                       continue;
+
+               ret = __drm_atomic_helper_disable_plane(plane, plane_state);
+               if (ret != 0)
+                       goto fail;
+       }
+
+       for(i = 0; i < fb_helper->crtc_count; i++) {
+               struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
+
+               ret = __drm_atomic_helper_set_config(mode_set, state);
+               if (ret != 0)
+                       goto fail;
+       }
+
+       ret = drm_atomic_commit(state);
+
+fail:
+       drm_atomic_clean_old_fb(dev, plane_mask, ret);
+
+       if (ret == -EDEADLK)
+               goto backoff;
+
+       if (ret != 0)
+               drm_atomic_state_free(state);
+
+       return ret;
+
+backoff:
+       drm_atomic_state_clear(state);
+       drm_atomic_legacy_backoff(state);
+
+       goto retry;
+}
+
 static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 {
        struct drm_device *dev = fb_helper->dev;
@@ -328,6 +410,9 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 
        drm_warn_on_modeset_not_all_locked(dev);
 
+       if (fb_helper->atomic)
+               return restore_fbdev_mode_atomic(fb_helper);
+
        drm_for_each_plane(plane, dev) {
                if (plane->type != DRM_PLANE_TYPE_PRIMARY)
                        drm_plane_force_disable(plane);
@@ -344,11 +429,7 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
                struct drm_crtc *crtc = mode_set->crtc;
                int ret;
 
-               if (crtc->funcs->cursor_set2) {
-                       ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0);
-                       if (ret)
-                               error = true;
-               } else if (crtc->funcs->cursor_set) {
+               if (crtc->funcs->cursor_set) {
                        ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
                        if (ret)
                                error = true;
@@ -368,12 +449,18 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
  * This should be called from driver's drm ->lastclose callback
  * when implementing an fbcon on top of kms using this helper. This ensures that
  * the user isn't greeted with a black screen when e.g. X dies.
+ *
+ * RETURNS:
+ * Zero if everything went ok, negative error code otherwise.
  */
-bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
+int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
 {
        struct drm_device *dev = fb_helper->dev;
-       bool ret;
-       bool do_delayed = false;
+       bool do_delayed;
+       int ret;
+
+       if (!drm_fbdev_emulation)
+               return -ENODEV;
 
        drm_modeset_lock_all(dev);
        ret = restore_fbdev_mode(fb_helper);
@@ -595,6 +682,9 @@ int drm_fb_helper_init(struct drm_device *dev,
        struct drm_crtc *crtc;
        int i;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        if (!max_conn_count)
                return -EINVAL;
 
@@ -628,6 +718,8 @@ int drm_fb_helper_init(struct drm_device *dev,
                i++;
        }
 
+       fb_helper->atomic = !!drm_core_check_feature(dev, DRIVER_ATOMIC);
+
        return 0;
 out_free:
        drm_fb_helper_crtc_free(fb_helper);
@@ -721,6 +813,9 @@ EXPORT_SYMBOL(drm_fb_helper_release_fbi);
 
 void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
 {
+       if (!drm_fbdev_emulation)
+               return;
+
        if (!list_empty(&fb_helper->kernel_fb_list)) {
                list_del(&fb_helper->kernel_fb_list);
                if (list_empty(&kernel_fb_helper_list)) {
@@ -1132,6 +1227,66 @@ int drm_fb_helper_set_par(struct fb_info *info)
 }
 EXPORT_SYMBOL(drm_fb_helper_set_par);
 
+static int pan_display_atomic(struct fb_var_screeninfo *var,
+                             struct fb_info *info)
+{
+       struct drm_fb_helper *fb_helper = info->par;
+       struct drm_device *dev = fb_helper->dev;
+       struct drm_atomic_state *state;
+       struct drm_plane *plane;
+       int i, ret;
+       unsigned plane_mask;
+
+       state = drm_atomic_state_alloc(dev);
+       if (!state)
+               return -ENOMEM;
+
+       state->acquire_ctx = dev->mode_config.acquire_ctx;
+retry:
+       plane_mask = 0;
+       for(i = 0; i < fb_helper->crtc_count; i++) {
+               struct drm_mode_set *mode_set;
+
+               mode_set = &fb_helper->crtc_info[i].mode_set;
+
+               mode_set->x = var->xoffset;
+               mode_set->y = var->yoffset;
+
+               ret = __drm_atomic_helper_set_config(mode_set, state);
+               if (ret != 0)
+                       goto fail;
+
+               plane = mode_set->crtc->primary;
+               plane_mask |= drm_plane_index(plane);
+               plane->old_fb = plane->fb;
+       }
+
+       ret = drm_atomic_commit(state);
+       if (ret != 0)
+               goto fail;
+
+       info->var.xoffset = var->xoffset;
+       info->var.yoffset = var->yoffset;
+
+
+fail:
+       drm_atomic_clean_old_fb(dev, plane_mask, ret);
+
+       if (ret == -EDEADLK)
+               goto backoff;
+
+       if (ret != 0)
+               drm_atomic_state_free(state);
+
+       return ret;
+
+backoff:
+       drm_atomic_state_clear(state);
+       drm_atomic_legacy_backoff(state);
+
+       goto retry;
+}
+
 /**
  * drm_fb_helper_pan_display - implementation for ->fb_pan_display
  * @var: updated screen information
@@ -1155,6 +1310,11 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
                return -EBUSY;
        }
 
+       if (fb_helper->atomic) {
+               ret = pan_display_atomic(var, info);
+               goto unlock;
+       }
+
        for (i = 0; i < fb_helper->crtc_count; i++) {
                modeset = &fb_helper->crtc_info[i].mode_set;
 
@@ -1169,6 +1329,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
                        }
                }
        }
+unlock:
        drm_modeset_unlock_all(dev);
        return ret;
 }
@@ -1987,6 +2148,9 @@ int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
        struct drm_device *dev = fb_helper->dev;
        int count = 0;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        mutex_lock(&dev->mode_config.mutex);
        count = drm_fb_helper_probe_connector_modes(fb_helper,
                                                    dev->mode_config.max_width,
@@ -2030,6 +2194,9 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
        struct drm_device *dev = fb_helper->dev;
        u32 max_width, max_height;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        mutex_lock(&fb_helper->dev->mode_config.mutex);
        if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
                fb_helper->delayed_hotplug = true;
index 1d0e298..9fa59fd 100644 (file)
@@ -725,7 +725,8 @@ EXPORT_SYMBOL(drm_gem_object_release);
 void
 drm_gem_object_free(struct kref *kref)
 {
-       struct drm_gem_object *obj = (struct drm_gem_object *) kref;
+       struct drm_gem_object *obj =
+               container_of(kref, struct drm_gem_object, refcount);
        struct drm_device *dev = obj->dev;
 
        WARN_ON(!mutex_is_locked(&dev->struct_mutex));
index d1cf063..dc1a781 100644 (file)
@@ -76,7 +76,7 @@ int drm_remove_magic(struct drm_device *dev, drm_magic_t magic);
 /* drm_sysfs.c */
 extern struct class *drm_class;
 
-struct class *drm_sysfs_create(struct module *owner, char *name);
+int drm_sysfs_init(void);
 void drm_sysfs_destroy(void);
 struct device *drm_sysfs_minor_alloc(struct drm_minor *minor);
 int drm_sysfs_connector_add(struct drm_connector *connector);
index af8c5e9..d4990ef 100644 (file)
@@ -41,7 +41,7 @@
 static int drm_version(struct drm_device *dev, void *data,
                       struct drm_file *file_priv);
 
-/**
+/*
  * Get the bus id.
  *
  * \param inode device inode.
@@ -66,7 +66,7 @@ static int drm_getunique(struct drm_device *dev, void *data,
        return 0;
 }
 
-/**
+/*
  * Set the bus id.
  *
  * \param inode device inode.
@@ -154,7 +154,7 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
        return 0;
 }
 
-/**
+/*
  * Get a mapping information.
  *
  * \param inode device inode.
@@ -206,7 +206,7 @@ static int drm_getmap(struct drm_device *dev, void *data,
        return 0;
 }
 
-/**
+/*
  * Get client information.
  *
  * \param inode device inode.
@@ -246,7 +246,7 @@ static int drm_getclient(struct drm_device *dev, void *data,
        return -EINVAL;
 }
 
-/**
+/*
  * Get statistics information.
  *
  * \param inode device inode.
@@ -282,7 +282,7 @@ static int drm_getstats(struct drm_device *dev, void *data,
        return 0;
 }
 
-/**
+/*
  * Get device/driver capabilities
  */
 static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
@@ -328,7 +328,7 @@ static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_
        return 0;
 }
 
-/**
+/*
  * Set device/driver capabilities
  */
 static int
@@ -362,7 +362,7 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
        return 0;
 }
 
-/**
+/*
  * Setversion ioctl.
  *
  * \param inode device inode.
@@ -419,7 +419,18 @@ static int drm_setversion(struct drm_device *dev, void *data, struct drm_file *f
        return 0;
 }
 
-/** No-op ioctl. */
+/**
+ * drm_noop - DRM no-op ioctl implemntation
+ * @dev: DRM device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: DRM file for the ioctl call
+ *
+ * This no-op implementation for drm ioctls is useful for deprecated
+ * functionality where we can't return a failure code because existing userspace
+ * checks the result of the ioctl, but doesn't care about the action.
+ *
+ * Always returns successfully with 0.
+ */
 int drm_noop(struct drm_device *dev, void *data,
             struct drm_file *file_priv)
 {
@@ -429,6 +440,28 @@ int drm_noop(struct drm_device *dev, void *data,
 EXPORT_SYMBOL(drm_noop);
 
 /**
+ * drm_invalid_op - DRM invalid ioctl implemntation
+ * @dev: DRM device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: DRM file for the ioctl call
+ *
+ * This no-op implementation for drm ioctls is useful for deprecated
+ * functionality where we really don't want to allow userspace to call the ioctl
+ * any more. This is the case for old ums interfaces for drivers that
+ * transitioned to kms gradually and so kept the old legacy tables around. This
+ * only applies to radeon and i915 kms drivers, other drivers shouldn't need to
+ * use this function.
+ *
+ * Always fails with a return value of -EINVAL.
+ */
+int drm_invalid_op(struct drm_device *dev, void *data,
+                  struct drm_file *file_priv)
+{
+       return -EINVAL;
+}
+EXPORT_SYMBOL(drm_invalid_op);
+
+/*
  * Copy and IOCTL return string to user space
  */
 static int drm_copy_field(char __user *buf, size_t *buf_len, const char *value)
@@ -451,7 +484,7 @@ static int drm_copy_field(char __user *buf, size_t *buf_len, const char *value)
        return 0;
 }
 
-/**
+/*
  * Get version information
  *
  * \param inode device inode.
@@ -483,7 +516,7 @@ static int drm_version(struct drm_device *dev, void *data,
        return err;
 }
 
-/**
+/*
  * drm_ioctl_permit - Check ioctl permissions against caller
  *
  * @flags: ioctl permission flags.
@@ -534,7 +567,7 @@ EXPORT_SYMBOL(drm_ioctl_permit);
                .name = #ioctl                  \
        }
 
-/** Ioctl table */
+/* Ioctl table */
 static const struct drm_ioctl_desc drm_ioctls[] = {
        DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version,
                      DRM_UNLOCKED|DRM_RENDER_ALLOW|DRM_CONTROL_ALLOW),
@@ -651,16 +684,16 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
 #define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
 
 /**
- * Called whenever a process performs an ioctl on /dev/drm.
- *
- * \param inode device inode.
- * \param file_priv DRM file private.
- * \param cmd command.
- * \param arg user argument.
- * \return zero on success or negative number on failure.
+ * drm_ioctl - ioctl callback implementation for DRM drivers
+ * @filp: file this ioctl is called on
+ * @cmd: ioctl cmd number
+ * @arg: user argument
  *
  * Looks up the ioctl function in the ::ioctls table, checking for root
  * previleges if so required, and dispatches to the respective function.
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
  */
 int drm_ioctl(struct dev_ioctl_args *ap)
 {
@@ -672,8 +705,8 @@ int drm_ioctl(struct dev_ioctl_args *ap)
        int retcode = 0;
        caddr_t data = ap->a_data;
        int (*func)(struct drm_device *dev, void *data, struct drm_file *file_priv);
-       int is_driver_ioctl = 0;
        struct drm_file *file_priv;
+       bool is_driver_ioctl;
 
        dev = drm_get_device_from_kdev(kdev);
 
@@ -688,6 +721,8 @@ int drm_ioctl(struct dev_ioctl_args *ap)
        if (drm_device_is_unplugged(dev))
                return ENODEV;
 
+       is_driver_ioctl = nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END;
+
        if (IOCGROUP(cmd) != DRM_IOCTL_BASE) {
                DRM_DEBUG_FIOCTL("Bad ioctl group 0x%x\n", (int)IOCGROUP(cmd));
                return EINVAL;
@@ -712,9 +747,10 @@ int drm_ioctl(struct dev_ioctl_args *ap)
        /* Do not trust userspace, use our own definition */
        func = ioctl->func;
 
-       if (unlikely(func == NULL)) {
-               DRM_DEBUG_FIOCTL("no function\n");
-               return EINVAL;
+       if (unlikely(!func)) {
+               DRM_DEBUG("no function\n");
+               retcode = -EINVAL;
+               goto err_i1;
        }
 
        if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) ||
@@ -722,6 +758,8 @@ int drm_ioctl(struct dev_ioctl_args *ap)
            ((ioctl->flags & DRM_MASTER) && !file_priv->master))
                return EACCES;
 
+       /* Enforce sane locking for kms driver ioctls. Core ioctls are
+        * too messy still. */
        if (is_driver_ioctl) {
                if ((ioctl->flags & DRM_UNLOCKED) == 0)
                        mutex_lock(&drm_global_mutex);
@@ -737,6 +775,7 @@ int drm_ioctl(struct dev_ioctl_args *ap)
                        retcode = EINTR;
        }
 
+      err_i1:
        if (!ioctl)
                DRM_DEBUG_FIOCTL("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02lx, nr=0x%02x\n",
                          DRM_CURRENTPID,
@@ -751,9 +790,15 @@ EXPORT_SYMBOL(drm_ioctl);
 
 /**
  * drm_ioctl_flags - Check for core ioctl and return ioctl permission flags
+ * @nr: ioctl number
+ * @flags: where to return the ioctl permission flags
+ *
+ * This ioctl is only used by the vmwgfx driver to augment the access checks
+ * done by the drm core and insofar a pretty decent layering violation. This
+ * shouldn't be used by any drivers.
  *
- * @nr: Ioctl number.
- * @flags: Where to return the ioctl permission flags
+ * Returns:
+ * True if the @nr corresponds to a DRM core ioctl numer, false otherwise.
  */
 bool drm_ioctl_flags(unsigned int nr, unsigned int *flags)
 {
index 18483f6..9072b70 100644 (file)
@@ -72,22 +72,22 @@ module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
 module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
 module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
 
-static void store_vblank(struct drm_device *dev, int crtc,
+static void store_vblank(struct drm_device *dev, unsigned int pipe,
                         u32 vblank_count_inc,
-                        struct timeval *t_vblank)
+                        struct timeval *t_vblank, u32 last)
 {
-       struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
+       struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
        u32 tslot;
 
        assert_spin_locked(&dev->vblank_time_lock);
 
-       if (t_vblank) {
-               /* All writers hold the spinlock, but readers are serialized by
-                * the latching of vblank->count below.
-                */
-               tslot = vblank->count + vblank_count_inc;
-               vblanktimestamp(dev, crtc, tslot) = *t_vblank;
-       }
+       vblank->last = last;
+
+       /* All writers hold the spinlock, but readers are serialized by
+        * the latching of vblank->count below.
+        */
+       tslot = vblank->count + vblank_count_inc;
+       vblanktimestamp(dev, pipe, tslot) = *t_vblank;
 
        /*
         * vblank timestamp updates are protected on the write side with
@@ -102,13 +102,61 @@ static void store_vblank(struct drm_device *dev, int crtc,
        smp_wmb();
 }
 
+/**
+ * drm_reset_vblank_timestamp - reset the last timestamp to the last vblank
+ * @dev: DRM device
+ * @pipe: index of CRTC for which to reset the timestamp
+ *
+ * Reset the stored timestamp for the current vblank count to correspond
+ * to the last vblank occurred.
+ *
+ * Only to be called from drm_vblank_on().
+ *
+ * Note: caller must hold dev->vbl_lock since this reads & writes
+ * device vblank fields.
+ */
+static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe)
+{
+       u32 cur_vblank;
+       bool rc;
+       struct timeval t_vblank;
+       int count = DRM_TIMESTAMP_MAXRETRIES;
+
+       lockmgr(&dev->vblank_time_lock, LK_EXCLUSIVE);
+
+       /*
+        * sample the current counter to avoid random jumps
+        * when drm_vblank_enable() applies the diff
+        */
+       do {
+               cur_vblank = dev->driver->get_vblank_counter(dev, pipe);
+               rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, 0);
+       } while (cur_vblank != dev->driver->get_vblank_counter(dev, pipe) && --count > 0);
+
+       /*
+        * Only reinitialize corresponding vblank timestamp if high-precision query
+        * available and didn't fail. Otherwise reinitialize delayed at next vblank
+        * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
+        */
+       if (!rc)
+               t_vblank = (struct timeval) {0, 0};
+
+       /*
+        * +1 to make sure user will never see the same
+        * vblank counter value before and after a modeset
+        */
+       store_vblank(dev, pipe, 1, &t_vblank, cur_vblank);
+
+       lockmgr(&dev->vblank_time_lock, LK_RELEASE);
+}
+
 /**
  * drm_update_vblank_count - update the master vblank counter
  * @dev: DRM device
  * @pipe: counter to update
  *
  * Call back into the driver to update the appropriate vblank counter
- * (specified by @crtc).  Deal with wraparound, if it occurred, and
+ * (specified by @pipe).  Deal with wraparound, if it occurred, and
  * update the last read value so we can deal with wraparound on the next
  * call if necessary.
  *
@@ -118,12 +166,15 @@ static void store_vblank(struct drm_device *dev, int crtc,
  * Note: caller must hold dev->vbl_lock since this reads & writes
  * device vblank fields.
  */
-static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe)
+static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
+                                   unsigned long flags)
 {
        struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
        u32 cur_vblank, diff;
        bool rc;
        struct timeval t_vblank;
+       int count = DRM_TIMESTAMP_MAXRETRIES;
+       int framedur_ns = vblank->framedur_ns;
 
        /*
         * Interrupts were disabled prior to this call, so deal with counter
@@ -139,33 +190,54 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe)
         */
        do {
                cur_vblank = dev->driver->get_vblank_counter(dev, pipe);
-               rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, 0);
-       } while (cur_vblank != dev->driver->get_vblank_counter(dev, pipe));
+               rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, flags);
+       } while (cur_vblank != dev->driver->get_vblank_counter(dev, pipe) && --count > 0);
+
+       if (dev->max_vblank_count != 0) {
+               /* trust the hw counter when it's around */
+               diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
+       } else if (rc && framedur_ns) {
+               const struct timeval *t_old;
+               u64 diff_ns;
 
-       /* Deal with counter wrap */
-       diff = cur_vblank - vblank->last;
-       if (cur_vblank < vblank->last) {
-               diff += dev->max_vblank_count + 1;
+               t_old = &vblanktimestamp(dev, pipe, vblank->count);
+               diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old);
 
-               DRM_DEBUG_VBLANK("last_vblank[%u]=0x%x, cur_vblank=0x%x => diff=0x%x\n",
-                         pipe, vblank->last, cur_vblank, diff);
+               /*
+                * Figure out how many vblanks we've missed based
+                * on the difference in the timestamps and the
+                * frame/field duration.
+                */
+               diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
+
+               if (diff == 0 && flags & DRM_CALLED_FROM_VBLIRQ)
+                       DRM_DEBUG("crtc %u: Redundant vblirq ignored."
+                                 " diff_ns = %lld, framedur_ns = %d)\n",
+                                 pipe, (long long) diff_ns, framedur_ns);
+       } else {
+               /* some kind of default for drivers w/o accurate vbl timestamping */
+               diff = (flags & DRM_CALLED_FROM_VBLIRQ) != 0;
        }
 
-       DRM_DEBUG_VBLANK("updating vblank count on crtc %u, missed %d\n",
-                 pipe, diff);
+       DRM_DEBUG_VBLANK("updating vblank count on crtc %u:"
+                 " current=%u, diff=%u, hw=%u hw_last=%u\n",
+                 pipe, vblank->count, diff, cur_vblank, vblank->last);
 
-       if (diff == 0)
+       if (diff == 0) {
+               WARN_ON_ONCE(cur_vblank != vblank->last);
                return;
+       }
 
        /*
         * Only reinitialize corresponding vblank timestamp if high-precision query
-        * available and didn't fail. Otherwise reinitialize delayed at next vblank
-        * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
+        * available and didn't fail, or we were called from the vblank interrupt.
+        * Otherwise reinitialize delayed at next vblank interrupt and assign 0
+        * for now, to mark the vblanktimestamp as invalid.
         */
-       if (!rc)
+       if (!rc && (flags & DRM_CALLED_FROM_VBLIRQ) == 0)
                t_vblank = (struct timeval) {0, 0};
 
-       store_vblank(dev, pipe, diff, &t_vblank);
+       store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
 }
 
 /*
@@ -178,11 +250,6 @@ static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
 {
        struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
        unsigned long irqflags;
-       u32 vblcount;
-       s64 diff_ns;
-       bool vblrc;
-       struct timeval tvblank;
-       int count = DRM_TIMESTAMP_MAXRETRIES;
 
        /* Prevent vblank irq processing while disabling vblank irqs,
         * so no updates of timestamps or count can happen after we've
@@ -190,26 +257,6 @@ static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
         */
        spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
 
-       /*
-        * If the vblank interrupt was already disabled update the count
-        * and timestamp to maintain the appearance that the counter
-        * has been ticking all along until this time. This makes the
-        * count account for the entire time between drm_vblank_on() and
-        * drm_vblank_off().
-        *
-        * But only do this if precise vblank timestamps are available.
-        * Otherwise we might read a totally bogus timestamp since drivers
-        * lacking precise timestamp support rely upon sampling the system clock
-        * at vblank interrupt time. Which obviously won't work out well if the
-        * vblank interrupt is disabled.
-        */
-       if (!vblank->enabled &&
-           drm_get_last_vbltimestamp(dev, pipe, &tvblank, 0)) {
-               drm_update_vblank_count(dev, pipe);
-               spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
-               return;
-       }
-
        /*
         * Only disable vblank interrupts if they're enabled. This avoids
         * calling the ->disable_vblank() operation in atomic context with the
@@ -220,47 +267,13 @@ static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
                vblank->enabled = false;
        }
 
-       /* No further vblank irq's will be processed after
-        * this point. Get current hardware vblank count and
-        * vblank timestamp, repeat until they are consistent.
-        *
-        * FIXME: There is still a race condition here and in
-        * drm_update_vblank_count() which can cause off-by-one
-        * reinitialization of software vblank counter. If gpu
-        * vblank counter doesn't increment exactly at the leading
-        * edge of a vblank interval, then we can lose 1 count if
-        * we happen to execute between start of vblank and the
-        * delayed gpu counter increment.
-        */
-       do {
-               vblank->last = dev->driver->get_vblank_counter(dev, pipe);
-               vblrc = drm_get_last_vbltimestamp(dev, pipe, &tvblank, 0);
-       } while (vblank->last != dev->driver->get_vblank_counter(dev, pipe) && (--count) && vblrc);
-
-       if (!count)
-               vblrc = 0;
-
-       /* Compute time difference to stored timestamp of last vblank
-        * as updated by last invocation of drm_handle_vblank() in vblank irq.
-        */
-       vblcount = vblank->count;
-       diff_ns = timeval_to_ns(&tvblank) -
-                 timeval_to_ns(&vblanktimestamp(dev, pipe, vblcount));
-
-       /* If there is at least 1 msec difference between the last stored
-        * timestamp and tvblank, then we are currently executing our
-        * disable inside a new vblank interval, the tvblank timestamp
-        * corresponds to this new vblank interval and the irq handler
-        * for this vblank didn't run yet and won't run due to our disable.
-        * Therefore we need to do the job of drm_handle_vblank() and
-        * increment the vblank counter by one to account for this vblank.
-        *
-        * Skip this step if there isn't any high precision timestamp
-        * available. In that case we can't account for this and just
-        * hope for the best.
+       /*
+        * Always update the count and timestamp to maintain the
+        * appearance that the counter has been ticking all along until
+        * this time. This makes the count account for the entire time
+        * between drm_vblank_on() and drm_vblank_off().
         */
-       if (vblrc && (abs64(diff_ns) > 1000000))
-               store_vblank(dev, pipe, 1, &tvblank);
+       drm_update_vblank_count(dev, pipe, 0);
 
        spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
 }
@@ -591,19 +604,27 @@ int drm_control(struct drm_device *dev, void *data,
 void drm_calc_timestamping_constants(struct drm_crtc *crtc,
                                     const struct drm_display_mode *mode)
 {
-       int linedur_ns = 0, pixeldur_ns = 0, framedur_ns = 0;
+       struct drm_device *dev = crtc->dev;
+       unsigned int pipe = drm_crtc_index(crtc);
+       struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+       int linedur_ns = 0, framedur_ns = 0;
        int dotclock = mode->crtc_clock;
 
+       if (!dev->num_crtcs)
+               return;
+
+       if (WARN_ON(pipe >= dev->num_crtcs))
+               return;
+
        /* Valid dotclock? */
        if (dotclock > 0) {
                int frame_size = mode->crtc_htotal * mode->crtc_vtotal;
 
                /*
                 * Convert scanline length in pixels and video
-                * dot clock to line duration, frame duration
-                * and pixel duration in nanoseconds:
+                * dot clock to line duration and frame duration
+                * in nanoseconds:
                 */
-               pixeldur_ns = 1000000 / dotclock;
                linedur_ns  = div_u64((u64) mode->crtc_htotal * 1000000, dotclock);
                framedur_ns = div_u64((u64) frame_size * 1000000, dotclock);
 
@@ -616,16 +637,14 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc,
                DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n",
                          crtc->base.id);
 
-       crtc->pixeldur_ns = pixeldur_ns;
-       crtc->linedur_ns  = linedur_ns;
-       crtc->framedur_ns = framedur_ns;
+       vblank->linedur_ns  = linedur_ns;
+       vblank->framedur_ns = framedur_ns;
 
        DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
                  crtc->base.id, mode->crtc_htotal,
                  mode->crtc_vtotal, mode->crtc_vdisplay);
-       DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d, pixeldur %d\n",
-                 crtc->base.id, dotclock, framedur_ns,
-                 linedur_ns, pixeldur_ns);
+       DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n",
+                 crtc->base.id, dotclock, framedur_ns, linedur_ns);
 }
 EXPORT_SYMBOL(drm_calc_timestamping_constants);
 
@@ -639,7 +658,6 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
  * @flags: Flags to pass to driver:
  *         0 = Default,
  *         DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler
- * @refcrtc: CRTC which defines scanout timing
  * @mode: mode which defines the scanout timings
  *
  * Implements calculation of exact vblank timestamps from given drm_display_mode
@@ -680,15 +698,14 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
                                          int *max_error,
                                          struct timeval *vblank_time,
                                          unsigned flags,
-                                         const struct drm_crtc *refcrtc,
                                          const struct drm_display_mode *mode)
 {
        struct timeval tv_etime;
        ktime_t stime, etime;
-       int vbl_status;
+       unsigned int vbl_status;
+       int ret = DRM_VBLANKTIME_SCANOUTPOS_METHOD;
        int vpos, hpos, i;
-       int framedur_ns, linedur_ns, pixeldur_ns, delta_ns, duration_ns;
-       bool invbl;
+       int delta_ns, duration_ns;
 
        if (pipe >= dev->num_crtcs) {
                DRM_ERROR("Invalid crtc %u\n", pipe);
@@ -701,15 +718,10 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
                return -EIO;
        }
 
-       /* Durations of frames, lines, pixels in nanoseconds. */
-       framedur_ns = refcrtc->framedur_ns;
-       linedur_ns  = refcrtc->linedur_ns;
-       pixeldur_ns = refcrtc->pixeldur_ns;
-
        /* If mode timing undefined, just return as no-op:
         * Happens during initial modesetting of a crtc.
         */
-       if (framedur_ns == 0) {
+       if (mode->crtc_clock == 0) {
                DRM_DEBUG_VBLANK("crtc %u: Noop due to uninitialized mode.\n", pipe);
                return -EAGAIN;
        }
@@ -726,12 +738,14 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
                 * Get vertical and horizontal scanout position vpos, hpos,
                 * and bounding timestamps stime, etime, pre/post query.
                 */
-               vbl_status = dev->driver->get_scanout_position(dev, pipe, flags, &vpos,
-                                                              &hpos, &stime, &etime);
+               vbl_status = dev->driver->get_scanout_position(dev, pipe, flags,
+                                                              &vpos, &hpos,
+                                                              &stime, &etime,
+                                                              mode);
 
                /* Return as no-op if scanout query unsupported or failed. */
                if (!(vbl_status & DRM_SCANOUTPOS_VALID)) {
-                       DRM_DEBUG_VBLANK("crtc %u : scanoutpos query failed [%d].\n",
+                       DRM_DEBUG_VBLANK("crtc %u : scanoutpos query failed [0x%x].\n",
                                  pipe, vbl_status);
                        return -EIO;
                }
@@ -758,13 +772,15 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
         * within vblank area, counting down the number of lines until
         * start of scanout.
         */
-       invbl = vbl_status & DRM_SCANOUTPOS_IN_VBLANK;
+       if (vbl_status & DRM_SCANOUTPOS_IN_VBLANK)
+               ret |= DRM_VBLANKTIME_IN_VBLANK;
 
        /* Convert scanout position into elapsed time at raw_time query
         * since start of scanout at first display scanline. delta_ns
         * can be negative if start of scanout hasn't happened yet.
         */
-       delta_ns = vpos * linedur_ns + hpos * pixeldur_ns;
+       delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos),
+                          mode->crtc_clock);
 
        if (!drm_timestamp_monotonic)
                etime = ktime_mono_to_real(etime);
@@ -780,17 +796,13 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
                etime = ktime_sub_ns(etime, delta_ns);
        *vblank_time = ktime_to_timeval(etime);
 
-       DRM_DEBUG_VBLANK("crtc %u : v %d p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
-                 pipe, (int)vbl_status, hpos, vpos,
+       DRM_DEBUG_VBLANK("crtc %u : v 0x%x p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
+                 pipe, vbl_status, hpos, vpos,
                  (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
                  (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
                  duration_ns/1000, i);
 
-       vbl_status = DRM_VBLANKTIME_SCANOUTPOS_METHOD;
-       if (invbl)
-               vbl_status |= DRM_VBLANKTIME_IN_VBLANK;
-
-       return vbl_status;
+       return ret;
 }
 EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
 
@@ -861,7 +873,7 @@ drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
  * Returns:
  * The software vblank counter.
  */
-u32 drm_vblank_count(struct drm_device *dev, int pipe)
+u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
 {
        struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 
@@ -902,11 +914,14 @@ EXPORT_SYMBOL(drm_crtc_vblank_count);
  * vblank events since the system was booted, including lost events due to
  * modesetting activity. Returns corresponding system timestamp of the time
  * of the vblank interval that corresponds to the current vblank counter value.
+ *
+ * This is the legacy version of drm_crtc_vblank_count_and_time().
  */
 u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
                              struct timeval *vblanktime)
 {
        struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+       int count = DRM_TIMESTAMP_MAXRETRIES;
        u32 cur_vblank;
 
        if (WARN_ON(pipe >= dev->num_crtcs))
@@ -922,19 +937,39 @@ u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
                smp_rmb();
                *vblanktime = vblanktimestamp(dev, pipe, cur_vblank);
                smp_rmb();
-       } while (cur_vblank != vblank->count);
+       } while (cur_vblank != vblank->count && --count > 0);
 
        return cur_vblank;
 }
 EXPORT_SYMBOL(drm_vblank_count_and_time);
 
+/**
+ * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
+ *     and the system timestamp corresponding to that vblank counter value
+ * @crtc: which counter to retrieve
+ * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
+ *
+ * Fetches the "cooked" vblank count value that represents the number of
+ * vblank events since the system was booted, including lost events due to
+ * modesetting activity. Returns corresponding system timestamp of the time
+ * of the vblank interval that corresponds to the current vblank counter value.
+ *
+ * This is the native KMS version of drm_vblank_count_and_time().
+ */
+u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
+                                  struct timeval *vblanktime)
+{
+       return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
+                                        vblanktime);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
+
 static void send_vblank_event(struct drm_device *dev,
                struct drm_pending_vblank_event *e,
                unsigned long seq, struct timeval *now)
 {
-#if 0
-       WARN_ON_SMP(!spin_is_locked(&dev->event_lock));
-#endif
+       assert_spin_locked(&dev->event_lock);
+
        e->event.sequence = seq;
        e->event.tv_sec = now->tv_sec;
        e->event.tv_usec = now->tv_usec;
@@ -949,6 +984,57 @@ static void send_vblank_event(struct drm_device *dev,
                                         e->event.sequence);
 }
 
+/**
+ * drm_arm_vblank_event - arm vblank event after pageflip
+ * @dev: DRM device
+ * @pipe: CRTC index
+ * @e: the event to prepare to send
+ *
+ * A lot of drivers need to generate vblank events for the very next vblank
+ * interrupt. For example when the page flip interrupt happens when the page
+ * flip gets armed, but not when it actually executes within the next vblank
+ * period. This helper function implements exactly the required vblank arming
+ * behaviour.
+ *
+ * Caller must hold event lock. Caller must also hold a vblank reference for
+ * the event @e, which will be dropped when the next vblank arrives.
+ *
+ * This is the legacy version of drm_crtc_arm_vblank_event().
+ */
+void drm_arm_vblank_event(struct drm_device *dev, unsigned int pipe,
+                         struct drm_pending_vblank_event *e)
+{
+       assert_spin_locked(&dev->event_lock);
+
+       e->pipe = pipe;
+       e->event.sequence = drm_vblank_count(dev, pipe);
+       list_add_tail(&e->base.link, &dev->vblank_event_list);
+}
+EXPORT_SYMBOL(drm_arm_vblank_event);
+
+/**
+ * drm_crtc_arm_vblank_event - arm vblank event after pageflip
+ * @crtc: the source CRTC of the vblank event
+ * @e: the event to send
+ *
+ * A lot of drivers need to generate vblank events for the very next vblank
+ * interrupt. For example when the page flip interrupt happens when the page
+ * flip gets armed, but not when it actually executes within the next vblank
+ * period. This helper function implements exactly the required vblank arming
+ * behaviour.
+ *
+ * Caller must hold event lock. Caller must also hold a vblank reference for
+ * the event @e, which will be dropped when the next vblank arrives.
+ *
+ * This is the native KMS version of drm_arm_vblank_event().
+ */
+void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
+                              struct drm_pending_vblank_event *e)
+{
+       drm_arm_vblank_event(crtc->dev, drm_crtc_index(crtc), e);
+}
+EXPORT_SYMBOL(drm_crtc_arm_vblank_event);
+
 /**
  * drm_send_vblank_event - helper to send vblank event after pageflip
  * @dev: DRM device
@@ -1026,7 +1112,7 @@ static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
                        atomic_dec(&vblank->refcount);
                else {
                        vblank->enabled = true;
-                       drm_update_vblank_count(dev, pipe);
+                       drm_update_vblank_count(dev, pipe, 0);
                }
        }
 
@@ -1147,8 +1233,8 @@ EXPORT_SYMBOL(drm_crtc_vblank_put);
  * @dev: DRM device
  * @pipe: CRTC index
  *
- * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
- * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
+ * This waits for one vblank to pass on @pipe, using the irq driver interfaces.
+ * It is a failure to call this when the vblank irq for @pipe is disabled, e.g.
  * due to lack of driver support or because the crtc is off.
  */
 void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
@@ -1269,7 +1355,7 @@ EXPORT_SYMBOL(drm_crtc_vblank_off);
 
 /**
  * drm_crtc_vblank_reset - reset vblank state to off on a CRTC
- * @drm_crtc: CRTC in question
+ * @crtc: CRTC in question
  *
  * Drivers can use this function to reset the vblank state to off at load time.
  * Drivers should use this together with the drm_crtc_vblank_off() and
@@ -1277,12 +1363,12 @@ EXPORT_SYMBOL(drm_crtc_vblank_off);
  * drm_crtc_vblank_off() is that this function doesn't save the vblank counter
  * and hence doesn't need to call any driver hooks.
  */
-void drm_crtc_vblank_reset(struct drm_crtc *drm_crtc)
+void drm_crtc_vblank_reset(struct drm_crtc *crtc)
 {
-       struct drm_device *dev = drm_crtc->dev;
+       struct drm_device *dev = crtc->dev;
        unsigned long irqflags;
-       int crtc = drm_crtc_index(drm_crtc);
-       struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
+       unsigned int pipe = drm_crtc_index(crtc);
+       struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 
        spin_lock_irqsave(&dev->vbl_lock, irqflags);
        /*
@@ -1326,16 +1412,8 @@ void drm_vblank_on(struct drm_device *dev, unsigned int pipe)
                vblank->inmodeset = 0;
        }
 
-       /*
-        * sample the current counter to avoid random jumps
-        * when drm_vblank_enable() applies the diff
-        *
-        * -1 to make sure user will never see the same
-        * vblank counter value before and after a modeset
-        */
-       vblank->last =
-               (dev->driver->get_vblank_counter(dev, pipe) - 1) &
-               dev->max_vblank_count;
+       drm_reset_vblank_timestamp(dev, pipe);
+
        /*
         * re-enable interrupts if there are users left, or the
         * user wishes vblank interrupts to be enabled all the time.
@@ -1734,9 +1812,6 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
 bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
 {
        struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-       u32 vblcount;
-       s64 diff_ns;
-       struct timeval tvblank;
        unsigned long irqflags;
 
        if (WARN_ON_ONCE(!dev->num_crtcs))
@@ -1760,32 +1835,7 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
                return false;
        }
 
-       /* Fetch corresponding timestamp for this vblank interval from
-        * driver and store it in proper slot of timestamp ringbuffer.
-        */
-
-       /* Get current timestamp and count. */
-       vblcount = vblank->count;
-       drm_get_last_vbltimestamp(dev, pipe, &tvblank, DRM_CALLED_FROM_VBLIRQ);
-
-       /* Compute time difference to timestamp of last vblank */
-       diff_ns = timeval_to_ns(&tvblank) -
-                 timeval_to_ns(&vblanktimestamp(dev, pipe, vblcount));
-
-       /* Update vblank timestamp and count if at least
-        * DRM_REDUNDANT_VBLIRQ_THRESH_NS nanoseconds
-        * difference between last stored timestamp and current
-        * timestamp. A smaller difference means basically
-        * identical timestamps. Happens if this vblank has
-        * been already processed and this is a redundant call,
-        * e.g., due to spurious vblank interrupts. We need to
-        * ignore those for accounting.
-        */
-       if (abs64(diff_ns) > DRM_REDUNDANT_VBLIRQ_THRESH_NS)
-               store_vblank(dev, pipe, 1, &tvblank);
-       else
-               DRM_DEBUG_VBLANK("crtc %u: Redundant vblirq ignored. diff_ns = %d\n",
-                         pipe, (int) diff_ns);
+       drm_update_vblank_count(dev, pipe, DRM_CALLED_FROM_VBLIRQ);
 
        lockmgr(&dev->vblank_time_lock, LK_RELEASE);
 
@@ -1815,3 +1865,20 @@ bool drm_crtc_handle_vblank(struct drm_crtc *crtc)
        return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc));
 }
 EXPORT_SYMBOL(drm_crtc_handle_vblank);
+
+/**
+ * drm_vblank_no_hw_counter - "No hw counter" implementation of .get_vblank_counter()
+ * @dev: DRM device
+ * @pipe: CRTC for which to read the counter
+ *
+ * Drivers can plug this into the .get_vblank_counter() function if
+ * there is no useable hardware frame counter available.
+ *
+ * Returns:
+ * 0
+ */
+u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe)
+{
+       return 0;
+}
+EXPORT_SYMBOL(drm_vblank_no_hw_counter);
index 355e62c..2eca137 100644 (file)
@@ -121,14 +121,8 @@ int drm_legacy_lock(struct drm_device *dev, void *data,
         * really probably not the correct answer but lets us debug xkb
         * xserver for now */
        if (!file_priv->is_master) {
-               sigemptyset(&dev->sigmask);
-               sigaddset(&dev->sigmask, SIGSTOP);
-               sigaddset(&dev->sigmask, SIGTSTP);
-               sigaddset(&dev->sigmask, SIGTTIN);
-               sigaddset(&dev->sigmask, SIGTTOU);
                dev->sigdata.context = lock->context;
                dev->sigdata.lock = master->lock.hw_lock;
-               block_all_signals(drm_notifier, dev, &dev->sigmask);
        }
 
        if (dev->driver->dma_quiescent && (lock->flags & _DRM_LOCK_QUIESCENT))
index 1d8fc17..0604558 100644 (file)
 
 #include <linux/highmem.h>
 #include <linux/export.h>
+#include <linux/kconfig.h>
 #include <drm/drmP.h>
 #include "drm_legacy.h"
 
-#if __OS_HAS_AGP
+#define CONFIG_AGP 1
+#if IS_ENABLED(CONFIG_AGP)
 
 #ifdef HAVE_PAGE_AGP
 # include <asm/agp.h>
@@ -51,8 +53,8 @@
 #endif
 
 
-#else  /*  __OS_HAS_AGP  */
-#endif                         /* agp */
+#else /*  CONFIG_AGP  */
+#endif /* CONFIG_AGP */
 
 void drm_legacy_ioremap(struct drm_local_map *map, struct drm_device *dev)
 {
@@ -74,31 +76,3 @@ void drm_legacy_ioremapfree(struct drm_local_map *map, struct drm_device *dev)
        pmap_unmapdev((vm_offset_t) map->handle, map->size);
 }
 EXPORT_SYMBOL(drm_legacy_ioremapfree);
-
-int
-drm_mtrr_add(unsigned long offset, size_t size, int flags)
-{
-       int act;
-       struct mem_range_desc mrdesc;
-
-       mrdesc.mr_base = offset;
-       mrdesc.mr_len = size;
-       mrdesc.mr_flags = flags;
-       act = MEMRANGE_SET_UPDATE;
-       strlcpy(mrdesc.mr_owner, "drm", sizeof(mrdesc.mr_owner));
-       return mem_range_attr_set(&mrdesc, &act);
-}
-
-int
-drm_mtrr_del(int __unused handle, unsigned long offset, size_t size, int flags)
-{
-       int act;
-       struct mem_range_desc mrdesc;
-
-       mrdesc.mr_base = offset;
-       mrdesc.mr_len = size;
-       mrdesc.mr_flags = flags;
-       act = MEMRANGE_SET_REMOVE;
-       strlcpy(mrdesc.mr_owner, "drm", sizeof(mrdesc.mr_owner));
-       return mem_range_attr_set(&mrdesc, &act);
-}
index 850f039..9aa3ada 100644 (file)
@@ -317,12 +317,12 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
        if (adj_end > end)
                adj_end = end;
 
-       if (flags & DRM_MM_CREATE_TOP)
-               adj_start = adj_end - size;
-
        if (mm->color_adjust)
                mm->color_adjust(hole_node, color, &adj_start, &adj_end);
 
+       if (flags & DRM_MM_CREATE_TOP)
+               adj_start = adj_end - size;
+
        if (alignment) {
                unsigned tmp = adj_start % alignment;
                if (tmp) {
index 413a3e2..d747d3c 100644 (file)
@@ -309,6 +309,10 @@ static inline int modeset_lock(struct drm_modeset_lock *lock,
        WARN_ON(ctx->contended);
 
        if (ctx->trylock_only) {
+#if 0
+               lockdep_assert_held(&ctx->ww_ctx);
+#endif
+
                if (!ww_mutex_trylock(&lock->mutex))
                        return -EBUSY;
                else
index 5e5a07a..d384ebc 100644 (file)
@@ -426,7 +426,7 @@ int drm_plane_helper_commit(struct drm_plane *plane,
 
        if (plane_funcs->prepare_fb && plane_state->fb &&
            plane_state->fb != old_fb) {
-               ret = plane_funcs->prepare_fb(plane, plane_state->fb,
+               ret = plane_funcs->prepare_fb(plane,
                                              plane_state);
                if (ret)
                        goto out;
@@ -479,8 +479,8 @@ int drm_plane_helper_commit(struct drm_plane *plane,
                ret = 0;
        }
 
-       if (plane_funcs->cleanup_fb && old_fb)
-               plane_funcs->cleanup_fb(plane, old_fb, plane_state);
+       if (plane_funcs->cleanup_fb)
+               plane_funcs->cleanup_fb(plane, plane_state);
 out:
        if (plane_state) {
                if (plane->funcs->atomic_destroy_state)
index bfd217d..822f7c8 100644 (file)
@@ -201,7 +201,8 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
                mode_flags |= DRM_MODE_FLAG_3D_MASK;
 
        list_for_each_entry(mode, &connector->modes, head) {
-               mode->status = drm_mode_validate_basic(mode);
+               if (mode->status == MODE_OK)
+                       mode->status = drm_mode_validate_basic(mode);
 
                if (mode->status == MODE_OK)
                        mode->status = drm_mode_validate_size(mode, maxX, maxY);
index 631f5af..531ac4c 100644 (file)
@@ -330,7 +330,7 @@ void drm_rect_rotate(struct drm_rect *r,
                }
        }
 
-       switch (rotation & 0xf) {
+       switch (rotation & DRM_ROTATE_MASK) {
        case BIT(DRM_ROTATE_0):
                break;
        case BIT(DRM_ROTATE_90):
@@ -390,7 +390,7 @@ void drm_rect_rotate_inv(struct drm_rect *r,
 {
        struct drm_rect tmp;
 
-       switch (rotation & 0xf) {
+       switch (rotation & DRM_ROTATE_MASK) {
        case BIT(DRM_ROTATE_0):
                break;
        case BIT(DRM_ROTATE_90):
index 8941ad3..13e9616 100644 (file)
@@ -105,7 +105,7 @@ void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr)
 EXPORT_SYMBOL(drm_vma_offset_manager_destroy);
 
 /**
- * drm_vma_offset_lookup() - Find node in offset space
+ * drm_vma_offset_lookup_locked() - Find node in offset space
  * @mgr: Manager object
  * @start: Start address for object (page-based)
  * @pages: Size of object (page-based)
index c57982a..ae79bef 100644 (file)
@@ -29,6 +29,10 @@ SRCS +=      i915_cmd_parser.c \
        intel_ringbuffer.c \
        intel_uncore.c
 
+# general-purpose microcontroller (GuC) support
+SRCS += intel_guc_loader.c \
+       i915_guc_submission.c
+
 # autogenerated null render state
 SRCS +=        intel_renderstate_gen6.c \
        intel_renderstate_gen7.c \
index 3121633..0e2c1b9 100644 (file)
@@ -94,8 +94,8 @@ struct intel_dvo_dev_ops {
         * after this function is called.
         */
        void (*mode_set)(struct intel_dvo_device *dvo,
-                        struct drm_display_mode *mode,
-                        struct drm_display_mode *adjusted_mode);
+                        const struct drm_display_mode *mode,
+                        const struct drm_display_mode *adjusted_mode);
 
        /*
         * Probe for a connected output, and return detect_status.
index 11e7680..e3c0698 100644 (file)
@@ -258,8 +258,8 @@ static enum drm_mode_status ch7017_mode_valid(struct intel_dvo_device *dvo,
 }
 
 static void ch7017_mode_set(struct intel_dvo_device *dvo,
-                           struct drm_display_mode *mode,
-                           struct drm_display_mode *adjusted_mode)
+                           const struct drm_display_mode *mode,
+                           const struct drm_display_mode *adjusted_mode)
 {
        uint8_t lvds_pll_feedback_div, lvds_pll_vco_control;
        uint8_t outputs_enable, lvds_control_2, lvds_power_down;
index da65421..a25d855 100644 (file)
@@ -284,8 +284,8 @@ static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo,
 }
 
 static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
-                           struct drm_display_mode *mode,
-                           struct drm_display_mode *adjusted_mode)
+                           const struct drm_display_mode *mode,
+                           const struct drm_display_mode *adjusted_mode)
 {
        uint8_t tvco, tpcp, tpd, tlpf, idf;
 
index 9b826c4..226e4a6 100644 (file)
@@ -400,8 +400,8 @@ static bool ivch_get_hw_state(struct intel_dvo_device *dvo)
 }
 
 static void ivch_mode_set(struct intel_dvo_device *dvo,
-                         struct drm_display_mode *mode,
-                         struct drm_display_mode *adjusted_mode)
+                         const struct drm_display_mode *mode,
+                         const struct drm_display_mode *adjusted_mode)
 {
        struct ivch_priv *priv = dvo->dev_priv;
        uint16_t vr40 = 0;
@@ -420,16 +420,16 @@ static void ivch_mode_set(struct intel_dvo_device *dvo,
        vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE |
                VR40_HORIZONTAL_INTERP_ENABLE);
 
-       if (mode->hdisplay != adjusted_mode->hdisplay ||
-           mode->vdisplay != adjusted_mode->vdisplay) {
+       if (mode->hdisplay != adjusted_mode->crtc_hdisplay ||
+           mode->vdisplay != adjusted_mode->crtc_vdisplay) {
                uint16_t x_ratio, y_ratio;
 
                vr01 |= VR01_PANEL_FIT_ENABLE;
                vr40 |= VR40_CLOCK_GATING_ENABLE;
                x_ratio = (((mode->hdisplay - 1) << 16) /
-                          (adjusted_mode->hdisplay - 1)) >> 2;
+                          (adjusted_mode->crtc_hdisplay - 1)) >> 2;
                y_ratio = (((mode->vdisplay - 1) << 16) /
-                          (adjusted_mode->vdisplay - 1)) >> 2;
+                          (adjusted_mode->crtc_vdisplay - 1)) >> 2;
                ivch_write(dvo, VR42, x_ratio);
                ivch_write(dvo, VR41, y_ratio);
        } else {
index b0b2b89..a022c12 100644 (file)
@@ -555,8 +555,8 @@ static enum drm_mode_status ns2501_mode_valid(struct intel_dvo_device *dvo,
 }
 
 static void ns2501_mode_set(struct intel_dvo_device *dvo,
-                           struct drm_display_mode *mode,
-                           struct drm_display_mode *adjusted_mode)
+                           const struct drm_display_mode *mode,
+                           const struct drm_display_mode *adjusted_mode)
 {
        const struct ns2501_configuration *conf;
        struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
index f807b2e..cd455a5 100644 (file)
@@ -199,8 +199,8 @@ static enum drm_mode_status sil164_mode_valid(struct intel_dvo_device *dvo,
 }
 
 static void sil164_mode_set(struct intel_dvo_device *dvo,
-                           struct drm_display_mode *mode,
-                           struct drm_display_mode *adjusted_mode)
+                           const struct drm_display_mode *mode,
+                           const struct drm_display_mode *adjusted_mode)
 {
        /* As long as the basics are set up, since we don't have clock
         * dependencies in the mode setup, we can just leave the
index 60cc184..93a637c 100644 (file)
@@ -231,8 +231,8 @@ static enum drm_mode_status tfp410_mode_valid(struct intel_dvo_device *dvo,
 }
 
 static void tfp410_mode_set(struct intel_dvo_device *dvo,
-                           struct drm_display_mode *mode,
-                           struct drm_display_mode *adjusted_mode)
+                           const struct drm_display_mode *mode,
+                           const struct drm_display_mode *adjusted_mode)
 {
        /* As long as the basics are set up, since we don't have clock dependencies
        * in the mode setup, we can just leave the registers alone and everything
index 0dbb5b0..95ff77c 100644 (file)
@@ -94,7 +94,7 @@
 #define CMD(op, opm, f, lm, fl, ...)                           \
        {                                                       \
                .flags = (fl) | ((f) ? CMD_DESC_FIXED : 0),     \
-               .cmd = { (op), (opm) },                         \
+               .cmd = { (op), (opm) },                         \
                .length = { (lm) },                             \
                __VA_ARGS__                                     \
        }
@@ -124,14 +124,14 @@ static const struct drm_i915_cmd_descriptor common_cmds[] = {
        CMD(  MI_STORE_DWORD_INDEX,             SMI,   !F,  0xFF,   R  ),
        CMD(  MI_LOAD_REGISTER_IMM(1),          SMI,   !F,  0xFF,   W,
              .reg = { .offset = 1, .mask = 0x007FFFFC, .step = 2 }    ),
-       CMD(  MI_STORE_REGISTER_MEM(1),         SMI,   !F,  0xFF,   W | B,
+       CMD(  MI_STORE_REGISTER_MEM,            SMI,    F,  3,     W | B,
              .reg = { .offset = 1, .mask = 0x007FFFFC },
              .bits = {{
                        .offset = 0,
                        .mask = MI_GLOBAL_GTT,
                        .expected = 0,
              }},                                                      ),
-       CMD(  MI_LOAD_REGISTER_MEM(1),             SMI,   !F,  0xFF,   W | B,
+       CMD(  MI_LOAD_REGISTER_MEM,             SMI,    F,  3,     W | B,
              .reg = { .offset = 1, .mask = 0x007FFFFC },
              .bits = {{
                        .offset = 0,
@@ -448,6 +448,9 @@ static const struct drm_i915_reg_descriptor gen7_render_regs[] = {
        REG32(GEN7_3DPRIM_INSTANCE_COUNT),
        REG32(GEN7_3DPRIM_START_INSTANCE),
        REG32(GEN7_3DPRIM_BASE_VERTEX),
+       REG32(GEN7_GPGPU_DISPATCHDIMX),
+       REG32(GEN7_GPGPU_DISPATCHDIMY),
+       REG32(GEN7_GPGPU_DISPATCHDIMZ),
        REG64(GEN7_SO_NUM_PRIMS_WRITTEN(0)),
        REG64(GEN7_SO_NUM_PRIMS_WRITTEN(1)),
        REG64(GEN7_SO_NUM_PRIMS_WRITTEN(2)),
@@ -1031,7 +1034,7 @@ static bool check_cmd(const struct intel_engine_cs *ring,
                         * only MI_LOAD_REGISTER_IMM commands.
                         */
                        if (reg_addr == OACONTROL) {
-                               if (desc->cmd.value == MI_LOAD_REGISTER_MEM(1)) {
+                               if (desc->cmd.value == MI_LOAD_REGISTER_MEM) {
                                        DRM_DEBUG_DRIVER("CMD: Rejected LRM to OACONTROL\n");
                                        return false;
                                }
@@ -1045,7 +1048,7 @@ static bool check_cmd(const struct intel_engine_cs *ring,
                         * allowed mask/value pair given in the whitelist entry.
                         */
                        if (reg->mask) {
-                               if (desc->cmd.value == MI_LOAD_REGISTER_MEM(1)) {
+                               if (desc->cmd.value == MI_LOAD_REGISTER_MEM) {
                                        DRM_DEBUG_DRIVER("CMD: Rejected LRM to masked register 0x%08X\n",
                                                         reg_addr);
                                        return false;
@@ -1223,6 +1226,8 @@ int i915_cmd_parser_get_version(void)
         * 2. Allow access to the MI_PREDICATE_SRC0 and
         *    MI_PREDICATE_SRC1 registers.
         * 3. Allow access to the GPGPU_THREADS_DISPATCHED register.
+        * 4. L3 atomic chicken bits of HSW_SCRATCH1 and HSW_ROW_CHICKEN3.
+        * 5. GPGPU dispatch compute indirect registers.
         */
-       return 3;
+       return 5;
 }
index b805090..ad46862 100644 (file)
@@ -60,7 +60,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
                value = 1;
                break;
        case I915_PARAM_NUM_FENCES_AVAIL:
-               value = dev_priv->num_fence_regs - dev_priv->fence_reg_start;
+               value = dev_priv->num_fence_regs;
                break;
        case I915_PARAM_HAS_OVERLAY:
                value = dev_priv->overlay ? 1 : 0;
@@ -159,35 +159,6 @@ static int i915_getparam(struct drm_device *dev, void *data,
        return 0;
 }
 
-static int i915_setparam(struct drm_device *dev, void *data,
-                        struct drm_file *file_priv)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       drm_i915_setparam_t *param = data;
-
-       switch (param->param) {
-       case I915_SETPARAM_USE_MI_BATCHBUFFER_START:
-       case I915_SETPARAM_TEX_LRU_LOG_GRANULARITY:
-       case I915_SETPARAM_ALLOW_BATCHBUFFER:
-               /* Reject all old ums/dri params. */
-               return -ENODEV;
-
-       case I915_SETPARAM_NUM_USED_FENCES:
-               if (param->value > dev_priv->num_fence_regs ||
-                   param->value < 0)
-                       return -EINVAL;
-               /* Userspace can use first N regs */
-               dev_priv->fence_reg_start = param->value;
-               break;
-       default:
-               DRM_DEBUG_DRIVER("unknown parameter %d\n",
-                                       param->param);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 static int i915_get_bridge_dev(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -347,12 +318,12 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_
                dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
                /* i915 resume handler doesn't set to D0 */
                pci_set_power_state(dev->pdev, PCI_D0);
-               i915_resume_legacy(dev);
+               i915_resume_switcheroo(dev);
                dev->switch_power_state = DRM_SWITCH_POWER_ON;
        } else {
                pr_err("switched off\n");
                dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
-               i915_suspend_legacy(dev, pmm);
+               i915_suspend_switcheroo(dev, pmm);
                dev->switch_power_state = DRM_SWITCH_POWER_OFF;
        }
 }
@@ -421,6 +392,13 @@ static int i915_load_modeset_init(struct drm_device *dev)
         * working irqs for e.g. gmbus and dp aux transfers. */
        intel_modeset_init(dev);
 
+       /* intel_guc_ucode_init() needs the mutex to allocate GEM objects */
+       mutex_lock(&dev->struct_mutex);
+#if 0
+       intel_guc_ucode_init(dev);
+#endif
+       mutex_unlock(&dev->struct_mutex);
+
        ret = i915_gem_init(dev);
        if (ret)
                goto cleanup_irq;
@@ -462,6 +440,7 @@ cleanup_gem:
        i915_gem_context_fini(dev);
        mutex_unlock(&dev->struct_mutex);
 cleanup_irq:
+       intel_guc_ucode_fini(dev);
        drm_irq_uninstall(dev);
 cleanup_gem_stolen:
        i915_gem_cleanup_stolen(dev);
@@ -613,17 +592,6 @@ static void gen9_sseu_info_init(struct drm_device *dev)
        u32 fuse2, s_enable, ss_disable, eu_disable;
        u8 eu_mask = 0xff;
 
-       /*
-        * BXT has a single slice. BXT also has at most 6 EU per subslice,
-        * and therefore only the lowest 6 bits of the 8-bit EU disable
-        * fields are valid.
-       */
-       if (IS_BROXTON(dev)) {
-               s_max = 1;
-               eu_max = 6;
-               eu_mask = 0x3f;
-       }
-
        info = (struct intel_device_info *)&dev_priv->info;
        fuse2 = I915_READ(GEN8_FUSE2);
        s_enable = (fuse2 & GEN8_F2_S_ENA_MASK) >>
@@ -695,6 +663,82 @@ static void gen9_sseu_info_init(struct drm_device *dev)
        info->has_eu_pg = (info->eu_per_subslice > 2);
 }
 
+static void broadwell_sseu_info_init(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_device_info *info;
+       const int s_max = 3, ss_max = 3, eu_max = 8;
+       int s, ss;
+       u32 fuse2, eu_disable[s_max], s_enable, ss_disable;
+
+       fuse2 = I915_READ(GEN8_FUSE2);
+       s_enable = (fuse2 & GEN8_F2_S_ENA_MASK) >> GEN8_F2_S_ENA_SHIFT;
+       ss_disable = (fuse2 & GEN8_F2_SS_DIS_MASK) >> GEN8_F2_SS_DIS_SHIFT;
+
+       eu_disable[0] = I915_READ(GEN8_EU_DISABLE0) & GEN8_EU_DIS0_S0_MASK;
+       eu_disable[1] = (I915_READ(GEN8_EU_DISABLE0) >> GEN8_EU_DIS0_S1_SHIFT) |
+                       ((I915_READ(GEN8_EU_DISABLE1) & GEN8_EU_DIS1_S1_MASK) <<
+                        (32 - GEN8_EU_DIS0_S1_SHIFT));
+       eu_disable[2] = (I915_READ(GEN8_EU_DISABLE1) >> GEN8_EU_DIS1_S2_SHIFT) |
+                       ((I915_READ(GEN8_EU_DISABLE2) & GEN8_EU_DIS2_S2_MASK) <<
+                        (32 - GEN8_EU_DIS1_S2_SHIFT));
+
+
+       info = (struct intel_device_info *)&dev_priv->info;
+       info->slice_total = hweight32(s_enable);
+
+       /*
+        * The subslice disable field is global, i.e. it applies
+        * to each of the enabled slices.
+        */
+       info->subslice_per_slice = ss_max - hweight32(ss_disable);
+       info->subslice_total = info->slice_total * info->subslice_per_slice;
+
+       /*
+        * Iterate through enabled slices and subslices to
+        * count the total enabled EU.
+        */
+       for (s = 0; s < s_max; s++) {
+               if (!(s_enable & (0x1 << s)))
+                       /* skip disabled slice */
+                       continue;
+
+               for (ss = 0; ss < ss_max; ss++) {
+                       u32 n_disabled;
+
+                       if (ss_disable & (0x1 << ss))
+                               /* skip disabled subslice */
+                               continue;
+
+                       n_disabled = hweight8(eu_disable[s] >> (ss * eu_max));
+
+                       /*
+                        * Record which subslices have 7 EUs.
+                        */
+                       if (eu_max - n_disabled == 7)
+                               info->subslice_7eu[s] |= 1 << ss;
+
+                       info->eu_total += eu_max - n_disabled;
+               }
+       }
+
+       /*
+        * BDW is expected to always have a uniform distribution of EU across
+        * subslices with the exception that any one EU in any one subslice may
+        * be fused off for die recovery.
+        */
+       info->eu_per_subslice = info->subslice_total ?
+               DIV_ROUND_UP(info->eu_total, info->subslice_total) : 0;
+
+       /*
+        * BDW supports slice power gating on devices with more than
+        * one slice.
+        */
+       info->has_slice_pg = (info->slice_total > 1);
+       info->has_subslice_pg = 0;
+       info->has_eu_pg = 0;
+}
+
 /*
  * Determine various intel_device_info fields at runtime.
  *
@@ -765,6 +809,8 @@ static void intel_device_info_runtime_init(struct drm_device *dev)
        /* Initialize slice/subslice/EU info */
        if (IS_CHERRYVIEW(dev))
                cherryview_sseu_info_init(dev);
+       else if (IS_BROADWELL(dev))
+               broadwell_sseu_info_init(dev);
        else if (INTEL_INFO(dev)->gen >= 9)
                gen9_sseu_info_init(dev);
 
@@ -781,6 +827,24 @@ static void intel_device_info_runtime_init(struct drm_device *dev)
                         info->has_eu_pg ? "y" : "n");
 }
 
+static void intel_init_dpio(struct drm_i915_private *dev_priv)
+{
+       if (!IS_VALLEYVIEW(dev_priv))
+               return;
+
+       /*
+        * IOSF_PORT_DPIO is used for VLV x2 PHY (DP/HDMI B and C),
+        * CHV x1 PHY (DP/HDMI D)
+        * IOSF_PORT_DPIO_2 is used for CHV x2 PHY (DP/HDMI B and C)
+        */
+       if (IS_CHERRYVIEW(dev_priv)) {
+               DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO_2;
+               DPIO_PHY_IOSF_PORT(DPIO_PHY1) = IOSF_PORT_DPIO;
+       } else {
+               DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO;
+       }
+}
+
 /**
  * i915_driver_load - setup chip and create an initial config
  * @dev: DRM device
@@ -796,7 +860,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_device_info *info, *device_info;
-       unsigned long base, size;
        int ret = 0, mmio_bar, mmio_size;
        uint32_t aperture_size;
 
@@ -824,6 +887,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        lockinit(&dev_priv->sb_lock, "i915sbl", 0, LK_CANRECURSE);
        lockinit(&dev_priv->modeset_restore_lock, "i915mrl", 0, LK_CANRECURSE);
        lockinit(&dev_priv->csr_lock, "i915csr", 0, LK_CANRECURSE);
+       lockinit(&dev_priv->av_mutex, "i915am", 0, LK_CANRECURSE);
 
        intel_pm_setup(dev);
 
@@ -863,13 +927,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
                ret = -EIO;
                goto put_bridge;
        }
-#ifdef __DragonFly__
-       base = drm_get_resource_start(dev, mmio_bar);
-       size = drm_get_resource_len(dev, mmio_bar);
-
-       ret = drm_legacy_addmap(dev, base, size, _DRM_REGISTERS,
-           _DRM_KERNEL | _DRM_DRIVER, &dev_priv->mmio_map);
-#endif
 
        /* This must be called before any calls to HAS_PCH_* */
        intel_detect_pch(dev);
@@ -972,8 +1029,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        intel_setup_gmbus(dev);
        intel_opregion_setup(dev);
 
-       intel_setup_bios(dev);
-
        i915_gem_load(dev);
 
        /* On the 945G/GM, the chipset reports the MSI capability on the
@@ -994,6 +1049,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 
        intel_device_info_runtime_init(dev);
 
+       intel_init_dpio(dev_priv);
+
        if (INTEL_INFO(dev)->num_pipes) {
                ret = drm_vblank_init(dev, INTEL_INFO(dev)->num_pipes);
                if (ret)
@@ -1113,6 +1170,10 @@ int i915_driver_unload(struct drm_device *dev)
                dev_priv->vbt.child_dev = NULL;
                dev_priv->vbt.child_dev_num = 0;
        }
+       kfree(dev_priv->vbt.sdvo_lvds_vbt_mode);
+       dev_priv->vbt.sdvo_lvds_vbt_mode = NULL;
+       kfree(dev_priv->vbt.lfp_lvds_vbt_mode);
+       dev_priv->vbt.lfp_lvds_vbt_mode = NULL;
 
 #if 0
        vga_switcheroo_unregister_client(dev->pdev);
@@ -1133,6 +1194,7 @@ int i915_driver_unload(struct drm_device *dev)
        /* Flush any outstanding unpin_work. */
        flush_workqueue(dev_priv->wq);
 
+       intel_guc_ucode_fini(dev);
        mutex_lock(&dev->struct_mutex);
        i915_gem_cleanup_ringbuffer(dev);
        i915_gem_context_fini(dev);
@@ -1231,8 +1293,8 @@ const struct drm_ioctl_desc i915_ioctls[] = {
        DRM_IOCTL_DEF_DRV(I915_BATCHBUFFER, drm_noop, DRM_AUTH),
        DRM_IOCTL_DEF_DRV(I915_IRQ_EMIT, drm_noop, DRM_AUTH),
        DRM_IOCTL_DEF_DRV(I915_IRQ_WAIT, drm_noop, DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(I915_GETPARAM, i915_getparam, DRM_AUTH|DRM_RENDER_ALLOW),
-       DRM_IOCTL_DEF_DRV(I915_SETPARAM, i915_setparam, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF_DRV(I915_GETPARAM, i915_getparam, DRM_AUTH|DRM_RENDER_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(I915_SETPARAM, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
        DRM_IOCTL_DEF_DRV(I915_ALLOC, drm_noop, DRM_AUTH),
        DRM_IOCTL_DEF_DRV(I915_FREE, drm_noop, DRM_AUTH),
        DRM_IOCTL_DEF_DRV(I915_INIT_HEAP, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
index 871c780..e01d07e 100644 (file)
@@ -358,6 +358,7 @@ static const struct intel_device_info intel_skylake_info = {
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
        .has_llc = 1,
        .has_ddi = 1,
+       .has_fpga_dbg = 1,
        .has_fbc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
        IVB_CURSOR_OFFSETS,
@@ -370,6 +371,7 @@ static const struct intel_device_info intel_skylake_gt3_info = {
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
        .has_llc = 1,
        .has_ddi = 1,
+       .has_fpga_dbg = 1,
        .has_fbc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
        IVB_CURSOR_OFFSETS,
@@ -382,6 +384,7 @@ static const struct intel_device_info intel_broxton_info = {
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
        .num_pipes = 3,
        .has_ddi = 1,
+       .has_fpga_dbg = 1,
        .has_fbc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
        IVB_CURSOR_OFFSETS,
@@ -435,6 +438,34 @@ static const struct pci_device_id pciidlist[] = {          /* aka */
 
 #define        PCI_VENDOR_INTEL        0x8086
 
+static enum intel_pch intel_virt_detect_pch(struct drm_device *dev)
+{
+       enum intel_pch ret = PCH_NOP;
+
+       /*
+        * In a virtualized passthrough environment we can be in a
+        * setup where the ISA bridge is not able to be passed through.
+        * In this case, a south bridge can be emulated and we have to
+        * make an educated guess as to which PCH is really there.
+        */
+
+       if (IS_GEN5(dev)) {
+               ret = PCH_IBX;
+               DRM_DEBUG_KMS("Assuming Ibex Peak PCH\n");
+       } else if (IS_GEN6(dev) || IS_IVYBRIDGE(dev)) {
+               ret = PCH_CPT;
+               DRM_DEBUG_KMS("Assuming CouarPoint PCH\n");
+       } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
+               ret = PCH_LPT;
+               DRM_DEBUG_KMS("Assuming LynxPoint PCH\n");
+       } else if (IS_SKYLAKE(dev)) {
+               ret = PCH_SPT;
+               DRM_DEBUG_KMS("Assuming SunrisePoint PCH\n");
+       }
+
+       return ret;
+}
+
 void intel_detect_pch(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -502,6 +533,8 @@ void intel_detect_pch(struct drm_device *dev)
                                dev_priv->pch_type = PCH_SPT;
                                DRM_DEBUG_KMS("Found SunrisePoint LP PCH\n");
                                WARN_ON(!IS_SKYLAKE(dev));
+                       } else if (id == INTEL_PCH_P2X_DEVICE_ID_TYPE) {
+                               dev_priv->pch_type = intel_virt_detect_pch(dev);
                        } else
                                continue;
 
@@ -615,6 +648,8 @@ static int i915_drm_suspend(struct drm_device *dev)
                return error;
        }
 
+       intel_guc_suspend(dev);
+
        intel_suspend_gt_powersave(dev);
 
        /*
@@ -695,7 +730,7 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)
        return 0;
 }
 
-int i915_suspend_legacy(device_t kdev)
+int i915_suspend_switcheroo(device_t kdev)
 {
        struct drm_device *dev = device_get_softc(kdev);
        int error;
@@ -753,6 +788,8 @@ static int i915_drm_resume(struct drm_device *dev)
        }
        mutex_unlock(&dev->struct_mutex);
 
+       intel_guc_resume(dev);
+
        intel_modeset_init_hw(dev);
 
        spin_lock_irq(&dev_priv->irq_lock);
@@ -835,7 +872,7 @@ static int i915_drm_resume_early(struct drm_device *dev)
        return ret;
 }
 
-int i915_resume_legacy(struct drm_device *dev)
+int i915_resume_switcheroo(struct drm_device *dev)
 {
        int ret;
 
@@ -1065,12 +1102,6 @@ static int skl_suspend_complete(struct drm_i915_private *dev_priv)
 {
        /* Enabling DC6 is not a hard requirement to enter runtime D3 */
 
-       /*
-        * This is to ensure that CSR isn't identified as loaded before
-        * CSR-loading program is called during runtime-resume.
-        */
-       intel_csr_load_status_set(dev_priv, FW_UNINITIALIZED);
-
        skl_uninit_cdclk(dev_priv);
 
        return 0;
@@ -1164,7 +1195,7 @@ static void vlv_save_gunit_s0ix_state(struct drm_i915_private *dev_priv)
        s->gfx_pend_tlb1        = I915_READ(GEN7_GFX_PEND_TLB1);
 
        for (i = 0; i < ARRAY_SIZE(s->lra_limits); i++)
-               s->lra_limits[i] = I915_READ(GEN7_LRA_LIMITS_BASE + i * 4);
+               s->lra_limits[i] = I915_READ(GEN7_LRA_LIMITS(i));
 
        s->media_max_req_count  = I915_READ(GEN7_MEDIA_MAX_REQ_COUNT);
        s->gfx_max_req_count    = I915_READ(GEN7_GFX_MAX_REQ_COUNT);
@@ -1208,7 +1239,7 @@ static void vlv_save_gunit_s0ix_state(struct drm_i915_private *dev_priv)
        s->pm_ier               = I915_READ(GEN6_PMIER);
 
        for (i = 0; i < ARRAY_SIZE(s->gt_scratch); i++)
-               s->gt_scratch[i] = I915_READ(GEN7_GT_SCRATCH_BASE + i * 4);
+               s->gt_scratch[i] = I915_READ(GEN7_GT_SCRATCH(i));
 
        /* GT SA CZ domain, 0x100000-0x138124 */
        s->tilectl              = I915_READ(TILECTL);
@@ -1246,7 +1277,7 @@ static void vlv_restore_gunit_s0ix_state(struct drm_i915_private *dev_priv)
        I915_WRITE(GEN7_GFX_PEND_TLB1,  s->gfx_pend_tlb1);
 
        for (i = 0; i < ARRAY_SIZE(s->lra_limits); i++)
-               I915_WRITE(GEN7_LRA_LIMITS_BASE + i * 4, s->lra_limits[i]);
+               I915_WRITE(GEN7_LRA_LIMITS(i), s->lra_limits[i]);
 
        I915_WRITE(GEN7_MEDIA_MAX_REQ_COUNT, s->media_max_req_count);
        I915_WRITE(GEN7_GFX_MAX_REQ_COUNT, s->gfx_max_req_count);
@@ -1290,7 +1321,7 @@ static void vlv_restore_gunit_s0ix_state(struct drm_i915_private *dev_priv)
        I915_WRITE(GEN6_PMIER,          s->pm_ier);
 
        for (i = 0; i < ARRAY_SIZE(s->gt_scratch); i++)
-               I915_WRITE(GEN7_GT_SCRATCH_BASE + i * 4, s->gt_scratch[i]);
+               I915_WRITE(GEN7_GT_SCRATCH(i), s->gt_scratch[i]);
 
        /* GT SA CZ domain, 0x100000-0x138124 */
        I915_WRITE(TILECTL,                     s->tilectl);
@@ -1521,6 +1552,8 @@ static int intel_runtime_suspend(struct device *device)
        i915_gem_release_all_mmaps(dev_priv);
        mutex_unlock(&dev->struct_mutex);
 
+       intel_guc_suspend(dev);
+
        intel_suspend_gt_powersave(dev);
        intel_runtime_pm_disable_interrupts(dev_priv);
 
@@ -1580,6 +1613,8 @@ static int intel_runtime_resume(struct device *device)
        intel_opregion_notify_adapter(dev, PCI_D0);
        dev_priv->pm.suspended = false;
 
+       intel_guc_resume(dev);
+
        if (IS_GEN6(dev_priv))
                intel_init_pch_refclk(dev);
 
@@ -1600,6 +1635,15 @@ static int intel_runtime_resume(struct device *device)
        gen6_update_ring_freq(dev);
 
        intel_runtime_pm_enable_interrupts(dev_priv);
+
+       /*
+        * On VLV/CHV display interrupts are part of the display
+        * power well, so hpd is reinitialized from there. For
+        * everyone else do it here.
+        */
+       if (!IS_VALLEYVIEW(dev_priv))
+               intel_hpd_init(dev_priv);
+
        intel_enable_gt_powersave(dev);
 
        if (ret)
@@ -1706,7 +1750,7 @@ static struct drm_driver driver = {
         */
        .driver_features =
            DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM |
-           DRIVER_RENDER,
+           DRIVER_RENDER | DRIVER_MODESET,
        .load = i915_driver_load,
        .unload = i915_driver_unload,
        .open = i915_driver_open,
@@ -1714,8 +1758,6 @@ static struct drm_driver driver = {
        .preclose = i915_driver_preclose,
        .postclose = i915_driver_postclose,
 
-       /* Used in place of i915_pm_ops for non-DRIVER_MODESET */
-
 #if defined(CONFIG_DEBUG_FS)
        .debugfs_init = i915_debugfs_init,
        .debugfs_cleanup = i915_debugfs_cleanup,
@@ -1762,8 +1804,8 @@ static device_method_t i915_methods[] = {
        /* Device interface */
        DEVMETHOD(device_probe,         i915_pci_probe),
        DEVMETHOD(device_attach,        i915_attach),
-       DEVMETHOD(device_suspend,       i915_suspend_legacy),
-       DEVMETHOD(device_resume,        i915_resume_legacy),
+       DEVMETHOD(device_suspend,       i915_suspend_switcheroo),
+       DEVMETHOD(device_resume,        i915_resume_switcheroo),
        DEVMETHOD(device_detach,        drm_release),
        DEVMETHOD_END
 };
@@ -1783,7 +1825,6 @@ static int __init i915_init(void)
         * either the i915.modeset prarameter or by the
         * vga_text_mode_force boot option.
         */
-       driver.driver_features |= DRIVER_MODESET;
 
        if (i915.modeset == 0)
                driver.driver_features &= ~DRIVER_MODESET;
@@ -1794,18 +1835,12 @@ static int __init i915_init(void)
 #endif
 
        if (!(driver.driver_features & DRIVER_MODESET)) {
-               driver.get_vblank_timestamp = NULL;
                /* Silently fail loading to not upset userspace. */
                DRM_DEBUG_DRIVER("KMS and UMS disabled.\n");
                return 0;
        }
 
-       /*
-        * FIXME: Note that we're lying to the DRM core here so that we can get access
-        * to the atomic ioctl and the atomic properties.  Only plane operations on
-        * a single CRTC will actually work.
-        */
-       if (driver.driver_features & DRIVER_MODESET)
+       if (i915.nuclear_pageflip)
                driver.driver_features |= DRIVER_ATOMIC;
 
 #if 0
index 50f6f86..b9140bc 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/kconfig.h>
 #include <linux/pm_qos.h>
 #include <linux/delay.h>
+#include "intel_guc.h"
 
 #define CONFIG_DRM_FBDEV_EMULATION             1
 #define CONFIG_DRM_I915_KMS                    1
@@ -62,7 +63,7 @@
 
 #define DRIVER_NAME            "i915"
 #define DRIVER_DESC            "Intel Graphics"
-#define DRIVER_DATE            "20150731"
+#define DRIVER_DATE            "20151010"
 
 #undef WARN_ON
 /* Many gcc seem to no see through this and fall over :( */
        unlikely(__ret_warn_on);                                        \
 })
 
+static inline const char *yesno(bool v)
+{
+       return v ? "yes" : "no";
+}
+
 enum i915_pipe {
        INVALID_PIPE = -1,
        PIPE_A = 0,
@@ -131,17 +137,17 @@ enum transcoder {
 #define transcoder_name(t) ((t) + 'A')
 
 /*
- * This is the maximum (across all platforms) number of planes (primary +
- * sprites) that can be active at the same time on one pipe.
- *
- * This value doesn't count the cursor plane.
+ * I915_MAX_PLANES in the enum below is the maximum (across all platforms)
+ * number of planes per CRTC.  Not all platforms really have this many planes,
+ * which means some arrays of size I915_MAX_PLANES may have unused entries
+ * between the topmost sprite plane and the cursor plane.
  */
-#define I915_MAX_PLANES        4
-
 enum plane {
        PLANE_A = 0,
        PLANE_B,
        PLANE_C,
+       PLANE_CURSOR,
+       I915_MAX_PLANES,
 };
 #define plane_name(p) ((p) + 'A')
 
@@ -199,6 +205,7 @@ enum intel_display_power_domain {
        POWER_DOMAIN_AUX_B,
        POWER_DOMAIN_AUX_C,
        POWER_DOMAIN_AUX_D,
+       POWER_DOMAIN_GMBUS,
        POWER_DOMAIN_INIT,
 
        POWER_DOMAIN_NUM,
@@ -351,6 +358,8 @@ enum intel_dpll_id {
        /* hsw/bdw */
        DPLL_ID_WRPLL1 = 0,
        DPLL_ID_WRPLL2 = 1,
+       DPLL_ID_SPLL = 2,
+
        /* skl */
        DPLL_ID_SKL_DPLL1 = 0,
        DPLL_ID_SKL_DPLL2 = 1,
@@ -367,6 +376,7 @@ struct intel_dpll_hw_state {
 
        /* hsw, bdw */
        uint32_t wrpll;
+       uint32_t spll;
 
        /* skl */
        /*
@@ -450,14 +460,14 @@ struct opregion_swsci;
 struct opregion_asle;
 
 struct intel_opregion {
-       struct opregion_header __iomem *header;
-       struct opregion_acpi __iomem *acpi;
-       struct opregion_swsci __iomem *swsci;
+       struct opregion_header *header;
+       struct opregion_acpi *acpi;
+       struct opregion_swsci *swsci;
        u32 swsci_gbda_sub_functions;
        u32 swsci_sbcb_sub_functions;
-       struct opregion_asle __iomem *asle;
-       void __iomem *vbt;
-       u32 __iomem *lid_state;
+       struct opregion_asle *asle;
+       void *vbt;
+       u32 *lid_state;
        struct work_struct asle_work;
 };
 #define OPREGION_SIZE            (8*1024)
@@ -555,7 +565,7 @@ struct drm_i915_error_state {
 
                struct drm_i915_error_object {
                        int page_count;
-                       u32 gtt_offset;
+                       u64 gtt_offset;
                        u32 *pages[0];
                } *ringbuffer, *batchbuffer, *wa_batchbuffer, *ctx, *hws_page;
 
@@ -581,7 +591,7 @@ struct drm_i915_error_state {
                u32 size;
                u32 name;
                u32 rseqno[I915_NUM_RINGS], wseqno;
-               u32 gtt_offset;
+               u64 gtt_offset;
                u32 read_domains;
                u32 write_domain;
                s32 fence_reg:I915_MAX_NUM_FENCE_BITS;
@@ -646,7 +656,7 @@ struct drm_i915_display_funcs {
        void (*crtc_disable)(struct drm_crtc *crtc);
        void (*audio_codec_enable)(struct drm_connector *connector,
                                   struct intel_encoder *encoder,
-                                  struct drm_display_mode *mode);
+                                  const struct drm_display_mode *adjusted_mode);
        void (*audio_codec_disable)(struct intel_encoder *encoder);
        void (*fdi_link_train)(struct drm_crtc *crtc);
        void (*init_clock_gating)(struct drm_device *dev);
@@ -664,13 +674,6 @@ struct drm_i915_display_funcs {
        /* render clock increase/decrease */
        /* display clock increase/decrease */
        /* pll clock increase/decrease */
-
-       int (*setup_backlight)(struct intel_connector *connector, enum i915_pipe pipe);
-       uint32_t (*get_backlight)(struct intel_connector *connector);
-       void (*set_backlight)(struct intel_connector *connector,
-                             uint32_t level);
-       void (*disable_backlight)(struct intel_connector *connector);
-       void (*enable_backlight)(struct intel_connector *connector);
 };
 
 enum forcewake_domain_id {
@@ -875,8 +878,8 @@ struct intel_context {
        struct kref ref;
        int user_handle;
        uint8_t remap_slice;
-       int flags;
        struct drm_i915_private *i915;
+       int flags;
        struct drm_i915_file_private *file_priv;
        struct i915_ctx_hang_stats hang_stats;
        struct i915_hw_ppgtt *ppgtt;
@@ -888,7 +891,6 @@ struct intel_context {
        } legacy_hw_ctx;
 
        /* Execlists */
-       bool rcs_initialized;
        struct {
                struct drm_i915_gem_object *state;
                struct intel_ringbuffer *ringbuf;
@@ -947,6 +949,9 @@ struct i915_fbc {
                FBC_CHIP_DEFAULT, /* disabled by default on this chip */
                FBC_ROTATION, /* rotation is not supported */
                FBC_IN_DBG_MASTER, /* kernel debugger is active */
+               FBC_BAD_STRIDE, /* stride is not supported */
+               FBC_PIXEL_RATE, /* pixel rate is too big */
+               FBC_PIXEL_FORMAT /* pixel format is invalid */
        } no_fbc_reason;
 
        bool (*fbc_enabled)(struct drm_i915_private *dev_priv);
@@ -1048,7 +1053,7 @@ struct i915_suspend_saved_registers {
        u32 saveMI_ARB_STATE;
        u32 saveSWF0[16];
        u32 saveSWF1[16];
-       u32 saveSWF2[3];
+       u32 saveSWF3[3];
        uint64_t saveFENCE[I915_MAX_NUM_FENCES];
        u32 savePCH_PORT_HOTPLUG;
        u16 saveGCDGMBUS;
@@ -1150,7 +1155,6 @@ struct intel_gen6_power_mgmt {
        u8 efficient_freq;      /* AKA RPe. Pre-determined balanced frequency */
        u8 rp1_freq;            /* "less than" RP0 power/freqency */
        u8 rp0_freq;            /* Non-overclocked max frequency. */
-       u32 cz_freq;
 
        u8 up_threshold; /* Current %busy required to uplock */
        u8 down_threshold; /* Current %busy required to downclock */
@@ -1594,8 +1598,7 @@ static inline bool skl_ddb_entry_equal(const struct skl_ddb_entry *e1,
 struct skl_ddb_allocation {
        struct skl_ddb_entry pipe[I915_MAX_PIPES];
        struct skl_ddb_entry plane[I915_MAX_PIPES][I915_MAX_PLANES]; /* packed/uv */
-       struct skl_ddb_entry y_plane[I915_MAX_PIPES][I915_MAX_PLANES]; /* y-plane */
-       struct skl_ddb_entry cursor[I915_MAX_PIPES];
+       struct skl_ddb_entry y_plane[I915_MAX_PIPES][I915_MAX_PLANES];
 };
 
 struct skl_wm_values {
@@ -1603,18 +1606,13 @@ struct skl_wm_values {
        struct skl_ddb_allocation ddb;
        uint32_t wm_linetime[I915_MAX_PIPES];
        uint32_t plane[I915_MAX_PIPES][I915_MAX_PLANES][8];
-       uint32_t cursor[I915_MAX_PIPES][8];
        uint32_t plane_trans[I915_MAX_PIPES][I915_MAX_PLANES];
-       uint32_t cursor_trans[I915_MAX_PIPES];
 };
 
 struct skl_wm_level {
        bool plane_en[I915_MAX_PLANES];
-       bool cursor_en;
        uint16_t plane_res_b[I915_MAX_PLANES];
        uint8_t plane_res_l[I915_MAX_PLANES];
-       uint16_t cursor_res_b;
-       uint8_t cursor_res_l;
 };
 
 /*
@@ -1709,15 +1707,13 @@ struct i915_execbuffer_params {
        struct drm_file                 *file;
        uint32_t                        dispatch_flags;
        uint32_t                        args_batch_start_offset;
-       uint32_t                        batch_obj_vm_offset;
+       uint64_t                        batch_obj_vm_offset;
        struct intel_engine_cs          *ring;
        struct drm_i915_gem_object      *batch_obj;
        struct intel_context            *ctx;
        struct drm_i915_gem_request     *request;
 };
 
-struct drm_local_map;
-
 struct drm_i915_private {
        struct drm_device *dev;
        struct kmem_cache *objects;
@@ -1732,13 +1728,14 @@ struct drm_i915_private {
        device_t *bbbus_bridge;
        device_t *bbbus;
 
-       struct drm_local_map *mmio_map;
        char __iomem *regs;
 
        struct intel_uncore uncore;
 
        struct i915_virtual_gpu vgpu;
 
+       struct intel_guc guc;
+
        struct intel_csr csr;
 
        /* Display CSR-related protection */
@@ -1746,7 +1743,6 @@ struct drm_i915_private {
 
        device_t *gmbus;
 
-
        /** gmbus_mutex protects against concurrent usage of the single hw gmbus
         * controller on different i2c buses. */
        struct lock gmbus_mutex;
@@ -1821,7 +1817,9 @@ struct drm_i915_private {
        unsigned int fsb_freq, mem_freq, is_ddr3;
        unsigned int skl_boot_cdclk;
        unsigned int cdclk_freq, max_cdclk_freq;
+       unsigned int max_dotclk_freq;
        unsigned int hpll_freq;
+       unsigned int czclk_freq;
 
        /**
         * wq - Driver workqueue for GEM.
@@ -1910,6 +1908,11 @@ struct drm_i915_private {
        /* hda/i915 audio component */
        struct i915_audio_component *audio_component;
        bool audio_component_registered;
+       /**
+        * av_mutex - mutex for audio/video sync
+        *
+        */
+       struct lock av_mutex;
 
        uint32_t hw_context_size;
        struct list_head context_list;
@@ -1974,6 +1977,9 @@ struct drm_i915_private {
 
        bool edp_low_vswing;
 
+       /* perform PHY state sanity checks? */
+       bool chv_phy_assert[2];
+
        /*
         * NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch
         * will be rejected. Instead look for a better place.
@@ -1990,6 +1996,11 @@ static inline struct drm_i915_private *dev_to_i915(struct device *dev)
        BUG();
 }
 
+static inline struct drm_i915_private *guc_to_i915(struct intel_guc *guc)
+{
+       return container_of(guc, struct drm_i915_private, guc);
+}
+
 /* Iterate over initialised rings */
 #define for_each_ring(ring__, dev_priv__, i__) \
        for ((i__) = 0; (i__) < I915_NUM_RINGS; (i__)++) \
@@ -2026,25 +2037,26 @@ struct drm_i915_gem_object_ops {
 
 /*
  * Frontbuffer tracking bits. Set in obj->frontbuffer_bits while a gem bo is
- * considered to be the frontbuffer for the given plane interface-vise. This
+ * considered to be the frontbuffer for the given plane interface-wise. This
  * doesn't mean that the hw necessarily already scans it out, but that any
  * rendering (by the cpu or gpu) will land in the frontbuffer eventually.
  *
  * We have one bit per pipe and per scanout plane type.
  */
-#define INTEL_FRONTBUFFER_BITS_PER_PIPE 4
+#define INTEL_MAX_SPRITE_BITS_PER_PIPE 5
+#define INTEL_FRONTBUFFER_BITS_PER_PIPE 8
 #define INTEL_FRONTBUFFER_BITS \
        (INTEL_FRONTBUFFER_BITS_PER_PIPE * I915_MAX_PIPES)
 #define INTEL_FRONTBUFFER_PRIMARY(pipe) \
        (1 << (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))
 #define INTEL_FRONTBUFFER_CURSOR(pipe) \
-       (1 << (1 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))))
-#define INTEL_FRONTBUFFER_SPRITE(pipe) \
-       (1 << (2 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))))
+       (1 << (1 + (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))))
+#define INTEL_FRONTBUFFER_SPRITE(pipe, plane) \
+       (1 << (2 + plane + (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))))
 #define INTEL_FRONTBUFFER_OVERLAY(pipe) \
-       (1 << (3 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))))
+       (1 << (2 + INTEL_MAX_SPRITE_BITS_PER_PIPE + (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))))
 #define INTEL_FRONTBUFFER_ALL_MASK(pipe) \
-       (0xf << (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))
+       (0xff << (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))
 
 struct drm_i915_gem_object {
        struct drm_gem_object base;
@@ -2205,8 +2217,17 @@ struct drm_i915_gem_request {
        struct drm_i915_private *i915;
        struct intel_engine_cs *ring;
 
-       /** GEM sequence number associated with this request. */
-       uint32_t seqno;
+        /** GEM sequence number associated with the previous request,
+         * when the HWS breadcrumb is equal to this the GPU is processing
+         * this request.
+         */
+       u32 previous_seqno;
+
+        /** GEM sequence number associated with this request,
+         * when the HWS breadcrumb is equal or greater than this the GPU
+         * has finished processing this request.
+         */
+       u32 seqno;
 
        /** Position in the ringbuffer of the start of the request */
        u32 head;
@@ -2500,6 +2521,11 @@ struct drm_i915_cmd_table {
 #define IS_SKL_ULX(dev)                (INTEL_DEVID(dev) == 0x190E || \
                                 INTEL_DEVID(dev) == 0x1915 || \
                                 INTEL_DEVID(dev) == 0x191E)
+#define IS_SKL_GT3(dev)                (IS_SKYLAKE(dev) && \
+                                (INTEL_DEVID(dev) & 0x00F0) == 0x0020)
+#define IS_SKL_GT4(dev)                (IS_SKYLAKE(dev) && \
+                                (INTEL_DEVID(dev) & 0x00F0) == 0x0030)
+
 #define IS_PRELIMINARY_HW(intel_info) ((intel_info)->is_preliminary)
 
 #define SKL_REVID_A0           (0x0)
@@ -2511,7 +2537,7 @@ struct drm_i915_cmd_table {
 
 #define BXT_REVID_A0           (0x0)
 #define BXT_REVID_B0           (0x3)
-#define BXT_REVID_C0           (0x6)
+#define BXT_REVID_C0           (0x9)
 
 /*
  * The genX designation typically refers to the render engine, so render
@@ -2545,7 +2571,8 @@ struct drm_i915_cmd_table {
 #define HAS_HW_CONTEXTS(dev)   (INTEL_INFO(dev)->gen >= 6)
 #define HAS_LOGICAL_RING_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 8)
 #define USES_PPGTT(dev)                (i915.enable_ppgtt)
-#define USES_FULL_PPGTT(dev)   (i915.enable_ppgtt == 2)
+#define USES_FULL_PPGTT(dev)   (i915.enable_ppgtt >= 2)
+#define USES_FULL_48BIT_PPGTT(dev)     (i915.enable_ppgtt == 3)
 
 #define HAS_OVERLAY(dev)               (INTEL_INFO(dev)->has_overlay)
 #define OVERLAY_NEEDS_PHYSICAL(dev)    (INTEL_INFO(dev)->overlay_needs_physical)
@@ -2589,7 +2616,10 @@ struct drm_i915_cmd_table {
 #define HAS_RC6(dev)           (INTEL_INFO(dev)->gen >= 6)
 #define HAS_RC6p(dev)          (INTEL_INFO(dev)->gen == 6 || IS_IVYBRIDGE(dev))
 
-#define HAS_CSR(dev)   (IS_SKYLAKE(dev))
+#define HAS_CSR(dev)   (IS_GEN9(dev))
+
+#define HAS_GUC_UCODE(dev)     (IS_GEN9(dev))
+#define HAS_GUC_SCHED(dev)     (IS_GEN9(dev))
 
 #define HAS_RESOURCE_STREAMER(dev) (IS_HASWELL(dev) || \
                                    INTEL_INFO(dev)->gen >= 8)
@@ -2605,10 +2635,12 @@ struct drm_i915_cmd_table {
 #define INTEL_PCH_LPT_LP_DEVICE_ID_TYPE                0x9c00
 #define INTEL_PCH_SPT_DEVICE_ID_TYPE           0xA100
 #define INTEL_PCH_SPT_LP_DEVICE_ID_TYPE                0x9D00
+#define INTEL_PCH_P2X_DEVICE_ID_TYPE           0x7100
 
 #define INTEL_PCH_TYPE(dev) (__I915__(dev)->pch_type)
 #define HAS_PCH_SPT(dev) (INTEL_PCH_TYPE(dev) == PCH_SPT)
 #define HAS_PCH_LPT(dev) (INTEL_PCH_TYPE(dev) == PCH_LPT)
+#define HAS_PCH_LPT_LP(dev) (__I915__(dev)->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE)
 #define HAS_PCH_CPT(dev) (INTEL_PCH_TYPE(dev) == PCH_CPT)
 #define HAS_PCH_IBX(dev) (INTEL_PCH_TYPE(dev) == PCH_IBX)
 #define HAS_PCH_NOP(dev) (INTEL_PCH_TYPE(dev) == PCH_NOP)
@@ -2628,8 +2660,8 @@ struct drm_i915_cmd_table {
 extern const struct drm_ioctl_desc i915_ioctls[];
 extern int i915_max_ioctl;
 
-extern int i915_suspend_legacy(device_t kdev);
-extern int i915_resume_legacy(struct drm_device *dev);
+extern int i915_suspend_switcheroo(device_t kdev);
+extern int i915_resume_switcheroo(struct drm_device *dev);
 
 /* i915_params.c */
 struct i915_params {
@@ -2662,6 +2694,7 @@ struct i915_params {
        int use_mmio_flip;
        int mmio_debug;
        bool verbose_state_checks;
+       bool nuclear_pageflip;
        int edp_vswing;
 };
 extern struct i915_params i915 __read_mostly;
@@ -2741,6 +2774,9 @@ i915_disable_pipestat(struct drm_i915_private *dev_priv, enum i915_pipe pipe,
 
 void valleyview_enable_display_irqs(struct drm_i915_private *dev_priv);
 void valleyview_disable_display_irqs(struct drm_i915_private *dev_priv);
+void i915_hotplug_interrupt_update(struct drm_i915_private *dev_priv,
+                                  uint32_t mask,
+                                  uint32_t bits);
 void
 ironlake_enable_display_irq(struct drm_i915_private *dev_priv, u32 mask);
 void
@@ -2808,8 +2844,6 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
                                                  size_t size);
 struct drm_i915_gem_object *i915_gem_object_create_from_data(
                struct drm_device *dev, const void *data, size_t size);
-void i915_init_vm(struct drm_i915_private *dev_priv,
-                 struct i915_address_space *vm);
 void i915_gem_free_object(struct drm_gem_object *obj);
 void i915_gem_vma_destroy(struct i915_vma *vma);
 
@@ -2820,6 +2854,8 @@ void i915_gem_vma_destroy(struct i915_vma *vma);
 #define PIN_OFFSET_BIAS        (1<<3)
 #define PIN_USER       (1<<4)
 #define PIN_UPDATE     (1<<5)
+#define PIN_ZONE_4G    (1<<6)
+#define PIN_HIGH       (1<<7)
 #define PIN_OFFSET_MASK (~4095)
 int __must_check
 i915_gem_object_pin(struct drm_i915_gem_object *obj,
@@ -2834,7 +2870,13 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
 
 int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
                  u32 flags);
+void __i915_vma_set_map_and_fenceable(struct i915_vma *vma);
 int __must_check i915_vma_unbind(struct i915_vma *vma);
+/*
+ * BEWARE: Do not use the function below unless you can _absolutely_
+ * _guarantee_ VMA in question is _not in use_ anywhere.
+ */
+int __must_check __i915_vma_unbind_no_wait(struct i915_vma *vma);
 int i915_gem_object_put_pages(struct drm_i915_gem_object *obj);
 void i915_gem_release_all_mmaps(struct drm_i915_private *dev_priv);
 void i915_gem_release_mmap(struct drm_i915_gem_object *obj);
@@ -2902,15 +2944,17 @@ i915_seqno_passed(uint32_t seq1, uint32_t seq2)
        return (int32_t)(seq1 - seq2) >= 0;
 }
 
+static inline bool i915_gem_request_started(struct drm_i915_gem_request *req,
+                                          bool lazy_coherency)
+{
+       u32 seqno = req->ring->get_seqno(req->ring, lazy_coherency);
+       return i915_seqno_passed(seqno, req->previous_seqno);
+}
+
 static inline bool i915_gem_request_completed(struct drm_i915_gem_request *req,
                                              bool lazy_coherency)
 {
-       u32 seqno;
-
-       BUG_ON(req == NULL);
-
-       seqno = req->ring->get_seqno(req->ring, lazy_coherency);
-
+       u32 seqno = req->ring->get_seqno(req->ring, lazy_coherency);
        return i915_seqno_passed(seqno, req->seqno);
 }
 
@@ -3015,13 +3059,11 @@ struct dma_buf *i915_gem_prime_export(struct drm_device *dev,
                                struct drm_gem_object *gem_obj, int flags);
 #endif
 
-unsigned long
-i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o,
-                             const struct i915_ggtt_view *view);
-unsigned long
-i915_gem_obj_offset(struct drm_i915_gem_object *o,
-                   struct i915_address_space *vm);
-static inline unsigned long
+u64 i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o,
+                                 const struct i915_ggtt_view *view);
+u64 i915_gem_obj_offset(struct drm_i915_gem_object *o,
+                       struct i915_address_space *vm);
+static inline u64
 i915_gem_obj_ggtt_offset(struct drm_i915_gem_object *o)
 {
        return i915_gem_obj_ggtt_offset_view(o, &i915_ggtt_view_normal);
@@ -3169,7 +3211,6 @@ int __must_check i915_gem_evict_something(struct drm_device *dev,
                                          unsigned long end,
                                          unsigned flags);
 int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle);
-int i915_gem_evict_everything(struct drm_device *dev);
 
 /* belongs in i915_gem_gtt.h */
 static inline void i915_gem_chipset_flush(struct drm_device *dev)
@@ -3182,6 +3223,10 @@ static inline void i915_gem_chipset_flush(struct drm_device *dev)
 int i915_gem_stolen_insert_node(struct drm_i915_private *dev_priv,
                                struct drm_mm_node *node, u64 size,
                                unsigned alignment);
+int i915_gem_stolen_insert_node_in_range(struct drm_i915_private *dev_priv,
+                                        struct drm_mm_node *node, u64 size,
+                                        unsigned alignment, u64 start,
+                                        u64 end);
 void i915_gem_stolen_remove_node(struct drm_i915_private *dev_priv,
                                 struct drm_mm_node *node);
 int i915_gem_init_stolen(struct drm_device *dev);
@@ -3196,11 +3241,12 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
 
 /* i915_gem_shrinker.c */
 unsigned long i915_gem_shrink(struct drm_i915_private *dev_priv,
-                             long target,
+                             unsigned long target,
                              unsigned flags);
 #define I915_SHRINK_PURGEABLE 0x1
 #define I915_SHRINK_UNBOUND 0x2
 #define I915_SHRINK_BOUND 0x4
+#define I915_SHRINK_ACTIVE 0x8
 unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv);
 void i915_gem_shrinker_init(struct drm_i915_private *dev_priv);
 
index 70519ce..5b1f6ad 100644 (file)
@@ -1007,12 +1007,14 @@ out:
                if (!needs_clflush_after &&
                    obj->base.write_domain != I915_GEM_DOMAIN_CPU) {
                        if (i915_gem_clflush_object(obj, obj->pin_display))
-                               i915_gem_chipset_flush(dev);
+                               needs_clflush_after = true;
                }
        }
 
        if (needs_clflush_after)
                i915_gem_chipset_flush(dev);
+       else
+               obj->cache_dirty = true;
 
        intel_fb_obj_flush(obj, false, ORIGIN_CPU);
        return ret;
@@ -1138,23 +1140,42 @@ static bool missed_irq(struct drm_i915_private *dev_priv,
 }
 
 #if 0
-static int __i915_spin_request(struct drm_i915_gem_request *req)
+static int __i915_spin_request(struct drm_i915_gem_request *req, int state)
 {
        unsigned long timeout;
+       unsigned cpu;
+
+       /* When waiting for high frequency requests, e.g. during synchronous
+        * rendering split between the CPU and GPU, the finite amount of time
+        * required to set up the irq and wait upon it limits the response
+        * rate. By busywaiting on the request completion for a short while we
+        * can service the high frequency waits as quick as possible. However,
+        * if it is a slow request, we want to sleep as quickly as possible.
+        * The tradeoff between waiting and sleeping is roughly the time it
+        * takes to sleep on a request, on the order of a microsecond.
+        */
 
-       if (i915_gem_request_get_ring(req)->irq_refcount)
+       if (req->ring->irq_refcount)
                return -EBUSY;
 
-       timeout = jiffies + 1;
+       /* Only spin if we know the GPU is processing this request */
+       if (!i915_gem_request_started(req, true))
+               return -EAGAIN;
+
+       timeout = local_clock_us(&cpu) + 5;
        while (!need_resched()) {
                if (i915_gem_request_completed(req, true))
                        return 0;
 
-               if (time_after_eq(jiffies, timeout))
+               if (signal_pending_state(state, current))
+                       break;
+
+               if (busywait_stop(timeout, cpu))
                        break;
 
                cpu_relax_lowlatency();
        }
+
        if (i915_gem_request_completed(req, false))
                return 0;
 
@@ -1202,8 +1223,16 @@ int __i915_wait_request(struct drm_i915_gem_request *req,
        if (i915_gem_request_completed(req, true))
                return 0;
 
-       timeout_expire = timeout ?
-               jiffies + nsecs_to_jiffies_timeout((u64)*timeout) : 0;
+       timeout_expire = 0;
+       if (timeout) {
+               if (WARN_ON(*timeout < 0))
+                       return -EINVAL;
+
+               if (*timeout == 0)
+                       return -ETIME;
+
+               timeout_expire = jiffies + nsecs_to_jiffies_timeout(*timeout);
+       }
 
        if (INTEL_INFO(dev_priv)->gen >= 6)
                gen6_rps_boost(dev_priv, rps, req->emitted_jiffies);
@@ -1751,8 +1780,8 @@ out:
  * to be able to retry.
  *
  * --
- * vma: VMA in question
- * vmf: fault info
+ * @vma: VMA in question
+ * @vmf: fault info
  *
  * The fault handler is set up by drm_gem_mmap() when a object is GTT mapped
  * from userspace.  The fault handler takes care of binding the object to
@@ -2696,6 +2725,7 @@ void __i915_add_request(struct drm_i915_gem_request *request,
        request->batch_obj = obj;
 
        request->emitted_jiffies = jiffies;
+       request->previous_seqno = ring->last_submitted_seqno;
        ring->last_submitted_seqno = request->seqno;
        list_add_tail(&request->list, &ring->request_list);
 
@@ -3357,7 +3387,7 @@ static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj)
                                            old_write_domain);
 }
 
-int i915_vma_unbind(struct i915_vma *vma)
+static int __i915_vma_unbind(struct i915_vma *vma, bool wait)
 {
        struct drm_i915_gem_object *obj = vma->obj;
        struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
@@ -3376,13 +3406,11 @@ int i915_vma_unbind(struct i915_vma *vma)
 
        BUG_ON(obj->pages == NULL);
 
-       ret = i915_gem_object_wait_rendering(obj, false);
-       if (ret)
-               return ret;
-       /* Continue on if we fail due to EIO, the GPU is hung so we
-        * should be safe and we need to cleanup or else we might
-        * cause memory corruption through use-after-free.
-        */
+       if (wait) {
+               ret = i915_gem_object_wait_rendering(obj, false);
+               if (ret)
+                       return ret;
+       }
 
        if (i915_is_ggtt(vma->vm) &&
            vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL) {
@@ -3427,6 +3455,16 @@ int i915_vma_unbind(struct i915_vma *vma)
        return 0;
 }
 
+int i915_vma_unbind(struct i915_vma *vma)
+{
+       return __i915_vma_unbind(vma, true);
+}
+
+int __i915_vma_unbind_no_wait(struct i915_vma *vma)
+{
+       return __i915_vma_unbind(vma, false);
+}
+
 int i915_gpu_idle(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3506,11 +3544,10 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
 {
        struct drm_device *dev = obj->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 size, fence_size, fence_alignment, unfenced_alignment;
-       u64 start =
-               flags & PIN_OFFSET_BIAS ? flags & PIN_OFFSET_MASK : 0;
-       u64 end =
-               flags & PIN_MAPPABLE ? dev_priv->gtt.mappable_end : vm->total;
+       u32 fence_alignment, unfenced_alignment;
+       u32 search_flag, alloc_flag;
+       u64 start, end;
+       u64 size, fence_size;
        struct i915_vma *vma;
        int ret;
 
@@ -3550,6 +3587,13 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
                size = flags & PIN_MAPPABLE ? fence_size : obj->base.size;
        }
 
+       start = flags & PIN_OFFSET_BIAS ? flags & PIN_OFFSET_MASK : 0;
+       end = vm->total;
+       if (flags & PIN_MAPPABLE)
+               end = min_t(u64, end, dev_priv->gtt.mappable_end);
+       if (flags & PIN_ZONE_4G)
+               end = min_t(u64, end, (1ULL << 32));
+
        if (alignment == 0)
                alignment = flags & PIN_MAPPABLE ? fence_alignment :
                                                unfenced_alignment;
@@ -3565,7 +3609,7 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
         * attempt to find space.
         */
        if (size > end) {
-               DRM_DEBUG("Attempting to bind an object (view type=%u) larger than the aperture: size=%u > %s aperture=%lu\n",
+               DRM_DEBUG("Attempting to bind an object (view type=%u) larger than the aperture: size=%lu > %s aperture=%lu\n",
                          ggtt_view ? ggtt_view->type : 0,
                          size,
                          flags & PIN_MAPPABLE ? "mappable" : "total",
@@ -3585,13 +3629,21 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
        if (IS_ERR(vma))
                goto err_unpin;
 
+       if (flags & PIN_HIGH) {
+               search_flag = DRM_MM_SEARCH_BELOW;
+               alloc_flag = DRM_MM_CREATE_TOP;
+       } else {
+               search_flag = DRM_MM_SEARCH_DEFAULT;
+               alloc_flag = DRM_MM_CREATE_DEFAULT;
+       }
+
 search_free:
        ret = drm_mm_insert_node_in_range_generic(&vm->mm, &vma->node,
                                                  size, alignment,
                                                  obj->cache_level,
                                                  start, end,
-                                                 DRM_MM_SEARCH_DEFAULT,
-                                                 DRM_MM_CREATE_DEFAULT);
+                                                 search_flag,
+                                                 alloc_flag);
        if (ret) {
                ret = i915_gem_evict_something(dev, vm, size, alignment,
                                               obj->cache_level,
@@ -3784,59 +3836,117 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
        return 0;
 }
 
+/**
+ * Changes the cache-level of an object across all VMA.
+ *
+ * After this function returns, the object will be in the new cache-level
+ * across all GTT and the contents of the backing storage will be coherent,
+ * with respect to the new cache-level. In order to keep the backing storage
+ * coherent for all users, we only allow a single cache level to be set
+ * globally on the object and prevent it from being changed whilst the
+ * hardware is reading from the object. That is if the object is currently
+ * on the scanout it will be set to uncached (or equivalent display
+ * cache coherency) and all non-MOCS GPU access will also be uncached so
+ * that all direct access to the scanout remains coherent.
+ */
 int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
                                    enum i915_cache_level cache_level)
 {
        struct drm_device *dev = obj->base.dev;
        struct i915_vma *vma, *next;
-       int ret;
+       bool bound = false;
+       int ret = 0;
 
        if (obj->cache_level == cache_level)
-               return 0;
-
-       if (i915_gem_obj_is_pinned(obj)) {
-               DRM_DEBUG("can not change the cache level of pinned objects\n");
-               return -EBUSY;
-       }
+               goto out;
 
+       /* Inspect the list of currently bound VMA and unbind any that would
+        * be invalid given the new cache-level. This is principally to
+        * catch the issue of the CS prefetch crossing page boundaries and
+        * reading an invalid PTE on older architectures.
+        */
        list_for_each_entry_safe(vma, next, &obj->vma_list, vma_link) {
+               if (!drm_mm_node_allocated(&vma->node))
+                       continue;
+
+               if (vma->pin_count) {
+                       DRM_DEBUG("can not change the cache level of pinned objects\n");
+                       return -EBUSY;
+               }
+
                if (!i915_gem_valid_gtt_space(vma, cache_level)) {
                        ret = i915_vma_unbind(vma);
                        if (ret)
                                return ret;
-               }
+               } else
+                       bound = true;
        }
 
-       if (i915_gem_obj_bound_any(obj)) {
+       /* We can reuse the existing drm_mm nodes but need to change the
+        * cache-level on the PTE. We could simply unbind them all and
+        * rebind with the correct cache-level on next use. However since
+        * we already have a valid slot, dma mapping, pages etc, we may as
+        * rewrite the PTE in the belief that doing so tramples upon less
+        * state and so involves less work.
+        */
+       if (bound) {
+               /* Before we change the PTE, the GPU must not be accessing it.
+                * If we wait upon the object, we know that all the bound
+                * VMA are no longer active.
+                */
                ret = i915_gem_object_wait_rendering(obj, false);
                if (ret)
                        return ret;
 
-               i915_gem_object_finish_gtt(obj);
-
-               /* Before SandyBridge, you could not use tiling or fence
-                * registers with snooped memory, so relinquish any fences
-                * currently pointing to our region in the aperture.
-                */
-               if (INTEL_INFO(dev)->gen < 6) {
+               if (!HAS_LLC(dev) && cache_level != I915_CACHE_NONE) {
+                       /* Access to snoopable pages through the GTT is
+                        * incoherent and on some machines causes a hard
+                        * lockup. Relinquish the CPU mmaping to force
+                        * userspace to refault in the pages and we can
+                        * then double check if the GTT mapping is still
+                        * valid for that pointer access.
+                        */
+                       i915_gem_release_mmap(obj);
+
+                       /* As we no longer need a fence for GTT access,
+                        * we can relinquish it now (and so prevent having
+                        * to steal a fence from someone else on the next
+                        * fence request). Note GPU activity would have
+                        * dropped the fence as all snoopable access is
+                        * supposed to be linear.
+                        */
                        ret = i915_gem_object_put_fence(obj);
                        if (ret)
                                return ret;
+               } else {
+                       /* We either have incoherent backing store and
+                        * so no GTT access or the architecture is fully
+                        * coherent. In such cases, existing GTT mmaps
+                        * ignore the cache bit in the PTE and we can
+                        * rewrite it without confusing the GPU or having
+                        * to force userspace to fault back in its mmaps.
+                        */
                }
 
-               list_for_each_entry(vma, &obj->vma_list, vma_link)
-                       if (drm_mm_node_allocated(&vma->node)) {
-                               ret = i915_vma_bind(vma, cache_level,
-                                                   PIN_UPDATE);
-                               if (ret)
-                                       return ret;
-                       }
+               list_for_each_entry(vma, &obj->vma_list, vma_link) {
+                       if (!drm_mm_node_allocated(&vma->node))
+                               continue;
+
+                       ret = i915_vma_bind(vma, cache_level, PIN_UPDATE);
+                       if (ret)
+                               return ret;
+               }
        }
 
        list_for_each_entry(vma, &obj->vma_list, vma_link)
                vma->node.color = cache_level;
        obj->cache_level = cache_level;
 
+out:
+       /* Flush the dirty CPU caches to the backing storage so that the
+        * object is now coherent at its new cache level (with respect
+        * to the access domain).
+        */
        if (obj->cache_dirty &&
            obj->base.write_domain != I915_GEM_DOMAIN_CPU &&
            cpu_write_needs_clflush(obj)) {
@@ -3879,6 +3989,7 @@ int i915_gem_get_caching_ioctl(struct drm_device *dev, void *data,
 int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data,
                               struct drm_file *file)
 {
+       struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_caching *args = data;
        struct drm_i915_gem_object *obj;
        enum i915_cache_level level;
@@ -3889,6 +4000,15 @@ int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data,
                level = I915_CACHE_NONE;
                break;
        case I915_CACHING_CACHED:
+               /*
+                * Due to a HW issue on BXT A stepping, GPU stores via a
+                * snooped mapping may leave stale data in a corresponding CPU
+                * cacheline, whereas normally such cachelines would get
+                * invalidated.
+                */
+               if (IS_BROXTON(dev) && INTEL_REVID(dev) < BXT_REVID_B0)
+                       return -ENODEV;
+
                level = I915_CACHE_LLC;
                break;
        case I915_CACHING_DISPLAY:
@@ -3898,9 +4018,11 @@ int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data,
                return -EINVAL;
        }
 
+       intel_runtime_pm_get(dev_priv);
+
        ret = i915_mutex_lock_interruptible(dev);
        if (ret)
-               return ret;
+               goto rpm_put;
 
        obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle));
        if (&obj->base == NULL) {
@@ -3913,6 +4035,9 @@ int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data,
        drm_gem_object_unreference(&obj->base);
 unlock:
        mutex_unlock(&dev->struct_mutex);
+rpm_put:
+       intel_runtime_pm_put(dev_priv);
+
        return ret;
 }
 
@@ -4127,6 +4252,29 @@ i915_vma_misplaced(struct i915_vma *vma, uint32_t alignment, uint64_t flags)
        return false;
 }
 
+void __i915_vma_set_map_and_fenceable(struct i915_vma *vma)
+{
+       struct drm_i915_gem_object *obj = vma->obj;
+       bool mappable, fenceable;
+       u32 fence_size, fence_alignment;
+
+       fence_size = i915_gem_get_gtt_size(obj->base.dev,
+                                          obj->base.size,
+                                          obj->tiling_mode);
+       fence_alignment = i915_gem_get_gtt_alignment(obj->base.dev,
+                                                    obj->base.size,
+                                                    obj->tiling_mode,
+                                                    true);
+
+       fenceable = (vma->node.size == fence_size &&
+                    (vma->node.start & (fence_alignment - 1)) == 0);
+
+       mappable = (vma->node.start + fence_size <=
+                   to_i915(obj->base.dev)->gtt.mappable_end);
+
+       obj->map_and_fenceable = mappable && fenceable;
+}
+
 static int
 i915_gem_object_do_pin(struct drm_i915_gem_object *obj,
                       struct i915_address_space *vm,
@@ -4162,15 +4310,13 @@ i915_gem_object_do_pin(struct drm_i915_gem_object *obj,
                        return -EBUSY;
 
                if (i915_vma_misplaced(vma, alignment, flags)) {
-                       unsigned long offset;
-                       offset = ggtt_view ? i915_gem_obj_ggtt_offset_view(obj, ggtt_view) :
-                                            i915_gem_obj_offset(obj, vm);
                        WARN(vma->pin_count,
                             "bo is already pinned in %s with incorrect alignment:"
-                            " offset=%lx, req.alignment=%x, req.map_and_fenceable=%d,"
+                            " offset=%08x %08x, req.alignment=%x, req.map_and_fenceable=%d,"
                             " obj->map_and_fenceable=%d\n",
                             ggtt_view ? "ggtt" : "ppgtt",
-                            offset,
+                            upper_32_bits(vma->node.start),
+                            lower_32_bits(vma->node.start),
                             alignment,
                             !!(flags & PIN_MAPPABLE),
                             obj->map_and_fenceable);
@@ -4196,25 +4342,7 @@ i915_gem_object_do_pin(struct drm_i915_gem_object *obj,
 
        if (ggtt_view && ggtt_view->type == I915_GGTT_VIEW_NORMAL &&
            (bound ^ vma->bound) & GLOBAL_BIND) {
-               bool mappable, fenceable;
-               u32 fence_size, fence_alignment;
-
-               fence_size = i915_gem_get_gtt_size(obj->base.dev,
-                                                  obj->base.size,
-                                                  obj->tiling_mode);
-               fence_alignment = i915_gem_get_gtt_alignment(obj->base.dev,
-                                                            obj->base.size,
-                                                            obj->tiling_mode,
-                                                            true);
-
-               fenceable = (vma->node.size == fence_size &&
-                            (vma->node.start & (fence_alignment - 1)) == 0);
-
-               mappable = (vma->node.start + fence_size <=
-                           dev_priv->gtt.mappable_end);
-
-               obj->map_and_fenceable = mappable && fenceable;
-
+               __i915_vma_set_map_and_fenceable(vma);
                WARN_ON(flags & PIN_MAPPABLE && !obj->map_and_fenceable);
        }
 
@@ -4689,22 +4817,6 @@ void i915_gem_init_swizzling(struct drm_device *dev)
                BUG();
 }
 
-static bool
-intel_enable_blt(struct drm_device *dev)
-{
-       if (!HAS_BLT(dev))
-               return false;
-
-       /* The blitter was dysfunctional on early prototypes */
-       if (IS_GEN6(dev) && dev->pdev->revision < 8) {
-               DRM_INFO("BLT not supported on this pre-production hardware;"
-                        " graphics performance will be degraded.\n");
-               return false;
-       }
-
-       return true;
-}
-
 static void init_unused_ring(struct drm_device *dev, u32 base)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4747,7 +4859,7 @@ int i915_gem_init_rings(struct drm_device *dev)
                        goto cleanup_render_ring;
        }
 
-       if (intel_enable_blt(dev)) {
+       if (HAS_BLT(dev)) {
                ret = intel_init_blt_ring_buffer(dev);
                if (ret)
                        goto cleanup_bsd_ring;
@@ -4765,14 +4877,8 @@ int i915_gem_init_rings(struct drm_device *dev)
                        goto cleanup_vebox_ring;
        }
 
-       ret = i915_gem_set_seqno(dev, ((u32)~0 - 0x1000));
-       if (ret)
-               goto cleanup_bsd2_ring;
-
        return 0;
 
-cleanup_bsd2_ring:
-       intel_cleanup_ring_buffer(&dev_priv->ring[VCS2]);
 cleanup_vebox_ring:
        intel_cleanup_ring_buffer(&dev_priv->ring[VECS]);
 cleanup_blt_ring:
@@ -4844,6 +4950,37 @@ i915_gem_init_hw(struct drm_device *dev)
                        goto out;
        }
 
+       /* We can't enable contexts until all firmware is loaded */
+       if (HAS_GUC_UCODE(dev)) {
+#ifndef __DragonFly__
+               ret = intel_guc_ucode_load(dev);
+#else
+               ret = -ENOEXEC;
+#endif
+               if (ret) {
+                       /*
+                        * If we got an error and GuC submission is enabled, map
+                        * the error to -EIO so the GPU will be declared wedged.
+                        * OTOH, if we didn't intend to use the GuC anyway, just
+                        * discard the error and carry on.
+                        */
+                       DRM_ERROR("Failed to initialize GuC, error %d%s\n", ret,
+                                 i915.enable_guc_submission ? "" :
+                                 " (ignored)");
+                       ret = i915.enable_guc_submission ? -EIO : 0;
+                       if (ret)
+                               goto out;
+               }
+       }
+
+       /*
+        * Increment the next seqno by 0x100 so we have a visible break
+        * on re-initialisation
+        */
+       ret = i915_gem_set_seqno(dev, dev_priv->next_seqno+0x100);
+       if (ret)
+               goto out;
+
        /* Now it is safe to go back round and do everything else: */
        for_each_ring(ring, dev_priv, i) {
                struct drm_i915_gem_request *req;
@@ -4981,18 +5118,6 @@ init_ring_lists(struct intel_engine_cs *ring)
        INIT_LIST_HEAD(&ring->request_list);
 }
 
-void i915_init_vm(struct drm_i915_private *dev_priv,
-                 struct i915_address_space *vm)
-{
-       if (!i915_is_ggtt(vm))
-               drm_mm_init(&vm->mm, vm->start, vm->total);
-       vm->dev = dev_priv->dev;
-       INIT_LIST_HEAD(&vm->active_list);
-       INIT_LIST_HEAD(&vm->inactive_list);
-       INIT_LIST_HEAD(&vm->global_link);
-       list_add_tail(&vm->global_link, &dev_priv->vm_list);
-}
-
 void
 i915_gem_load(struct drm_device *dev)
 {
@@ -5000,8 +5125,6 @@ i915_gem_load(struct drm_device *dev)
        int i;
 
        INIT_LIST_HEAD(&dev_priv->vm_list);
-       i915_init_vm(dev_priv, &dev_priv->gtt.base);
-
        INIT_LIST_HEAD(&dev_priv->context_list);
        INIT_LIST_HEAD(&dev_priv->mm.unbound_list);
        INIT_LIST_HEAD(&dev_priv->mm.bound_list);
@@ -5029,6 +5152,14 @@ i915_gem_load(struct drm_device *dev)
                dev_priv->num_fence_regs =
                                I915_READ(vgtif_reg(avail_rs.fence_num));
 
+       /*
+        * Set initial sequence number for requests.
+        * Using this number allows the wraparound to happen early,
+        * catching any obvious problems.
+        */
+       dev_priv->next_seqno = ((u32)~0 - 0x1100);
+       dev_priv->last_seqno = ((u32)~0 - 0x1101);
+
        /* Initialize fence registers to zero */
        INIT_LIST_HEAD(&dev_priv->mm.fence_list);
        i915_gem_restore_fences(dev);
@@ -5122,9 +5253,9 @@ int i915_gem_open(struct drm_device *dev, struct drm_file *file)
 
 /**
  * i915_gem_track_fb - update frontbuffer tracking
- * old: current GEM buffer for the frontbuffer slots
- * new: new GEM buffer for the frontbuffer slots
- * frontbuffer_bits: bitmask of frontbuffer slots
+ * @old: current GEM buffer for the frontbuffer slots
+ * @new: new GEM buffer for the frontbuffer slots
+ * @frontbuffer_bits: bitmask of frontbuffer slots
  *
  * This updates the frontbuffer tracking bits @frontbuffer_bits by clearing them
  * from @old and setting them in @new. Both @old and @new can be NULL.
@@ -5147,9 +5278,8 @@ void i915_gem_track_fb(struct drm_i915_gem_object *old,
 }
 
 /* All the new VM stuff */
-unsigned long
-i915_gem_obj_offset(struct drm_i915_gem_object *o,
-                   struct i915_address_space *vm)
+u64 i915_gem_obj_offset(struct drm_i915_gem_object *o,
+                       struct i915_address_space *vm)
 {
        struct drm_i915_private *dev_priv = o->base.dev->dev_private;
        struct i915_vma *vma;
@@ -5169,9 +5299,8 @@ i915_gem_obj_offset(struct drm_i915_gem_object *o,
        return -1;
 }
 
-unsigned long
-i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o,
-                             const struct i915_ggtt_view *view)
+u64 i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o,
+                                 const struct i915_ggtt_view *view)
 {
        struct i915_address_space *ggtt = i915_obj_to_ggtt(o);
        struct i915_vma *vma;
index 8e893b3..02ceb7a 100644 (file)
@@ -133,6 +133,21 @@ static int get_context_size(struct drm_device *dev)
        return ret;
 }
 
+static void i915_gem_context_clean(struct intel_context *ctx)
+{
+       struct i915_hw_ppgtt *ppgtt = ctx->ppgtt;
+       struct i915_vma *vma, *next;
+
+       if (!ppgtt)
+               return;
+
+       list_for_each_entry_safe(vma, next, &ppgtt->base.inactive_list,
+                                mm_list) {
+               if (WARN_ON(__i915_vma_unbind_no_wait(vma)))
+                       break;
+       }
+}
+
 void i915_gem_context_free(struct kref *ctx_ref)
 {
        struct intel_context *ctx = container_of(ctx_ref, typeof(*ctx), ref);
@@ -142,6 +157,13 @@ void i915_gem_context_free(struct kref *ctx_ref)
        if (i915.enable_execlists)
                intel_lr_context_free(ctx);
 
+       /*
+        * This context is going away and we need to remove all VMAs still
+        * around. This is to handle imported shared objects for which
+        * destructor did not run when their handles were closed.
+        */
+       i915_gem_context_clean(ctx);
+
        i915_ppgtt_put(ctx->ppgtt);
 
        if (ctx->legacy_hw_ctx.rcs_state)
@@ -332,6 +354,13 @@ int i915_gem_context_init(struct drm_device *dev)
        if (WARN_ON(dev_priv->ring[RCS].default_context))
                return 0;
 
+       if (intel_vgpu_active(dev) && HAS_LOGICAL_RING_CONTEXTS(dev)) {
+               if (!i915.enable_execlists) {
+                       DRM_INFO("Only EXECLIST mode is supported in vgpu.\n");
+                       return -EINVAL;
+               }
+       }
+
        if (i915.enable_execlists) {
                /* NB: intentionally left blank. We will allocate our own
                 * backing objects as we need them, thank you very much */
index d09e35e..d71a133 100644 (file)
@@ -237,48 +237,3 @@ int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle)
 
        return 0;
 }
-
-/**
- * i915_gem_evict_everything - Try to evict all objects
- * @dev: Device to evict objects for
- *
- * This functions tries to evict all gem objects from all address spaces. Used
- * by the shrinker as a last-ditch effort and for suspend, before releasing the
- * backing storage of all unbound objects.
- */
-int
-i915_gem_evict_everything(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct i915_address_space *vm, *v;
-       bool lists_empty = true;
-       int ret;
-
-       list_for_each_entry(vm, &dev_priv->vm_list, global_link) {
-               lists_empty = (list_empty(&vm->inactive_list) &&
-                              list_empty(&vm->active_list));
-               if (!lists_empty)
-                       lists_empty = false;
-       }
-
-       if (lists_empty)
-               return -ENOSPC;
-
-       trace_i915_gem_evict_everything(dev);
-
-       /* The gpu_idle will flush everything in the write domain to the
-        * active list. Then we must move everything off the active list
-        * with retire requests.
-        */
-       ret = i915_gpu_idle(dev);
-       if (ret)
-               return ret;
-
-       i915_gem_retire_requests(dev);
-
-       /* Having flushed everything, unbind() should never raise an error */
-       list_for_each_entry_safe(vm, v, &dev_priv->vm_list, global_link)
-               WARN_ON(i915_gem_evict_vm(vm, false));
-
-       return 0;
-}
index 4395099..8ac65c8 100644 (file)
@@ -590,10 +590,17 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
                flags |= PIN_GLOBAL;
 
        if (!drm_mm_node_allocated(&vma->node)) {
+               /* Wa32bitGeneralStateOffset & Wa32bitInstructionBaseOffset,
+                * limit address to the first 4GBs for unflagged objects.
+                */
+               if ((entry->flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS) == 0)
+                       flags |= PIN_ZONE_4G;
                if (entry->flags & __EXEC_OBJECT_NEEDS_MAP)
                        flags |= PIN_GLOBAL | PIN_MAPPABLE;
                if (entry->flags & __EXEC_OBJECT_NEEDS_BIAS)
                        flags |= BATCH_OFFSET_BIAS | PIN_OFFSET_BIAS;
+               if ((flags & PIN_MAPPABLE) == 0)
+                       flags |= PIN_HIGH;
        }
 
        ret = i915_gem_object_pin(obj, vma->vm, entry->alignment, flags);
@@ -671,6 +678,10 @@ eb_vma_misplaced(struct i915_vma *vma)
        if (entry->flags & __EXEC_OBJECT_NEEDS_MAP && !obj->map_and_fenceable)
                return !only_mappable_for_reloc(entry->flags);
 
+       if ((entry->flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS) == 0 &&
+           (vma->node.start + vma->node.size - 1) >> 32)
+               return true;
+
        return false;
 }
 
@@ -934,7 +945,21 @@ i915_gem_check_execbuffer(struct drm_i915_gem_execbuffer2 *exec)
        if (exec->flags & __I915_EXEC_UNKNOWN_FLAGS)
                return false;
 
-       return ((exec->batch_start_offset | exec->batch_len) & 0x7) == 0;
+       /* Kernel clipping was a DRI1 misfeature */
+       if (exec->num_cliprects || exec->cliprects_ptr)
+               return false;
+
+       if (exec->DR4 == 0xffffffff) {
+               DRM_DEBUG("UXA submitting garbage DR4, fixing up\n");
+               exec->DR4 = 0;
+       }
+       if (exec->DR1 || exec->DR4)
+               return false;
+
+       if ((exec->batch_start_offset | exec->batch_len) & 0x7)
+               return false;
+
+       return true;
 }
 
 static int
@@ -1011,7 +1036,7 @@ i915_gem_validate_context(struct drm_device *dev, struct drm_file *file,
        }
 
        if (i915.enable_execlists && !ctx->engine[ring->id].state) {
-               int ret = intel_lr_context_deferred_create(ctx, ring);
+               int ret = intel_lr_context_deferred_alloc(ctx, ring);
                if (ret) {
                        DRM_DEBUG("Could not create LRC %u: %d\n", ctx_id, ret);
                        return ERR_PTR(ret);
@@ -1100,47 +1125,6 @@ i915_reset_gen7_sol_offsets(struct drm_device *dev,
        return 0;
 }
 
-static int
-i915_emit_box(struct drm_i915_gem_request *req,
-             struct drm_clip_rect *box,
-             int DR1, int DR4)
-{
-       struct intel_engine_cs *ring = req->ring;
-       int ret;
-
-       if (box->y2 <= box->y1 || box->x2 <= box->x1 ||
-           box->y2 <= 0 || box->x2 <= 0) {
-               DRM_ERROR("Bad box %d,%d..%d,%d\n",
-                         box->x1, box->y1, box->x2, box->y2);
-               return -EINVAL;
-       }
-
-       if (INTEL_INFO(ring->dev)->gen >= 4) {
-               ret = intel_ring_begin(req, 4);
-               if (ret)
-                       return ret;
-
-               intel_ring_emit(ring, GFX_OP_DRAWRECT_INFO_I965);
-               intel_ring_emit(ring, (box->x1 & 0xffff) | box->y1 << 16);
-               intel_ring_emit(ring, ((box->x2 - 1) & 0xffff) | (box->y2 - 1) << 16);
-               intel_ring_emit(ring, DR4);
-       } else {
-               ret = intel_ring_begin(req, 6);
-               if (ret)
-                       return ret;
-
-               intel_ring_emit(ring, GFX_OP_DRAWRECT_INFO);
-               intel_ring_emit(ring, DR1);
-               intel_ring_emit(ring, (box->x1 & 0xffff) | box->y1 << 16);
-               intel_ring_emit(ring, ((box->x2 - 1) & 0xffff) | (box->y2 - 1) << 16);
-               intel_ring_emit(ring, DR4);
-               intel_ring_emit(ring, 0);
-       }
-       intel_ring_advance(ring);
-
-       return 0;
-}
-
 static struct drm_i915_gem_object*
 i915_gem_execbuffer_parse(struct intel_engine_cs *ring,
                          struct drm_i915_gem_exec_object2 *shadow_exec_entry,
@@ -1199,65 +1183,21 @@ i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params,
                               struct drm_i915_gem_execbuffer2 *args,
                               struct list_head *vmas)
 {
-       struct drm_clip_rect *cliprects = NULL;
        struct drm_device *dev = params->dev;
        struct intel_engine_cs *ring = params->ring;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u64 exec_start, exec_len;
        int instp_mode;
        u32 instp_mask;
-       int i, ret = 0;
-
-       if (args->num_cliprects != 0) {
-               if (ring != &dev_priv->ring[RCS]) {
-                       DRM_DEBUG("clip rectangles are only valid with the render ring\n");
-                       return -EINVAL;
-               }
-
-               if (INTEL_INFO(dev)->gen >= 5) {
-                       DRM_DEBUG("clip rectangles are only valid on pre-gen5\n");
-                       return -EINVAL;
-               }
-
-               if (args->num_cliprects > UINT_MAX / sizeof(*cliprects)) {
-                       DRM_DEBUG("execbuf with %u cliprects\n",
-                                 args->num_cliprects);
-                       return -EINVAL;
-               }
-
-               cliprects = kcalloc(args->num_cliprects,
-                                   sizeof(*cliprects),
-                                   GFP_KERNEL);
-               if (cliprects == NULL) {
-                       ret = -ENOMEM;
-                       goto error;
-               }
-
-               if (copy_from_user(cliprects,
-                                  to_user_ptr(args->cliprects_ptr),
-                                  sizeof(*cliprects)*args->num_cliprects)) {
-                       ret = -EFAULT;
-                       goto error;
-               }
-       } else {
-               if (args->DR4 == 0xffffffff) {
-                       DRM_DEBUG("UXA submitting garbage DR4, fixing up\n");
-                       args->DR4 = 0;
-               }
-
-               if (args->DR1 || args->DR4 || args->cliprects_ptr) {
-                       DRM_DEBUG("0 cliprects but dirt in cliprects fields\n");
-                       return -EINVAL;
-               }
-       }
+       int ret;
 
        ret = i915_gem_execbuffer_move_to_gpu(params->request, vmas);
        if (ret)
-               goto error;
+               return ret;
 
        ret = i915_switch_context(params->request);
        if (ret)
-               goto error;
+               return ret;
 
        WARN(params->ctx->ppgtt && params->ctx->ppgtt->pd_dirty_rings & (1<<ring->id),
             "%s didn't clear reload\n", ring->name);
@@ -1270,22 +1210,19 @@ i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params,
        case I915_EXEC_CONSTANTS_REL_SURFACE:
                if (instp_mode != 0 && ring != &dev_priv->ring[RCS]) {
                        DRM_DEBUG("non-0 rel constants mode on non-RCS\n");
-                       ret = -EINVAL;
-                       goto error;
+                       return -EINVAL;
                }
 
                if (instp_mode != dev_priv->relative_constants_mode) {
                        if (INTEL_INFO(dev)->gen < 4) {
                                DRM_DEBUG("no rel constants on pre-gen4\n");
-                               ret = -EINVAL;
-                               goto error;
+                               return -EINVAL;
                        }
 
                        if (INTEL_INFO(dev)->gen > 5 &&
                            instp_mode == I915_EXEC_CONSTANTS_REL_SURFACE) {
                                DRM_DEBUG("rel surface constants mode invalid on gen5+\n");
-                               ret = -EINVAL;
-                               goto error;
+                               return -EINVAL;
                        }
 
                        /* The HW changed the meaning on this bit on gen6 */
@@ -1295,15 +1232,14 @@ i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params,
                break;
        default:
                DRM_DEBUG("execbuf with unknown constants: %d\n", instp_mode);
-               ret = -EINVAL;
-               goto error;
+               return -EINVAL;
        }
 
        if (ring == &dev_priv->ring[RCS] &&
-                       instp_mode != dev_priv->relative_constants_mode) {
+           instp_mode != dev_priv->relative_constants_mode) {
                ret = intel_ring_begin(params->request, 4);
                if (ret)
-                       goto error;
+                       return ret;
 
                intel_ring_emit(ring, MI_NOOP);
                intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
@@ -1317,42 +1253,25 @@ i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params,
        if (args->flags & I915_EXEC_GEN7_SOL_RESET) {
                ret = i915_reset_gen7_sol_offsets(dev, params->request);
                if (ret)
-                       goto error;
+                       return ret;
        }
 
        exec_len   = args->batch_len;
        exec_start = params->batch_obj_vm_offset +
                     params->args_batch_start_offset;
 
-       if (cliprects) {
-               for (i = 0; i < args->num_cliprects; i++) {
-                       ret = i915_emit_box(params->request, &cliprects[i],
-                                           args->DR1, args->DR4);
-                       if (ret)
-                               goto error;
-
-                       ret = ring->dispatch_execbuffer(params->request,
-                                                       exec_start, exec_len,
-                                                       params->dispatch_flags);
-                       if (ret)
-                               goto error;
-               }
-       } else {
-               ret = ring->dispatch_execbuffer(params->request,
-                                               exec_start, exec_len,
-                                               params->dispatch_flags);
-               if (ret)
-                       return ret;
-       }
+       ret = ring->dispatch_execbuffer(params->request,
+                                       exec_start, exec_len,
+                                       params->dispatch_flags);
+       if (ret)
+               return ret;
 
        trace_i915_gem_ring_dispatch(params->request, params->dispatch_flags);
 
        i915_gem_execbuffer_move_to_active(vmas, params->request);
        i915_gem_execbuffer_retire_commands(params);
 
-error:
-       kfree(cliprects);
-       return ret;
+       return 0;
 }
 
 /**
index fe1ae5e..010fda0 100644 (file)
@@ -60,19 +60,19 @@ static void i965_write_fence_reg(struct drm_device *dev, int reg,
                                 struct drm_i915_gem_object *obj)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int fence_reg;
+       int fence_reg_lo, fence_reg_hi;
        int fence_pitch_shift;
 
        if (INTEL_INFO(dev)->gen >= 6) {
-               fence_reg = FENCE_REG_SANDYBRIDGE_0;
-               fence_pitch_shift = SANDYBRIDGE_FENCE_PITCH_SHIFT;
+               fence_reg_lo = FENCE_REG_GEN6_LO(reg);
+               fence_reg_hi = FENCE_REG_GEN6_HI(reg);
+               fence_pitch_shift = GEN6_FENCE_PITCH_SHIFT;
        } else {
-               fence_reg = FENCE_REG_965_0;
+               fence_reg_lo = FENCE_REG_965_LO(reg);
+               fence_reg_hi = FENCE_REG_965_HI(reg);
                fence_pitch_shift = I965_FENCE_PITCH_SHIFT;
        }
 
-       fence_reg += reg * 8;
-
        /* To w/a incoherency with non-atomic 64-bit register updates,
         * we split the 64-bit update into two 32-bit writes. In order
         * for a partial fence not to be evaluated between writes, we
@@ -82,8 +82,8 @@ static void i965_write_fence_reg(struct drm_device *dev, int reg,
         * For extra levels of paranoia, we make sure each step lands
         * before applying the next step.
         */
-       I915_WRITE(fence_reg, 0);
-       POSTING_READ(fence_reg);
+       I915_WRITE(fence_reg_lo, 0);
+       POSTING_READ(fence_reg_lo);
 
        if (obj) {
                u32 size = i915_gem_obj_ggtt_size(obj);
@@ -104,14 +104,14 @@ static void i965_write_fence_reg(struct drm_device *dev, int reg,
                        val |= 1 << I965_FENCE_TILING_Y_SHIFT;
                val |= I965_FENCE_REG_VALID;
 
-               I915_WRITE(fence_reg + 4, val >> 32);
-               POSTING_READ(fence_reg + 4);
+               I915_WRITE(fence_reg_hi, val >> 32);
+               POSTING_READ(fence_reg_hi);
 
-               I915_WRITE(fence_reg + 0, val);
-               POSTING_READ(fence_reg);
+               I915_WRITE(fence_reg_lo, val);
+               POSTING_READ(fence_reg_lo);
        } else {
-               I915_WRITE(fence_reg + 4, 0);
-               POSTING_READ(fence_reg + 4);
+               I915_WRITE(fence_reg_hi, 0);
+               POSTING_READ(fence_reg_hi);
        }
 }
 
@@ -150,13 +150,8 @@ static void i915_write_fence_reg(struct drm_device *dev, int reg,
        } else
                val = 0;
 
-       if (reg < 8)
-               reg = FENCE_REG_830_0 + reg * 4;
-       else
-               reg = FENCE_REG_945_8 + (reg - 8) * 4;
-
-       I915_WRITE(reg, val);
-       POSTING_READ(reg);
+       I915_WRITE(FENCE_REG(reg), val);
+       POSTING_READ(FENCE_REG(reg));
 }
 
 static void i830_write_fence_reg(struct drm_device *dev, int reg,
@@ -187,8 +182,8 @@ static void i830_write_fence_reg(struct drm_device *dev, int reg,
        } else
                val = 0;
 
-       I915_WRITE(FENCE_REG_830_0 + reg * 4, val);
-       POSTING_READ(FENCE_REG_830_0 + reg * 4);
+       I915_WRITE(FENCE_REG(reg), val);
+       POSTING_READ(FENCE_REG(reg));
 }
 
 inline static bool i915_gem_object_needs_mb(struct drm_i915_gem_object *obj)
@@ -323,7 +318,7 @@ i915_find_fence_reg(struct drm_device *dev)
 
        /* First try to find a free reg */
        avail = NULL;
-       for (i = dev_priv->fence_reg_start; i < dev_priv->num_fence_regs; i++) {
+       for (i = 0; i < dev_priv->num_fence_regs; i++) {
                reg = &dev_priv->fence_regs[i];
                if (!reg->obj)
                        return reg;
@@ -648,11 +643,10 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
                }
 
                /* check for L-shaped memory aka modified enhanced addressing */
-               if (IS_GEN4(dev)) {
-                       uint32_t ddc2 = I915_READ(DCC2);
-
-                       if (!(ddc2 & DCC2_MODIFIED_ENHANCED_DISABLE))
-                               dev_priv->quirks |= QUIRK_PIN_SWIZZLED_PAGES;
+               if (IS_GEN4(dev) &&
+                   !(I915_READ(DCC2) & DCC2_MODIFIED_ENHANCED_DISABLE)) {
+                       swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
+                       swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
                }
 
                if (dcc == 0xffffffff) {
@@ -681,16 +675,35 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
                 * matching, which was the case for the swizzling required in
                 * the table above, or from the 1-ch value being less than
                 * the minimum size of a rank.
+                *
+                * Reports indicate that the swizzling actually
+                * varies depending upon page placement inside the
+                * channels, i.e. we see swizzled pages where the
+                * banks of memory are paired and unswizzled on the
+                * uneven portion, so leave that as unknown.
                 */
-               if (I915_READ16(C0DRB3) != I915_READ16(C1DRB3)) {
-                       swizzle_x = I915_BIT_6_SWIZZLE_NONE;
-                       swizzle_y = I915_BIT_6_SWIZZLE_NONE;
-               } else {
+               if (I915_READ16(C0DRB3) == I915_READ16(C1DRB3)) {
                        swizzle_x = I915_BIT_6_SWIZZLE_9_10;
                        swizzle_y = I915_BIT_6_SWIZZLE_9;
                }
        }
 
+       if (swizzle_x == I915_BIT_6_SWIZZLE_UNKNOWN ||
+           swizzle_y == I915_BIT_6_SWIZZLE_UNKNOWN) {
+               /* Userspace likes to explode if it sees unknown swizzling,
+                * so lie. We will finish the lie when reporting through
+                * the get-tiling-ioctl by reporting the physical swizzle
+                * mode as unknown instead.
+                *
+                * As we don't strictly know what the swizzling is, it may be
+                * bit17 dependent, and so we need to also prevent the pages
+                * from being moved.
+                */
+               dev_priv->quirks |= QUIRK_PIN_SWIZZLED_PAGES;
+               swizzle_x = I915_BIT_6_SWIZZLE_NONE;
+               swizzle_y = I915_BIT_6_SWIZZLE_NONE;
+       }
+
        dev_priv->mm.bit_6_swizzle_x = swizzle_x;
        dev_priv->mm.bit_6_swizzle_y = swizzle_y;
 }
index e8948c2..9e4355c 100644 (file)
@@ -207,6 +207,9 @@ static gen8_pde_t gen8_pde_encode(const dma_addr_t addr,
        return pde;
 }
 
+#define gen8_pdpe_encode gen8_pde_encode
+#define gen8_pml4e_encode gen8_pde_encode
+
 static gen6_pte_t snb_pte_encode(dma_addr_t addr,
                                 enum i915_cache_level level,
                                 bool valid, u32 unused)
@@ -569,6 +572,127 @@ static void gen8_initialize_pd(struct i915_address_space *vm,
        fill_px(vm->dev, pd, scratch_pde);
 }
 
+static int __pdp_init(struct drm_device *dev,
+                     struct i915_page_directory_pointer *pdp)
+{
+       size_t pdpes = I915_PDPES_PER_PDP(dev);
+
+       pdp->used_pdpes = kcalloc(BITS_TO_LONGS(pdpes),
+                                 sizeof(unsigned long),
+                                 GFP_KERNEL);
+       if (!pdp->used_pdpes)
+               return -ENOMEM;
+
+       pdp->page_directory = kcalloc(pdpes, sizeof(*pdp->page_directory),
+                                     GFP_KERNEL);
+       if (!pdp->page_directory) {
+               kfree(pdp->used_pdpes);
+               /* the PDP might be the statically allocated top level. Keep it
+                * as clean as possible */
+               pdp->used_pdpes = NULL;
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void __pdp_fini(struct i915_page_directory_pointer *pdp)
+{
+       kfree(pdp->used_pdpes);
+       kfree(pdp->page_directory);
+       pdp->page_directory = NULL;
+}
+
+static struct
+i915_page_directory_pointer *alloc_pdp(struct drm_device *dev)
+{
+       struct i915_page_directory_pointer *pdp;
+       int ret = -ENOMEM;
+
+       WARN_ON(!USES_FULL_48BIT_PPGTT(dev));
+
+       pdp = kzalloc(sizeof(*pdp), GFP_KERNEL);
+       if (!pdp)
+               return ERR_PTR(-ENOMEM);
+
+       ret = __pdp_init(dev, pdp);
+       if (ret)
+               goto fail_bitmap;
+
+       ret = setup_px(dev, pdp);
+       if (ret)
+               goto fail_page_m;
+
+       return pdp;
+
+fail_page_m:
+       __pdp_fini(pdp);
+fail_bitmap:
+       kfree(pdp);
+
+       return ERR_PTR(ret);
+}
+
+static void free_pdp(struct drm_device *dev,
+                    struct i915_page_directory_pointer *pdp)
+{
+       __pdp_fini(pdp);
+       if (USES_FULL_48BIT_PPGTT(dev)) {
+               cleanup_px(dev, pdp);
+               kfree(pdp);
+       }
+}
+
+static void gen8_initialize_pdp(struct i915_address_space *vm,
+                               struct i915_page_directory_pointer *pdp)
+{
+       gen8_ppgtt_pdpe_t scratch_pdpe;
+
+       scratch_pdpe = gen8_pdpe_encode(px_dma(vm->scratch_pd), I915_CACHE_LLC);
+
+       fill_px(vm->dev, pdp, scratch_pdpe);
+}
+
+static void gen8_initialize_pml4(struct i915_address_space *vm,
+                                struct i915_pml4 *pml4)
+{
+       gen8_ppgtt_pml4e_t scratch_pml4e;
+
+       scratch_pml4e = gen8_pml4e_encode(px_dma(vm->scratch_pdp),
+                                         I915_CACHE_LLC);
+
+       fill_px(vm->dev, pml4, scratch_pml4e);
+}
+
+static void
+gen8_setup_page_directory(struct i915_hw_ppgtt *ppgtt,
+                         struct i915_page_directory_pointer *pdp,
+                         struct i915_page_directory *pd,
+                         int index)
+{
+       gen8_ppgtt_pdpe_t *page_directorypo;
+
+       if (!USES_FULL_48BIT_PPGTT(ppgtt->base.dev))
+               return;
+
+       page_directorypo = kmap_px(pdp);
+       page_directorypo[index] = gen8_pdpe_encode(px_dma(pd), I915_CACHE_LLC);
+       kunmap_px(ppgtt, page_directorypo);
+}
+
+static void
+gen8_setup_page_directory_pointer(struct i915_hw_ppgtt *ppgtt,
+                                 struct i915_pml4 *pml4,
+                                 struct i915_page_directory_pointer *pdp,
+                                 int index)
+{
+       gen8_ppgtt_pml4e_t *pagemap = kmap_px(pml4);
+
+       WARN_ON(!USES_FULL_48BIT_PPGTT(ppgtt->base.dev));
+       pagemap[index] = gen8_pml4e_encode(px_dma(pdp), I915_CACHE_LLC);
+       kunmap_px(ppgtt, pagemap);
+}
+
 /* Broadwell Page Directory Pointer Descriptors */
 static int gen8_write_pdp(struct drm_i915_gem_request *req,
                          unsigned entry,
@@ -594,8 +718,8 @@ static int gen8_write_pdp(struct drm_i915_gem_request *req,
        return 0;
 }
 
-static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt,
-                         struct drm_i915_gem_request *req)
+static int gen8_legacy_mm_switch(struct i915_hw_ppgtt *ppgtt,
+                                struct drm_i915_gem_request *req)
 {
        int i, ret;
 
@@ -610,39 +734,46 @@ static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt,
        return 0;
 }
 
-static void gen8_ppgtt_clear_range(struct i915_address_space *vm,
-                                  uint64_t start,
-                                  uint64_t length,
-                                  bool use_scratch)
+static int gen8_48b_mm_switch(struct i915_hw_ppgtt *ppgtt,
+                             struct drm_i915_gem_request *req)
+{
+       return gen8_write_pdp(req, 0, px_dma(&ppgtt->pml4));
+}
+
+static void gen8_ppgtt_clear_pte_range(struct i915_address_space *vm,
+                                      struct i915_page_directory_pointer *pdp,
+                                      uint64_t start,
+                                      uint64_t length,
+                                      gen8_pte_t scratch_pte)
 {
        struct i915_hw_ppgtt *ppgtt =
                container_of(vm, struct i915_hw_ppgtt, base);
-       gen8_pte_t *pt_vaddr, scratch_pte;
-       unsigned pdpe = start >> GEN8_PDPE_SHIFT & GEN8_PDPE_MASK;
-       unsigned pde = start >> GEN8_PDE_SHIFT & GEN8_PDE_MASK;
-       unsigned pte = start >> GEN8_PTE_SHIFT & GEN8_PTE_MASK;
+       gen8_pte_t *pt_vaddr;
+       unsigned pdpe = gen8_pdpe_index(start);
+       unsigned pde = gen8_pde_index(start);
+       unsigned pte = gen8_pte_index(start);
        unsigned num_entries = length >> PAGE_SHIFT;
        unsigned last_pte, i;
 
-       scratch_pte = gen8_pte_encode(px_dma(ppgtt->base.scratch_page),
-                                     I915_CACHE_LLC, use_scratch);
+       if (WARN_ON(!pdp))
+               return;
 
        while (num_entries) {
                struct i915_page_directory *pd;
                struct i915_page_table *pt;
 
-               if (WARN_ON(!ppgtt->pdp.page_directory[pdpe]))
-                       continue;
+               if (WARN_ON(!pdp->page_directory[pdpe]))
+                       break;
 
-               pd = ppgtt->pdp.page_directory[pdpe];
+               pd = pdp->page_directory[pdpe];
 
                if (WARN_ON(!pd->page_table[pde]))
-                       continue;
+                       break;
 
                pt = pd->page_table[pde];
 
                if (WARN_ON(!px_page(pt)))
-                       continue;
+                       break;
 
                last_pte = pte + num_entries;
                if (last_pte > GEN8_PTES)
@@ -659,46 +790,70 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm,
 
                pte = 0;
                if (++pde == I915_PDES) {
-                       pdpe++;
+                       if (++pdpe == I915_PDPES_PER_PDP(vm->dev))
+                               break;
                        pde = 0;
                }
        }
 }
 
-static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
-                                     struct sg_table *pages,
-                                     uint64_t start,
-                                     enum i915_cache_level cache_level, u32 unused)
+static void gen8_ppgtt_clear_range(struct i915_address_space *vm,
+                                  uint64_t start,
+                                  uint64_t length,
+                                  bool use_scratch)
+{
+       struct i915_hw_ppgtt *ppgtt =
+               container_of(vm, struct i915_hw_ppgtt, base);
+       gen8_pte_t scratch_pte = gen8_pte_encode(px_dma(vm->scratch_page),
+                                                I915_CACHE_LLC, use_scratch);
+
+       if (!USES_FULL_48BIT_PPGTT(vm->dev)) {
+               gen8_ppgtt_clear_pte_range(vm, &ppgtt->pdp, start, length,
+                                          scratch_pte);
+       } else {
+               uint64_t templ4, pml4e;
+               struct i915_page_directory_pointer *pdp;
+
+               gen8_for_each_pml4e(pdp, &ppgtt->pml4, start, length, templ4, pml4e) {
+                       gen8_ppgtt_clear_pte_range(vm, pdp, start, length,
+                                                  scratch_pte);
+               }
+       }
+}
+
+static void
+gen8_ppgtt_insert_pte_entries(struct i915_address_space *vm,
+                             struct i915_page_directory_pointer *pdp,
+                             struct sg_page_iter *sg_iter,
+                             uint64_t start,
+                             enum i915_cache_level cache_level)
 {
        struct i915_hw_ppgtt *ppgtt =
                container_of(vm, struct i915_hw_ppgtt, base);
        gen8_pte_t *pt_vaddr;
-       unsigned pdpe = start >> GEN8_PDPE_SHIFT & GEN8_PDPE_MASK;
-       unsigned pde = start >> GEN8_PDE_SHIFT & GEN8_PDE_MASK;
-       unsigned pte = start >> GEN8_PTE_SHIFT & GEN8_PTE_MASK;
-       struct sg_page_iter sg_iter;
+       unsigned pdpe = gen8_pdpe_index(start);
+       unsigned pde = gen8_pde_index(start);
+       unsigned pte = gen8_pte_index(start);
 
        pt_vaddr = NULL;
 
-       for_each_sg_page(pages->sgl, &sg_iter, pages->nents, 0) {
-               if (WARN_ON(pdpe >= GEN8_LEGACY_PDPES))
-                       break;
-
+       while (__sg_page_iter_next(sg_iter)) {
                if (pt_vaddr == NULL) {
-                       struct i915_page_directory *pd = ppgtt->pdp.page_directory[pdpe];
+                       struct i915_page_directory *pd = pdp->page_directory[pdpe];
                        struct i915_page_table *pt = pd->page_table[pde];
 
                        pt_vaddr = kmap_px(pt);
                }
 
                pt_vaddr[pte] =
-                       gen8_pte_encode(sg_page_iter_dma_address(&sg_iter),
+                       gen8_pte_encode(sg_page_iter_dma_address(sg_iter),
                                        cache_level, true);
                if (++pte == GEN8_PTES) {
                        kunmap_px(ppgtt, pt_vaddr);
                        pt_vaddr = NULL;
                        if (++pde == I915_PDES) {
-                               pdpe++;
+                               if (++pdpe == I915_PDPES_PER_PDP(vm->dev))
+                                       break;
                                pde = 0;
                        }
                        pte = 0;
@@ -709,6 +864,33 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
                kunmap_px(ppgtt, pt_vaddr);
 }
 
+static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
+                                     struct sg_table *pages,
+                                     uint64_t start,
+                                     enum i915_cache_level cache_level,
+                                     u32 unused)
+{
+       struct i915_hw_ppgtt *ppgtt =
+               container_of(vm, struct i915_hw_ppgtt, base);
+       struct sg_page_iter sg_iter;
+
+       __sg_page_iter_start(&sg_iter, pages->sgl, sg_nents(pages->sgl), 0);
+
+       if (!USES_FULL_48BIT_PPGTT(vm->dev)) {
+               gen8_ppgtt_insert_pte_entries(vm, &ppgtt->pdp, &sg_iter, start,
+                                             cache_level);
+       } else {
+               struct i915_page_directory_pointer *pdp;
+               uint64_t templ4, pml4e;
+               uint64_t length = (uint64_t)pages->orig_nents << PAGE_SHIFT;
+
+               gen8_for_each_pml4e(pdp, &ppgtt->pml4, start, length, templ4, pml4e) {
+                       gen8_ppgtt_insert_pte_entries(vm, pdp, &sg_iter,
+                                                     start, cache_level);
+               }
+       }
+}
+
 static void gen8_free_page_tables(struct drm_device *dev,
                                  struct i915_page_directory *pd)
 {
@@ -747,8 +929,55 @@ static int gen8_init_scratch(struct i915_address_space *vm)
                return PTR_ERR(vm->scratch_pd);
        }
 
+       if (USES_FULL_48BIT_PPGTT(dev)) {
+               vm->scratch_pdp = alloc_pdp(dev);
+               if (IS_ERR(vm->scratch_pdp)) {
+                       free_pd(dev, vm->scratch_pd);
+                       free_pt(dev, vm->scratch_pt);
+                       free_scratch_page(dev, vm->scratch_page);
+                       return PTR_ERR(vm->scratch_pdp);
+               }
+       }
+
        gen8_initialize_pt(vm, vm->scratch_pt);
        gen8_initialize_pd(vm, vm->scratch_pd);
+       if (USES_FULL_48BIT_PPGTT(dev))
+               gen8_initialize_pdp(vm, vm->scratch_pdp);
+
+       return 0;
+}
+
+static int gen8_ppgtt_notify_vgt(struct i915_hw_ppgtt *ppgtt, bool create)
+{
+       enum vgt_g2v_type msg;
+       struct drm_device *dev = ppgtt->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned int offset = vgtif_reg(pdp0_lo);
+       int i;
+
+       if (USES_FULL_48BIT_PPGTT(dev)) {
+               u64 daddr = px_dma(&ppgtt->pml4);
+
+               I915_WRITE(offset, lower_32_bits(daddr));
+               I915_WRITE(offset + 4, upper_32_bits(daddr));
+
+               msg = (create ? VGT_G2V_PPGTT_L4_PAGE_TABLE_CREATE :
+                               VGT_G2V_PPGTT_L4_PAGE_TABLE_DESTROY);
+       } else {
+               for (i = 0; i < GEN8_LEGACY_PDPES; i++) {
+                       u64 daddr = i915_page_dir_dma_addr(ppgtt, i);
+
+                       I915_WRITE(offset, lower_32_bits(daddr));
+                       I915_WRITE(offset + 4, upper_32_bits(daddr));
+
+                       offset += 8;
+               }
+
+               msg = (create ? VGT_G2V_PPGTT_L3_PAGE_TABLE_CREATE :
+                               VGT_G2V_PPGTT_L3_PAGE_TABLE_DESTROY);
+       }
+
+       I915_WRITE(vgtif_reg(g2v_notify), msg);
 
        return 0;
 }
@@ -757,35 +986,65 @@ static void gen8_free_scratch(struct i915_address_space *vm)
 {
        struct drm_device *dev = vm->dev;
 
+       if (USES_FULL_48BIT_PPGTT(dev))
+               free_pdp(dev, vm->scratch_pdp);
        free_pd(dev, vm->scratch_pd);
        free_pt(dev, vm->scratch_pt);
        free_scratch_page(dev, vm->scratch_page);
 }
 
-static void gen8_ppgtt_cleanup(struct i915_address_space *vm)
+static void gen8_ppgtt_cleanup_3lvl(struct drm_device *dev,
+                                   struct i915_page_directory_pointer *pdp)
 {
-       struct i915_hw_ppgtt *ppgtt =
-               container_of(vm, struct i915_hw_ppgtt, base);
        int i;
 
-       for_each_set_bit(i, ppgtt->pdp.used_pdpes, GEN8_LEGACY_PDPES) {
-               if (WARN_ON(!ppgtt->pdp.page_directory[i]))
+       for_each_set_bit(i, pdp->used_pdpes, I915_PDPES_PER_PDP(dev)) {
+               if (WARN_ON(!pdp->page_directory[i]))
                        continue;
 
-               gen8_free_page_tables(ppgtt->base.dev,
-                                     ppgtt->pdp.page_directory[i]);
-               free_pd(ppgtt->base.dev, ppgtt->pdp.page_directory[i]);
+               gen8_free_page_tables(dev, pdp->page_directory[i]);
+               free_pd(dev, pdp->page_directory[i]);
        }
 
+       free_pdp(dev, pdp);
+}
+
+static void gen8_ppgtt_cleanup_4lvl(struct i915_hw_ppgtt *ppgtt)
+{
+       int i;
+
+       for_each_set_bit(i, ppgtt->pml4.used_pml4es, GEN8_PML4ES_PER_PML4) {
+               if (WARN_ON(!ppgtt->pml4.pdps[i]))
+                       continue;
+
+               gen8_ppgtt_cleanup_3lvl(ppgtt->base.dev, ppgtt->pml4.pdps[i]);
+       }
+
+       cleanup_px(ppgtt->base.dev, &ppgtt->pml4);
+}
+
+static void gen8_ppgtt_cleanup(struct i915_address_space *vm)
+{
+       struct i915_hw_ppgtt *ppgtt =
+               container_of(vm, struct i915_hw_ppgtt, base);
+
+       if (intel_vgpu_active(vm->dev))
+               gen8_ppgtt_notify_vgt(ppgtt, false);
+
+       if (!USES_FULL_48BIT_PPGTT(ppgtt->base.dev))
+               gen8_ppgtt_cleanup_3lvl(ppgtt->base.dev, &ppgtt->pdp);
+       else
+               gen8_ppgtt_cleanup_4lvl(ppgtt);
+
        gen8_free_scratch(vm);
 }
 
 /**
  * gen8_ppgtt_alloc_pagetabs() - Allocate page tables for VA range.
- * @ppgtt:     Master ppgtt structure.
- * @pd:                Page directory for this address range.
+ * @vm:        Master vm structure.
+ * @pd:        Page directory for this address range.
  * @start:     Starting virtual address to begin allocations.
- * @length     Size of the allocations.
+ * @length:    Size of the allocations.
  * @new_pts:   Bitmap set by function with new allocations. Likely used by the
  *             caller to free on error.
  *
@@ -798,22 +1057,22 @@ static void gen8_ppgtt_cleanup(struct i915_address_space *vm)
  *
  * Return: 0 if success; negative error code otherwise.
  */
-static int gen8_ppgtt_alloc_pagetabs(struct i915_hw_ppgtt *ppgtt,
+static int gen8_ppgtt_alloc_pagetabs(struct i915_address_space *vm,
                                     struct i915_page_directory *pd,
                                     uint64_t start,
                                     uint64_t length,
                                     unsigned long *new_pts)
 {
-       struct drm_device *dev = ppgtt->base.dev;
+       struct drm_device *dev = vm->dev;
        struct i915_page_table *pt;
        uint64_t temp;
        uint32_t pde;
 
        gen8_for_each_pde(pt, pd, start, length, temp, pde) {
                /* Don't reallocate page tables */
-               if (pt) {
+               if (test_bit(pde, pd->used_pdes)) {
                        /* Scratch is never allocated this way */
-                       WARN_ON(pt == ppgtt->base.scratch_pt);
+                       WARN_ON(pt == vm->scratch_pt);
                        continue;
                }
 
@@ -821,9 +1080,10 @@ static int gen8_ppgtt_alloc_pagetabs(struct i915_hw_ppgtt *ppgtt,
                if (IS_ERR(pt))
                        goto unwind_out;
 
-               gen8_initialize_pt(&ppgtt->base, pt);
+               gen8_initialize_pt(vm, pt);
                pd->page_table[pde] = pt;
                __set_bit(pde, new_pts);
+               trace_i915_page_table_entry_alloc(vm, pde, start, GEN8_PDE_SHIFT);
        }
 
        return 0;
@@ -837,11 +1097,11 @@ unwind_out:
 
 /**
  * gen8_ppgtt_alloc_page_directories() - Allocate page directories for VA range.
- * @ppgtt:     Master ppgtt structure.
+ * @vm:        Master vm structure.
  * @pdp:       Page directory pointer for this address range.
  * @start:     Starting virtual address to begin allocations.
- * @length     Size of the allocations.
- * @new_pds    Bitmap set by function with new allocations. Likely used by the
+ * @length:    Size of the allocations.
+ * @new_pds:   Bitmap set by function with new allocations. Likely used by the
  *             caller to free on error.
  *
  * Allocate the required number of page directories starting at the pde index of
@@ -858,48 +1118,102 @@ unwind_out:
  *
  * Return: 0 if success; negative error code otherwise.
  */
-static int gen8_ppgtt_alloc_page_directories(struct i915_hw_ppgtt *ppgtt,
-                                    struct i915_page_directory_pointer *pdp,
-                                    uint64_t start,
-                                    uint64_t length,
-                                    unsigned long *new_pds)
+static int
+gen8_ppgtt_alloc_page_directories(struct i915_address_space *vm,
+                                 struct i915_page_directory_pointer *pdp,
+                                 uint64_t start,
+                                 uint64_t length,
+                                 unsigned long *new_pds)
 {
-       struct drm_device *dev = ppgtt->base.dev;
+       struct drm_device *dev = vm->dev;
        struct i915_page_directory *pd;
        uint64_t temp;
        uint32_t pdpe;
+       uint32_t pdpes = I915_PDPES_PER_PDP(dev);
 
-       WARN_ON(!bitmap_empty(new_pds, GEN8_LEGACY_PDPES));
+       WARN_ON(!bitmap_empty(new_pds, pdpes));
 
        gen8_for_each_pdpe(pd, pdp, start, length, temp, pdpe) {
-               if (pd)
+               if (test_bit(pdpe, pdp->used_pdpes))
                        continue;
 
                pd = alloc_pd(dev);
                if (IS_ERR(pd))
                        goto unwind_out;
 
-               gen8_initialize_pd(&ppgtt->base, pd);
+               gen8_initialize_pd(vm, pd);
                pdp->page_directory[pdpe] = pd;
                __set_bit(pdpe, new_pds);
+               trace_i915_page_directory_entry_alloc(vm, pdpe, start, GEN8_PDPE_SHIFT);
        }
 
        return 0;
 
 unwind_out:
-       for_each_set_bit(pdpe, new_pds, GEN8_LEGACY_PDPES)
+       for_each_set_bit(pdpe, new_pds, pdpes)
                free_pd(dev, pdp->page_directory[pdpe]);
 
        return -ENOMEM;
 }
 
-static void
-free_gen8_temp_bitmaps(unsigned long *new_pds, unsigned long **new_pts)
+/**
+ * gen8_ppgtt_alloc_page_dirpointers() - Allocate pdps for VA range.
+ * @vm:        Master vm structure.
+ * @pml4:      Page map level 4 for this address range.
+ * @start:     Starting virtual address to begin allocations.
+ * @length:    Size of the allocations.
+ * @new_pdps:  Bitmap set by function with new allocations. Likely used by the
+ *             caller to free on error.
+ *
+ * Allocate the required number of page directory pointers. Extremely similar to
+ * gen8_ppgtt_alloc_page_directories() and gen8_ppgtt_alloc_pagetabs().
+ * The main difference is here we are limited by the pml4 boundary (instead of
+ * the page directory pointer).
+ *
+ * Return: 0 if success; negative error code otherwise.
+ */
+static int
+gen8_ppgtt_alloc_page_dirpointers(struct i915_address_space *vm,
+                                 struct i915_pml4 *pml4,
+                                 uint64_t start,
+                                 uint64_t length,
+                                 unsigned long *new_pdps)
 {
-       int i;
+       struct drm_device *dev = vm->dev;
+       struct i915_page_directory_pointer *pdp;
+       uint64_t temp;
+       uint32_t pml4e;
+
+       WARN_ON(!bitmap_empty(new_pdps, GEN8_PML4ES_PER_PML4));
+
+       gen8_for_each_pml4e(pdp, pml4, start, length, temp, pml4e) {
+               if (!test_bit(pml4e, pml4->used_pml4es)) {
+                       pdp = alloc_pdp(dev);
+                       if (IS_ERR(pdp))
+                               goto unwind_out;
+
+                       gen8_initialize_pdp(vm, pdp);
+                       pml4->pdps[pml4e] = pdp;
+                       __set_bit(pml4e, new_pdps);
+                       trace_i915_page_directory_pointer_entry_alloc(vm,
+                                                                     pml4e,
+                                                                     start,
+                                                                     GEN8_PML4E_SHIFT);
+               }
+       }
+
+       return 0;
+
+unwind_out:
+       for_each_set_bit(pml4e, new_pdps, GEN8_PML4ES_PER_PML4)
+               free_pdp(dev, pml4->pdps[pml4e]);
+
+       return -ENOMEM;
+}
 
-       for (i = 0; i < GEN8_LEGACY_PDPES; i++)
-               kfree(new_pts[i]);
+static void
+free_gen8_temp_bitmaps(unsigned long *new_pds, unsigned long *new_pts)
+{
        kfree(new_pts);
        kfree(new_pds);
 }
@@ -909,28 +1223,20 @@ free_gen8_temp_bitmaps(unsigned long *new_pds, unsigned long **new_pts)
  */
 static
 int __must_check alloc_gen8_temp_bitmaps(unsigned long **new_pds,
-                                        unsigned long ***new_pts)
+                                        unsigned long **new_pts,
+                                        uint32_t pdpes)
 {
-       int i;
        unsigned long *pds;
-       unsigned long **pts;
+       unsigned long *pts;
 
-       pds = kcalloc(BITS_TO_LONGS(GEN8_LEGACY_PDPES), sizeof(unsigned long), GFP_KERNEL);
+       pds = kcalloc(BITS_TO_LONGS(pdpes), sizeof(unsigned long), GFP_TEMPORARY);
        if (!pds)
                return -ENOMEM;
 
-       pts = kcalloc(GEN8_LEGACY_PDPES, sizeof(unsigned long *), GFP_KERNEL);
-       if (!pts) {
-               kfree(pds);
-               return -ENOMEM;
-       }
-
-       for (i = 0; i < GEN8_LEGACY_PDPES; i++) {
-               pts[i] = kcalloc(BITS_TO_LONGS(I915_PDES),
-                                sizeof(unsigned long), GFP_KERNEL);
-               if (!pts[i])
-                       goto err_out;
-       }
+       pts = kcalloc(pdpes, BITS_TO_LONGS(I915_PDES) * sizeof(unsigned long),
+                     GFP_TEMPORARY);
+       if (!pts)
+               goto err_out;
 
        *new_pds = pds;
        *new_pts = pts;
@@ -952,18 +1258,21 @@ static void mark_tlbs_dirty(struct i915_hw_ppgtt *ppgtt)
        ppgtt->pd_dirty_rings = INTEL_INFO(ppgtt->base.dev)->ring_mask;
 }
 
-static int gen8_alloc_va_range(struct i915_address_space *vm,
-                              uint64_t start,
-                              uint64_t length)
+static int gen8_alloc_va_range_3lvl(struct i915_address_space *vm,
+                                   struct i915_page_directory_pointer *pdp,
+                                   uint64_t start,
+                                   uint64_t length)
 {
        struct i915_hw_ppgtt *ppgtt =
                container_of(vm, struct i915_hw_ppgtt, base);
-       unsigned long *new_page_dirs, **new_page_tables;
+       unsigned long *new_page_dirs, *new_page_tables;
+       struct drm_device *dev = vm->dev;
        struct i915_page_directory *pd;
        const uint64_t orig_start = start;
        const uint64_t orig_length = length;
        uint64_t temp;
        uint32_t pdpe;
+       uint32_t pdpes = I915_PDPES_PER_PDP(dev);
        int ret;
 
        /* Wrap is never okay since we can only represent 48b, and we don't
@@ -972,25 +1281,25 @@ static int gen8_alloc_va_range(struct i915_address_space *vm,
        if (WARN_ON(start + length < start))
                return -ENODEV;
 
-       if (WARN_ON(start + length > ppgtt->base.total))
+       if (WARN_ON(start + length > vm->total))
                return -ENODEV;
 
-       ret = alloc_gen8_temp_bitmaps(&new_page_dirs, &new_page_tables);
+       ret = alloc_gen8_temp_bitmaps(&new_page_dirs, &new_page_tables, pdpes);
        if (ret)
                return ret;
 
        /* Do the allocations first so we can easily bail out */
-       ret = gen8_ppgtt_alloc_page_directories(ppgtt, &ppgtt->pdp, start, length,
-                                       new_page_dirs);
+       ret = gen8_ppgtt_alloc_page_directories(vm, pdp, start, length,
+                                               new_page_dirs);
        if (ret) {
                free_gen8_temp_bitmaps(new_page_dirs, new_page_tables);
                return ret;
        }
 
        /* For every page directory referenced, allocate page tables */
-       gen8_for_each_pdpe(pd, &ppgtt->pdp, start, length, temp, pdpe) {
-               ret = gen8_ppgtt_alloc_pagetabs(ppgtt, pd, start, length,
-                                               new_page_tables[pdpe]);
+       gen8_for_each_pdpe(pd, pdp, start, length, temp, pdpe) {
+               ret = gen8_ppgtt_alloc_pagetabs(vm, pd, start, length,
+                                               new_page_tables + pdpe * BITS_TO_LONGS(I915_PDES));
                if (ret)
                        goto err_out;
        }
@@ -1000,10 +1309,10 @@ static int gen8_alloc_va_range(struct i915_address_space *vm,
 
        /* Allocations have completed successfully, so set the bitmaps, and do
         * the mappings. */
-       gen8_for_each_pdpe(pd, &ppgtt->pdp, start, length, temp, pdpe) {
+       gen8_for_each_pdpe(pd, pdp, start, length, temp, pdpe) {
                gen8_pde_t *const page_directory = kmap_px(pd);
                struct i915_page_table *pt;
-               uint64_t pd_len = gen8_clamp_pd(start, length);
+               uint64_t pd_len = length;
                uint64_t pd_start = start;
                uint32_t pde;
 
@@ -1027,14 +1336,18 @@ static int gen8_alloc_va_range(struct i915_address_space *vm,
                        /* Map the PDE to the page table */
                        page_directory[pde] = gen8_pde_encode(px_dma(pt),
                                                              I915_CACHE_LLC);
+                       trace_i915_page_table_entry_map(&ppgtt->base, pde, pt,
+                                                       gen8_pte_index(start),
+                                                       gen8_pte_count(start, length),
+                                                       GEN8_PTES);
 
                        /* NB: We haven't yet mapped ptes to pages. At this
                         * point we're still relying on insert_entries() */
                }
 
                kunmap_px(ppgtt, page_directory);
-
-               __set_bit(pdpe, ppgtt->pdp.used_pdpes);
+               __set_bit(pdpe, pdp->used_pdpes);
+               gen8_setup_page_directory(ppgtt, pdp, pd, pdpe);
        }
 
        free_gen8_temp_bitmaps(new_page_dirs, new_page_tables);
@@ -1043,18 +1356,191 @@ static int gen8_alloc_va_range(struct i915_address_space *vm,
 
 err_out:
        while (pdpe--) {
-               for_each_set_bit(temp, new_page_tables[pdpe], I915_PDES)
-                       free_pt(vm->dev, ppgtt->pdp.page_directory[pdpe]->page_table[temp]);
+               for_each_set_bit(temp, new_page_tables + pdpe *
+                               BITS_TO_LONGS(I915_PDES), I915_PDES)
+                       free_pt(dev, pdp->page_directory[pdpe]->page_table[temp]);
        }
 
-       for_each_set_bit(pdpe, new_page_dirs, GEN8_LEGACY_PDPES)
-               free_pd(vm->dev, ppgtt->pdp.page_directory[pdpe]);
+       for_each_set_bit(pdpe, new_page_dirs, pdpes)
+               free_pd(dev, pdp->page_directory[pdpe]);
 
        free_gen8_temp_bitmaps(new_page_dirs, new_page_tables);
        mark_tlbs_dirty(ppgtt);
        return ret;
 }
 
+static int gen8_alloc_va_range_4lvl(struct i915_address_space *vm,
+                                   struct i915_pml4 *pml4,
+                                   uint64_t start,
+                                   uint64_t length)
+{
+       DECLARE_BITMAP(new_pdps, GEN8_PML4ES_PER_PML4);
+       struct i915_hw_ppgtt *ppgtt =
+                       container_of(vm, struct i915_hw_ppgtt, base);
+       struct i915_page_directory_pointer *pdp;
+       uint64_t temp, pml4e;
+       int ret = 0;
+
+       /* Do the pml4 allocations first, so we don't need to track the newly
+        * allocated tables below the pdp */
+       bitmap_zero(new_pdps, GEN8_PML4ES_PER_PML4);
+
+       /* The pagedirectory and pagetable allocations are done in the shared 3
+        * and 4 level code. Just allocate the pdps.
+        */
+       ret = gen8_ppgtt_alloc_page_dirpointers(vm, pml4, start, length,
+                                               new_pdps);
+       if (ret)
+               return ret;
+
+       WARN(bitmap_weight(new_pdps, GEN8_PML4ES_PER_PML4) > 2,
+            "The allocation has spanned more than 512GB. "
+            "It is highly likely this is incorrect.");
+
+       gen8_for_each_pml4e(pdp, pml4, start, length, temp, pml4e) {
+               WARN_ON(!pdp);
+
+               ret = gen8_alloc_va_range_3lvl(vm, pdp, start, length);
+               if (ret)
+                       goto err_out;
+
+               gen8_setup_page_directory_pointer(ppgtt, pml4, pdp, pml4e);
+       }
+
+       bitmap_or(pml4->used_pml4es, new_pdps, pml4->used_pml4es,
+                 GEN8_PML4ES_PER_PML4);
+
+       return 0;
+
+err_out:
+       for_each_set_bit(pml4e, new_pdps, GEN8_PML4ES_PER_PML4)
+               gen8_ppgtt_cleanup_3lvl(vm->dev, pml4->pdps[pml4e]);
+
+       return ret;
+}
+
+static int gen8_alloc_va_range(struct i915_address_space *vm,
+                              uint64_t start, uint64_t length)
+{
+       struct i915_hw_ppgtt *ppgtt =
+               container_of(vm, struct i915_hw_ppgtt, base);
+
+       if (USES_FULL_48BIT_PPGTT(vm->dev))
+               return gen8_alloc_va_range_4lvl(vm, &ppgtt->pml4, start, length);
+       else
+               return gen8_alloc_va_range_3lvl(vm, &ppgtt->pdp, start, length);
+}
+
+static void gen8_dump_pdp(struct i915_page_directory_pointer *pdp,
+                         uint64_t start, uint64_t length,
+                         gen8_pte_t scratch_pte,
+                         struct seq_file *m)
+{
+       struct i915_page_directory *pd;
+       uint64_t temp;
+       uint32_t pdpe;
+
+       gen8_for_each_pdpe(pd, pdp, start, length, temp, pdpe) {
+               struct i915_page_table *pt;
+               uint64_t pd_len = length;
+               uint64_t pd_start = start;
+               uint32_t pde;
+
+               if (!test_bit(pdpe, pdp->used_pdpes))
+                       continue;
+
+               seq_printf(m, "\tPDPE #%d\n", pdpe);
+               gen8_for_each_pde(pt, pd, pd_start, pd_len, temp, pde) {
+                       uint32_t  pte;
+                       gen8_pte_t *pt_vaddr;
+
+                       if (!test_bit(pde, pd->used_pdes))
+                               continue;
+
+                       pt_vaddr = kmap_px(pt);
+                       for (pte = 0; pte < GEN8_PTES; pte += 4) {
+                               uint64_t va =
+                                       (pdpe << GEN8_PDPE_SHIFT) |
+                                       (pde << GEN8_PDE_SHIFT) |
+                                       (pte << GEN8_PTE_SHIFT);
+                               int i;
+                               bool found = false;
+
+                               for (i = 0; i < 4; i++)
+                                       if (pt_vaddr[pte + i] != scratch_pte)
+                                               found = true;
+                               if (!found)
+                                       continue;
+
+                               seq_printf(m, "\t\t0x%lx [%03d,%03d,%04d]: =", va, pdpe, pde, pte);
+                               for (i = 0; i < 4; i++) {
+                                       if (pt_vaddr[pte + i] != scratch_pte)
+                                               seq_printf(m, " %lx", pt_vaddr[pte + i]);
+                                       else
+                                               seq_puts(m, "  SCRATCH ");
+                               }
+                               seq_puts(m, "\n");
+                       }
+                       /* don't use kunmap_px, it could trigger
+                        * an unnecessary flush.
+                        */
+                       kunmap_atomic(pt_vaddr);
+               }
+       }
+}
+
+static void gen8_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m)
+{
+       struct i915_address_space *vm = &ppgtt->base;
+       uint64_t start = ppgtt->base.start;
+       uint64_t length = ppgtt->base.total;
+       gen8_pte_t scratch_pte = gen8_pte_encode(px_dma(vm->scratch_page),
+                                                I915_CACHE_LLC, true);
+
+       if (!USES_FULL_48BIT_PPGTT(vm->dev)) {
+               gen8_dump_pdp(&ppgtt->pdp, start, length, scratch_pte, m);
+       } else {
+               uint64_t templ4, pml4e;
+               struct i915_pml4 *pml4 = &ppgtt->pml4;
+               struct i915_page_directory_pointer *pdp;
+
+               gen8_for_each_pml4e(pdp, pml4, start, length, templ4, pml4e) {
+                       if (!test_bit(pml4e, pml4->used_pml4es))
+                               continue;
+
+                       seq_printf(m, "    PML4E #%lu\n", pml4e);
+                       gen8_dump_pdp(pdp, start, length, scratch_pte, m);
+               }
+       }
+}
+
+static int gen8_preallocate_top_level_pdps(struct i915_hw_ppgtt *ppgtt)
+{
+       unsigned long *new_page_dirs, *new_page_tables;
+       uint32_t pdpes = I915_PDPES_PER_PDP(dev);
+       int ret;
+
+       /* We allocate temp bitmap for page tables for no gain
+        * but as this is for init only, lets keep the things simple
+        */
+       ret = alloc_gen8_temp_bitmaps(&new_page_dirs, &new_page_tables, pdpes);
+       if (ret)
+               return ret;
+
+       /* Allocate for all pdps regardless of how the ppgtt
+        * was defined.
+        */
+       ret = gen8_ppgtt_alloc_page_directories(&ppgtt->base, &ppgtt->pdp,
+                                               0, 1ULL << 32,
+                                               new_page_dirs);
+       if (!ret)
+               *ppgtt->pdp.used_pdpes = *new_page_dirs;
+
+       free_gen8_temp_bitmaps(new_page_dirs, new_page_tables);
+
+       return ret;
+}
+
 /*
  * GEN8 legacy ppgtt programming is accomplished through a max 4 PDP registers
  * with a net effect resembling a 2-level page table in normal x86 terms. Each
@@ -1071,26 +1557,50 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
                return ret;
 
        ppgtt->base.start = 0;
-#ifndef __DragonFly__
-       ppgtt->base.total = 1ULL << 32;
-       if (IS_ENABLED(CONFIG_X86_32))
-               /* While we have a proliferation of size_t variables
-                * we cannot represent the full ppgtt size on 32bit,
-                * so limit it to the same size as the GGTT (currently
-                * 2GiB).
-                */
-#endif
-               ppgtt->base.total = to_i915(ppgtt->base.dev)->gtt.base.total;
        ppgtt->base.cleanup = gen8_ppgtt_cleanup;
        ppgtt->base.allocate_va_range = gen8_alloc_va_range;
        ppgtt->base.insert_entries = gen8_ppgtt_insert_entries;
        ppgtt->base.clear_range = gen8_ppgtt_clear_range;
        ppgtt->base.unbind_vma = ppgtt_unbind_vma;
        ppgtt->base.bind_vma = ppgtt_bind_vma;
+       ppgtt->debug_dump = gen8_dump_ppgtt;
+
+       if (USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) {
+               ret = setup_px(ppgtt->base.dev, &ppgtt->pml4);
+               if (ret)
+                       goto free_scratch;
+
+               gen8_initialize_pml4(&ppgtt->base, &ppgtt->pml4);
+
+               ppgtt->base.total = 1ULL << 48;
+               ppgtt->switch_mm = gen8_48b_mm_switch;
+       } else {
+               ret = __pdp_init(ppgtt->base.dev, &ppgtt->pdp);
+               if (ret)
+                       goto free_scratch;
+
+#define CONFIG_X86_32  0
+               ppgtt->base.total = 1ULL << 32;
+               ppgtt->switch_mm = gen8_legacy_mm_switch;
+               trace_i915_page_directory_pointer_entry_alloc(&ppgtt->base,
+                                                             0, 0,
+                                                             GEN8_PML4E_SHIFT);
+
+               if (intel_vgpu_active(ppgtt->base.dev)) {
+                       ret = gen8_preallocate_top_level_pdps(ppgtt);
+                       if (ret)
+                               goto free_scratch;
+               }
+       }
 
-       ppgtt->switch_mm = gen8_mm_switch;
+       if (intel_vgpu_active(ppgtt->base.dev))
+               gen8_ppgtt_notify_vgt(ppgtt, true);
 
        return 0;
+
+free_scratch:
+       gen8_free_scratch(&ppgtt->base);
+       return ret;
 }
 
 static int gen8_aliasing_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
@@ -1305,8 +1815,9 @@ static void gen8_ppgtt_enable(struct drm_device *dev)
        int j;
 
        for_each_ring(ring, dev_priv, j) {
+               u32 four_level = USES_FULL_48BIT_PPGTT(dev) ? GEN8_GFX_PPGTT_48B : 0;
                I915_WRITE(RING_MODE_GEN7(ring),
-                          _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE));
+                          _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE | four_level));
        }
 }
 
@@ -1703,6 +2214,16 @@ static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt,
                return gen8_ppgtt_init(ppgtt);
 }
 
+static void i915_address_space_init(struct i915_address_space *vm,
+                                   struct drm_i915_private *dev_priv)
+{
+       drm_mm_init(&vm->mm, vm->start, vm->total);
+       vm->dev = dev_priv->dev;
+       INIT_LIST_HEAD(&vm->active_list);
+       INIT_LIST_HEAD(&vm->inactive_list);
+       list_add_tail(&vm->global_link, &dev_priv->vm_list);
+}
+
 int i915_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1711,9 +2232,7 @@ int i915_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
        ret = __hw_ppgtt_init(dev, ppgtt, false);
        if (ret == 0) {
                kref_init(&ppgtt->ref);
-               drm_mm_init(&ppgtt->base.mm, ppgtt->base.start,
-                           ppgtt->base.total);
-               i915_init_vm(dev_priv, &ppgtt->base);
+               i915_address_space_init(&ppgtt->base, dev_priv);
        }
 
        return ret;
@@ -2076,6 +2595,36 @@ static void i915_ggtt_clear_range(struct i915_address_space *vm,
 static int ggtt_bind_vma(struct i915_vma *vma,
                         enum i915_cache_level cache_level,
                         u32 flags)
+{
+       struct drm_i915_gem_object *obj = vma->obj;
+       u32 pte_flags = 0;
+       int ret;
+
+       ret = i915_get_ggtt_vma_pages(vma);
+       if (ret)
+               return ret;
+
+       /* Currently applicable only to VLV */
+       if (obj->gt_ro)
+               pte_flags |= PTE_READ_ONLY;
+
+       vma->vm->insert_entries(vma->vm, vma->ggtt_view.pages,
+                               vma->node.start,
+                               cache_level, pte_flags);
+
+       /*
+        * Without aliasing PPGTT there's no difference between
+        * GLOBAL/LOCAL_BIND, it's all the same ptes. Hence unconditionally
+        * upgrade to both bound if we bind either to avoid double-binding.
+        */
+       vma->bound |= GLOBAL_BIND | LOCAL_BIND;
+
+       return 0;
+}
+
+static int aliasing_gtt_bind_vma(struct i915_vma *vma,
+                                enum i915_cache_level cache_level,
+                                u32 flags)
 {
        struct drm_device *dev = vma->vm->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2094,13 +2643,13 @@ static int ggtt_bind_vma(struct i915_vma *vma,
                pte_flags |= PTE_READ_ONLY;
 
 
-       if (!dev_priv->mm.aliasing_ppgtt || flags & GLOBAL_BIND) {
+       if (flags & GLOBAL_BIND) {
                vma->vm->insert_entries(vma->vm, pages,
                                        vma->node.start,
                                        cache_level, pte_flags);
        }
 
-       if (dev_priv->mm.aliasing_ppgtt && flags & LOCAL_BIND) {
+       if (flags & LOCAL_BIND) {
                struct i915_hw_ppgtt *appgtt = dev_priv->mm.aliasing_ppgtt;
                appgtt->base.insert_entries(&appgtt->base, pages,
                                            vma->node.start,
@@ -2168,9 +2717,9 @@ static void i915_gtt_color_adjust(struct drm_mm_node *node,
 }
 
 static int i915_gem_setup_global_gtt(struct drm_device *dev,
-                                    unsigned long start,
-                                    unsigned long mappable_end,
-                                    unsigned long end)
+                                    u64 start,
+                                    u64 mappable_end,
+                                    u64 end)
 {
        /* Let GEM Manage all of the aperture.
         *
@@ -2193,11 +2742,13 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev,
        mappable = min(end, mappable_end) - start;
        BUG_ON(mappable_end > end);
 
-       /* Subtract the guard page ... */
-       drm_mm_init(&ggtt_vm->mm, start, end - start - PAGE_SIZE);
+       ggtt_vm->start = start;
 
-       dev_priv->gtt.base.start = start;
-       dev_priv->gtt.base.total = end - start;
+       /* Subtract the guard page before address space initialization to
+        * shrink the range used by drm_mm */
+       ggtt_vm->total = end - start - PAGE_SIZE;
+       i915_address_space_init(ggtt_vm, dev_priv);
+       ggtt_vm->total += PAGE_SIZE;
 
        if (intel_vgpu_active(dev)) {
                ret = intel_vgt_balloon(dev);
@@ -2206,7 +2757,7 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev,
        }
 
        if (!HAS_LLC(dev))
-               dev_priv->gtt.base.mm.color_adjust = i915_gtt_color_adjust;
+               ggtt_vm->mm.color_adjust = i915_gtt_color_adjust;
 
        /* Mark any preallocated objects as occupied */
        list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
@@ -2222,6 +2773,8 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev,
                        return ret;
                }
                vma->bound |= GLOBAL_BIND;
+               __i915_vma_set_map_and_fenceable(vma);
+               list_add_tail(&vma->mm_list, &ggtt_vm->inactive_list);
        }
 
        /* Clear any non-preallocated blocks */
@@ -2272,6 +2825,8 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev,
                                        true);
 
                dev_priv->mm.aliasing_ppgtt = ppgtt;
+               WARN_ON(dev_priv->gtt.base.bind_vma != ggtt_bind_vma);
+               dev_priv->gtt.base.bind_vma = aliasing_gtt_bind_vma;
        }
 
        return 0;
@@ -2462,8 +3017,8 @@ static void bdw_setup_private_ppat(struct drm_i915_private *dev_priv)
 
        /* XXX: spec defines this as 2 distinct registers. It's unclear if a 64b
         * write would work. */
-       I915_WRITE(GEN8_PRIVATE_PAT, pat);
-       I915_WRITE(GEN8_PRIVATE_PAT + 4, pat >> 32);
+       I915_WRITE(GEN8_PRIVATE_PAT_LO, pat);
+       I915_WRITE(GEN8_PRIVATE_PAT_HI, pat >> 32);
 }
 
 static void chv_setup_private_ppat(struct drm_i915_private *dev_priv)
@@ -2497,8 +3052,8 @@ static void chv_setup_private_ppat(struct drm_i915_private *dev_priv)
              GEN8_PPAT(6, CHV_PPAT_SNOOP) |
              GEN8_PPAT(7, CHV_PPAT_SNOOP);
 
-       I915_WRITE(GEN8_PRIVATE_PAT, pat);
-       I915_WRITE(GEN8_PRIVATE_PAT + 4, pat >> 32);
+       I915_WRITE(GEN8_PRIVATE_PAT_LO, pat);
+       I915_WRITE(GEN8_PRIVATE_PAT_HI, pat >> 32);
 }
 
 static int gen8_gmch_probe(struct drm_device *dev,
@@ -2813,15 +3368,18 @@ i915_gem_obj_lookup_or_create_ggtt_vma(struct drm_i915_gem_object *obj,
 
 }
 
-static void
-rotate_pages(dma_addr_t *in, unsigned int width, unsigned int height,
-            struct sg_table *st)
+static struct scatterlist *
+rotate_pages(dma_addr_t *in, unsigned int offset,
+            unsigned int width, unsigned int height,
+            struct sg_table *st, struct scatterlist *sg)
 {
        unsigned int column, row;
        unsigned int src_idx;
-       struct scatterlist *sg = st->sgl;
 
-       st->nents = 0;