Merge drm/drm-next into drm-intel-next-queued
authorJani Nikula <jani.nikula@intel.com>
Tue, 28 May 2019 08:07:59 +0000 (11:07 +0300)
committerJani Nikula <jani.nikula@intel.com>
Tue, 28 May 2019 08:07:59 +0000 (11:07 +0300)
Get the HDR dependencies originally merged via drm-misc. Sync up all
i915 changes applied via other trees. And get v5.2-rc2 as the baseline.

Signed-off-by: Jani Nikula <jani.nikula@intel.com>
22 files changed:
drivers/gpu/drm/i915/Kconfig.profile
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_gtt.c
drivers/gpu/drm/i915/i915_gem_pm.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_atomic_plane.c
drivers/gpu/drm/i915/intel_atomic_plane.h
drivers/gpu/drm/i915/intel_bw.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_bw.h [new file with mode: 0644]
drivers/gpu/drm/i915/intel_combo_phy.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/i915/intel_sideband.c
drivers/gpu/drm/i915/intel_sideband.h
drivers/gpu/drm/i915/intel_wakeref.c
drivers/gpu/drm/i915/intel_wakeref.h
drivers/gpu/drm/i915/vlv_dsi.c

index 0e5db98da8f3a952b14d5e1b8d0cb14185a607bd..4fd1ea639d0f74d28e8354cda267e63e532b8778 100644 (file)
@@ -1,3 +1,17 @@
+config DRM_I915_USERFAULT_AUTOSUSPEND
+       int "Runtime autosuspend delay for userspace GGTT mmaps (ms)"
+       default 250 # milliseconds
+       help
+         On runtime suspend, as we suspend the device, we have to revoke
+         userspace GGTT mmaps and force userspace to take a pagefault on
+         their next access. The revocation and subsequent recreation of
+         the GGTT mmap can be very slow and so we impose a small hysteris
+         that complements the runtime-pm autosuspend and provides a lower
+         floor on the autosuspend delay.
+
+         May be 0 to disable the extra delay and solely use the device level
+         runtime pm autosuspend delay tunable.
+
 config DRM_I915_SPIN_REQUEST
        int
        default 5 # microseconds
index 68106fe35a04bfa29c9eb6e8835c8af33e0cee6c..139a0fc1939020a9cce19531b2c20c97f5a6621b 100644 (file)
@@ -138,6 +138,7 @@ i915-y += intel_audio.o \
          intel_atomic.o \
          intel_atomic_plane.o \
          intel_bios.o \
+         intel_bw.o \
          intel_cdclk.o \
          intel_color.o \
          intel_combo_phy.o \
index 633a08c0f9079f65051b41557bf8946602d4c6a2..344beab229a03d9d7b0a13cfbd20e08d16591f55 100644 (file)
@@ -1500,7 +1500,7 @@ static int gen6_drpc_info(struct seq_file *m)
 
        if (INTEL_GEN(dev_priv) <= 7)
                sandybridge_pcode_read(dev_priv, GEN6_PCODE_READ_RC6VIDS,
-                                      &rc6vids);
+                                      &rc6vids, NULL);
 
        seq_printf(m, "RC1e Enabled: %s\n",
                   yesno(rcctl1 & GEN6_RC_CTL_RC1e_ENABLE));
@@ -1783,7 +1783,7 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused)
                ia_freq = gpu_freq;
                sandybridge_pcode_read(dev_priv,
                                       GEN6_PCODE_READ_MIN_FREQ_TABLE,
-                                      &ia_freq);
+                                      &ia_freq, NULL);
                seq_printf(m, "%d\t\t%d\t\t\t\t%d\n",
                           intel_gpu_freq(dev_priv, (gpu_freq *
                                                     (IS_GEN9_BC(dev_priv) ||
index 83d2eb9e74cb72bb47198bd89bb8766a0b081c2a..6699e3a9427220e9fbb2448ef3456f1637b09d5f 100644 (file)
@@ -60,6 +60,7 @@
 #include "i915_vgpu.h"
 #include "intel_acpi.h"
 #include "intel_audio.h"
+#include "intel_bw.h"
 #include "intel_cdclk.h"
 #include "intel_csr.h"
 #include "intel_dp.h"
@@ -1657,6 +1658,7 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
         */
        intel_get_dram_info(dev_priv);
 
+       intel_bw_init_hw(dev_priv);
 
        return 0;
 
index a2664ea1395b54927e9b85b8982fa619440d6385..87cc80c7b6269e244c3eb92597a6f7b8394e419f 100644 (file)
@@ -54,6 +54,7 @@
 #include <drm/drm_cache.h>
 #include <drm/drm_util.h>
 #include <drm/drm_dsc.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_connector.h>
 #include <drm/i915_mei_hdcp_interface.h>
 
@@ -874,6 +875,9 @@ struct i915_gem_mm {
         */
        struct list_head userfault_list;
 
+       /* Manual runtime pm autosuspend delay for user GGTT mmaps */
+       struct intel_wakeref_auto userfault_wakeref;
+
        /**
         * List of objects which are pending destruction.
         */
@@ -1841,6 +1845,13 @@ struct drm_i915_private {
                } type;
        } dram_info;
 
+       struct intel_bw_info {
+               int num_planes;
+               int deratedbw[3];
+       } max_bw[6];
+
+       struct drm_private_obj bw_obj;
+
        struct i915_runtime_pm runtime_pm;
 
        struct {
index d3b7dac527dc7783a5d87bca0fb3fdac3c22d3ad..902162c04d3593a377ad4a84d55a75453304a59c 100644 (file)
@@ -1834,6 +1834,9 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
        assert_rpm_wakelock_held(dev_priv);
        if (!i915_vma_set_userfault(vma) && !obj->userfault_count++)
                list_add(&obj->userfault_link, &dev_priv->mm.userfault_list);
+       if (CONFIG_DRM_I915_USERFAULT_AUTOSUSPEND)
+               intel_wakeref_auto(&dev_priv->mm.userfault_wakeref,
+                                  msecs_to_jiffies_timeout(CONFIG_DRM_I915_USERFAULT_AUTOSUSPEND));
        GEM_BUG_ON(!obj->userfault_count);
 
        i915_vma_set_ggtt_write(vma);
@@ -4671,6 +4674,8 @@ void i915_gem_fini(struct drm_i915_private *dev_priv)
 {
        GEM_BUG_ON(dev_priv->gt.awake);
 
+       intel_wakeref_auto_fini(&dev_priv->mm.userfault_wakeref);
+
        i915_gem_suspend_late(dev_priv);
        intel_disable_gt_powersave(dev_priv);
 
@@ -4746,7 +4751,9 @@ static void i915_gem_init__mm(struct drm_i915_private *i915)
        INIT_LIST_HEAD(&i915->mm.unbound_list);
        INIT_LIST_HEAD(&i915->mm.bound_list);
        INIT_LIST_HEAD(&i915->mm.fence_list);
+
        INIT_LIST_HEAD(&i915->mm.userfault_list);
+       intel_wakeref_auto_init(&i915->mm.userfault_wakeref, i915);
 
        INIT_WORK(&i915->mm.free_work, __i915_gem_free_work);
 }
index 266baa11df64b6e4379160f1936eee2b417f6a73..bc5ddeb845b0c90eb035b024b9d7d6508bd9d33d 100644 (file)
@@ -2063,8 +2063,10 @@ static struct i915_hw_ppgtt *gen6_ppgtt_create(struct drm_i915_private *i915)
        ppgtt->base.vm.pte_encode = ggtt->vm.pte_encode;
 
        ppgtt->work = kmalloc(sizeof(*ppgtt->work), GFP_KERNEL);
-       if (!ppgtt->work)
+       if (!ppgtt->work) {
+               err = -ENOMEM;
                goto err_free;
+       }
 
        err = gen6_ppgtt_init_scratch(ppgtt);
        if (err)
index fa9c2ebd966ab84415161a322125b29121d70a10..c0ad196052972e56a9f4ea050734607e9efa247f 100644 (file)
@@ -126,6 +126,7 @@ void i915_gem_suspend(struct drm_i915_private *i915)
 {
        GEM_TRACE("\n");
 
+       intel_wakeref_auto(&i915->mm.userfault_wakeref, 0);
        flush_workqueue(i915->wq);
 
        mutex_lock(&i915->drm.struct_mutex);
index 49dce04dd688e6fe87a6096f8ee4fcc25da5f8e1..edae92f5a45e4f29dcf63d174a4567ac604e3285 100644 (file)
  */
 #define REG_BIT(__n)                                                   \
        ((u32)(BIT(__n) +                                               \
-              BUILD_BUG_ON_ZERO(__builtin_constant_p(__n) &&           \
+              BUILD_BUG_ON_ZERO(__is_constexpr(__n) &&         \
                                 ((__n) < 0 || (__n) > 31))))
 
 /**
  */
 #define REG_GENMASK(__high, __low)                                     \
        ((u32)(GENMASK(__high, __low) +                                 \
-              BUILD_BUG_ON_ZERO(__builtin_constant_p(__high) &&        \
-                                __builtin_constant_p(__low) &&         \
+              BUILD_BUG_ON_ZERO(__is_constexpr(__high) &&      \
+                                __is_constexpr(__low) &&               \
                                 ((__low) < 0 || (__high) > 31 || (__low) > (__high)))))
 
 /*
@@ -1846,6 +1846,9 @@ enum i915_power_well_id {
 #define   VOLTAGE_INFO_MASK            (3 << 24)
 #define   VOLTAGE_INFO_SHIFT           24
 
+#define ICL_PORT_COMP_DW8(port)                _MMIO(_ICL_PORT_COMP_DW(8, port))
+#define   IREFGEN                      (1 << 24)
+
 #define CNL_PORT_COMP_DW9              _MMIO(0x162124)
 #define ICL_PORT_COMP_DW9(port)                _MMIO(_ICL_PORT_COMP_DW(9, port))
 
@@ -8777,6 +8780,9 @@ enum {
 #define   GEN6_PCODE_WRITE_MIN_FREQ_TABLE      0x8
 #define   GEN6_PCODE_READ_MIN_FREQ_TABLE       0x9
 #define   GEN6_READ_OC_PARAMS                  0xc
+#define   ICL_PCODE_MEM_SUBSYSYSTEM_INFO       0xd
+#define     ICL_PCODE_MEM_SS_READ_GLOBAL_INFO  (0x0 << 8)
+#define     ICL_PCODE_MEM_SS_READ_QGV_POINT_INFO(point)        (((point) << 16) | (0x1 << 8))
 #define   GEN6_PCODE_READ_D_COMP               0x10
 #define   GEN6_PCODE_WRITE_D_COMP              0x11
 #define   HSW_PCODE_DE_WRITE_FREQ_REQ          0x17
index d11681d71add7cf280a068e6279de8ca20c08ee3..58ea1b672a1a4c381cf76ae091fac2ff9c3e3038 100644 (file)
@@ -114,6 +114,29 @@ intel_plane_destroy_state(struct drm_plane *plane,
        drm_atomic_helper_plane_destroy_state(plane, state);
 }
 
+unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state,
+                                  const struct intel_plane_state *plane_state)
+{
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       unsigned int cpp;
+
+       if (!plane_state->base.visible)
+               return 0;
+
+       cpp = fb->format->cpp[0];
+
+       /*
+        * Based on HSD#:1408715493
+        * NV12 cpp == 4, P010 cpp == 8
+        *
+        * FIXME what is the logic behind this?
+        */
+       if (fb->format->is_yuv && fb->format->num_planes > 1)
+               cpp *= 4;
+
+       return cpp * crtc_state->pixel_rate;
+}
+
 int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_state,
                                        struct intel_crtc_state *new_crtc_state,
                                        const struct intel_plane_state *old_plane_state,
@@ -125,6 +148,7 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
        new_crtc_state->active_planes &= ~BIT(plane->id);
        new_crtc_state->nv12_planes &= ~BIT(plane->id);
        new_crtc_state->c8_planes &= ~BIT(plane->id);
+       new_crtc_state->data_rate[plane->id] = 0;
        new_plane_state->base.visible = false;
 
        if (!new_plane_state->base.crtc && !old_plane_state->base.crtc)
@@ -149,6 +173,9 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
        if (new_plane_state->base.visible || old_plane_state->base.visible)
                new_crtc_state->update_planes |= BIT(plane->id);
 
+       new_crtc_state->data_rate[plane->id] =
+               intel_plane_data_rate(new_crtc_state, new_plane_state);
+
        return intel_plane_atomic_calc_changes(old_crtc_state,
                                               &new_crtc_state->base,
                                               old_plane_state,
index 14678620440fc048c310e131006e5cb8b62f8edf..0a9651376d0e67bba988aeb98d5c5a1e560a09c1 100644 (file)
@@ -15,6 +15,8 @@ struct intel_plane_state;
 
 extern const struct drm_plane_helper_funcs intel_plane_helper_funcs;
 
+unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state,
+                                  const struct intel_plane_state *plane_state);
 void intel_update_plane(struct intel_plane *plane,
                        const struct intel_crtc_state *crtc_state,
                        const struct intel_plane_state *plane_state);
diff --git a/drivers/gpu/drm/i915/intel_bw.c b/drivers/gpu/drm/i915/intel_bw.c
new file mode 100644 (file)
index 0000000..753ac31
--- /dev/null
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2019 Intel Corporation
+ */
+
+#include <drm/drm_atomic_state_helper.h>
+
+#include "intel_bw.h"
+#include "intel_drv.h"
+#include "intel_sideband.h"
+
+/* Parameters for Qclk Geyserville (QGV) */
+struct intel_qgv_point {
+       u16 dclk, t_rp, t_rdpre, t_rc, t_ras, t_rcd;
+};
+
+struct intel_qgv_info {
+       struct intel_qgv_point points[3];
+       u8 num_points;
+       u8 num_channels;
+       u8 t_bl;
+       enum intel_dram_type dram_type;
+};
+
+static int icl_pcode_read_mem_global_info(struct drm_i915_private *dev_priv,
+                                         struct intel_qgv_info *qi)
+{
+       u32 val = 0;
+       int ret;
+
+       ret = sandybridge_pcode_read(dev_priv,
+                                    ICL_PCODE_MEM_SUBSYSYSTEM_INFO |
+                                    ICL_PCODE_MEM_SS_READ_GLOBAL_INFO,
+                                    &val, NULL);
+       if (ret)
+               return ret;
+
+       switch (val & 0xf) {
+       case 0:
+               qi->dram_type = INTEL_DRAM_DDR4;
+               break;
+       case 1:
+               qi->dram_type = INTEL_DRAM_DDR3;
+               break;
+       case 2:
+               qi->dram_type = INTEL_DRAM_LPDDR3;
+               break;
+       case 3:
+               qi->dram_type = INTEL_DRAM_LPDDR3;
+               break;
+       default:
+               MISSING_CASE(val & 0xf);
+               break;
+       }
+
+       qi->num_channels = (val & 0xf0) >> 4;
+       qi->num_points = (val & 0xf00) >> 8;
+
+       qi->t_bl = qi->dram_type == INTEL_DRAM_DDR4 ? 4 : 8;
+
+       return 0;
+}
+
+static int icl_pcode_read_qgv_point_info(struct drm_i915_private *dev_priv,
+                                        struct intel_qgv_point *sp,
+                                        int point)
+{
+       u32 val = 0, val2;
+       int ret;
+
+       ret = sandybridge_pcode_read(dev_priv,
+                                    ICL_PCODE_MEM_SUBSYSYSTEM_INFO |
+                                    ICL_PCODE_MEM_SS_READ_QGV_POINT_INFO(point),
+                                    &val, &val2);
+       if (ret)
+               return ret;
+
+       sp->dclk = val & 0xffff;
+       sp->t_rp = (val & 0xff0000) >> 16;
+       sp->t_rcd = (val & 0xff000000) >> 24;
+
+       sp->t_rdpre = val2 & 0xff;
+       sp->t_ras = (val2 & 0xff00) >> 8;
+
+       sp->t_rc = sp->t_rp + sp->t_ras;
+
+       return 0;
+}
+
+static int icl_get_qgv_points(struct drm_i915_private *dev_priv,
+                             struct intel_qgv_info *qi)
+{
+       int i, ret;
+
+       ret = icl_pcode_read_mem_global_info(dev_priv, qi);
+       if (ret)
+               return ret;
+
+       if (WARN_ON(qi->num_points > ARRAY_SIZE(qi->points)))
+               qi->num_points = ARRAY_SIZE(qi->points);
+
+       for (i = 0; i < qi->num_points; i++) {
+               struct intel_qgv_point *sp = &qi->points[i];
+
+               ret = icl_pcode_read_qgv_point_info(dev_priv, sp, i);
+               if (ret)
+                       return ret;
+
+               DRM_DEBUG_KMS("QGV %d: DCLK=%d tRP=%d tRDPRE=%d tRAS=%d tRCD=%d tRC=%d\n",
+                             i, sp->dclk, sp->t_rp, sp->t_rdpre, sp->t_ras,
+                             sp->t_rcd, sp->t_rc);
+       }
+
+       return 0;
+}
+
+static int icl_calc_bw(int dclk, int num, int den)
+{
+       /* multiples of 16.666MHz (100/6) */
+       return DIV_ROUND_CLOSEST(num * dclk * 100, den * 6);
+}
+
+static int icl_sagv_max_dclk(const struct intel_qgv_info *qi)
+{
+       u16 dclk = 0;
+       int i;
+
+       for (i = 0; i < qi->num_points; i++)
+               dclk = max(dclk, qi->points[i].dclk);
+
+       return dclk;
+}
+
+struct intel_sa_info {
+       u8 deburst, mpagesize, deprogbwlimit, displayrtids;
+};
+
+static const struct intel_sa_info icl_sa_info = {
+       .deburst = 8,
+       .mpagesize = 16,
+       .deprogbwlimit = 25, /* GB/s */
+       .displayrtids = 128,
+};
+
+static int icl_get_bw_info(struct drm_i915_private *dev_priv)
+{
+       struct intel_qgv_info qi = {};
+       const struct intel_sa_info *sa = &icl_sa_info;
+       bool is_y_tile = true; /* assume y tile may be used */
+       int num_channels;
+       int deinterleave;
+       int ipqdepth, ipqdepthpch;
+       int dclk_max;
+       int maxdebw;
+       int i, ret;
+
+       ret = icl_get_qgv_points(dev_priv, &qi);
+       if (ret) {
+               DRM_DEBUG_KMS("Failed to get memory subsystem information, ignoring bandwidth limits");
+               return ret;
+       }
+       num_channels = qi.num_channels;
+
+       deinterleave = DIV_ROUND_UP(num_channels, is_y_tile ? 4 : 2);
+       dclk_max = icl_sagv_max_dclk(&qi);
+
+       ipqdepthpch = 16;
+
+       maxdebw = min(sa->deprogbwlimit * 1000,
+                     icl_calc_bw(dclk_max, 16, 1) * 6 / 10); /* 60% */
+       ipqdepth = min(ipqdepthpch, sa->displayrtids / num_channels);
+
+       for (i = 0; i < ARRAY_SIZE(dev_priv->max_bw); i++) {
+               struct intel_bw_info *bi = &dev_priv->max_bw[i];
+               int clpchgroup;
+               int j;
+
+               clpchgroup = (sa->deburst * deinterleave / num_channels) << i;
+               bi->num_planes = (ipqdepth - clpchgroup) / clpchgroup + 1;
+
+               for (j = 0; j < qi.num_points; j++) {
+                       const struct intel_qgv_point *sp = &qi.points[j];
+                       int ct, bw;
+
+                       /*
+                        * Max row cycle time
+                        *
+                        * FIXME what is the logic behind the
+                        * assumed burst length?
+                        */
+                       ct = max_t(int, sp->t_rc, sp->t_rp + sp->t_rcd +
+                                  (clpchgroup - 1) * qi.t_bl + sp->t_rdpre);
+                       bw = icl_calc_bw(sp->dclk, clpchgroup * 32 * num_channels, ct);
+
+                       bi->deratedbw[j] = min(maxdebw,
+                                              bw * 9 / 10); /* 90% */
+
+                       DRM_DEBUG_KMS("BW%d / QGV %d: num_planes=%d deratedbw=%d\n",
+                                     i, j, bi->num_planes, bi->deratedbw[j]);
+               }
+
+               if (bi->num_planes == 1)
+                       break;
+       }
+
+       return 0;
+}
+
+static unsigned int icl_max_bw(struct drm_i915_private *dev_priv,
+                              int num_planes, int qgv_point)
+{
+       int i;
+
+       /* Did we initialize the bw limits successfully? */
+       if (dev_priv->max_bw[0].num_planes == 0)
+               return UINT_MAX;
+
+       for (i = 0; i < ARRAY_SIZE(dev_priv->max_bw); i++) {
+               const struct intel_bw_info *bi =
+                       &dev_priv->max_bw[i];
+
+               if (num_planes >= bi->num_planes)
+                       return bi->deratedbw[qgv_point];
+       }
+
+       return 0;
+}
+
+void intel_bw_init_hw(struct drm_i915_private *dev_priv)
+{
+       if (IS_GEN(dev_priv, 11))
+               icl_get_bw_info(dev_priv);
+}
+
+static unsigned int intel_max_data_rate(struct drm_i915_private *dev_priv,
+                                       int num_planes)
+{
+       if (IS_GEN(dev_priv, 11))
+               /*
+                * FIXME with SAGV disabled maybe we can assume
+                * point 1 will always be used? Seems to match
+                * the behaviour observed in the wild.
+                */
+               return min3(icl_max_bw(dev_priv, num_planes, 0),
+                           icl_max_bw(dev_priv, num_planes, 1),
+                           icl_max_bw(dev_priv, num_planes, 2));
+       else
+               return UINT_MAX;
+}
+
+static unsigned int intel_bw_crtc_num_active_planes(const struct intel_crtc_state *crtc_state)
+{
+       /*
+        * We assume cursors are small enough
+        * to not not cause bandwidth problems.
+        */
+       return hweight8(crtc_state->active_planes & ~BIT(PLANE_CURSOR));
+}
+
+static unsigned int intel_bw_crtc_data_rate(const struct intel_crtc_state *crtc_state)
+{
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       unsigned int data_rate = 0;
+       enum plane_id plane_id;
+
+       for_each_plane_id_on_crtc(crtc, plane_id) {
+               /*
+                * We assume cursors are small enough
+                * to not not cause bandwidth problems.
+                */
+               if (plane_id == PLANE_CURSOR)
+                       continue;
+
+               data_rate += crtc_state->data_rate[plane_id];
+       }
+
+       return data_rate;
+}
+
+void intel_bw_crtc_update(struct intel_bw_state *bw_state,
+                         const struct intel_crtc_state *crtc_state)
+{
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+
+       bw_state->data_rate[crtc->pipe] =
+               intel_bw_crtc_data_rate(crtc_state);
+       bw_state->num_active_planes[crtc->pipe] =
+               intel_bw_crtc_num_active_planes(crtc_state);
+
+       DRM_DEBUG_KMS("pipe %c data rate %u num active planes %u\n",
+                     pipe_name(crtc->pipe),
+                     bw_state->data_rate[crtc->pipe],
+                     bw_state->num_active_planes[crtc->pipe]);
+}
+
+static unsigned int intel_bw_num_active_planes(struct drm_i915_private *dev_priv,
+                                              const struct intel_bw_state *bw_state)
+{
+       unsigned int num_active_planes = 0;
+       enum pipe pipe;
+
+       for_each_pipe(dev_priv, pipe)
+               num_active_planes += bw_state->num_active_planes[pipe];
+
+       return num_active_planes;
+}
+
+static unsigned int intel_bw_data_rate(struct drm_i915_private *dev_priv,
+                                      const struct intel_bw_state *bw_state)
+{
+       unsigned int data_rate = 0;
+       enum pipe pipe;
+
+       for_each_pipe(dev_priv, pipe)
+               data_rate += bw_state->data_rate[pipe];
+
+       return data_rate;
+}
+
+int intel_bw_atomic_check(struct intel_atomic_state *state)
+{
+       struct drm_i915_private *dev_priv = to_i915(state->base.dev);
+       struct intel_crtc_state *new_crtc_state, *old_crtc_state;
+       struct intel_bw_state *bw_state = NULL;
+       unsigned int data_rate, max_data_rate;
+       unsigned int num_active_planes;
+       struct intel_crtc *crtc;
+       int i;
+
+       /* FIXME earlier gens need some checks too */
+       if (INTEL_GEN(dev_priv) < 11)
+               return 0;
+
+       for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
+                                           new_crtc_state, i) {
+               unsigned int old_data_rate =
+                       intel_bw_crtc_data_rate(old_crtc_state);
+               unsigned int new_data_rate =
+                       intel_bw_crtc_data_rate(new_crtc_state);
+               unsigned int old_active_planes =
+                       intel_bw_crtc_num_active_planes(old_crtc_state);
+               unsigned int new_active_planes =
+                       intel_bw_crtc_num_active_planes(new_crtc_state);
+
+               /*
+                * Avoid locking the bw state when
+                * nothing significant has changed.
+                */
+               if (old_data_rate == new_data_rate &&
+                   old_active_planes == new_active_planes)
+                       continue;
+
+               bw_state  = intel_atomic_get_bw_state(state);
+               if (IS_ERR(bw_state))
+                       return PTR_ERR(bw_state);
+
+               bw_state->data_rate[crtc->pipe] = new_data_rate;
+               bw_state->num_active_planes[crtc->pipe] = new_active_planes;
+
+               DRM_DEBUG_KMS("pipe %c data rate %u num active planes %u\n",
+                             pipe_name(crtc->pipe),
+                             bw_state->data_rate[crtc->pipe],
+                             bw_state->num_active_planes[crtc->pipe]);
+       }
+
+       if (!bw_state)
+               return 0;
+
+       data_rate = intel_bw_data_rate(dev_priv, bw_state);
+       num_active_planes = intel_bw_num_active_planes(dev_priv, bw_state);
+
+       max_data_rate = intel_max_data_rate(dev_priv, num_active_planes);
+
+       data_rate = DIV_ROUND_UP(data_rate, 1000);
+
+       if (data_rate > max_data_rate) {
+               DRM_DEBUG_KMS("Bandwidth %u MB/s exceeds max available %d MB/s (%d active planes)\n",
+                             data_rate, max_data_rate, num_active_planes);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static struct drm_private_state *intel_bw_duplicate_state(struct drm_private_obj *obj)
+{
+       struct intel_bw_state *state;
+
+       state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return NULL;
+
+       __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
+
+       return &state->base;
+}
+
+static void intel_bw_destroy_state(struct drm_private_obj *obj,
+                                  struct drm_private_state *state)
+{
+       kfree(state);
+}
+
+static const struct drm_private_state_funcs intel_bw_funcs = {
+       .atomic_duplicate_state = intel_bw_duplicate_state,
+       .atomic_destroy_state = intel_bw_destroy_state,
+};
+
+int intel_bw_init(struct drm_i915_private *dev_priv)
+{
+       struct intel_bw_state *state;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       drm_atomic_private_obj_init(&dev_priv->drm, &dev_priv->bw_obj,
+                                   &state->base, &intel_bw_funcs);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/i915/intel_bw.h b/drivers/gpu/drm/i915/intel_bw.h
new file mode 100644 (file)
index 0000000..e9d9c6d
--- /dev/null
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2019 Intel Corporation
+ */
+
+#ifndef __INTEL_BW_H__
+#define __INTEL_BW_H__
+
+#include <drm/drm_atomic.h>
+
+#include "i915_drv.h"
+#include "intel_display.h"
+
+struct drm_i915_private;
+struct intel_atomic_state;
+struct intel_crtc_state;
+
+struct intel_bw_state {
+       struct drm_private_state base;
+
+       unsigned int data_rate[I915_MAX_PIPES];
+       u8 num_active_planes[I915_MAX_PIPES];
+};
+
+#define to_intel_bw_state(x) container_of((x), struct intel_bw_state, base)
+
+static inline struct intel_bw_state *
+intel_atomic_get_bw_state(struct intel_atomic_state *state)
+{
+       struct drm_i915_private *dev_priv = to_i915(state->base.dev);
+       struct drm_private_state *bw_state;
+
+       bw_state = drm_atomic_get_private_obj_state(&state->base,
+                                                   &dev_priv->bw_obj);
+       if (IS_ERR(bw_state))
+               return ERR_CAST(bw_state);
+
+       return to_intel_bw_state(bw_state);
+}
+
+void intel_bw_init_hw(struct drm_i915_private *dev_priv);
+int intel_bw_init(struct drm_i915_private *dev_priv);
+int intel_bw_atomic_check(struct intel_atomic_state *state);
+void intel_bw_crtc_update(struct intel_bw_state *bw_state,
+                         const struct intel_crtc_state *crtc_state);
+
+#endif /* __INTEL_BW_H__ */
index 19a9333b727a1a9fdfc07dafc8d70d04227c1174..98213cc587363f7a81ed94ccb19e5c4e92aba7fb 100644 (file)
@@ -275,6 +275,12 @@ static void icl_combo_phys_init(struct drm_i915_private *dev_priv)
 
                cnl_set_procmon_ref_values(dev_priv, port);
 
+               if (port == PORT_A) {
+                       val = I915_READ(ICL_PORT_COMP_DW8(port));
+                       val |= IREFGEN;
+                       I915_WRITE(ICL_PORT_COMP_DW8(port), val);
+               }
+
                val = I915_READ(ICL_PORT_COMP_DW0(port));
                val |= COMP_INIT;
                I915_WRITE(ICL_PORT_COMP_DW0(port), val);
index 012ad08f38c338a3239f92c0127bdf4e84f46fd1..909171d3ec254aff4e1bedbf5e12cc21cb9f8a75 100644 (file)
@@ -50,6 +50,7 @@
 #include "intel_acpi.h"
 #include "intel_atomic.h"
 #include "intel_atomic_plane.h"
+#include "intel_bw.h"
 #include "intel_color.h"
 #include "intel_cdclk.h"
 #include "intel_crt.h"
@@ -3155,6 +3156,7 @@ static void intel_plane_disable_noatomic(struct intel_crtc *crtc,
 
        intel_set_plane_visible(crtc_state, plane_state, false);
        fixup_active_planes(crtc_state);
+       crtc_state->data_rate[plane->id] = 0;
 
        if (plane->id == PLANE_PRIMARY)
                intel_pre_disable_primary_noatomic(&crtc->base);
@@ -6879,6 +6881,8 @@ static void intel_crtc_disable_noatomic(struct drm_crtc *crtc,
        struct intel_encoder *encoder;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct drm_i915_private *dev_priv = to_i915(crtc->dev);
+       struct intel_bw_state *bw_state =
+               to_intel_bw_state(dev_priv->bw_obj.state);
        enum intel_display_power_domain domain;
        struct intel_plane *plane;
        u64 domains;
@@ -6941,6 +6945,9 @@ static void intel_crtc_disable_noatomic(struct drm_crtc *crtc,
        dev_priv->active_crtcs &= ~(1 << intel_crtc->pipe);
        dev_priv->min_cdclk[intel_crtc->pipe] = 0;
        dev_priv->min_voltage_level[intel_crtc->pipe] = 0;
+
+       bw_state->data_rate[intel_crtc->pipe] = 0;
+       bw_state->num_active_planes[intel_crtc->pipe] = 0;
 }
 
 /*
@@ -11280,6 +11287,7 @@ int intel_plane_atomic_calc_changes(const struct intel_crtc_state *old_crtc_stat
        if (!is_crtc_enabled) {
                plane_state->visible = visible = false;
                to_intel_crtc_state(crtc_state)->active_planes &= ~BIT(plane->id);
+               to_intel_crtc_state(crtc_state)->data_rate[plane->id] = 0;
        }
 
        if (!was_visible && !visible)
@@ -13406,7 +13414,15 @@ static int intel_atomic_check(struct drm_device *dev,
                return ret;
 
        intel_fbc_choose_crtc(dev_priv, intel_state);
-       return calc_watermark_data(intel_state);
+       ret = calc_watermark_data(intel_state);
+       if (ret)
+               return ret;
+
+       ret = intel_bw_atomic_check(intel_state);
+       if (ret)
+               return ret;
+
+       return 0;
 }
 
 static int intel_atomic_prepare_commit(struct drm_device *dev,
@@ -15788,6 +15804,10 @@ int intel_modeset_init(struct drm_device *dev)
 
        drm_mode_config_init(dev);
 
+       ret = intel_bw_init(dev_priv);
+       if (ret)
+               return ret;
+
        dev->mode_config.min_width = 0;
        dev->mode_config.min_height = 0;
 
@@ -16416,8 +16436,11 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
        drm_connector_list_iter_end(&conn_iter);
 
        for_each_intel_crtc(dev, crtc) {
+               struct intel_bw_state *bw_state =
+                       to_intel_bw_state(dev_priv->bw_obj.state);
                struct intel_crtc_state *crtc_state =
                        to_intel_crtc_state(crtc->base.state);
+               struct intel_plane *plane;
                int min_cdclk = 0;
 
                memset(&crtc->base.mode, 0, sizeof(crtc->base.mode));
@@ -16456,6 +16479,21 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
                dev_priv->min_voltage_level[crtc->pipe] =
                        crtc_state->min_voltage_level;
 
+               for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) {
+                       const struct intel_plane_state *plane_state =
+                               to_intel_plane_state(plane->base.state);
+
+                       /*
+                        * FIXME don't have the fb yet, so can't
+                        * use intel_plane_data_rate() :(
+                        */
+                       if (plane_state->base.visible)
+                               crtc_state->data_rate[plane->id] =
+                                       4 * crtc_state->pixel_rate;
+               }
+
+               intel_bw_crtc_update(bw_state, crtc_state);
+
                intel_pipe_config_sanity_check(dev_priv, crtc_state);
        }
 }
index 0fbdbe559b92a87d2e6d01acfbac6d7ea3986805..f341042b6c79c184997dd09dfeb21283f067516b 100644 (file)
@@ -885,6 +885,8 @@ struct intel_crtc_state {
 
        struct intel_crtc_wm_state wm;
 
+       u32 data_rate[I915_MAX_PLANES];
+
        /* Gamma mode programmed on the pipe */
        u32 gamma_mode;
 
index decdd79c3805363df503c6beffa620e2e7e3f8a3..8f82cb72d3a6d6ffcd1a2980ed102bbd2c053f4f 100644 (file)
@@ -2822,7 +2822,7 @@ static void intel_read_wm_latency(struct drm_i915_private *dev_priv,
                val = 0; /* data0 to be programmed to 0 for first set */
                ret = sandybridge_pcode_read(dev_priv,
                                             GEN9_PCODE_READ_MEM_LATENCY,
-                                            &val);
+                                            &val, NULL);
 
                if (ret) {
                        DRM_ERROR("SKL Mailbox read error = %d\n", ret);
@@ -2841,7 +2841,7 @@ static void intel_read_wm_latency(struct drm_i915_private *dev_priv,
                val = 1; /* data0 to be programmed to 1 for second set */
                ret = sandybridge_pcode_read(dev_priv,
                                             GEN9_PCODE_READ_MEM_LATENCY,
-                                            &val);
+                                            &val, NULL);
                if (ret) {
                        DRM_ERROR("SKL Mailbox read error = %d\n", ret);
                        return;
@@ -7072,7 +7072,7 @@ static void gen6_init_rps_frequencies(struct drm_i915_private *dev_priv)
 
                if (sandybridge_pcode_read(dev_priv,
                                           HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL,
-                                          &ddcc_status) == 0)
+                                          &ddcc_status, NULL) == 0)
                        rps->efficient_freq =
                                clamp_t(u8,
                                        ((ddcc_status >> 8) & 0xff),
@@ -7419,7 +7419,8 @@ static void gen6_enable_rc6(struct drm_i915_private *dev_priv)
                   GEN6_RC_CTL_HW_ENABLE);
 
        rc6vids = 0;
-       ret = sandybridge_pcode_read(dev_priv, GEN6_PCODE_READ_RC6VIDS, &rc6vids);
+       ret = sandybridge_pcode_read(dev_priv, GEN6_PCODE_READ_RC6VIDS,
+                                    &rc6vids, NULL);
        if (IS_GEN(dev_priv, 6) && ret) {
                DRM_DEBUG_DRIVER("Couldn't check for BIOS workaround\n");
        } else if (IS_GEN(dev_priv, 6) && (GEN6_DECODE_RC6_VID(rc6vids & 0xff) < 450)) {
@@ -8566,7 +8567,8 @@ void intel_init_gt_powersave(struct drm_i915_private *dev_priv)
            IS_IVYBRIDGE(dev_priv) || IS_HASWELL(dev_priv)) {
                u32 params = 0;
 
-               sandybridge_pcode_read(dev_priv, GEN6_READ_OC_PARAMS, &params);
+               sandybridge_pcode_read(dev_priv, GEN6_READ_OC_PARAMS,
+                                      &params, NULL);
                if (params & BIT(31)) { /* OC supported */
                        DRM_DEBUG_DRIVER("Overclocking supported, max: %dMHz, overclock: %dMHz\n",
                                         (rps->max_freq & 0xff) * 50,
index 87b5a14c7ca887f262a0d101746bd60b4315dbbb..a115625e980c4416204fec8e90fb3ee09b1e22bb 100644 (file)
@@ -374,7 +374,7 @@ static inline int gen7_check_mailbox_status(u32 mbox)
 }
 
 static int __sandybridge_pcode_rw(struct drm_i915_private *i915,
-                                 u32 mbox, u32 *val,
+                                 u32 mbox, u32 *val, u32 *val1,
                                  int fast_timeout_us,
                                  int slow_timeout_ms,
                                  bool is_read)
@@ -393,7 +393,7 @@ static int __sandybridge_pcode_rw(struct drm_i915_private *i915,
                return -EAGAIN;
 
        intel_uncore_write_fw(uncore, GEN6_PCODE_DATA, *val);
-       intel_uncore_write_fw(uncore, GEN6_PCODE_DATA1, 0);
+       intel_uncore_write_fw(uncore, GEN6_PCODE_DATA1, val1 ? *val1 : 0);
        intel_uncore_write_fw(uncore,
                              GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | mbox);
 
@@ -407,6 +407,8 @@ static int __sandybridge_pcode_rw(struct drm_i915_private *i915,
 
        if (is_read)
                *val = intel_uncore_read_fw(uncore, GEN6_PCODE_DATA);
+       if (is_read && val1)
+               *val1 = intel_uncore_read_fw(uncore, GEN6_PCODE_DATA1);
 
        if (INTEL_GEN(i915) > 6)
                return gen7_check_mailbox_status(mbox);
@@ -414,12 +416,13 @@ static int __sandybridge_pcode_rw(struct drm_i915_private *i915,
                return gen6_check_mailbox_status(mbox);
 }
 
-int sandybridge_pcode_read(struct drm_i915_private *i915, u32 mbox, u32 *val)
+int sandybridge_pcode_read(struct drm_i915_private *i915, u32 mbox,
+                          u32 *val, u32 *val1)
 {
        int err;
 
        mutex_lock(&i915->sb_lock);
-       err = __sandybridge_pcode_rw(i915, mbox, val,
+       err = __sandybridge_pcode_rw(i915, mbox, val, val1,
                                     500, 0,
                                     true);
        mutex_unlock(&i915->sb_lock);
@@ -440,7 +443,7 @@ int sandybridge_pcode_write_timeout(struct drm_i915_private *i915,
        int err;
 
        mutex_lock(&i915->sb_lock);
-       err = __sandybridge_pcode_rw(i915, mbox, &val,
+       err = __sandybridge_pcode_rw(i915, mbox, &val, NULL,
                                     fast_timeout_us, slow_timeout_ms,
                                     false);
        mutex_unlock(&i915->sb_lock);
@@ -457,7 +460,7 @@ static bool skl_pcode_try_request(struct drm_i915_private *i915, u32 mbox,
                                  u32 request, u32 reply_mask, u32 reply,
                                  u32 *status)
 {
-       *status = __sandybridge_pcode_rw(i915, mbox, &request,
+       *status = __sandybridge_pcode_rw(i915, mbox, &request, NULL,
                                         500, 0,
                                         true);
 
index a0907e2c499251443394f5a72172c22abd6b7b56..7fb95745a444948c681cae095777660832d9a47a 100644 (file)
@@ -127,7 +127,8 @@ u32 intel_sbi_read(struct drm_i915_private *i915, u16 reg,
 void intel_sbi_write(struct drm_i915_private *i915, u16 reg, u32 value,
                     enum intel_sbi_destination destination);
 
-int sandybridge_pcode_read(struct drm_i915_private *i915, u32 mbox, u32 *val);
+int sandybridge_pcode_read(struct drm_i915_private *i915, u32 mbox,
+                          u32 *val, u32 *val1);
 int sandybridge_pcode_write_timeout(struct drm_i915_private *i915, u32 mbox,
                                    u32 val, int fast_timeout_us,
                                    int slow_timeout_ms);
index 91196d9612bbf2f4a8d089b94c03d384b1d75341..c2dda5a375f09ba6a5371970d15e76daf4feae40 100644 (file)
@@ -73,3 +73,66 @@ void __intel_wakeref_init(struct intel_wakeref *wf, struct lock_class_key *key)
        atomic_set(&wf->count, 0);
        wf->wakeref = 0;
 }
+
+static void wakeref_auto_timeout(struct timer_list *t)
+{
+       struct intel_wakeref_auto *wf = from_timer(wf, t, timer);
+       intel_wakeref_t wakeref;
+       unsigned long flags;
+
+       if (!refcount_dec_and_lock_irqsave(&wf->count, &wf->lock, &flags))
+               return;
+
+       wakeref = fetch_and_zero(&wf->wakeref);
+       spin_unlock_irqrestore(&wf->lock, flags);
+
+       intel_runtime_pm_put(wf->i915, wakeref);
+}
+
+void intel_wakeref_auto_init(struct intel_wakeref_auto *wf,
+                            struct drm_i915_private *i915)
+{
+       spin_lock_init(&wf->lock);
+       timer_setup(&wf->timer, wakeref_auto_timeout, 0);
+       refcount_set(&wf->count, 0);
+       wf->wakeref = 0;
+       wf->i915 = i915;
+}
+
+void intel_wakeref_auto(struct intel_wakeref_auto *wf, unsigned long timeout)
+{
+       unsigned long flags;
+
+       if (!timeout) {
+               if (del_timer_sync(&wf->timer))
+                       wakeref_auto_timeout(&wf->timer);
+               return;
+       }
+
+       /* Our mission is that we only extend an already active wakeref */
+       assert_rpm_wakelock_held(wf->i915);
+
+       if (!refcount_inc_not_zero(&wf->count)) {
+               spin_lock_irqsave(&wf->lock, flags);
+               if (!refcount_read(&wf->count)) {
+                       GEM_BUG_ON(wf->wakeref);
+                       wf->wakeref = intel_runtime_pm_get_if_in_use(wf->i915);
+               }
+               refcount_inc(&wf->count);
+               spin_unlock_irqrestore(&wf->lock, flags);
+       }
+
+       /*
+        * If we extend a pending timer, we will only get a single timer
+        * callback and so need to cancel the local inc by running the
+        * elided callback to keep the wf->count balanced.
+        */
+       if (mod_timer(&wf->timer, jiffies + timeout))
+               wakeref_auto_timeout(&wf->timer);
+}
+
+void intel_wakeref_auto_fini(struct intel_wakeref_auto *wf)
+{
+       intel_wakeref_auto(wf, 0);
+       GEM_BUG_ON(wf->wakeref);
+}
index db742291211cfc7625db020eeaa7209f2e30092b..8a5f85c000ce3aff70ec08ae63c1689003f7e0d6 100644 (file)
@@ -9,7 +9,9 @@
 
 #include <linux/atomic.h>
 #include <linux/mutex.h>
+#include <linux/refcount.h>
 #include <linux/stackdepot.h>
+#include <linux/timer.h>
 
 struct drm_i915_private;
 
@@ -130,4 +132,33 @@ intel_wakeref_active(struct intel_wakeref *wf)
        return READ_ONCE(wf->wakeref);
 }
 
+struct intel_wakeref_auto {
+       struct drm_i915_private *i915;
+       struct timer_list timer;
+       intel_wakeref_t wakeref;
+       spinlock_t lock;
+       refcount_t count;
+};
+
+/**
+ * intel_wakeref_auto: Delay the runtime-pm autosuspend
+ * @wf: the wakeref
+ * @timeout: relative timeout in jiffies
+ *
+ * The runtime-pm core uses a suspend delay after the last wakeref
+ * is released before triggering runtime suspend of the device. That
+ * delay is configurable via sysfs with little regard to the device
+ * characteristics. Instead, we want to tune the autosuspend based on our
+ * HW knowledge. intel_wakeref_auto() delays the sleep by the supplied
+ * timeout.
+ *
+ * Pass @timeout = 0 to cancel a previous autosuspend by executing the
+ * suspend immediately.
+ */
+void intel_wakeref_auto(struct intel_wakeref_auto *wf, unsigned long timeout);
+
+void intel_wakeref_auto_init(struct intel_wakeref_auto *wf,
+                            struct drm_i915_private *i915);
+void intel_wakeref_auto_fini(struct intel_wakeref_auto *wf);
+
 #endif /* INTEL_WAKEREF_H */
index 895ea1a72a69b4b0aa8ed4aa61beba711f0a6541..bfe2891eac372313c787880eed7dbe2f3bd45545 100644 (file)
@@ -1793,7 +1793,7 @@ void vlv_dsi_init(struct drm_i915_private *dev_priv)
 
        if (!fixed_mode) {
                DRM_DEBUG_KMS("no fixed mode\n");
-               goto err;
+               goto err_cleanup_connector;
        }
 
        intel_panel_init(&intel_connector->panel, fixed_mode, NULL);
@@ -1803,6 +1803,8 @@ void vlv_dsi_init(struct drm_i915_private *dev_priv)
 
        return;
 
+err_cleanup_connector:
+       drm_connector_cleanup(&intel_connector->base);
 err:
        drm_encoder_cleanup(&intel_encoder->base);
        kfree(intel_dsi);