From 1e15bc5bd7665558f4296b4cc50a561460b9f236 Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Fri, 8 Nov 2024 09:57:11 -0300 Subject: [PATCH 01/16] drm/i915/dmc_wl: Use sentinel item for range tables We are currently using ARRAY_SIZE() to iterate address ranges in intel_dmc_wl_check_range(). In upcoming changes, we will be using more than a single table and will extract the range checking logic into a dedicated function that takes a range table as argument. As we will not able to use ARRAY_SIZE() then, let's make range tables contain a sentinel item at the end and use that instead of having to pass the size as parameter in this future function. Reviewed-by: Luca Coelho Signed-off-by: Gustavo Sousa Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20241108130218.24125-7-gustavo.sousa@intel.com --- drivers/gpu/drm/i915/display/intel_dmc_wl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c index e837c39491bb..1753c334f3fd 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c @@ -53,6 +53,7 @@ struct intel_dmc_wl_range { static struct intel_dmc_wl_range lnl_wl_range[] = { { .start = 0x60000, .end = 0x7ffff }, + {}, }; static void __intel_dmc_wl_release(struct intel_display *display) @@ -104,7 +105,7 @@ static bool intel_dmc_wl_check_range(i915_reg_t reg) bool wl_needed = false; u32 offset = i915_mmio_reg_offset(reg); - for (i = 0; i < ARRAY_SIZE(lnl_wl_range); i++) { + for (i = 0; lnl_wl_range[i].start; i++) { if (offset >= lnl_wl_range[i].start && offset <= lnl_wl_range[i].end) { wl_needed = true; -- 2.51.0 From 83329df1be0c883801251d9aeaca0df317e14a14 Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Fri, 8 Nov 2024 09:57:12 -0300 Subject: [PATCH 02/16] drm/i915/dmc_wl: Extract intel_dmc_wl_reg_in_range() We will be using more than one range table in intel_dmc_wl_check_range(). As such, move the logic to a new function and name it intel_dmc_wl_reg_in_range(). Reviewed-by: Luca Coelho Signed-off-by: Gustavo Sousa Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20241108130218.24125-8-gustavo.sousa@intel.com --- drivers/gpu/drm/i915/display/intel_dmc_wl.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c index 1753c334f3fd..4b958a4c4358 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c @@ -99,21 +99,22 @@ out_unlock: spin_unlock_irqrestore(&wl->lock, flags); } -static bool intel_dmc_wl_check_range(i915_reg_t reg) +static bool intel_dmc_wl_reg_in_range(i915_reg_t reg, + const struct intel_dmc_wl_range ranges[]) { - int i; - bool wl_needed = false; u32 offset = i915_mmio_reg_offset(reg); - for (i = 0; lnl_wl_range[i].start; i++) { - if (offset >= lnl_wl_range[i].start && - offset <= lnl_wl_range[i].end) { - wl_needed = true; - break; - } + for (int i = 0; ranges[i].start; i++) { + if (ranges[i].start <= offset && offset <= ranges[i].end) + return true; } - return wl_needed; + return false; +} + +static bool intel_dmc_wl_check_range(i915_reg_t reg) +{ + return intel_dmc_wl_reg_in_range(reg, lnl_wl_range); } static bool __intel_dmc_wl_supported(struct intel_display *display) -- 2.51.0 From 089156e33d747829f508b1fda64f292b19917e17 Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Fri, 8 Nov 2024 09:57:13 -0300 Subject: [PATCH 03/16] drm/i915/dmc_wl: Rename lnl_wl_range to powered_off_ranges In an upcoming change, we will add extra range tables for registers that are touched by the DMC during DC states. The range table that we are currently using is meant for registers that are powered off during DC states. As such, let's rename the table to powered_off_ranges and also add a comment regarding its purpose in the function that uses it. Reviewed-by: Luca Coelho Signed-off-by: Gustavo Sousa Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20241108130218.24125-9-gustavo.sousa@intel.com --- drivers/gpu/drm/i915/display/intel_dmc_wl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c index 4b958a4c4358..1877a89affab 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c @@ -51,7 +51,7 @@ struct intel_dmc_wl_range { u32 end; }; -static struct intel_dmc_wl_range lnl_wl_range[] = { +static struct intel_dmc_wl_range powered_off_ranges[] = { { .start = 0x60000, .end = 0x7ffff }, {}, }; @@ -114,7 +114,11 @@ static bool intel_dmc_wl_reg_in_range(i915_reg_t reg, static bool intel_dmc_wl_check_range(i915_reg_t reg) { - return intel_dmc_wl_reg_in_range(reg, lnl_wl_range); + /* + * Check that the offset is in one of the ranges for which + * registers are powered off during DC states. + */ + return intel_dmc_wl_reg_in_range(reg, powered_off_ranges); } static bool __intel_dmc_wl_supported(struct intel_display *display) -- 2.51.0 From 0c48ff896a8a72e2182b48a051c1e5bde38e15e2 Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Fri, 8 Nov 2024 09:57:14 -0300 Subject: [PATCH 04/16] drm/i915/dmc_wl: Track registers touched by the DMC There are extra registers that require the DMC wakelock when specific dynamic DC states are in place. Those are registers that are touched by the DMC and require DC exit for proper access. Add the range tables for them and use the correct one depending on the enabled DC state. v2: - Do not look into power domains guts (i.e. display->power.domains.dc_state). (Jani) - Come up with better names for variables containing register ranges. (Luca) - Keep a copy of dc_state in struct intel_dmc_wl. - Update commit message for a clearer explanation for the need of these new tables. Bspec: 71583 Cc: Jani Nikula Reviewed-by: Luca Coelho Signed-off-by: Gustavo Sousa Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20241108130218.24125-10-gustavo.sousa@intel.com --- .../i915/display/intel_display_power_well.c | 4 +- drivers/gpu/drm/i915/display/intel_dmc_wl.c | 126 ++++++++++++++++-- drivers/gpu/drm/i915/display/intel_dmc_wl.h | 11 +- 3 files changed, 128 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.c b/drivers/gpu/drm/i915/display/intel_display_power_well.c index 0c77b6252969..578959ff2d75 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -833,7 +833,7 @@ void gen9_enable_dc5(struct intel_display *display) intel_de_rmw(display, GEN8_CHICKEN_DCPR_1, 0, SKL_SELECT_ALTERNATE_DC_EXIT); - intel_dmc_wl_enable(display); + intel_dmc_wl_enable(display, DC_STATE_EN_UPTO_DC5); gen9_set_dc_state(display, DC_STATE_EN_UPTO_DC5); } @@ -866,7 +866,7 @@ void skl_enable_dc6(struct intel_display *display) intel_de_rmw(display, GEN8_CHICKEN_DCPR_1, 0, SKL_SELECT_ALTERNATE_DC_EXIT); - intel_dmc_wl_enable(display); + intel_dmc_wl_enable(display, DC_STATE_EN_UPTO_DC6); gen9_set_dc_state(display, DC_STATE_EN_UPTO_DC6); } diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c index 1877a89affab..db01b65cb05d 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c @@ -5,6 +5,7 @@ #include +#include "i915_reg.h" #include "intel_de.h" #include "intel_dmc.h" #include "intel_dmc_regs.h" @@ -56,6 +57,87 @@ static struct intel_dmc_wl_range powered_off_ranges[] = { {}, }; +static struct intel_dmc_wl_range xe3lpd_dc5_dc6_dmc_ranges[] = { + { .start = 0x45500, .end = 0x45500 }, /* DC_STATE_SEL */ + { .start = 0x457a0, .end = 0x457b0 }, /* DC*_RESIDENCY_COUNTER */ + { .start = 0x45504, .end = 0x45504 }, /* DC_STATE_EN */ + { .start = 0x45400, .end = 0x4540c }, /* PWR_WELL_CTL_* */ + { .start = 0x454f0, .end = 0x454f0 }, /* RETENTION_CTRL */ + + /* DBUF_CTL_* */ + { .start = 0x44300, .end = 0x44300 }, + { .start = 0x44304, .end = 0x44304 }, + { .start = 0x44f00, .end = 0x44f00 }, + { .start = 0x44f04, .end = 0x44f04 }, + { .start = 0x44fe8, .end = 0x44fe8 }, + { .start = 0x45008, .end = 0x45008 }, + + { .start = 0x46070, .end = 0x46070 }, /* CDCLK_PLL_ENABLE */ + { .start = 0x46000, .end = 0x46000 }, /* CDCLK_CTL */ + { .start = 0x46008, .end = 0x46008 }, /* CDCLK_SQUASH_CTL */ + + /* TRANS_CMTG_CTL_* */ + { .start = 0x6fa88, .end = 0x6fa88 }, + { .start = 0x6fb88, .end = 0x6fb88 }, + + { .start = 0x46430, .end = 0x46430 }, /* CHICKEN_DCPR_1 */ + { .start = 0x46434, .end = 0x46434 }, /* CHICKEN_DCPR_2 */ + { .start = 0x454a0, .end = 0x454a0 }, /* CHICKEN_DCPR_4 */ + { .start = 0x42084, .end = 0x42084 }, /* CHICKEN_MISC_2 */ + { .start = 0x42088, .end = 0x42088 }, /* CHICKEN_MISC_3 */ + { .start = 0x46160, .end = 0x46160 }, /* CMTG_CLK_SEL */ + { .start = 0x8f000, .end = 0x8ffff }, /* Main DMC registers */ + + {}, +}; + +static struct intel_dmc_wl_range xe3lpd_dc3co_dmc_ranges[] = { + { .start = 0x454a0, .end = 0x454a0 }, /* CHICKEN_DCPR_4 */ + + { .start = 0x45504, .end = 0x45504 }, /* DC_STATE_EN */ + + /* DBUF_CTL_* */ + { .start = 0x44300, .end = 0x44300 }, + { .start = 0x44304, .end = 0x44304 }, + { .start = 0x44f00, .end = 0x44f00 }, + { .start = 0x44f04, .end = 0x44f04 }, + { .start = 0x44fe8, .end = 0x44fe8 }, + { .start = 0x45008, .end = 0x45008 }, + + { .start = 0x46070, .end = 0x46070 }, /* CDCLK_PLL_ENABLE */ + { .start = 0x46000, .end = 0x46000 }, /* CDCLK_CTL */ + { .start = 0x46008, .end = 0x46008 }, /* CDCLK_SQUASH_CTL */ + { .start = 0x8f000, .end = 0x8ffff }, /* Main DMC registers */ + + /* Scanline registers */ + { .start = 0x70000, .end = 0x70000 }, + { .start = 0x70004, .end = 0x70004 }, + { .start = 0x70014, .end = 0x70014 }, + { .start = 0x70018, .end = 0x70018 }, + { .start = 0x71000, .end = 0x71000 }, + { .start = 0x71004, .end = 0x71004 }, + { .start = 0x71014, .end = 0x71014 }, + { .start = 0x71018, .end = 0x71018 }, + { .start = 0x72000, .end = 0x72000 }, + { .start = 0x72004, .end = 0x72004 }, + { .start = 0x72014, .end = 0x72014 }, + { .start = 0x72018, .end = 0x72018 }, + { .start = 0x73000, .end = 0x73000 }, + { .start = 0x73004, .end = 0x73004 }, + { .start = 0x73014, .end = 0x73014 }, + { .start = 0x73018, .end = 0x73018 }, + { .start = 0x7b000, .end = 0x7b000 }, + { .start = 0x7b004, .end = 0x7b004 }, + { .start = 0x7b014, .end = 0x7b014 }, + { .start = 0x7b018, .end = 0x7b018 }, + { .start = 0x7c000, .end = 0x7c000 }, + { .start = 0x7c004, .end = 0x7c004 }, + { .start = 0x7c014, .end = 0x7c014 }, + { .start = 0x7c018, .end = 0x7c018 }, + + {}, +}; + static void __intel_dmc_wl_release(struct intel_display *display) { struct drm_i915_private *i915 = to_i915(display->drm); @@ -112,13 +194,37 @@ static bool intel_dmc_wl_reg_in_range(i915_reg_t reg, return false; } -static bool intel_dmc_wl_check_range(i915_reg_t reg) +static bool intel_dmc_wl_check_range(i915_reg_t reg, u32 dc_state) { + const struct intel_dmc_wl_range *ranges; + /* * Check that the offset is in one of the ranges for which * registers are powered off during DC states. */ - return intel_dmc_wl_reg_in_range(reg, powered_off_ranges); + if (intel_dmc_wl_reg_in_range(reg, powered_off_ranges)) + return true; + + /* + * Check that the offset is for a register that is touched by + * the DMC and requires a DC exit for proper access. + */ + switch (dc_state) { + case DC_STATE_EN_DC3CO: + ranges = xe3lpd_dc3co_dmc_ranges; + break; + case DC_STATE_EN_UPTO_DC5: + case DC_STATE_EN_UPTO_DC6: + ranges = xe3lpd_dc5_dc6_dmc_ranges; + break; + default: + ranges = NULL; + } + + if (ranges && intel_dmc_wl_reg_in_range(reg, ranges)) + return true; + + return false; } static bool __intel_dmc_wl_supported(struct intel_display *display) @@ -144,7 +250,7 @@ void intel_dmc_wl_init(struct intel_display *display) refcount_set(&wl->refcount, 0); } -void intel_dmc_wl_enable(struct intel_display *display) +void intel_dmc_wl_enable(struct intel_display *display, u32 dc_state) { struct intel_dmc_wl *wl = &display->wl; unsigned long flags; @@ -154,6 +260,8 @@ void intel_dmc_wl_enable(struct intel_display *display) spin_lock_irqsave(&wl->lock, flags); + wl->dc_state = dc_state; + if (wl->enabled) goto out_unlock; @@ -205,11 +313,11 @@ void intel_dmc_wl_get(struct intel_display *display, i915_reg_t reg) if (!__intel_dmc_wl_supported(display)) return; - if (i915_mmio_reg_valid(reg) && !intel_dmc_wl_check_range(reg)) - return; - spin_lock_irqsave(&wl->lock, flags); + if (i915_mmio_reg_valid(reg) && !intel_dmc_wl_check_range(reg, wl->dc_state)) + goto out_unlock; + if (!wl->enabled) goto out_unlock; @@ -257,11 +365,11 @@ void intel_dmc_wl_put(struct intel_display *display, i915_reg_t reg) if (!__intel_dmc_wl_supported(display)) return; - if (i915_mmio_reg_valid(reg) && !intel_dmc_wl_check_range(reg)) - return; - spin_lock_irqsave(&wl->lock, flags); + if (i915_mmio_reg_valid(reg) && !intel_dmc_wl_check_range(reg, wl->dc_state)) + goto out_unlock; + if (!wl->enabled) goto out_unlock; diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.h b/drivers/gpu/drm/i915/display/intel_dmc_wl.h index 9aa72a4bf153..147eeb4d8432 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.h +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.h @@ -15,15 +15,22 @@ struct intel_display; struct intel_dmc_wl { - spinlock_t lock; /* protects enabled, taken and refcount */ + spinlock_t lock; /* protects enabled, taken, dc_state and refcount */ bool enabled; bool taken; refcount_t refcount; + /* + * We are keeping a copy of the enabled DC state because + * intel_display.power.domains is protected by a mutex and we do + * not want call mutex_lock() in atomic context, where some of + * the tracked MMIO operations happen. + */ + u32 dc_state; struct delayed_work work; }; void intel_dmc_wl_init(struct intel_display *display); -void intel_dmc_wl_enable(struct intel_display *display); +void intel_dmc_wl_enable(struct intel_display *display, u32 dc_state); void intel_dmc_wl_disable(struct intel_display *display); void intel_dmc_wl_get(struct intel_display *display, i915_reg_t reg); void intel_dmc_wl_put(struct intel_display *display, i915_reg_t reg); -- 2.51.0 From 02e2224796a3b609d47bc3a1b78cc833289d7d1f Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Fri, 8 Nov 2024 09:57:15 -0300 Subject: [PATCH 05/16] drm/i915/dmc_wl: Allow simpler syntax for single reg in range tables Allow simpler syntax for defining entries for single registers in range tables. That makes them easier to type as well as to read, allowing one to quickly tell whether a range actually refers to a single register or a "true range". Reviewed-by: Luca Coelho Signed-off-by: Gustavo Sousa Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20241108130218.24125-11-gustavo.sousa@intel.com --- drivers/gpu/drm/i915/display/intel_dmc_wl.c | 118 ++++++++++---------- 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c index db01b65cb05d..4a182a049374 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c @@ -58,82 +58,82 @@ static struct intel_dmc_wl_range powered_off_ranges[] = { }; static struct intel_dmc_wl_range xe3lpd_dc5_dc6_dmc_ranges[] = { - { .start = 0x45500, .end = 0x45500 }, /* DC_STATE_SEL */ + { .start = 0x45500 }, /* DC_STATE_SEL */ { .start = 0x457a0, .end = 0x457b0 }, /* DC*_RESIDENCY_COUNTER */ - { .start = 0x45504, .end = 0x45504 }, /* DC_STATE_EN */ + { .start = 0x45504 }, /* DC_STATE_EN */ { .start = 0x45400, .end = 0x4540c }, /* PWR_WELL_CTL_* */ - { .start = 0x454f0, .end = 0x454f0 }, /* RETENTION_CTRL */ + { .start = 0x454f0 }, /* RETENTION_CTRL */ /* DBUF_CTL_* */ - { .start = 0x44300, .end = 0x44300 }, - { .start = 0x44304, .end = 0x44304 }, - { .start = 0x44f00, .end = 0x44f00 }, - { .start = 0x44f04, .end = 0x44f04 }, - { .start = 0x44fe8, .end = 0x44fe8 }, - { .start = 0x45008, .end = 0x45008 }, + { .start = 0x44300 }, + { .start = 0x44304 }, + { .start = 0x44f00 }, + { .start = 0x44f04 }, + { .start = 0x44fe8 }, + { .start = 0x45008 }, - { .start = 0x46070, .end = 0x46070 }, /* CDCLK_PLL_ENABLE */ - { .start = 0x46000, .end = 0x46000 }, /* CDCLK_CTL */ - { .start = 0x46008, .end = 0x46008 }, /* CDCLK_SQUASH_CTL */ + { .start = 0x46070 }, /* CDCLK_PLL_ENABLE */ + { .start = 0x46000 }, /* CDCLK_CTL */ + { .start = 0x46008 }, /* CDCLK_SQUASH_CTL */ /* TRANS_CMTG_CTL_* */ - { .start = 0x6fa88, .end = 0x6fa88 }, - { .start = 0x6fb88, .end = 0x6fb88 }, - - { .start = 0x46430, .end = 0x46430 }, /* CHICKEN_DCPR_1 */ - { .start = 0x46434, .end = 0x46434 }, /* CHICKEN_DCPR_2 */ - { .start = 0x454a0, .end = 0x454a0 }, /* CHICKEN_DCPR_4 */ - { .start = 0x42084, .end = 0x42084 }, /* CHICKEN_MISC_2 */ - { .start = 0x42088, .end = 0x42088 }, /* CHICKEN_MISC_3 */ - { .start = 0x46160, .end = 0x46160 }, /* CMTG_CLK_SEL */ + { .start = 0x6fa88 }, + { .start = 0x6fb88 }, + + { .start = 0x46430 }, /* CHICKEN_DCPR_1 */ + { .start = 0x46434 }, /* CHICKEN_DCPR_2 */ + { .start = 0x454a0 }, /* CHICKEN_DCPR_4 */ + { .start = 0x42084 }, /* CHICKEN_MISC_2 */ + { .start = 0x42088 }, /* CHICKEN_MISC_3 */ + { .start = 0x46160 }, /* CMTG_CLK_SEL */ { .start = 0x8f000, .end = 0x8ffff }, /* Main DMC registers */ {}, }; static struct intel_dmc_wl_range xe3lpd_dc3co_dmc_ranges[] = { - { .start = 0x454a0, .end = 0x454a0 }, /* CHICKEN_DCPR_4 */ + { .start = 0x454a0 }, /* CHICKEN_DCPR_4 */ - { .start = 0x45504, .end = 0x45504 }, /* DC_STATE_EN */ + { .start = 0x45504 }, /* DC_STATE_EN */ /* DBUF_CTL_* */ - { .start = 0x44300, .end = 0x44300 }, - { .start = 0x44304, .end = 0x44304 }, - { .start = 0x44f00, .end = 0x44f00 }, - { .start = 0x44f04, .end = 0x44f04 }, - { .start = 0x44fe8, .end = 0x44fe8 }, - { .start = 0x45008, .end = 0x45008 }, - - { .start = 0x46070, .end = 0x46070 }, /* CDCLK_PLL_ENABLE */ - { .start = 0x46000, .end = 0x46000 }, /* CDCLK_CTL */ - { .start = 0x46008, .end = 0x46008 }, /* CDCLK_SQUASH_CTL */ + { .start = 0x44300 }, + { .start = 0x44304 }, + { .start = 0x44f00 }, + { .start = 0x44f04 }, + { .start = 0x44fe8 }, + { .start = 0x45008 }, + + { .start = 0x46070 }, /* CDCLK_PLL_ENABLE */ + { .start = 0x46000 }, /* CDCLK_CTL */ + { .start = 0x46008 }, /* CDCLK_SQUASH_CTL */ { .start = 0x8f000, .end = 0x8ffff }, /* Main DMC registers */ /* Scanline registers */ - { .start = 0x70000, .end = 0x70000 }, - { .start = 0x70004, .end = 0x70004 }, - { .start = 0x70014, .end = 0x70014 }, - { .start = 0x70018, .end = 0x70018 }, - { .start = 0x71000, .end = 0x71000 }, - { .start = 0x71004, .end = 0x71004 }, - { .start = 0x71014, .end = 0x71014 }, - { .start = 0x71018, .end = 0x71018 }, - { .start = 0x72000, .end = 0x72000 }, - { .start = 0x72004, .end = 0x72004 }, - { .start = 0x72014, .end = 0x72014 }, - { .start = 0x72018, .end = 0x72018 }, - { .start = 0x73000, .end = 0x73000 }, - { .start = 0x73004, .end = 0x73004 }, - { .start = 0x73014, .end = 0x73014 }, - { .start = 0x73018, .end = 0x73018 }, - { .start = 0x7b000, .end = 0x7b000 }, - { .start = 0x7b004, .end = 0x7b004 }, - { .start = 0x7b014, .end = 0x7b014 }, - { .start = 0x7b018, .end = 0x7b018 }, - { .start = 0x7c000, .end = 0x7c000 }, - { .start = 0x7c004, .end = 0x7c004 }, - { .start = 0x7c014, .end = 0x7c014 }, - { .start = 0x7c018, .end = 0x7c018 }, + { .start = 0x70000 }, + { .start = 0x70004 }, + { .start = 0x70014 }, + { .start = 0x70018 }, + { .start = 0x71000 }, + { .start = 0x71004 }, + { .start = 0x71014 }, + { .start = 0x71018 }, + { .start = 0x72000 }, + { .start = 0x72004 }, + { .start = 0x72014 }, + { .start = 0x72018 }, + { .start = 0x73000 }, + { .start = 0x73004 }, + { .start = 0x73014 }, + { .start = 0x73018 }, + { .start = 0x7b000 }, + { .start = 0x7b004 }, + { .start = 0x7b014 }, + { .start = 0x7b018 }, + { .start = 0x7c000 }, + { .start = 0x7c004 }, + { .start = 0x7c014 }, + { .start = 0x7c018 }, {}, }; @@ -187,7 +187,9 @@ static bool intel_dmc_wl_reg_in_range(i915_reg_t reg, u32 offset = i915_mmio_reg_offset(reg); for (int i = 0; ranges[i].start; i++) { - if (ranges[i].start <= offset && offset <= ranges[i].end) + u32 end = ranges[i].end ?: ranges[i].start; + + if (ranges[i].start <= offset && offset <= end) return true; } -- 2.51.0 From 9fe9cd95feacce9ae1c30c88a5513f556623d24d Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Fri, 8 Nov 2024 09:57:16 -0300 Subject: [PATCH 06/16] drm/i915/dmc_wl: Deal with existing references when disabling It is possible that there are active wakelock references at the time we are disabling the DMC wakelock mechanism. We need to deal with that in two ways: (A) Implement the missing step from Bspec: The Bspec instructs us to clear any existing wakelock request bit after disabling the mechanism. That gives a clue that it is okay to disable while there are locks held and we do not need to wait for them. However, since the spec is not explicit about it, we need still to get confirmation with the hardware team. Let's thus implement the spec and add a TODO note. (B) Ensure a consistent driver state: The enable/disable logic would be problematic if the following sequence of events would happen: 1. Function A calls intel_dmc_wl_get(); 2. Some function calls intel_dmc_wl_disable(); 3. Some function calls intel_dmc_wl_enable(); 4. Function A is done and calls intel_dmc_wl_put(). At (2), the refcount becomes zero and then (4) causes an invalid decrement to the refcount. That would cause some issues: - At the time between (3) and (4), function A would think that the hardware lock is held but it could not be really held until intel_dmc_wl_get() is called by something else. - The call made to (4) could cause the refcount to become zero and consequently the hardware lock to be released while there could be innocent paths trusting they still have the lock. To fix that, we need to keep the refcount correctly in sync with intel_dmc_wl_{get,put}() calls and retake the hardware lock when enabling the DMC wakelock with a non-zero refcount. One missing piece left to be handled here is the following scenario: 1. Function A calls intel_dmc_wl_get(); 2. Some function calls intel_dmc_wl_disable(); 3. Some function calls intel_dmc_wl_enable(); 4. Concurrently with (3), function A performs the MMIO in between setting DMC_WAKELOCK_CFG_ENABLE and asserting the lock with __intel_dmc_wl_take(). I'm mostly sure this would cause issues future display IPs if DMC trap implementation was completely removed. We need to check with the hardware team whether it would be safe to assert the hardware lock before setting DMC_WAKELOCK_CFG_ENABLE to avoid this scenario. If not, then we would have to deal with that via software synchronization. Reviewed-by: Luca Coelho Signed-off-by: Gustavo Sousa Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20241108130218.24125-12-gustavo.sousa@intel.com --- drivers/gpu/drm/i915/display/intel_dmc_wl.c | 97 ++++++++++++++------- 1 file changed, 67 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c index 4a182a049374..b8887216a684 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c @@ -181,6 +181,37 @@ out_unlock: spin_unlock_irqrestore(&wl->lock, flags); } +static void __intel_dmc_wl_take(struct intel_display *display) +{ + struct intel_dmc_wl *wl = &display->wl; + + /* + * Only try to take the wakelock if it's not marked as taken + * yet. It may be already taken at this point if we have + * already released the last reference, but the work has not + * run yet. + */ + if (wl->taken) + return; + + __intel_de_rmw_nowl(display, DMC_WAKELOCK1_CTL, 0, + DMC_WAKELOCK_CTL_REQ); + + /* + * We need to use the atomic variant of the waiting routine + * because the DMC wakelock is also taken in atomic context. + */ + if (__intel_de_wait_for_register_atomic_nowl(display, DMC_WAKELOCK1_CTL, + DMC_WAKELOCK_CTL_ACK, + DMC_WAKELOCK_CTL_ACK, + DMC_WAKELOCK_CTL_TIMEOUT_US)) { + WARN_RATELIMIT(1, "DMC wakelock ack timed out"); + return; + } + + wl->taken = true; +} + static bool intel_dmc_wl_reg_in_range(i915_reg_t reg, const struct intel_dmc_wl_range ranges[]) { @@ -275,7 +306,23 @@ void intel_dmc_wl_enable(struct intel_display *display, u32 dc_state) __intel_de_rmw_nowl(display, DMC_WAKELOCK_CFG, 0, DMC_WAKELOCK_CFG_ENABLE); wl->enabled = true; - wl->taken = false; + + /* + * This would be racy in the following scenario: + * + * 1. Function A calls intel_dmc_wl_get(); + * 2. Some function calls intel_dmc_wl_disable(); + * 3. Some function calls intel_dmc_wl_enable(); + * 4. Concurrently with (3), function A performs the MMIO in between + * setting DMC_WAKELOCK_CFG_ENABLE and asserting the lock with + * __intel_dmc_wl_take(). + * + * TODO: Check with the hardware team whether it is safe to assert the + * hardware lock before enabling to avoid such a scenario. Otherwise, we + * would need to deal with it via software synchronization. + */ + if (refcount_read(&wl->refcount)) + __intel_dmc_wl_take(display); out_unlock: spin_unlock_irqrestore(&wl->lock, flags); @@ -299,8 +346,18 @@ void intel_dmc_wl_disable(struct intel_display *display) /* Disable wakelock in DMC */ __intel_de_rmw_nowl(display, DMC_WAKELOCK_CFG, DMC_WAKELOCK_CFG_ENABLE, 0); - refcount_set(&wl->refcount, 0); wl->enabled = false; + + /* + * The spec is not explicit about the expectation of existing + * lock users at the moment of disabling, but it does say that we must + * clear DMC_WAKELOCK_CTL_REQ, which gives us a clue that it is okay to + * disable with existing lock users. + * + * TODO: Get the correct expectation from the hardware team. + */ + __intel_de_rmw_nowl(display, DMC_WAKELOCK1_CTL, DMC_WAKELOCK_CTL_REQ, 0); + wl->taken = false; out_unlock: @@ -320,8 +377,11 @@ void intel_dmc_wl_get(struct intel_display *display, i915_reg_t reg) if (i915_mmio_reg_valid(reg) && !intel_dmc_wl_check_range(reg, wl->dc_state)) goto out_unlock; - if (!wl->enabled) + if (!wl->enabled) { + if (!refcount_inc_not_zero(&wl->refcount)) + refcount_set(&wl->refcount, 1); goto out_unlock; + } cancel_delayed_work(&wl->work); @@ -330,30 +390,7 @@ void intel_dmc_wl_get(struct intel_display *display, i915_reg_t reg) refcount_set(&wl->refcount, 1); - /* - * Only try to take the wakelock if it's not marked as taken - * yet. It may be already taken at this point if we have - * already released the last reference, but the work has not - * run yet. - */ - if (!wl->taken) { - __intel_de_rmw_nowl(display, DMC_WAKELOCK1_CTL, 0, - DMC_WAKELOCK_CTL_REQ); - - /* - * We need to use the atomic variant of the waiting routine - * because the DMC wakelock is also taken in atomic context. - */ - if (__intel_de_wait_for_register_atomic_nowl(display, DMC_WAKELOCK1_CTL, - DMC_WAKELOCK_CTL_ACK, - DMC_WAKELOCK_CTL_ACK, - DMC_WAKELOCK_CTL_TIMEOUT_US)) { - WARN_RATELIMIT(1, "DMC wakelock ack timed out"); - goto out_unlock; - } - - wl->taken = true; - } + __intel_dmc_wl_take(display); out_unlock: spin_unlock_irqrestore(&wl->lock, flags); @@ -372,14 +409,14 @@ void intel_dmc_wl_put(struct intel_display *display, i915_reg_t reg) if (i915_mmio_reg_valid(reg) && !intel_dmc_wl_check_range(reg, wl->dc_state)) goto out_unlock; - if (!wl->enabled) - goto out_unlock; - if (WARN_RATELIMIT(!refcount_read(&wl->refcount), "Tried to put wakelock with refcount zero\n")) goto out_unlock; if (refcount_dec_and_test(&wl->refcount)) { + if (!wl->enabled) + goto out_unlock; + __intel_dmc_wl_release(display); goto out_unlock; -- 2.51.0 From 5a83381fc4715e885e4951e76b4c094bda47d16a Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Fri, 8 Nov 2024 09:57:17 -0300 Subject: [PATCH 07/16] drm/i915/dmc_wl: Couple enable/disable with dynamic DC states Enabling and disabling the DMC wakelock should be done as part of enabling and disabling of dynamic DC states, respectively. We should not enable or disable DMC wakelock independently of DC states, otherwise we would risk ending up with an inconsistent state where dynamic DC states are enabled and the DMC wakelock is disabled, going against current recommendations and making MMIO transactions potentially slower. In future display IPs that could have a worse outcome if DMC trap implementation is completely removed. So, let's make things safer by tying stuff together, removing the independent calls, and also put warnings in place to detect inconsistent calls. Reviewed-by: Luca Coelho Signed-off-by: Gustavo Sousa Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20241108130218.24125-13-gustavo.sousa@intel.com --- drivers/gpu/drm/i915/display/intel_display_power_well.c | 5 ++++- drivers/gpu/drm/i915/display/intel_dmc.c | 4 ---- drivers/gpu/drm/i915/display/intel_dmc_wl.c | 6 ++++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.c b/drivers/gpu/drm/i915/display/intel_display_power_well.c index 578959ff2d75..bdf6c690a03b 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -988,6 +988,7 @@ void gen9_disable_dc_states(struct intel_display *display) struct drm_i915_private *dev_priv = to_i915(display->drm); struct i915_power_domains *power_domains = &display->power.domains; struct intel_cdclk_config cdclk_config = {}; + u32 old_state = power_domains->dc_state; if (power_domains->target_dc_state == DC_STATE_EN_DC3CO) { tgl_disable_dc3co(display); @@ -1003,7 +1004,9 @@ void gen9_disable_dc_states(struct intel_display *display) return; } - intel_dmc_wl_disable(display); + if (old_state == DC_STATE_EN_UPTO_DC5 || + old_state == DC_STATE_EN_UPTO_DC6) + intel_dmc_wl_disable(display); intel_cdclk_get_cdclk(display, &cdclk_config); /* Can't read out voltage_level so can't use intel_cdclk_changed() */ diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 87bdacfd9edf..221d3abda791 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -638,8 +638,6 @@ void intel_dmc_disable_program(struct intel_display *display) pipedmc_clock_gating_wa(display, true); disable_all_event_handlers(display); pipedmc_clock_gating_wa(display, false); - - intel_dmc_wl_disable(display); } void assert_dmc_loaded(struct intel_display *display) @@ -1146,8 +1144,6 @@ void intel_dmc_suspend(struct intel_display *display) if (dmc) flush_work(&dmc->work); - intel_dmc_wl_disable(display); - /* Drop the reference held in case DMC isn't loaded. */ if (!intel_dmc_has_payload(display)) intel_dmc_runtime_pm_put(display); diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c index b8887216a684..f2d64954916a 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c @@ -283,6 +283,7 @@ void intel_dmc_wl_init(struct intel_display *display) refcount_set(&wl->refcount, 0); } +/* Must only be called as part of enabling dynamic DC states. */ void intel_dmc_wl_enable(struct intel_display *display, u32 dc_state) { struct intel_dmc_wl *wl = &display->wl; @@ -295,7 +296,7 @@ void intel_dmc_wl_enable(struct intel_display *display, u32 dc_state) wl->dc_state = dc_state; - if (wl->enabled) + if (drm_WARN_ON(display->drm, wl->enabled)) goto out_unlock; /* @@ -328,6 +329,7 @@ out_unlock: spin_unlock_irqrestore(&wl->lock, flags); } +/* Must only be called as part of disabling dynamic DC states. */ void intel_dmc_wl_disable(struct intel_display *display) { struct intel_dmc_wl *wl = &display->wl; @@ -340,7 +342,7 @@ void intel_dmc_wl_disable(struct intel_display *display) spin_lock_irqsave(&wl->lock, flags); - if (!wl->enabled) + if (drm_WARN_ON(display->drm, !wl->enabled)) goto out_unlock; /* Disable wakelock in DMC */ -- 2.51.0 From c92ae71c1d06580395a230d78049ab59259e9ff1 Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Fri, 8 Nov 2024 09:57:18 -0300 Subject: [PATCH 08/16] drm/i915/dmc_wl: Add and use HAS_DMC_WAKELOCK() A HAS_DMC_WAKELOCK() macro gives more semantic than openly checking the display version. Define it and use it where appropriate. v2: - Make this patch contain only the non-functional refactor. Functional changes related to including HAS_DMC() in the macro are done in upcoming changes. (Jani) Cc: Jani Nikula Reviewed-by: Luca Coelho Signed-off-by: Gustavo Sousa Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20241108130218.24125-14-gustavo.sousa@intel.com --- drivers/gpu/drm/i915/display/intel_display_device.h | 1 + drivers/gpu/drm/i915/display/intel_dmc_wl.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_device.h b/drivers/gpu/drm/i915/display/intel_display_device.h index 14d16d111ae3..a8a0b4332247 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.h +++ b/drivers/gpu/drm/i915/display/intel_display_device.h @@ -147,6 +147,7 @@ struct intel_display_platforms { #define HAS_DDI(i915) (DISPLAY_INFO(i915)->has_ddi) #define HAS_DISPLAY(i915) (DISPLAY_RUNTIME_INFO(i915)->pipe_mask != 0) #define HAS_DMC(i915) (DISPLAY_RUNTIME_INFO(i915)->has_dmc) +#define HAS_DMC_WAKELOCK(i915) (DISPLAY_VER(i915) >= 20) #define HAS_DOUBLE_BUFFERED_M_N(i915) (DISPLAY_VER(i915) >= 9 || IS_BROADWELL(i915)) #define HAS_DOUBLE_WIDE(i915) (DISPLAY_VER(i915) < 4) #define HAS_DP_MST(i915) (DISPLAY_INFO(i915)->has_dp_mst) diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c index f2d64954916a..4ca2b990ec6a 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c @@ -262,7 +262,7 @@ static bool intel_dmc_wl_check_range(i915_reg_t reg, u32 dc_state) static bool __intel_dmc_wl_supported(struct intel_display *display) { - if (DISPLAY_VER(display) < 20 || + if (!HAS_DMC_WAKELOCK(display) || !intel_dmc_has_payload(display) || !display->params.enable_dmc_wl) return false; @@ -275,7 +275,7 @@ void intel_dmc_wl_init(struct intel_display *display) struct intel_dmc_wl *wl = &display->wl; /* don't call __intel_dmc_wl_supported(), DMC is not loaded yet */ - if (DISPLAY_VER(display) < 20 || !display->params.enable_dmc_wl) + if (!HAS_DMC_WAKELOCK(display) || !display->params.enable_dmc_wl) return; INIT_DELAYED_WORK(&wl->work, intel_dmc_wl_work); -- 2.51.0 From c01e78a96e125b08bb8103d2737546dd4eb2ce00 Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Fri, 8 Nov 2024 09:57:19 -0300 Subject: [PATCH 09/16] drm/i915/dmc_wl: Sanitize enable_dmc_wl according to hardware support Instead of checking for HAS_DMC_WAKELOCK() multiple times, let's use it to sanitize the enable_dmc_wl parameter and use that variable when necessary. Reviewed-by: Luca Coelho Signed-off-by: Gustavo Sousa Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20241108130218.24125-15-gustavo.sousa@intel.com --- drivers/gpu/drm/i915/display/intel_dmc_wl.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c index 4ca2b990ec6a..c164ac6e1ada 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c @@ -5,6 +5,8 @@ #include +#include + #include "i915_reg.h" #include "intel_de.h" #include "intel_dmc.h" @@ -262,20 +264,25 @@ static bool intel_dmc_wl_check_range(i915_reg_t reg, u32 dc_state) static bool __intel_dmc_wl_supported(struct intel_display *display) { - if (!HAS_DMC_WAKELOCK(display) || - !intel_dmc_has_payload(display) || - !display->params.enable_dmc_wl) - return false; + return display->params.enable_dmc_wl && intel_dmc_has_payload(display); +} - return true; +static void intel_dmc_wl_sanitize_param(struct intel_display *display) +{ + if (!HAS_DMC_WAKELOCK(display)) + display->params.enable_dmc_wl = false; + + drm_dbg_kms(display->drm, "Sanitized enable_dmc_wl value: %d\n", + display->params.enable_dmc_wl); } void intel_dmc_wl_init(struct intel_display *display) { struct intel_dmc_wl *wl = &display->wl; - /* don't call __intel_dmc_wl_supported(), DMC is not loaded yet */ - if (!HAS_DMC_WAKELOCK(display) || !display->params.enable_dmc_wl) + intel_dmc_wl_sanitize_param(display); + + if (!display->params.enable_dmc_wl) return; INIT_DELAYED_WORK(&wl->work, intel_dmc_wl_work); -- 2.51.0 From 34796ce4b2a11f29aa764ad67fe0fc028c86756a Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Fri, 8 Nov 2024 09:57:20 -0300 Subject: [PATCH 10/16] drm/i915/xe3lpd: Use DMC wakelock by default Although Bspec doesn't explicitly mentions that, as of Xe3_LPD, using DMC wakelock is the officially recommended way of accessing registers that would be off during DC5/DC6 and the legacy method (where the DMC intercepts MMIO to wake up the hardware) is to be avoided. As such, update the driver to use the DMC wakelock by default starting with Xe3_LPD. Since the feature is somewhat new to the driver, also allow disabling it via a module parameter for debugging purposes. For that, make the existing parameter allow values -1 (per-chip default), 0 (disabled) and 1 (enabled), similarly to what is done for other parameters. v2: - Describe -1 in the same area where 0 and 1 are described. (Luca) Reviewed-by: Luca Coelho Signed-off-by: Gustavo Sousa Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20241108130218.24125-16-gustavo.sousa@intel.com --- drivers/gpu/drm/i915/display/intel_display_params.c | 6 +++--- drivers/gpu/drm/i915/display/intel_display_params.h | 2 +- drivers/gpu/drm/i915/display/intel_dmc_wl.c | 6 +++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_params.c b/drivers/gpu/drm/i915/display/intel_display_params.c index 024de8abcb1a..dc666aefa362 100644 --- a/drivers/gpu/drm/i915/display/intel_display_params.c +++ b/drivers/gpu/drm/i915/display/intel_display_params.c @@ -123,10 +123,10 @@ intel_display_param_named_unsafe(enable_psr2_sel_fetch, bool, 0400, "(0=disabled, 1=enabled) " "Default: 1"); -intel_display_param_named_unsafe(enable_dmc_wl, bool, 0400, +intel_display_param_named_unsafe(enable_dmc_wl, int, 0400, "Enable DMC wakelock " - "(0=disabled, 1=enabled) " - "Default: 0"); + "(-1=use per-chip default, 0=disabled, 1=enabled) " + "Default: -1"); __maybe_unused static void _param_print_bool(struct drm_printer *p, const char *driver_name, diff --git a/drivers/gpu/drm/i915/display/intel_display_params.h b/drivers/gpu/drm/i915/display/intel_display_params.h index dcb6face936a..5317138e6044 100644 --- a/drivers/gpu/drm/i915/display/intel_display_params.h +++ b/drivers/gpu/drm/i915/display/intel_display_params.h @@ -47,7 +47,7 @@ struct drm_printer; param(int, enable_psr, -1, 0600) \ param(bool, psr_safest_params, false, 0400) \ param(bool, enable_psr2_sel_fetch, true, 0400) \ - param(bool, enable_dmc_wl, false, 0400) \ + param(int, enable_dmc_wl, -1, 0400) \ #define MEMBER(T, member, ...) T member; struct intel_display_params { diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c index c164ac6e1ada..853d75610489 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c @@ -270,7 +270,11 @@ static bool __intel_dmc_wl_supported(struct intel_display *display) static void intel_dmc_wl_sanitize_param(struct intel_display *display) { if (!HAS_DMC_WAKELOCK(display)) - display->params.enable_dmc_wl = false; + display->params.enable_dmc_wl = 0; + else if (display->params.enable_dmc_wl >= 0) + display->params.enable_dmc_wl = !!display->params.enable_dmc_wl; + else + display->params.enable_dmc_wl = DISPLAY_VER(display) >= 30; drm_dbg_kms(display->drm, "Sanitized enable_dmc_wl value: %d\n", display->params.enable_dmc_wl); -- 2.51.0 From b08d1274e3fe70eb3e9b13e8af835032bf792f7e Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 6 Nov 2024 18:23:25 +0200 Subject: [PATCH 11/16] drm/i915/dp: demote source OUI read/write failure logging to debug Commit 1f12d63a14d7 ("drm/i915/dp: Write the source OUI for non-eDP sinks as well") started writing source OUI for non-eDP sinks as well, increasing the possibilities of hitting read/write failures either due to the sink behaviour or hotplug or whatever. Even before that, commit 3fb0501f0c07 ("drm/i915/display/dp: Reduce log level for SOURCE OUI write failures") already reduced write failures to info level when source OUI was just for eDP. Further reduce the log level to just debug. Switch to struct intel_display while at it. Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/3372 Cc: Clint Taylor Cc: Imre Deak Reviewed-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20241106162325.4065078-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_dp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 5b918363df16..95c71e425fbe 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3432,7 +3432,7 @@ void intel_dp_sink_disable_decompression(struct intel_atomic_state *state, static void intel_dp_init_source_oui(struct intel_dp *intel_dp) { - struct drm_i915_private *i915 = dp_to_i915(intel_dp); + struct intel_display *display = to_intel_display(intel_dp); u8 oui[] = { 0x00, 0xaa, 0x01 }; u8 buf[3] = {}; @@ -3446,7 +3446,7 @@ intel_dp_init_source_oui(struct intel_dp *intel_dp) * already set to what we want, so as to avoid clearing any state by accident */ if (drm_dp_dpcd_read(&intel_dp->aux, DP_SOURCE_OUI, buf, sizeof(buf)) < 0) - drm_err(&i915->drm, "Failed to read source OUI\n"); + drm_dbg_kms(display->drm, "Failed to read source OUI\n"); if (memcmp(oui, buf, sizeof(oui)) == 0) { /* Assume the OUI was written now. */ @@ -3455,7 +3455,7 @@ intel_dp_init_source_oui(struct intel_dp *intel_dp) } if (drm_dp_dpcd_write(&intel_dp->aux, DP_SOURCE_OUI, oui, sizeof(oui)) < 0) { - drm_info(&i915->drm, "Failed to write source OUI\n"); + drm_dbg_kms(display->drm, "Failed to write source OUI\n"); WRITE_ONCE(intel_dp->oui_valid, false); } -- 2.51.0 From 16806984572a2f82a51e46a92ceeabefe6c06943 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 29 Oct 2024 13:32:48 +0200 Subject: [PATCH 12/16] drm/i915/psr: add LATENCY_REPORTING_REMOVED() register bit helper MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Drop the wa_16013835468_bit_get() function in favour of the register macro. It doesn't have to be so complicated, and we don't have to use the workaround name in everything that's related to it. Cc: Jouni Högander Cc: Suraj Kandpal Reviewed-by: Jouni Högander Link: https://patchwork.freedesktop.org/patch/msgid/22934fee1ea37c777c35e4b520d5f11b6cd953d0.1730201504.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_psr.c | 24 ++++-------------------- drivers/gpu/drm/i915/i915_reg.h | 13 +++++++++---- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index ed5b4d110fba..6b7b154e67b5 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1772,23 +1772,6 @@ static void intel_psr_activate(struct intel_dp *intel_dp) intel_dp->psr.active = true; } -static u32 wa_16013835468_bit_get(struct intel_dp *intel_dp) -{ - switch (intel_dp->psr.pipe) { - case PIPE_A: - return LATENCY_REPORTING_REMOVED_PIPE_A; - case PIPE_B: - return LATENCY_REPORTING_REMOVED_PIPE_B; - case PIPE_C: - return LATENCY_REPORTING_REMOVED_PIPE_C; - case PIPE_D: - return LATENCY_REPORTING_REMOVED_PIPE_D; - default: - MISSING_CASE(intel_dp->psr.pipe); - return 0; - } -} - /* * Wa_16013835468 * Wa_14015648006 @@ -1797,6 +1780,7 @@ static void wm_optimization_wa(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(intel_dp); + enum pipe pipe = intel_dp->psr.pipe; bool set_wa_bit = false; /* Wa_14015648006 */ @@ -1810,10 +1794,10 @@ static void wm_optimization_wa(struct intel_dp *intel_dp, if (set_wa_bit) intel_de_rmw(display, GEN8_CHICKEN_DCPR_1, - 0, wa_16013835468_bit_get(intel_dp)); + 0, LATENCY_REPORTING_REMOVED(pipe)); else intel_de_rmw(display, GEN8_CHICKEN_DCPR_1, - wa_16013835468_bit_get(intel_dp), 0); + LATENCY_REPORTING_REMOVED(pipe), 0); } static void intel_psr_enable_source(struct intel_dp *intel_dp, @@ -2113,7 +2097,7 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp) */ if (DISPLAY_VER(display) >= 11) intel_de_rmw(display, GEN8_CHICKEN_DCPR_1, - wa_16013835468_bit_get(intel_dp), 0); + LATENCY_REPORTING_REMOVED(intel_dp->psr.pipe), 0); if (intel_dp->psr.sel_update_enabled) { /* Wa_16012604467:adlp,mtl[a0,b0] */ diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index c160e087972a..6b9d8d2cb722 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2863,11 +2863,16 @@ #define RESET_PCH_HANDSHAKE_ENABLE REG_BIT(4) #define GEN8_CHICKEN_DCPR_1 _MMIO(0x46430) -#define LATENCY_REPORTING_REMOVED_PIPE_D REG_BIT(31) +#define _LATENCY_REPORTING_REMOVED_PIPE_D REG_BIT(31) #define SKL_SELECT_ALTERNATE_DC_EXIT REG_BIT(30) -#define LATENCY_REPORTING_REMOVED_PIPE_C REG_BIT(25) -#define LATENCY_REPORTING_REMOVED_PIPE_B REG_BIT(24) -#define LATENCY_REPORTING_REMOVED_PIPE_A REG_BIT(23) +#define _LATENCY_REPORTING_REMOVED_PIPE_C REG_BIT(25) +#define _LATENCY_REPORTING_REMOVED_PIPE_B REG_BIT(24) +#define _LATENCY_REPORTING_REMOVED_PIPE_A REG_BIT(23) +#define LATENCY_REPORTING_REMOVED(pipe) _PICK((pipe), \ + _LATENCY_REPORTING_REMOVED_PIPE_A, \ + _LATENCY_REPORTING_REMOVED_PIPE_B, \ + _LATENCY_REPORTING_REMOVED_PIPE_C, \ + _LATENCY_REPORTING_REMOVED_PIPE_D) #define ICL_DELAY_PMRSP REG_BIT(22) #define DISABLE_FLR_SRC REG_BIT(15) #define MASK_WAKEMEM REG_BIT(13) -- 2.51.0 From 87d052bfe6ebd7da995297170a23546624d06fa2 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 29 Oct 2024 13:32:49 +0200 Subject: [PATCH 13/16] drm/i915/psr: stop using bitwise OR with booleans in wm_optimization_wa() MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Bitwise OR for booleans is a bit of an abuse. We can trivially drop the usage. Cc: Jouni Högander Cc: Suraj Kandpal Reviewed-by: Jouni Högander Link: https://patchwork.freedesktop.org/patch/msgid/f7ea5854c9a306f56f4142f8d37d567ee2f768a7.1730201504.git.jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_psr.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 6b7b154e67b5..16888935b33a 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1781,18 +1781,19 @@ static void wm_optimization_wa(struct intel_dp *intel_dp, { struct intel_display *display = to_intel_display(intel_dp); enum pipe pipe = intel_dp->psr.pipe; - bool set_wa_bit = false; + bool activate = false; /* Wa_14015648006 */ - if (IS_DISPLAY_VER(display, 11, 14)) - set_wa_bit |= crtc_state->wm_level_disabled; + if (IS_DISPLAY_VER(display, 11, 14) && crtc_state->wm_level_disabled) + activate = true; /* Wa_16013835468 */ - if (DISPLAY_VER(display) == 12) - set_wa_bit |= crtc_state->hw.adjusted_mode.crtc_vblank_start != - crtc_state->hw.adjusted_mode.crtc_vdisplay; + if (DISPLAY_VER(display) == 12 && + crtc_state->hw.adjusted_mode.crtc_vblank_start != + crtc_state->hw.adjusted_mode.crtc_vdisplay) + activate = true; - if (set_wa_bit) + if (activate) intel_de_rmw(display, GEN8_CHICKEN_DCPR_1, 0, LATENCY_REPORTING_REMOVED(pipe)); else -- 2.51.0 From dc3806d9eb66d0105f8d55d462d4ef681d9eac59 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 7 Nov 2024 18:11:14 +0200 Subject: [PATCH 14/16] drm/i915: Grab intel_display from the encoder to avoid potential oopsies MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Grab the intel_display from 'encoder' rather than 'state' in the encoder hooks to avoid the massive footgun that is intel_sanitize_encoder(), which passes NULL as the 'state' argument to encoder .disable() and .post_disable(). TODO: figure out how to actually fix intel_sanitize_encoder()... Fixes: 40eb34c3f491 ("drm/i915/crt: convert to struct intel_display") Fixes: ab0b0eb5c85c ("drm/i915/tv: convert to struct intel_display") Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20241107161123.16269-2-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_crt.c | 10 +++++----- drivers/gpu/drm/i915/display/intel_tv.c | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c index 74c1983fe07e..1be55bdb48b9 100644 --- a/drivers/gpu/drm/i915/display/intel_crt.c +++ b/drivers/gpu/drm/i915/display/intel_crt.c @@ -244,7 +244,7 @@ static void hsw_disable_crt(struct intel_atomic_state *state, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { - struct intel_display *display = to_intel_display(state); + struct intel_display *display = to_intel_display(encoder); struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); drm_WARN_ON(display->drm, !old_crtc_state->has_pch_encoder); @@ -257,7 +257,7 @@ static void hsw_post_disable_crt(struct intel_atomic_state *state, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { - struct intel_display *display = to_intel_display(state); + struct intel_display *display = to_intel_display(encoder); struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); @@ -287,7 +287,7 @@ static void hsw_pre_pll_enable_crt(struct intel_atomic_state *state, const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state) { - struct intel_display *display = to_intel_display(state); + struct intel_display *display = to_intel_display(encoder); struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); drm_WARN_ON(display->drm, !crtc_state->has_pch_encoder); @@ -300,7 +300,7 @@ static void hsw_pre_enable_crt(struct intel_atomic_state *state, const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state) { - struct intel_display *display = to_intel_display(state); + struct intel_display *display = to_intel_display(encoder); struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum pipe pipe = crtc->pipe; @@ -319,7 +319,7 @@ static void hsw_enable_crt(struct intel_atomic_state *state, const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state) { - struct intel_display *display = to_intel_display(state); + struct intel_display *display = to_intel_display(encoder); struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum pipe pipe = crtc->pipe; diff --git a/drivers/gpu/drm/i915/display/intel_tv.c b/drivers/gpu/drm/i915/display/intel_tv.c index bfd16054ca05..27c530218ee6 100644 --- a/drivers/gpu/drm/i915/display/intel_tv.c +++ b/drivers/gpu/drm/i915/display/intel_tv.c @@ -929,7 +929,7 @@ intel_enable_tv(struct intel_atomic_state *state, const struct intel_crtc_state *pipe_config, const struct drm_connector_state *conn_state) { - struct intel_display *display = to_intel_display(state); + struct intel_display *display = to_intel_display(encoder); /* Prevents vblank waits from timing out in intel_tv_detect_type() */ intel_crtc_wait_for_next_vblank(to_intel_crtc(pipe_config->uapi.crtc)); @@ -943,7 +943,7 @@ intel_disable_tv(struct intel_atomic_state *state, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { - struct intel_display *display = to_intel_display(state); + struct intel_display *display = to_intel_display(encoder); intel_de_rmw(display, TV_CTL, TV_ENC_ENABLE, 0); } -- 2.51.0 From 585abd0002bc1f9b79e3fc030254402a16e8b922 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 7 Nov 2024 18:11:15 +0200 Subject: [PATCH 15/16] drm/i915/crt: Split long line MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Split an overly long line in the CRT code. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20241107161123.16269-3-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_crt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c index 1be55bdb48b9..134a4e6a4ac0 100644 --- a/drivers/gpu/drm/i915/display/intel_crt.c +++ b/drivers/gpu/drm/i915/display/intel_crt.c @@ -1047,7 +1047,9 @@ void intel_crt_init(struct intel_display *display) * it and see what happens. */ intel_de_write(display, adpa_reg, - adpa | ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); + adpa | ADPA_DAC_ENABLE | + ADPA_HSYNC_CNTL_DISABLE | + ADPA_VSYNC_CNTL_DISABLE); if ((intel_de_read(display, adpa_reg) & ADPA_DAC_ENABLE) == 0) return; intel_de_write(display, adpa_reg, adpa); -- 2.51.0 From 0e94cd606f7400a26d5994de05ff967c76e3ac1d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 7 Nov 2024 18:11:16 +0200 Subject: [PATCH 16/16] drm/i915/crt: Drop the unused ADPA_DPMS bit definitions MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The ADPA_DPMS bit definitions are just an alias for the sync disable bits, and unused one at that. Drop the pointless definitions. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20241107161123.16269-4-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/i915_reg.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 6b9d8d2cb722..03638302e909 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1188,11 +1188,6 @@ #define ADPA_VSYNC_ACTIVE_LOW 0 #define ADPA_HSYNC_ACTIVE_HIGH (1 << 3) #define ADPA_HSYNC_ACTIVE_LOW 0 -#define ADPA_DPMS_MASK (~(3 << 10)) -#define ADPA_DPMS_ON (0 << 10) -#define ADPA_DPMS_SUSPEND (1 << 10) -#define ADPA_DPMS_STANDBY (2 << 10) -#define ADPA_DPMS_OFF (3 << 10) /* Hotplug control (945+ only) */ #define PORT_HOTPLUG_EN(dev_priv) _MMIO(DISPLAY_MMIO_BASE(dev_priv) + 0x61110) -- 2.51.0