[RFC v2 1/4] drm/i915/display: add support for DMC wakelocks

Gustavo Sousa gustavo.sousa at intel.com
Thu Jan 11 14:15:03 UTC 2024


Hi, Luca.

Quoting Luca Coelho (2024-01-11 06:16:19-03:00)
>In order to reduce the DC5->DC2 restore time, wakelocks have been
>introduced in DMC so the driver can tell it when registers and other
>memory areas are going to be accessed and keep their respective blocks
>awake.
>
>Implement this in the driver by adding the concept of DMC wakelocks.
>When the driver needs to access memory which lies inside pre-defined
>ranges, it will tell DMC to set the wakelock, access the memory, then
>wait for a while and clear the wakelock.
>
>The wakelock state is protected in the driver with spinlocks to
>prevent concurrency issues.
>
>RFC: this is still very rough on the corners and, for now, we are
>only handling RMW cases.  The locking mechanisms still need to be
>fine-tuned and verified.  Additionally, there are loads of logs that
>will be removed and things like hardcoded delays when reading ACKs
>will be changed to proper waiting functions etc.  We are also using
>dummy ranges for now, but these need to be specified properly,
>according to the bspecs.

Some comments/questions:

1. DMC_WAKELOCK*_CTL registers

   I see that, with the current design, we use only DMC_WAKELOCK1_CTL.
   Do we plan on using the other available DMC_WAKELOCK*_CTL registers?

   I would agree that the refcounting approach seems to be a good one
   and maybe adding the complexity necessary in the code to use the
   other ones might not be worth it. I decided to ask anyway since you
   kept the definitions here.

2. Enabling and disabling

   I think we shouldn't enable DMC Wakelock as part of the modeset
   commit.

   I would assume that enabling DMC Wakelock should be part of the DC
   states enabling sequence, since the former should be done prior to
   the latter.

   Also, aren't we missing the disabling counterpart for
   intel_display_wl_enable()?


As an RFC, I guess we are more concerned on the overall design of the
thing at this point. Nevertheless, trying to help, I decided to provide
my 2 cents on the current implementation below. I hope it is helpful.

>
>Signed-off-by: Luca Coelho <luciano.coelho at intel.com>
>---
> drivers/gpu/drm/i915/Makefile                 |   1 +
> drivers/gpu/drm/i915/display/intel_de.h       |  16 +-
> drivers/gpu/drm/i915/display/intel_display.c  |   4 +
> .../gpu/drm/i915/display/intel_display_core.h |   4 +
> .../drm/i915/display/intel_display_driver.c   |   1 +
> drivers/gpu/drm/i915/display/intel_dmc_regs.h |  18 ++
> drivers/gpu/drm/i915/display/intel_wakelock.c | 191 ++++++++++++++++++
> drivers/gpu/drm/i915/display/intel_wakelock.h |  34 ++++
> drivers/gpu/drm/xe/Makefile                   |   1 +
> 9 files changed, 269 insertions(+), 1 deletion(-)
> create mode 100644 drivers/gpu/drm/i915/display/intel_wakelock.c
> create mode 100644 drivers/gpu/drm/i915/display/intel_wakelock.h
>
>diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
>index f7d13a7dcd63..1738305b8c15 100644
>--- a/drivers/gpu/drm/i915/Makefile
>+++ b/drivers/gpu/drm/i915/Makefile
>@@ -312,6 +312,7 @@ i915-y += \
>         display/intel_tc.o \
>         display/intel_vblank.o \
>         display/intel_vga.o \
>+        display/intel_wakelock.o \
>         display/intel_wm.o \
>         display/skl_scaler.o \
>         display/skl_universal_plane.o \
>diff --git a/drivers/gpu/drm/i915/display/intel_de.h b/drivers/gpu/drm/i915/display/intel_de.h
>index 42552d8c151e..0597ddf49f70 100644
>--- a/drivers/gpu/drm/i915/display/intel_de.h
>+++ b/drivers/gpu/drm/i915/display/intel_de.h
>@@ -42,11 +42,25 @@ intel_de_write(struct drm_i915_private *i915, i915_reg_t reg, u32 val)
> }
> 
> static inline u32
>-intel_de_rmw(struct drm_i915_private *i915, i915_reg_t reg, u32 clear, u32 set)
>+__intel_de_rmw(struct drm_i915_private *i915, i915_reg_t reg, u32 clear, u32 set)
> {
>         return intel_uncore_rmw(&i915->uncore, reg, clear, set);
> }
> 
>+static inline u32
>+intel_de_rmw(struct drm_i915_private *i915, i915_reg_t reg, u32 clear, u32 set)
>+{
>+        u32 val;
>+
>+        intel_display_wl_get(i915, reg);
>+
>+        val = __intel_de_rmw(i915, reg, clear, set);
>+
>+        intel_display_wl_put(i915, reg);
>+
>+        return val;
>+}
>+
> static inline int
> intel_de_wait_for_register(struct drm_i915_private *i915, i915_reg_t reg,
>                            u32 mask, u32 value, unsigned int timeout)
>diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
>index f15d238ce579..318757af3905 100644
>--- a/drivers/gpu/drm/i915/display/intel_display.c
>+++ b/drivers/gpu/drm/i915/display/intel_display.c
>@@ -115,6 +115,7 @@
> #include "intel_vdsc_regs.h"
> #include "intel_vga.h"
> #include "intel_vrr.h"
>+#include "intel_wakelock.h"
> #include "intel_wm.h"
> #include "skl_scaler.h"
> #include "skl_universal_plane.h"
>@@ -7359,6 +7360,9 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
>                  * the culprit.
>                  */
>                 intel_uncore_arm_unclaimed_mmio_detection(&dev_priv->uncore);
>+
>+                /* Enable DMC wakelock */
>+                intel_display_wl_enable(dev_priv);
>         }
>         /*
>          * Delay re-enabling DC states by 17 ms to avoid the off->on->off
>diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h
>index 7e82b87e9cde..ea7e5f4dfcea 100644
>--- a/drivers/gpu/drm/i915/display/intel_display_core.h
>+++ b/drivers/gpu/drm/i915/display/intel_display_core.h
>@@ -26,6 +26,7 @@
> #include "intel_global_state.h"
> #include "intel_gmbus.h"
> #include "intel_opregion.h"
>+#include "intel_wakelock.h"
> #include "intel_wm_types.h"
> 
> struct drm_i915_private;
>@@ -523,7 +524,10 @@ struct intel_display {
>         struct intel_overlay *overlay;
>         struct intel_display_params params;
>         struct intel_vbt_data vbt;
>+        struct intel_display_wl wl;
>         struct intel_wm wm;
>+
>+
> };
> 
> #endif /* __INTEL_DISPLAY_CORE_H__ */
>diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c
>index 62f7b10484be..bafea28e3343 100644
>--- a/drivers/gpu/drm/i915/display/intel_display_driver.c
>+++ b/drivers/gpu/drm/i915/display/intel_display_driver.c
>@@ -196,6 +196,7 @@ void intel_display_driver_early_probe(struct drm_i915_private *i915)
>         intel_dpll_init_clock_hook(i915);
>         intel_init_display_hooks(i915);
>         intel_fdi_init_hook(i915);
>+        intel_display_wl_init(i915);
> }
> 
> /* part #1: call before irq install */
>diff --git a/drivers/gpu/drm/i915/display/intel_dmc_regs.h b/drivers/gpu/drm/i915/display/intel_dmc_regs.h
>index cf10094acae3..e68a2afcf14f 100644
>--- a/drivers/gpu/drm/i915/display/intel_dmc_regs.h
>+++ b/drivers/gpu/drm/i915/display/intel_dmc_regs.h
>@@ -96,4 +96,22 @@
> #define TGL_DMC_DEBUG3                _MMIO(0x101090)
> #define DG1_DMC_DEBUG3                _MMIO(0x13415c)
> 
>+#define DMC_WAKELOCK_CFG        _MMIO(0x8F1B0)
>+#define  DMC_WAKELOCK_CFG_ENABLE REG_BIT(31)
>+#define DMC_WAKELOCK1_CTL        _MMIO(0x8F140)
>+#define DMC_WAKELOCK2_CTL        _MMIO(0x8F144)
>+#define DMC_WAKELOCK3_CTL        _MMIO(0x8F148)
>+#define DMC_WAKELOCK4_CTL        _MMIO(0x8F14C)
>+#define DMC_WAKELOCK5_CTL        _MMIO(0x8F150)
>+#define DMC_WAKELOCK6_CTL        _MMIO(0x8F154)
>+#define DMC_WAKELOCK7_CTL        _MMIO(0x8F158)
>+#define DMC_WAKELOCK8_CTL        _MMIO(0x8F15C)
>+#define DMC_WAKELOCK9_CTL        _MMIO(0x8F160)
>+#define DMC_WAKELOCK10_CTL        _MMIO(0x8F164)
>+#define DMC_WAKELOCK11_CTL        _MMIO(0x8F168)
>+#define DMC_WAKELOCK12_CTL        _MMIO(0x8F16C)
>+#define DMC_WAKELOCK13_CTL        _MMIO(0x8F170)
>+#define  DMC_WAKELOCK_CTL_REQ         REG_BIT(31)
>+#define  DMC_WAKELOCK_CTL_ACK         REG_BIT(15)
>+
> #endif /* __INTEL_DMC_REGS_H__ */
>diff --git a/drivers/gpu/drm/i915/display/intel_wakelock.c b/drivers/gpu/drm/i915/display/intel_wakelock.c
>new file mode 100644
>index 000000000000..63ec2976a8f7
>--- /dev/null
>+++ b/drivers/gpu/drm/i915/display/intel_wakelock.c
>@@ -0,0 +1,191 @@
>+/* SPDX-License-Identifier: MIT */
>+/*
>+ * Copyright (C) 2023 Intel Corporation
>+ */
>+
>+#include <linux/kernel.h>
>+
>+#include "intel_de.h"
>+#include "intel_dmc_regs.h"
>+#include "intel_wakelock.h"
>+
>+static struct intel_display_wl_range lnl_wl_range[] = {
>+        { .start = 0x60000, .end = 0x7ffff },
>+};
>+
>+static void __intel_display_wl_release(struct drm_i915_private *i915)
>+{
>+        struct intel_display_wl *wl = &i915->display.wl;
>+
>+        WARN_ON(refcount_read(&wl->refcount));
>+
>+        drm_info(&i915->drm,
>+                 "DMC_WAKELOCK1_CTL to be released (refcount = %d)\n",
>+                 refcount_read(&wl->refcount));
>+
>+        queue_delayed_work(i915->unordered_wq, &wl->work,
>+                           msecs_to_jiffies(2000));
>+}
>+
>+static void intel_display_wl_work(struct work_struct *work)
>+{
>+        struct intel_display_wl *wl =
>+                container_of(work, struct intel_display_wl, work.work);
>+        struct drm_i915_private *i915 =
>+                container_of(wl, struct drm_i915_private, display.wl);
>+        unsigned long flags;
>+        u32 ctl_value;
>+
>+        spin_lock_irqsave(&wl->lock, flags);
>+
>+        drm_info(&i915->drm, "DMC_WAKELOCK1_CTL work running (refcount = %d)\n",
>+                 refcount_read(&wl->refcount));

Shouldn't we bail out if wl->refcount > 0?

>+
>+        ctl_value = __intel_de_rmw(i915, DMC_WAKELOCK1_CTL,
>+                                   DMC_WAKELOCK_CFG_ENABLE, 0);

s/DMC_WAKELOCK_CFG_ENABLE/DMC_WAKELOCK_CTL_REQ/?

>+
>+        drm_info(&i915->drm, "DMC_WAKELOCK1_CTL 0x%0x before\n", ctl_value);
>+
>+        drm_info(&i915->drm, "DMC_WAKELOCK1_CTL read 0x%0x immediately after\n",
>+                 intel_de_read(i915, DMC_WAKELOCK1_CTL));
>+
>+        udelay(100);
>+
>+        drm_info(&i915->drm, "DMC_WAKELOCK1_CTL read 0x%0x after delay\n",
>+                 intel_de_read(i915, DMC_WAKELOCK1_CTL));
>+
>+        spin_unlock_irqrestore(&wl->lock, flags);
>+}
>+
>+static bool intel_display_wl_check_range(u32 address)
>+{
>+        int i;
>+        bool wl_needed = false;
>+
>+        for (i = 0; i < ARRAY_SIZE(lnl_wl_range); i++) {
>+                if (address >= lnl_wl_range[i].start &&
>+                    address <= lnl_wl_range[i].end) {
>+                        wl_needed = true;
>+                        break;
>+                }
>+        }
>+
>+        return wl_needed;
>+}
>+
>+void intel_display_wl_init(struct drm_i915_private *i915)
>+{
>+        struct intel_display_wl *wl = &i915->display.wl;
>+        unsigned long flags;
>+
>+        spin_lock_init(&wl->lock);
>+
>+        spin_lock_irqsave(&wl->lock, flags);
>+        refcount_set(&wl->refcount, 0);
>+        spin_unlock_irqrestore(&wl->lock, flags);
>+}
>+
>+void intel_display_wl_enable(struct drm_i915_private *i915)
>+{
>+        struct intel_display_wl *wl = &i915->display.wl;
>+        unsigned long flags;
>+
>+        spin_lock_irqsave(&wl->lock, flags);
>+
>+        if (wl->enabled)
>+                goto out_unlock;
>+
>+        drm_info(&i915->drm, "DMC_WAKELOCK_CFG refcount %d\n",
>+                 refcount_read(&wl->refcount));
>+
>+        INIT_DELAYED_WORK(&wl->work, intel_display_wl_work);
>+
>+        /*
>+         * Enable wakelock in DMC.  We shouldn't try to take the
>+         * wakelock, because we're just enabling it, so call the
>+         * non-locking version directly here.
>+         */
>+        __intel_de_rmw(i915, DMC_WAKELOCK_CFG, 0, DMC_WAKELOCK_CFG_ENABLE);
>+
>+        drm_info(&i915->drm, "DMC_WAKELOCK_CFG read 0x%0x\n",
>+                 intel_de_read(i915, DMC_WAKELOCK_CFG));
>+
>+        wl->enabled = true;
>+
>+out_unlock:
>+        spin_unlock_irqrestore(&wl->lock, flags);
>+        return;
>+}
>+
>+void intel_display_wl_get(struct drm_i915_private *i915, i915_reg_t reg)
>+{
>+        struct intel_display_wl *wl = &i915->display.wl;
>+        unsigned long flags;
>+        u32 ctl_value;
>+
>+        if (!intel_display_wl_check_range(reg.reg))
>+                return;
>+
>+        spin_lock_irqsave(&wl->lock, flags);
>+
>+        if (!wl->enabled)
>+                goto out_unlock;
>+
>+        if (refcount_inc_not_zero(&wl->refcount)) {
>+                drm_info(&i915->drm,
>+                         "DMC_WAKELOCK1_CTL refcount is now %d -- wakelock already taken\n",
>+                         refcount_read(&wl->refcount));
>+                goto out_unlock;
>+        }
>+
>+        refcount_set(&wl->refcount, 1);
>+
>+        drm_info(&i915->drm, "DMC_WAKELOCK1_CTL refcount is now %d\n",
>+                 refcount_read(&wl->refcount));
>+
>+        /* TODO: split this to a separate function to align with _release() */
>+        ctl_value = __intel_de_rmw(i915, DMC_WAKELOCK1_CTL,
>+                                   0, DMC_WAKELOCK_CFG_ENABLE);

s/DMC_WAKELOCK_CFG_ENABLE/DMC_WAKELOCK_CTL_REQ/?

>+
>+        drm_info(&i915->drm, "DMC_WAKELOCK1_CTL 0x%0x before\n", ctl_value);
>+
>+        drm_info(&i915->drm, "DMC_WAKELOCK1_CTL read 0x%0x immediately after\n",
>+                 intel_de_read(i915, DMC_WAKELOCK1_CTL));
>+
>+        udelay(100);
>+
>+        drm_info(&i915->drm, "DMC_WAKELOCK1_CTL read 0x%0x after delay\n",
>+                 intel_de_read(i915, DMC_WAKELOCK1_CTL));
>+
>+out_unlock:
>+        spin_unlock_irqrestore(&wl->lock, flags);
>+}
>+
>+void intel_display_wl_put(struct drm_i915_private *i915, i915_reg_t reg)
>+{
>+        struct intel_display_wl *wl = &i915->display.wl;
>+        unsigned long flags;
>+
>+        if (!intel_display_wl_check_range(reg.reg))
>+                return;
>+
>+        spin_lock_irqsave(&wl->lock, flags);
>+
>+        if (!wl->enabled || !refcount_read(&wl->refcount))

Shouldn't we warn if wl->refcount is zero?

>+                goto out_unlock;
>+
>+        if (refcount_dec_and_test(&wl->refcount)) {
>+                drm_info(&i915->drm,
>+                         "DMC_WAKELOCK1_CTL refcount is now ZERO\n");
>+
>+                __intel_display_wl_release(i915);
>+
>+                goto out_unlock;
>+        }
>+
>+        drm_info(&i915->drm, "DMC_WAKELOCK1_CTL refcount is now %d\n",
>+                 refcount_read(&wl->refcount));
>+
>+out_unlock:
>+        spin_unlock_irqrestore(&wl->lock, flags);
>+}
>diff --git a/drivers/gpu/drm/i915/display/intel_wakelock.h b/drivers/gpu/drm/i915/display/intel_wakelock.h
>new file mode 100644
>index 000000000000..a47205e1ea32
>--- /dev/null
>+++ b/drivers/gpu/drm/i915/display/intel_wakelock.h
>@@ -0,0 +1,34 @@
>+/* SPDX-License-Identifier: MIT */
>+/*
>+ * Copyright (C) 2023 Intel Corporation
>+ */
>+
>+#ifndef __INTEL_WAKELOCK_H__
>+#define __INTEL_WAKELOCK_H__
>+
>+#include <linux/types.h>
>+#include <linux/workqueue.h>
>+#include <linux/refcount.h>
>+
>+#include "i915_reg_defs.h"
>+
>+struct drm_i915_private;
>+
>+struct intel_display_wl {
>+        spinlock_t lock;
>+        bool enabled;
>+        refcount_t refcount;
>+        struct delayed_work work;
>+};
>+
>+struct intel_display_wl_range {
>+        u32 start;
>+        u32 end;
>+};
>+
>+void intel_display_wl_init(struct drm_i915_private *i915);
>+void intel_display_wl_enable(struct drm_i915_private *i915);
>+void intel_display_wl_get(struct drm_i915_private *i915, i915_reg_t reg);
>+void intel_display_wl_put(struct drm_i915_private *i915, i915_reg_t reg);
>+
>+#endif /* __INTEL_WAKELOCK_H__ */
>diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
>index 0292b79852a0..387eccab28ae 100644
>--- a/drivers/gpu/drm/xe/Makefile
>+++ b/drivers/gpu/drm/xe/Makefile
>@@ -279,6 +279,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
>         i915-display/intel_vdsc.o \
>         i915-display/intel_vga.o \
>         i915-display/intel_vrr.o \
>+        i915-display/intel_wakelock.o \
>         i915-display/intel_wm.o \
>         i915-display/skl_scaler.o \
>         i915-display/skl_universal_plane.o \
>-- 
>2.39.2
>

--
Gustavo Sousa


More information about the Intel-xe mailing list