/** * DOC: DMC wakelock support * * Wake lock is the mechanism to cause display engine to exit DC * states to allow programming to registers that are powered down in * those states. Previous projects exited DC states automatically when * detecting programming. Now software controls the exit by * programming the wake lock. This improves system performance and * system interactions and better fits the flip queue style of * programming. Wake lock is only required when DC5, DC6, or DC6v have * been enabled in DC_STATE_EN and the wake lock mode of operation has * been enabled. * * The wakelock mechanism in DMC allows the display engine to exit DC * states explicitly before programming registers that may be powered * down. In earlier hardware, this was done automatically and * implicitly when the display engine accessed a register. With the * wakelock implementation, the driver asserts a wakelock in DMC, * which forces it to exit the DC state until the wakelock is * deasserted. * * The mechanism can be enabled and disabled by writing to the * DMC_WAKELOCK_CFG register. There are also 13 control registers * that can be used to hold and release different wakelocks. In the * current implementation, we only need one wakelock, so only * DMC_WAKELOCK1_CTL is used. The other definitions are here for * potential future use.
*/
/* * Define DMC_WAKELOCK_CTL_TIMEOUT_US in microseconds because we use the * atomic variant of waiting MMIO.
*/ #define DMC_WAKELOCK_CTL_TIMEOUT_US 5000 #define DMC_WAKELOCK_HOLD_TIME 50
/* * Possible non-negative values for the enable_dmc_wl param.
*/ enum {
ENABLE_DMC_WL_DISABLED,
ENABLE_DMC_WL_ENABLED,
ENABLE_DMC_WL_ANY_REGISTER,
ENABLE_DMC_WL_ALWAYS_LOCKED,
ENABLE_DMC_WL_MAX,
};
/* * Bail out if refcount became non-zero while waiting for the spinlock, * meaning that the lock is now taken again.
*/ if (refcount_read(&wl->refcount)) goto out_unlock;
/* * 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;
/* * 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;
}
if (display->params.enable_dmc_wl == ENABLE_DMC_WL_ANY_REGISTER) returntrue;
/* * Check that the offset is in one of the ranges for which * registers are powered off during DC states.
*/ if (intel_dmc_wl_reg_in_range(reg, powered_off_ranges)) returntrue;
/* * 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)) returntrue;
/* 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; unsignedlong flags;
if (!__intel_dmc_wl_supported(display)) return;
spin_lock_irqsave(&wl->lock, flags);
wl->dc_state = dc_state;
if (drm_WARN_ON(display->drm, wl->enabled)) goto out_unlock;
/* * 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_nowl(display, DMC_WAKELOCK_CFG, 0, DMC_WAKELOCK_CFG_ENABLE);
wl->enabled = true;
/* * 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);
/* 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; unsignedlong flags;
if (!__intel_dmc_wl_supported(display)) return;
intel_dmc_wl_flush_release_work(display);
spin_lock_irqsave(&wl->lock, flags);
if (drm_WARN_ON(display->drm, !wl->enabled)) goto out_unlock;
/* Disable wakelock in DMC */
__intel_de_rmw_nowl(display, DMC_WAKELOCK_CFG, DMC_WAKELOCK_CFG_ENABLE, 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);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.